From fe6663f9f44261009ebb057485b0a4b4de09e217 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Sun, 19 Jan 2025 23:15:53 +0100 Subject: [PATCH] issue(106): Fix service names with dashes See #106, I need to add a test on "same-pod" label. --- generator/deployment_test.go | 133 ++++++++++++++++++++++++++++++++++- generator/generator.go | 35 +++++++++ 2 files changed, 165 insertions(+), 3 deletions(-) diff --git a/generator/deployment_test.go b/generator/deployment_test.go index 37e4f31..aa058ef 100644 --- a/generator/deployment_test.go +++ b/generator/deployment_test.go @@ -6,6 +6,7 @@ import ( "os" "testing" + yaml3 "gopkg.in/yaml.v3" v1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/yaml" @@ -324,14 +325,140 @@ services: Environment map[string]string `yaml:"environment"` } `yaml:"web"` }{} - if err := yaml.Unmarshal(valuesContent, &mapping); err != nil { + if err := yaml3.Unmarshal(valuesContent, &mapping); err != nil { t.Errorf(unmarshalError, err) } - if _, ok := mapping.Web.Environment["FOO"]; !ok { + if v, ok := mapping.Web.Environment["FOO"]; !ok { t.Errorf("Expected FOO in web environment") + if v != "bar" { + t.Errorf("Expected FOO to be bar, got %s", v) + } } - if _, ok := mapping.Web.Environment["BAZ"]; ok { + if v, ok := mapping.Web.Environment["BAZ"]; ok { t.Errorf("Expected BAZ not in web environment") + if v != "qux" { + t.Errorf("Expected BAZ to be qux, got %s", v) + } + } +} + +func TestWithDashes(t *testing.T) { + composeFile := ` +services: + web-app: + image: nginx:1.29 + environment: + FOO: BAR + labels: + %s/values: | + - FOO +` + + composeFile = fmt.Sprintf(composeFile, labels.Prefix()) + tmpDir := setup(composeFile) + defer teardown(tmpDir) + + currentDir, _ := os.Getwd() + os.Chdir(tmpDir) + defer os.Chdir(currentDir) + + output := internalCompileTest(t, "-s", "templates/web_app/deployment.yaml") + dt := v1.Deployment{} + if err := yaml.Unmarshal([]byte(output), &dt); err != nil { + t.Errorf(unmarshalError, err) + } + + valuesFile := "./chart/values.yaml" + if _, err := os.Stat(valuesFile); os.IsNotExist(err) { + t.Errorf("values.yaml does not exist") + } + valuesContent, err := os.ReadFile(valuesFile) + if err != nil { + t.Errorf("Error reading values.yaml: %s", err) + } + mapping := struct { + Web struct { + Environment map[string]string `yaml:"environment"` + } `yaml:"web_app"` + }{} + if err := yaml3.Unmarshal(valuesContent, &mapping); err != nil { + t.Errorf(unmarshalError, err) + } + + // we must have FOO in web_app environment (not web-app) + // this validates that the service name is converted to a valid k8s name + if v, ok := mapping.Web.Environment["FOO"]; !ok { + t.Errorf("Expected FOO in web_app environment") + if v != "BAR" { + t.Errorf("Expected FOO to be BAR, got %s", v) + } + } +} + +func TestDashesWithValueFrom(t *testing.T) { + composeFile := ` +services: + web-app: + image: nginx:1.29 + environment: + FOO: BAR + labels: + %[1]s/values: | + - FOO + web2: + image: nginx:1.29 + labels: + %[1]s/values-from: | + BAR: web-app.FOO +` + + composeFile = fmt.Sprintf(composeFile, labels.Prefix()) + tmpDir := setup(composeFile) + defer teardown(tmpDir) + + currentDir, _ := os.Getwd() + os.Chdir(tmpDir) + defer os.Chdir(currentDir) + + output := internalCompileTest(t, "-s", "templates/web2/deployment.yaml") + dt := v1.Deployment{} + if err := yaml.Unmarshal([]byte(output), &dt); err != nil { + t.Errorf(unmarshalError, err) + } + + valuesFile := "./chart/values.yaml" + if _, err := os.Stat(valuesFile); os.IsNotExist(err) { + t.Errorf("values.yaml does not exist") + } + valuesContent, err := os.ReadFile(valuesFile) + if err != nil { + t.Errorf("Error reading values.yaml: %s", err) + } + mapping := struct { + Web struct { + Environment map[string]string `yaml:"environment"` + } `yaml:"web_app"` + }{} + if err := yaml3.Unmarshal(valuesContent, &mapping); err != nil { + t.Errorf(unmarshalError, err) + } + + // we must have FOO in web_app environment (not web-app) + // this validates that the service name is converted to a valid k8s name + if v, ok := mapping.Web.Environment["FOO"]; !ok { + t.Errorf("Expected FOO in web_app environment") + if v != "BAR" { + t.Errorf("Expected FOO to be BAR, got %s", v) + } + } + + // ensure that the deployment has the value from the other service + barenv := dt.Spec.Template.Spec.Containers[0].Env[0] + if barenv.Value != "" { + t.Errorf("Expected value to be empty") + } + if barenv.ValueFrom == nil { + t.Errorf("Expected valueFrom to be set") } } diff --git a/generator/generator.go b/generator/generator.go index bc16941..6509b18 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "katenary/generator/labels" + "katenary/generator/labels/labelStructs" "katenary/utils" "log" "regexp" @@ -11,6 +12,7 @@ import ( "github.com/compose-spec/compose-go/types" corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/yaml" ) // Generate a chart from a compose project. @@ -43,6 +45,39 @@ func Generate(project *types.Project) (*HelmChart, error) { Annotations[labels.LabelName("compose-hash")] = hash chart.composeHash = &hash + // rename all services name to remove dashes + for i, service := range project.Services { + if service.Name != utils.AsResourceName(service.Name) { + fixed := utils.AsResourceName(service.Name) + for j, s := range project.Services { + // for the same-pod services, we need to keep the original name + if samepod, ok := s.Labels[labels.LabelSamePod]; ok && samepod == service.Name { + s.Labels[labels.LabelSamePod] = fixed + project.Services[j] = s + } + // also, the value-from label should be updated + if valuefrom, ok := s.Labels[labels.LabelValueFrom]; ok { + vf, err := labelStructs.GetValueFrom(valuefrom) + if err != nil { + return nil, err + } + for varname, bind := range *vf { + log.Printf("service %s, varname %s, bind %s", service.Name, varname, bind) + bind := strings.ReplaceAll(bind, service.Name, fixed) + (*vf)[varname] = bind + } + output, err := yaml.Marshal(vf) + if err != nil { + return nil, err + } + s.Labels[labels.LabelValueFrom] = string(output) + } + } + service.Name = fixed + project.Services[i] = service + } + } + // find the "main-app" label, and set chart.AppVersion to the tag if exists mainCount := 0 for _, service := range project.Services {