From 5c939383bec0e4e2f85d1bc295f22358b95866db Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Thu, 5 Dec 2024 06:43:43 +0100 Subject: [PATCH 1/5] chore(tls): Add secretName to the values The seret name for TLS wasn't editable, it may be useful to change it when we generate TLS certificates for specific installation. --- generator/converter.go | 25 ++++++++++++ generator/ingress.go | 32 ++++++++------- generator/ingress_test.go | 85 ++++++++++++++++++++++++++++++++++++++- generator/values.go | 7 +++- 4 files changed, 133 insertions(+), 16 deletions(-) diff --git a/generator/converter.go b/generator/converter.go index 1d55e46..2d12c18 100644 --- a/generator/converter.go +++ b/generator/converter.go @@ -85,6 +85,12 @@ var unwantedLines = []string{ "status:", } +var ingressTLSHelp = `# Ingress TLS configuration +# If enabled, a secret containing the certificate and the key should be +# created by the ingress controller. If the name if emtpy, so the secret +# name is generated. You can specify the secret name to use your own secret. +` + // keyRegExp checks if the line starts by a # var keyRegExp = regexp.MustCompile(`^\s*[^#]+:.*`) @@ -486,6 +492,24 @@ func addYAMLSelectorPath(values []byte) []byte { return []byte(strings.Join(toReturn, "\n")) } +// addTLSHelp adds a comment to the values.yaml file to explain how to +// use the tls option. +func addTLSHelp(values []byte) []byte { + lines := strings.Split(string(values), "\n") + for i, line := range lines { + if strings.Contains(line, "tls:") { + spaces := utils.CountStartingSpaces(line) + spacesString := strings.Repeat(" ", spaces) + // indent ingressClassHelper comment + ingressTLSHelp := strings.ReplaceAll(ingressTLSHelp, "\n", "\n"+spacesString) + ingressTLSHelp = strings.TrimRight(ingressTLSHelp, " ") + ingressTLSHelp = spacesString + ingressTLSHelp + lines[i] = ingressTLSHelp + line + } + } + return []byte(strings.Join(lines, "\n")) +} + func buildCharYamlFile(chart *HelmChart, project *types.Project, chartPath string) { // calculate the sha1 hash of the services yamlChart, err := utils.EncodeBasicYaml(chart) @@ -537,6 +561,7 @@ func buildValues(chart *HelmChart, project *types.Project, valuesPath string) { values = addVariablesDoc(values, project) values = addMainTagAppDoc(values, project) values = addResourceHelp(values) + values = addTLSHelp(values) values = addYAMLSelectorPath(values) values = append([]byte(headerHelp), values...) diff --git a/generator/ingress.go b/generator/ingress.go index 5f869c8..7e2d753 100644 --- a/generator/ingress.go +++ b/generator/ingress.go @@ -17,6 +17,7 @@ var _ Yaml = (*Ingress)(nil) type Ingress struct { *networkv1.Ingress service *types.ServiceConfig `yaml:"-"` + appName string `yaml:"-"` } // NewIngress creates a new Ingress from a compose service. @@ -42,7 +43,11 @@ func NewIngress(service types.ServiceConfig, Chart *HelmChart) *Ingress { // create the ingress pathType := networkv1.PathTypeImplementationSpecific - serviceName := `{{ include "` + appName + `.fullname" . }}-` + service.Name + + // fix the service name, and create the full name from variable name + // which is injected in the YAML() method + serviceName := strings.ReplaceAll(service.Name, "_", "-") + fullName := `{{ $fullname }}-` + serviceName // Add the ingress host to the values.yaml if Chart.Values[service.Name] == nil { @@ -63,7 +68,7 @@ func NewIngress(service types.ServiceConfig, Chart *HelmChart) *Ingress { servicePortName := utils.GetServiceNameByPort(int(*mapping.Port)) ingressService := &networkv1.IngressServiceBackend{ - Name: serviceName, + Name: fullName, Port: networkv1.ServiceBackendPort{}, } if servicePortName != "" { @@ -74,26 +79,27 @@ func NewIngress(service types.ServiceConfig, Chart *HelmChart) *Ingress { ing := &Ingress{ service: &service, + appName: appName, Ingress: &networkv1.Ingress{ TypeMeta: metav1.TypeMeta{ Kind: "Ingress", APIVersion: "networking.k8s.io/v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: utils.TplName(service.Name, appName), - Labels: GetLabels(service.Name, appName), + Name: fullName, + Labels: GetLabels(serviceName, appName), Annotations: Annotations, }, Spec: networkv1.IngressSpec{ IngressClassName: &ingressClassName, Rules: []networkv1.IngressRule{ { - Host: utils.TplValue(service.Name, "ingress.host"), + Host: utils.TplValue(serviceName, "ingress.host"), IngressRuleValue: networkv1.IngressRuleValue{ HTTP: &networkv1.HTTPIngressRuleValue{ Paths: []networkv1.HTTPIngressPath{ { - Path: utils.TplValue(service.Name, "ingress.path"), + Path: utils.TplValue(serviceName, "ingress.path"), PathType: &pathType, Backend: networkv1.IngressBackend{ Service: ingressService, @@ -107,9 +113,9 @@ func NewIngress(service types.ServiceConfig, Chart *HelmChart) *Ingress { TLS: []networkv1.IngressTLS{ { Hosts: []string{ - `{{ tpl .Values.` + service.Name + `.ingress.host . }}`, + `{{ tpl .Values.` + serviceName + `.ingress.host . }}`, }, - SecretName: `{{ include "` + appName + `.fullname" . }}-` + service.Name + `-tls`, + SecretName: `{{ .Values.` + serviceName + `.ingress.tls.secretName | default $tlsname }}`, }, }, }, @@ -131,9 +137,7 @@ func (ingress *Ingress) Yaml() ([]byte, error) { } serviceName := ingress.service.Name - if err != nil { - return nil, err - } + ret = UnWrapTPL(ret) lines := strings.Split(string(ret), "\n") @@ -141,9 +145,7 @@ func (ingress *Ingress) Yaml() ([]byte, error) { // first pass, wrap the tls part with `{{- if .Values.serviceName.ingress.tlsEnabled -}}` // and `{{- end -}}` - from := -1 - to := -1 - spaces := -1 + from, to, spaces := -1, -1, -1 for i, line := range lines { if strings.Contains(line, "tls:") { from = i @@ -167,6 +169,8 @@ func (ingress *Ingress) Yaml() ([]byte, error) { out := []string{ `{{- if .Values.` + serviceName + `.ingress.enabled -}}`, + `{{- $fullname := include "` + ingress.appName + `.fullname" . -}}`, + `{{- $tlsname := printf "%s-%s-tls" $fullname "` + ingress.service.Name + `" -}}`, } for _, line := range lines { if strings.Contains(line, "loadBalancer: ") { diff --git a/generator/ingress_test.go b/generator/ingress_test.go index d79f2b8..506d86c 100644 --- a/generator/ingress_test.go +++ b/generator/ingress_test.go @@ -31,7 +31,11 @@ services: os.Chdir(tmpDir) defer os.Chdir(currentDir) - output := internalCompileTest(t, "-s", "templates/web/ingress.yaml", "--set", "web.ingress.enabled=true") + output := internalCompileTest( + t, + "-s", "templates/web/ingress.yaml", + "--set", "web.ingress.enabled=true", + ) ingress := v1.Ingress{} if err := yaml.Unmarshal([]byte(output), &ingress); err != nil { t.Errorf(unmarshalError, err) @@ -43,3 +47,82 @@ services: t.Errorf("Expected host to be my.test.tld, got %s", ingress.Spec.Rules[0].Host) } } + +func TestTLS(t *testing.T) { + composeFile := ` +services: + web: + image: nginx:1.29 + ports: + - 80:80 + - 443:443 + labels: + %s/ingress: |- + hostname: my.test.tld + port: 80 +` + composeFile = fmt.Sprintf(composeFile, labels.KatenaryLabelPrefix) + tmpDir := setup(composeFile) + defer teardown(tmpDir) + + currentDir, _ := os.Getwd() + os.Chdir(tmpDir) + defer os.Chdir(currentDir) + + output := internalCompileTest( + t, + "-s", "templates/web/ingress.yaml", + "--set", "web.ingress.enabled=true", + ) + ingress := v1.Ingress{} + if err := yaml.Unmarshal([]byte(output), &ingress); err != nil { + t.Errorf(unmarshalError, err) + } + // find the tls section + tls := ingress.Spec.TLS + if len(tls) != 1 { + t.Errorf("Expected 1 tls section, got %d", len(tls)) + } +} + +func TestTLSName(t *testing.T) { + composeFile := ` +services: + web: + image: nginx:1.29 + ports: + - 80:80 + - 443:443 + labels: + %s/ingress: |- + hostname: my.test.tld + port: 80 +` + composeFile = fmt.Sprintf(composeFile, labels.KatenaryLabelPrefix) + tmpDir := setup(composeFile) + defer teardown(tmpDir) + + currentDir, _ := os.Getwd() + os.Chdir(tmpDir) + defer os.Chdir(currentDir) + + output := internalCompileTest( + t, + "-s", + "templates/web/ingress.yaml", + "--set", "web.ingress.enabled=true", + "--set", "web.ingress.tls.secretName=mysecret", + ) + ingress := v1.Ingress{} + if err := yaml.Unmarshal([]byte(output), &ingress); err != nil { + t.Errorf(unmarshalError, err) + } + // find the tls section + tls := ingress.Spec.TLS + if len(tls) != 1 { + t.Errorf("Expected 1 tls section, got %d", len(tls)) + } + if tls[0].SecretName != "mysecret" { + t.Errorf("Expected secretName to be mysecret, got %s", tls[0].SecretName) + } +} diff --git a/generator/values.go b/generator/values.go index e253a51..a52304d 100644 --- a/generator/values.go +++ b/generator/values.go @@ -21,7 +21,8 @@ type PersistenceValue struct { } type TLS struct { - Enabled bool `yaml:"enabled"` + Enabled bool `yaml:"enabled"` + SecretName string `yaml:"secretName"` } // IngressValue is a ingress configuration that will be saved in values.yaml. @@ -92,6 +93,10 @@ func (v *Value) AddIngress(host, path string) { Host: host, Path: path, Class: "-", + TLS: TLS{ + Enabled: true, + SecretName: "", + }, } } From 23d0afd85f90581c550723d9a64e8dfe6faf7fa0 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Thu, 5 Dec 2024 06:54:38 +0100 Subject: [PATCH 2/5] test(gh): Use 2 steps I prefer to split tests and sonar upload --- .github/workflows/go-test.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/go-test.yaml b/.github/workflows/go-test.yaml index fb00245..ef5c0bd 100644 --- a/.github/workflows/go-test.yaml +++ b/.github/workflows/go-test.yaml @@ -12,6 +12,12 @@ jobs: test: runs-on: ubuntu-latest steps: + - uses: actions/upload-artifact@v4 + with: + name: tests-results + path: | + coverprofile.out + gotest.json - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 @@ -25,6 +31,13 @@ jobs: - name: Launch Test run: | go vet ./... && go test -coverprofile=coverprofile.out -json -v ./... > gotest.json + sonar: + runs-on: ubuntu-latest + needs: tests + steps: + - uses: actions/download-artifact@v4 + with: + name: tests-results - name: SonarCloud Scan uses: SonarSource/sonarcloud-github-action@master env: From 4768330c1a211db7aee4fe492e8234d88b2525d3 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Thu, 5 Dec 2024 06:55:50 +0100 Subject: [PATCH 3/5] test(gh): Fix job name --- .github/workflows/go-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go-test.yaml b/.github/workflows/go-test.yaml index ef5c0bd..6723c77 100644 --- a/.github/workflows/go-test.yaml +++ b/.github/workflows/go-test.yaml @@ -9,7 +9,7 @@ on: - master - develop jobs: - test: + tests: runs-on: ubuntu-latest steps: - uses: actions/upload-artifact@v4 From b93a3df98c8f6a120b0efd7b3a37142f2f049d65 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Thu, 5 Dec 2024 07:04:47 +0100 Subject: [PATCH 4/5] test(gh): Checkout project --- .github/workflows/go-test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/go-test.yaml b/.github/workflows/go-test.yaml index 6723c77..6f9efea 100644 --- a/.github/workflows/go-test.yaml +++ b/.github/workflows/go-test.yaml @@ -35,6 +35,7 @@ jobs: runs-on: ubuntu-latest needs: tests steps: + - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: tests-results From dcd282779f349b2e2a9f803ceaf7e21b8169bf3d Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Thu, 5 Dec 2024 07:08:16 +0100 Subject: [PATCH 5/5] test(gh): Ordering steps... I'm stupid... I need to upload artifacts after the creation. --- .github/workflows/go-test.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/go-test.yaml b/.github/workflows/go-test.yaml index 6f9efea..9c6b405 100644 --- a/.github/workflows/go-test.yaml +++ b/.github/workflows/go-test.yaml @@ -12,12 +12,6 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: actions/upload-artifact@v4 - with: - name: tests-results - path: | - coverprofile.out - gotest.json - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 @@ -31,6 +25,12 @@ jobs: - name: Launch Test run: | go vet ./... && go test -coverprofile=coverprofile.out -json -v ./... > gotest.json + - uses: actions/upload-artifact@v4 + with: + name: tests-results + path: | + coverprofile.out + gotest.json sonar: runs-on: ubuntu-latest needs: tests