diff --git a/generator/main.go b/generator/main.go index 0499cd3..2a5fc35 100644 --- a/generator/main.go +++ b/generator/main.go @@ -59,14 +59,27 @@ func CreateReplicaObject(name string, s types.ServiceConfig, linked map[string]t // This function will try to yied deployment and services based on a service from the compose file structure. func parseService(name string, s types.ServiceConfig, linked map[string]types.ServiceConfig, ret chan interface{}) { + // TODO: this function is a mess, need a complete refactorisation + logger.Magenta(ICON_PACKAGE+" Generating deployment for ", name) + deployment := helm.NewDeployment(name) // adapt env applyEnvMapLabel(&s) - setEnvToValues(name, &s) - deployment := helm.NewDeployment(name) + // create a container for the deployment. container := helm.NewContainer(name, s.Image, s.Environment, s.Labels) + + // If some variables are secrets, set them now ! + if secretFile := setSecretVar(name, &s, container); secretFile != nil { + ret <- secretFile + container.EnvFrom = append(container.EnvFrom, map[string]map[string]string{ + "secretRef": { + "name": secretFile.Metadata().Name, + }, + }) + } + setEnvToValues(name, &s, container) prepareContainer(container, s, name) prepareEnvFromFiles(name, s, container, ret) @@ -90,8 +103,18 @@ func parseService(name string, s types.ServiceConfig, linked map[string]types.Se // Now, the linked services for lname, link := range linked { applyEnvMapLabel(&link) - setEnvToValues(lname, &link) container := helm.NewContainer(lname, link.Image, link.Environment, link.Labels) + + // If some variables are secrets, set them now ! + if secretFile := setSecretVar(lname, &link, container); secretFile != nil { + ret <- secretFile + container.EnvFrom = append(container.EnvFrom, map[string]map[string]string{ + "secretRef": { + "name": secretFile.Metadata().Name, + }, + }) + } + setEnvToValues(lname, &link, container) prepareContainer(container, link, lname) prepareEnvFromFiles(lname, link, container, ret) deployment.Spec.Template.Spec.Containers = append(deployment.Spec.Template.Spec.Containers, container) @@ -321,23 +344,23 @@ func prepareVolumes(deployment, name string, s types.ServiceConfig, container *h continue } - isCM := false + isConfigMap := false for _, cmVol := range configMapsVolumes { cmVol = strings.TrimSpace(cmVol) if volname == cmVol { - isCM = true + isConfigMap = true break } } - if !isCM && (strings.HasPrefix(volname, ".") || strings.HasPrefix(volname, "/")) { + if !isConfigMap && (strings.HasPrefix(volname, ".") || strings.HasPrefix(volname, "/")) { // local volume cannt be mounted logger.ActivateColors = true logger.Redf("You cannot, at this time, have local volume in %s deployment\n", name) logger.ActivateColors = false continue } - if isCM { + if isConfigMap { // check if the volname path points on a file, if so, we need to add subvolume to the interface stat, err := os.Stat(volname) if err != nil { @@ -690,16 +713,52 @@ func applyEnvMapLabel(s *types.ServiceConfig) { } // setEnvToValues will set the environment variables to the values.yaml map. -func setEnvToValues(name string, s *types.ServiceConfig) { +func setEnvToValues(name string, s *types.ServiceConfig, c *helm.Container) { // crete the "environment" key + env := make(map[string]interface{}) for k, v := range s.Environment { env[k] = v } + if len(env) == 0 { + return + } AddValues(name, map[string]interface{}{"environment": env}) - for k := range s.Environment { - v := "{{ .Values." + name + ".environment." + k + " }}" + for k := range env { + v := "{{ tpl .Values." + name + ".environment." + k + " . }}" s.Environment[k] = &v + for _, c := range c.Env { + if c.Name == k { + c.Value = v + } + } } } + +func setSecretVar(name string, s *types.ServiceConfig, c *helm.Container) *helm.Secret { + // get the list of secret vars + secretvars, ok := s.Labels[helm.LABEL_SECRETVARS] + if !ok { + return nil + } + + store := helm.NewSecret(name + "-secrets") + for _, secretvar := range strings.Split(secretvars, ",") { + // get the value from env + _, ok := s.Environment[secretvar] + if !ok { + continue + } + // add the secret + store.AddEnv(secretvar, ".Values."+name+".environment."+secretvar) + envs := c.Env + for i, env := range envs { + if env.Name == secretvar { + envs = append(envs[:i], envs[i+1:]...) + } + } + c.Env = envs + } + return store +} diff --git a/generator/main_test.go b/generator/main_test.go index 2342cf6..c02f17f 100644 --- a/generator/main_test.go +++ b/generator/main_test.go @@ -50,7 +50,8 @@ services: ANOTHER_ENV_VAR: another_value DB_HOST: database labels: - katenary.io/env-to-service: DB_HOST + katenary.io/mapenv: | + DB_HOST: {{ .Release.Name }}-database database: image: mysql:5.7 @@ -221,20 +222,20 @@ func TestEnvs(t *testing.T) { lines, _ := ioutil.ReadAll(fp) next := false for _, line := range strings.Split(string(lines), "\n") { - if strings.Contains(line, "DB_HOST") { + if !next && strings.Contains(line, "name: DB_HOST") { next = true continue - } - if next { + } else if next && strings.Contains(line, "value:") { matched = true - if !strings.Contains(line, helm.ReleaseNameTpl+"-database") { - t.Error("DB_HOST variable should be set to " + helm.ReleaseNameTpl + "-database") + if !strings.Contains(line, "{{ tpl .Values.php.environment.DB_HOST . }}") { + t.Error("DB_HOST variable should be set to {{ tpl .Values.php.environment.DB_HOST . }}", line, string(lines)) } break } } if !matched { t.Error("DB_HOST variable not found in ", path) + t.Log(string(lines)) } } } @@ -382,8 +383,9 @@ func TestEqualSignOnEnv(t *testing.T) { match++ } } - if match != 2 { + if match != 4 { // because the value points on .Values... t.Error("eqenv service should have 2 environment variables") + t.Log(string(lines)) } } } diff --git a/helm/configAndSecretMap.go b/helm/configAndSecretMap.go index 90b6b10..b319257 100644 --- a/helm/configAndSecretMap.go +++ b/helm/configAndSecretMap.go @@ -10,6 +10,7 @@ import ( // InlineConfig is made to represent a configMap or a secret type InlineConfig interface { AddEnvFile(filename string) error + AddEnv(key, val string) error Metadata() *Metadata } @@ -56,9 +57,12 @@ func (c *ConfigMap) AddEnvFile(file string) error { } c.Data[parts[0]] = parts[1] } - return nil +} +func (c *ConfigMap) AddEnv(key, val string) error { + c.Data[key] = val + return nil } // Secret is made to represent a secret with data. @@ -108,3 +112,9 @@ func (s *Secret) AddEnvFile(file string) error { func (s *Secret) Metadata() *Metadata { return s.K8sBase.Metadata } + +// AddEnv adds an environment variable to the secret. +func (s *Secret) AddEnv(key, val string) error { + s.Data[key] = fmt.Sprintf(`{{ %s | b64enc }}`, val) + return nil +} diff --git a/helm/labels.go b/helm/labels.go index 0abad59..6fd824d 100644 --- a/helm/labels.go +++ b/helm/labels.go @@ -16,6 +16,7 @@ const ( LABEL_SAMEPOD = K + "/same-pod" LABEL_EMPTYDIRS = K + "/empty-dirs" LABEL_IGNORE = K + "/ignore" + LABEL_SECRETVARS = K + "/secret-vars" //deprecated: use LABEL_MAP_ENV instead LABEL_ENV_SERVICE = K + "/env-to-service" @@ -26,6 +27,7 @@ func GetLabelsDocumentation() string { t, _ := template.New("labels").Parse(` # Labels {{.LABEL_IGNORE | printf "%-33s"}}: ignore the container, it will not yied any object in the helm chart +{{.LABEL_SECRETVARS | printf "%-33s"}}: secret variables to push on a secret file {{.LABEL_ENV_SECRET | printf "%-33s"}}: set the given file names as a secret instead of configmap {{.LABEL_MAP_ENV | printf "%-33s"}}: map environment variable to a template string (yaml style) {{.LABEL_PORT | printf "%-33s"}}: set the ports to expose as a service (coma separated) @@ -52,6 +54,7 @@ func GetLabelsDocumentation() string { "LABEL_EMPTYDIRS": LABEL_EMPTYDIRS, "LABEL_IGNORE": LABEL_IGNORE, "LABEL_MAP_ENV": LABEL_MAP_ENV, + "LABEL_SECRETVARS": LABEL_SECRETVARS, }) return buff.String() }