Add healtcheck + some fixes

- better docs
- add healtcheck based on docker-compoe commands or labels
- fix some problems on secret names
- better dependency check
This commit is contained in:
2022-02-14 14:37:09 +01:00
parent 8164603b47
commit 5a4d9e396d
7 changed files with 395 additions and 209 deletions

View File

@@ -15,6 +15,15 @@ func NewCompose() *Compose {
return c return c
} }
// HealthCheck manage generic type to handle TCP, HTTP and TCP health check.
type HealthCheck struct {
Test []string `yaml:"test"`
Interval string `yaml:"interval"`
Timeout string `yaml:"timeout"`
Retries int `yaml:"retries"`
StartPeriod string `yaml:"start_period"`
}
// Service represent a "service" in a docker-compose file. // Service represent a "service" in a docker-compose file.
type Service struct { type Service struct {
Image string `yaml:"image"` Image string `yaml:"image"`
@@ -26,4 +35,5 @@ type Service struct {
Expose []int `yaml:"expose"` Expose []int `yaml:"expose"`
EnvFiles []string `yaml:"env_file"` EnvFiles []string `yaml:"env_file"`
RawBuild interface{} `yaml:"build"` RawBuild interface{} `yaml:"build"`
HealthCheck *HealthCheck `yaml:"healthcheck"`
} }

View File

@@ -6,6 +6,7 @@ import (
"katenary/compose" "katenary/compose"
"katenary/helm" "katenary/helm"
"log" "log"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
@@ -14,6 +15,8 @@ import (
"time" "time"
"errors" "errors"
"github.com/google/shlex"
) )
var servicesMap = make(map[string]int) var servicesMap = make(map[string]int)
@@ -42,8 +45,7 @@ OK=0
echo "Checking __service__ port" echo "Checking __service__ port"
while [ $OK != 1 ]; do while [ $OK != 1 ]; do
echo -n "." echo -n "."
nc -z ` + RELEASE_NAME + `-__service__ __port__ && OK=1 nc -z ` + RELEASE_NAME + `-__service__ __port__ 2>&1 >/dev/null && OK=1 || sleep 1
sleep 1
done done
echo echo
echo "Done" echo "Done"
@@ -63,53 +65,8 @@ func parseService(name string, s *compose.Service, ret chan interface{}) {
o := helm.NewDeployment(name) o := helm.NewDeployment(name)
container := helm.NewContainer(name, s.Image, s.Environment, s.Labels) container := helm.NewContainer(name, s.Image, s.Environment, s.Labels)
// prepare secrets // prepare cm and secrets
secretsFiles := make([]string, 0) prepareEnvFromFiles(name, s, container, ret)
if v, ok := s.Labels[helm.LABEL_ENV_SECRET]; ok {
secretsFiles = strings.Split(v, ",")
}
// manage environment files (env_file in compose)
for _, envfile := range s.EnvFiles {
f := strings.ReplaceAll(envfile, "_", "-")
f = strings.ReplaceAll(f, ".env", "")
f = strings.ReplaceAll(f, ".", "")
f = strings.ReplaceAll(f, "/", "")
cf := f + "-" + name
isSecret := false
for _, s := range secretsFiles {
if s == envfile {
isSecret = true
}
}
var store helm.InlineConfig
if !isSecret {
Bluef(ICON_CONF+" Generating configMap %s\n", cf)
store = helm.NewConfigMap(cf)
} else {
Bluef(ICON_SECRET+" Generating secret %s\n", cf)
store = helm.NewSecret(cf)
}
if err := store.AddEnvFile(envfile); err != nil {
ActivateColors = true
Red(err.Error())
ActivateColors = false
os.Exit(2)
}
section := "configMapRef"
if isSecret {
section = "secretRef"
}
container.EnvFrom = append(container.EnvFrom, map[string]map[string]string{
section: {
"name": store.Metadata().Name,
},
})
ret <- store
}
// check the image, and make it "variable" in values.yaml // check the image, and make it "variable" in values.yaml
container.Image = "{{ .Values." + name + ".image }}" container.Image = "{{ .Values." + name + ".image }}"
@@ -117,164 +74,30 @@ func parseService(name string, s *compose.Service, ret chan interface{}) {
"image": s.Image, "image": s.Image,
} }
// manage the healthcheck property, if any
prepareProbes(name, s, container)
// manage ports // manage ports
exists := make(map[int]string) generateContainerPorts(s, name, container)
for _, port := range s.Ports {
_p := strings.Split(port, ":")
port = _p[0]
if len(_p) > 1 {
port = _p[1]
}
portNumber, _ := strconv.Atoi(port)
portName := name
for _, n := range exists {
if name == n {
portName = fmt.Sprintf("%s-%d", name, portNumber)
}
}
container.Ports = append(container.Ports, &helm.ContainerPort{
Name: portName,
ContainerPort: portNumber,
})
exists[portNumber] = name
}
// manage the "expose" section to be a NodePort in Kubernetes // Set the container to the deployment
for _, port := range s.Expose {
if _, exist := exists[port]; exist {
continue
}
container.Ports = append(container.Ports, &helm.ContainerPort{
Name: name,
ContainerPort: port,
})
}
// Prepare volumes
volumes := make([]map[string]interface{}, 0)
mountPoints := make([]interface{}, 0)
configMapsVolumes := make([]string, 0)
if v, ok := s.Labels[helm.LABEL_VOL_CM]; ok {
configMapsVolumes = strings.Split(v, ",")
}
for _, volume := range s.Volumes {
parts := strings.Split(volume, ":")
volname := parts[0]
volepath := parts[1]
isCM := false
for _, cmVol := range configMapsVolumes {
cmVol = strings.TrimSpace(cmVol)
if volname == cmVol {
isCM = true
break
}
}
if !isCM && (strings.HasPrefix(volname, ".") || strings.HasPrefix(volname, "/")) {
// local volume cannt be mounted
ActivateColors = true
Redf("You cannot, at this time, have local volume in %s deployment\n", name)
ActivateColors = false
continue
}
if isCM {
// the volume is a path and it's explicitally asked to be a configmap in labels
cm := buildCMFromPath(volname)
volname = strings.Replace(volname, "./", "", 1)
volname = strings.ReplaceAll(volname, ".", "-")
cm.K8sBase.Metadata.Name = RELEASE_NAME + "-" + volname + "-" + name
// build a configmap from the volume path
volumes = append(volumes, map[string]interface{}{
"name": volname,
"configMap": map[string]string{
"name": cm.K8sBase.Metadata.Name,
},
})
mountPoints = append(mountPoints, map[string]interface{}{
"name": volname,
"mountPath": volepath,
})
ret <- cm
} else {
// rmove minus sign from volume name
volname = strings.ReplaceAll(volname, "-", "")
pvc := helm.NewPVC(name, volname)
volumes = append(volumes, map[string]interface{}{
"name": volname,
"persistentVolumeClaim": map[string]string{
"claimName": RELEASE_NAME + "-" + volname,
},
})
mountPoints = append(mountPoints, map[string]interface{}{
"name": volname,
"mountPath": volepath,
})
Yellow(ICON_STORE+" Generate volume values for ", volname, " in deployment ", name)
locker.Lock()
if _, ok := VolumeValues[name]; !ok {
VolumeValues[name] = make(map[string]map[string]interface{})
}
VolumeValues[name][volname] = map[string]interface{}{
"enabled": false,
"capacity": "1Gi",
}
locker.Unlock()
ret <- pvc
}
}
container.VolumeMounts = mountPoints
o.Spec.Template.Spec.Volumes = volumes
o.Spec.Template.Spec.Containers = []*helm.Container{container} o.Spec.Template.Spec.Containers = []*helm.Container{container}
// Add some labels // Prepare volumes
o.Spec.Template.Spec.Volumes = prepareVolumes(name, s, container, ret)
// Add selectors
selectors := buildSelector(name, s)
o.Spec.Selector = map[string]interface{}{ o.Spec.Selector = map[string]interface{}{
"matchLabels": buildSelector(name, s), "matchLabels": selectors,
} }
o.Spec.Template.Metadata.Labels = buildSelector(name, s) o.Spec.Template.Metadata.Labels = selectors
// Now, for "depends_on" section, it's a bit tricky... // Now, for "depends_on" section, it's a bit tricky to get dependencies, see the function below.
// We need to detect "others" services, but we probably not have parsed them yet, so o.Spec.Template.Spec.InitContainers = prepareInitContainers(name, s, container)
// we will wait for them for a while.
initContainers := make([]*helm.Container, 0)
for _, dp := range s.DependsOn {
c := helm.NewContainer("check-"+dp, "busybox", nil, s.Labels)
command := strings.ReplaceAll(strings.TrimSpace(dependScript), "__service__", dp)
foundPort := -1 // Then, create Services and possible Ingresses for ingress labels, "ports" and "expose" section
if defaultPort, err := getPort(dp); err != nil {
// BUG: Sometimes the chan remains opened
foundPort = <-waitPort(dp)
} else {
foundPort = defaultPort
}
if foundPort == -1 {
log.Fatalf(
"ERROR, the %s service is waiting for %s port number, "+
"but it is never discovered. You must declare at least one port in "+
"the \"ports\" section of the service in the docker-compose file",
name,
dp,
)
}
command = strings.ReplaceAll(command, "__port__", strconv.Itoa(foundPort))
c.Command = []string{
"sh",
"-c",
command,
}
initContainers = append(initContainers, c)
}
o.Spec.Template.Spec.InitContainers = initContainers
// Then, create services for "ports" and "expose" section
if len(s.Ports) > 0 || len(s.Expose) > 0 { if len(s.Ports) > 0 || len(s.Expose) > 0 {
for _, s := range createService(name, s) { for _, s := range generateServicesAndIngresses(name, s) {
ret <- s ret <- s
} }
} }
@@ -283,7 +106,7 @@ func parseService(name string, s *compose.Service, ret chan interface{}) {
// But... some other deployment can wait for it, so we alert that this deployment hasn't got any // But... some other deployment can wait for it, so we alert that this deployment hasn't got any
// associated service. // associated service.
if len(s.Ports) == 0 { if len(s.Ports) == 0 {
// alert any current or **futur** waiters that this service is not exposed // alert any current or **future** waiters that this service is not exposed
go func() { go func() {
for { for {
select { select {
@@ -314,9 +137,9 @@ func parseService(name string, s *compose.Service, ret chan interface{}) {
} }
// Create a service (k8s). // Create a service (k8s).
func createService(name string, s *compose.Service) []interface{} { func generateServicesAndIngresses(name string, s *compose.Service) []interface{} {
ret := make([]interface{}, 0) ret := make([]interface{}, 0) // can handle helm.Service or helm.Ingress
Magenta(ICON_SERVICE+" Generating service for ", name) Magenta(ICON_SERVICE+" Generating service for ", name)
ks := helm.NewService(name) ks := helm.NewService(name)
@@ -434,6 +257,7 @@ func waitPort(name string) chan int {
return c return c
} }
// Build the selector for the service.
func buildSelector(name string, s *compose.Service) map[string]string { func buildSelector(name string, s *compose.Service) map[string]string {
return map[string]string{ return map[string]string{
"katenary.io/component": name, "katenary.io/component": name,
@@ -441,6 +265,7 @@ func buildSelector(name string, s *compose.Service) map[string]string {
} }
} }
// buildCMFromPath generates a ConfigMap from a path.
func buildCMFromPath(path string) *helm.ConfigMap { func buildCMFromPath(path string) *helm.ConfigMap {
stat, err := os.Stat(path) stat, err := os.Stat(path)
if err != nil { if err != nil {
@@ -472,3 +297,279 @@ func buildCMFromPath(path string) *helm.ConfigMap {
cm.Data = files cm.Data = files
return cm return cm
} }
// generateContainerPorts add the container ports of a service.
func generateContainerPorts(s *compose.Service, name string, container *helm.Container) {
exists := make(map[int]string)
for _, port := range s.Ports {
_p := strings.Split(port, ":")
port = _p[0]
if len(_p) > 1 {
port = _p[1]
}
portNumber, _ := strconv.Atoi(port)
portName := name
for _, n := range exists {
if name == n {
portName = fmt.Sprintf("%s-%d", name, portNumber)
}
}
container.Ports = append(container.Ports, &helm.ContainerPort{
Name: portName,
ContainerPort: portNumber,
})
exists[portNumber] = name
}
// manage the "expose" section to be a NodePort in Kubernetes
for _, port := range s.Expose {
if _, exist := exists[port]; exist {
continue
}
container.Ports = append(container.Ports, &helm.ContainerPort{
Name: name,
ContainerPort: port,
})
}
}
// prepareVolumes add the volumes of a service.
func prepareVolumes(name string, s *compose.Service, container *helm.Container, ret chan interface{}) []map[string]interface{} {
volumes := make([]map[string]interface{}, 0)
mountPoints := make([]interface{}, 0)
configMapsVolumes := make([]string, 0)
if v, ok := s.Labels[helm.LABEL_VOL_CM]; ok {
configMapsVolumes = strings.Split(v, ",")
}
for _, volume := range s.Volumes {
parts := strings.Split(volume, ":")
volname := parts[0]
volepath := parts[1]
isCM := false
for _, cmVol := range configMapsVolumes {
cmVol = strings.TrimSpace(cmVol)
if volname == cmVol {
isCM = true
break
}
}
if !isCM && (strings.HasPrefix(volname, ".") || strings.HasPrefix(volname, "/")) {
// local volume cannt be mounted
ActivateColors = true
Redf("You cannot, at this time, have local volume in %s deployment\n", name)
ActivateColors = false
continue
}
if isCM {
// the volume is a path and it's explicitally asked to be a configmap in labels
cm := buildCMFromPath(volname)
volname = strings.Replace(volname, "./", "", 1)
volname = strings.ReplaceAll(volname, ".", "-")
cm.K8sBase.Metadata.Name = RELEASE_NAME + "-" + volname + "-" + name
// build a configmap from the volume path
volumes = append(volumes, map[string]interface{}{
"name": volname,
"configMap": map[string]string{
"name": cm.K8sBase.Metadata.Name,
},
})
mountPoints = append(mountPoints, map[string]interface{}{
"name": volname,
"mountPath": volepath,
})
ret <- cm
} else {
// rmove minus sign from volume name
volname = strings.ReplaceAll(volname, "-", "")
pvc := helm.NewPVC(name, volname)
volumes = append(volumes, map[string]interface{}{
"name": volname,
"persistentVolumeClaim": map[string]string{
"claimName": RELEASE_NAME + "-" + volname,
},
})
mountPoints = append(mountPoints, map[string]interface{}{
"name": volname,
"mountPath": volepath,
})
Yellow(ICON_STORE+" Generate volume values for ", volname, " in deployment ", name)
locker.Lock()
if _, ok := VolumeValues[name]; !ok {
VolumeValues[name] = make(map[string]map[string]interface{})
}
VolumeValues[name][volname] = map[string]interface{}{
"enabled": false,
"capacity": "1Gi",
}
locker.Unlock()
ret <- pvc
}
}
container.VolumeMounts = mountPoints
return volumes
}
// prepareInitContainers add the init containers of a service.
func prepareInitContainers(name string, s *compose.Service, container *helm.Container) []*helm.Container {
// We need to detect others services, but we probably not have parsed them yet, so
// we will wait for them for a while.
initContainers := make([]*helm.Container, 0)
for _, dp := range s.DependsOn {
c := helm.NewContainer("check-"+dp, "busybox", nil, s.Labels)
command := strings.ReplaceAll(strings.TrimSpace(dependScript), "__service__", dp)
foundPort := -1
if defaultPort, err := getPort(dp); err != nil {
// BUG: Sometimes the chan remains opened
foundPort = <-waitPort(dp)
} else {
foundPort = defaultPort
}
if foundPort == -1 {
log.Fatalf(
"ERROR, the %s service is waiting for %s port number, "+
"but it is never discovered. You must declare at least one port in "+
"the \"ports\" section of the service in the docker-compose file",
name,
dp,
)
}
command = strings.ReplaceAll(command, "__port__", strconv.Itoa(foundPort))
c.Command = []string{
"sh",
"-c",
command,
}
initContainers = append(initContainers, c)
}
return initContainers
}
// prepareProbes generate http/tcp/command probes for a service.
func prepareProbes(name string, s *compose.Service, container *helm.Container) {
// manage the healthcheck property, if any
if s.HealthCheck != nil {
if s.HealthCheck.Interval == "" {
s.HealthCheck.Interval = "10s"
}
interval, err := time.ParseDuration(s.HealthCheck.Interval)
if err != nil {
log.Fatal(err)
}
if s.HealthCheck.StartPeriod == "" {
s.HealthCheck.StartPeriod = "0s"
}
initialDelaySeconds, err := time.ParseDuration(s.HealthCheck.StartPeriod)
if err != nil {
log.Fatal(err)
}
probe := helm.NewProbe(int(interval.Seconds()), int(initialDelaySeconds.Seconds()), 1, s.HealthCheck.Retries)
healthCheckLabel := s.Labels[helm.LABEL_HEALTHCHECK]
if healthCheckLabel != "" {
path := "/"
port := 80
u, err := url.Parse(healthCheckLabel)
if err == nil {
path = u.Path
port, _ = strconv.Atoi(u.Port())
} else {
path = "/"
port = 80
}
if strings.HasPrefix(healthCheckLabel, "http://") {
probe.HttpGet = &helm.HttpGet{
Path: path,
Port: port,
}
} else if strings.HasPrefix(healthCheckLabel, "tcp://") {
if err != nil {
log.Fatal(err)
}
probe.TCP = &helm.TCP{
Port: port,
}
} else {
c, _ := shlex.Split(healthCheckLabel)
probe.Exec = &helm.Exec{
Command: c,
}
}
} else if s.HealthCheck.Test[0] == "CMD" {
probe.Exec = &helm.Exec{
Command: s.HealthCheck.Test[1:],
}
}
container.LivenessProbe = probe
}
}
// prepareEnvFromFiles generate configMap or secrets from environment files.
func prepareEnvFromFiles(name string, s *compose.Service, container *helm.Container, ret chan interface{}) {
// prepare secrets
secretsFiles := make([]string, 0)
if v, ok := s.Labels[helm.LABEL_ENV_SECRET]; ok {
secretsFiles = strings.Split(v, ",")
}
// manage environment files (env_file in compose)
for _, envfile := range s.EnvFiles {
f := strings.ReplaceAll(envfile, "_", "-")
f = strings.ReplaceAll(f, ".env", "")
f = strings.ReplaceAll(f, ".", "")
f = strings.ReplaceAll(f, "/", "")
cf := f + "-" + name
isSecret := false
for _, s := range secretsFiles {
if s == envfile {
isSecret = true
}
}
var store helm.InlineConfig
if !isSecret {
Bluef(ICON_CONF+" Generating configMap %s\n", cf)
store = helm.NewConfigMap(cf)
} else {
Bluef(ICON_SECRET+" Generating secret %s\n", cf)
store = helm.NewSecret(cf)
}
if err := store.AddEnvFile(envfile); err != nil {
ActivateColors = true
Red(err.Error())
ActivateColors = false
os.Exit(2)
}
section := "configMapRef"
if isSecret {
section = "secretRef"
}
container.EnvFrom = append(container.EnvFrom, map[string]map[string]string{
section: {
"name": store.Metadata().Name,
},
})
ret <- store
}
}

5
go.mod
View File

@@ -2,4 +2,7 @@ module katenary
go 1.16 go 1.16
require gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)

2
go.sum
View File

@@ -1,3 +1,5 @@
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=

View File

@@ -40,13 +40,46 @@ type ContainerPort struct {
} }
type Container struct { type Container struct {
Name string `yaml:"name,omitempty"` Name string `yaml:"name,omitempty"`
Image string `yaml:"image"` Image string `yaml:"image"`
Ports []*ContainerPort `yaml:"ports,omitempty"` Ports []*ContainerPort `yaml:"ports,omitempty"`
Env []Value `yaml:"env,omitempty"` Env []Value `yaml:"env,omitempty"`
EnvFrom []map[string]map[string]string `yaml:"envFrom,omitempty"` EnvFrom []map[string]map[string]string `yaml:"envFrom,omitempty"`
Command []string `yaml:"command,omitempty"` Command []string `yaml:"command,omitempty"`
VolumeMounts []interface{} `yaml:"volumeMounts,omitempty"` VolumeMounts []interface{} `yaml:"volumeMounts,omitempty"`
LivenessProbe *Probe `yaml:"livenessProbe,omitempty"`
}
type HttpGet struct {
Path string `yaml:"path"`
Port int `yaml:"port"`
}
type Exec struct {
Command []string `yaml:"command"`
}
type TCP struct {
Port int `yaml:"port"`
}
type Probe struct {
HttpGet *HttpGet `yaml:"httpGet,omitempty"`
Exec *Exec `yaml:"exec,omitempty"`
TCP *TCP `yaml:"tcp,omitempty"`
Period int `yaml:"periodSeconds"`
Success int `yaml:"successThreshold"`
Failure int `yaml:"failureThreshold"`
InitialDelay int `yaml:"initialDelaySeconds"`
}
func NewProbe(period, initialDelaySeconds, success, failure int) *Probe {
return &Probe{
Period: period,
Success: success,
Failure: failure,
InitialDelay: initialDelaySeconds,
}
} }
func NewContainer(name, image string, environment, labels map[string]string) *Container { func NewContainer(name, image string, environment, labels map[string]string) *Container {

View File

@@ -1,11 +1,13 @@
package helm package helm
import ( import (
"bytes"
"crypto/sha1" "crypto/sha1"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"strings" "strings"
"text/template"
) )
const K = "katenary.io" const K = "katenary.io"
@@ -16,8 +18,35 @@ const (
LABEL_INGRESS = K + "/ingress" LABEL_INGRESS = K + "/ingress"
LABEL_ENV_SERVICE = K + "/env-to-service" LABEL_ENV_SERVICE = K + "/env-to-service"
LABEL_VOL_CM = K + "/configmap-volumes" LABEL_VOL_CM = K + "/configmap-volumes"
LABEL_HEALTHCHECK = K + "/healthcheck"
) )
func GetLabelsDocumentation() string {
t, _ := template.New("labels").Parse(`
# Labels
{{.LABEL_ENV_SECRET | printf "%-33s"}}: set the given file names as a secret instead of configmap
{{.LABEL_PORT | printf "%-33s"}}: set the port to expose as a service
{{.LABEL_INGRESS | printf "%-33s"}}: set the port to expose in an ingress
{{.LABEL_ENV_SERVICE | printf "%-33s"}}: specifies that the environment variable points on a service name
{{.LABEL_VOL_CM | printf "%-33s"}}: specifies that the volume points on a configmap
{{.LABEL_HEALTHCHECK | printf "%-33s"}}: specifies that the container should be monitored by a healthcheck, **it overrides the docker-compose healthcheck**.
{{ printf "%-34s" ""}} You can use these form of label values:
{{ printf "%-35s" ""}}- "http://[not used address][:port][/path]" to specify an http healthcheck
{{ printf "%-35s" ""}}- "tcp://[not used address]:port" to specify a tcp healthcheck
{{ printf "%-35s" ""}}- other string is condidered as a "command" healthcheck
`)
buff := bytes.NewBuffer(nil)
t.Execute(buff, map[string]string{
"LABEL_ENV_SECRET": LABEL_ENV_SECRET,
"LABEL_ENV_SERVICE": LABEL_ENV_SERVICE,
"LABEL_PORT": LABEL_PORT,
"LABEL_INGRESS": LABEL_INGRESS,
"LABEL_VOL_CM": LABEL_VOL_CM,
"LABEL_HEALTHCHECK": LABEL_HEALTHCHECK,
})
return buff.String()
}
var ( var (
Appname = "" Appname = ""
Version = "1.0" // should be set from main.Version Version = "1.0" // should be set from main.Version

View File

@@ -88,8 +88,16 @@ func main() {
flag.StringVar(&appVersion, "appversion", appVersion, helpMessageForAppversion) flag.StringVar(&appVersion, "appversion", appVersion, helpMessageForAppversion)
version := flag.Bool("version", false, "show version and exit") version := flag.Bool("version", false, "show version and exit")
force := flag.Bool("force", false, "force the removal of the chart-dir") force := flag.Bool("force", false, "force the removal of the chart-dir")
showLabels := flag.Bool("labels", false, "show possible labels and exit")
flag.Parse() flag.Parse()
if *showLabels {
// display labels from helm/types.go
fmt.Println(helm.GetLabelsDocumentation())
os.Exit(0)
}
// Only display the version // Only display the version
if *version { if *version {
fmt.Println(Version) fmt.Println(Version)