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: "", + }, } }