diff --git a/library/common-test/tests/ingress/homepage_test.yaml b/library/common-test/tests/ingress/homepage_test.yaml index 674d4d53..01f6625c 100644 --- a/library/common-test/tests/ingress/homepage_test.yaml +++ b/library/common-test/tests/ingress/homepage_test.yaml @@ -18,6 +18,9 @@ tests: operator: &operator verify: enabled: false + global: + traefik: + addIngressAnnotations: false ingress: my-ingress1: enabled: true diff --git a/library/common-test/tests/ingress/metadata_test.yaml b/library/common-test/tests/ingress/metadata_test.yaml index d46113be..f8202170 100644 --- a/library/common-test/tests/ingress/metadata_test.yaml +++ b/library/common-test/tests/ingress/metadata_test.yaml @@ -14,6 +14,8 @@ tests: annotation1: annotation1 annotation2: global_annotation2 global: + traefik: + addIngressAnnotations: false labels: g_label1: global_label1 g_label2: "{{ .Values.label2 }}" diff --git a/library/common-test/tests/ingress/traefik_test.yaml b/library/common-test/tests/ingress/traefik_test.yaml new file mode 100644 index 00000000..a85d188b --- /dev/null +++ b/library/common-test/tests/ingress/traefik_test.yaml @@ -0,0 +1,495 @@ +suite: ingress - traefik metadata test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +release: + name: test-release-name + namespace: test-release-namespace +tests: + - it: should pass with ingress created with annotations from traefik by default + set: + operator: &operator + verify: + enabled: false + ingress: + my-ingress: + enabled: true + primary: true + asserts: + - documentIndex: &ingressDoc 0 + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.middlewares: chain-basic-tc-system@kubernetescrd + - documentIndex: *ingressDoc + equal: + path: metadata.namespace + value: test-release-namespace + + - it: should pass with ingress created without traefik annotations when traefik false + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: false + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + isNull: + path: metadata.annotations + + - it: should replace local fixedMiddlewares when allowCors true + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + allowCors: true + fixedMiddlewares: + - some-fixed-middleware + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.middlewares: tc-opencors-chain-tc-system@kubernetescrd + + - it: should replace global fixedMiddlewares when allowCors true + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + allowCors: true + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.middlewares: tc-opencors-chain-tc-system@kubernetescrd + + - it: should replace global fixedMiddlewares when local fixedMiddlewares is defined + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + fixedMiddlewares: + - some-fixed-middleware + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.middlewares: some-fixed-middleware-tc-system@kubernetescrd + + - it: should override default entrypoint(s) + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + entrypoints: + - web + - websecure + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + traefik.ingress.kubernetes.io/router.entrypoints: web,websecure + traefik.ingress.kubernetes.io/router.middlewares: chain-basic-tc-system@kubernetescrd + + - it: should not contain fixed middlewares when global is disabled + set: + operator: *operator + global: + traefik: + enableFixedMiddlewares: false + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + + - it: should not contain fixed middlewares when local is disabled + set: + operator: *operator + global: + traefik: + enableFixedMiddlewares: true + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + enableFixedMiddlewares: false + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + + - it: should set correct namespace when operator is registered + set: + operator: + verify: + enabled: false + traefik: + namespace: some-ns + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + entrypoints: + - web + - websecure + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + traefik.ingress.kubernetes.io/router.entrypoints: web,websecure + traefik.ingress.kubernetes.io/router.middlewares: chain-basic-some-ns@kubernetescrd + + - it: should set correct namespace when ingressClassName is defined regardless of operator + set: + operator: + verify: + enabled: false + traefik: + namespace: some-ns + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + ingressClassName: some-class + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.middlewares: chain-basic-some-class@kubernetescrd + + - it: should set correct namespace when ingressClassName is defined in SCALE regardless of operator + set: + operator: + verify: + enabled: false + traefik: + namespace: some-ns + global: + ixChartContext: + imNotEmpty: true + namespace: ix-namespace + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + ingressClassName: some-class + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.middlewares: chain-basic-ix-some-class@kubernetescrd + + - it: should add the defined middlewares to the ingress + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + middlewares: + - some-middleware + - some-other-middleware + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.middlewares: chain-basic-tc-system@kubernetescrd,some-middleware-tc-system@kubernetescrd,some-other-middleware-tc-system@kubernetescrd + + - it: should disable traefik annotations in ingress + set: + operator: *operator + global: + traefik: + addIngressAnnotations: false + ingress: + my-ingress: + enabled: true + primary: true + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + isNull: + path: metadata.annotations + + - it: should override global disable for traefik annotations in ingress + set: + operator: *operator + global: + traefik: + addIngressAnnotations: false + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.middlewares: chain-basic-tc-system@kubernetescrd + + - it: should override global disable for traefik annotations in ingress + set: + operator: *operator + global: + traefik: + addIngressAnnotations: true + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: false + asserts: + - documentIndex: *ingressDoc + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + isNull: + path: metadata.annotations + + # Failures + - it: should fail with entrypoint not a slice + set: + operator: *operator + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + traefik: + enabled: true + entrypoints: "not a string" + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [integrations.traefik.entrypoints] to be a [slice], but got [string] + + - it: should fail with middlewares not a slice + set: + operator: *operator + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + traefik: + enabled: true + middlewares: "not a slice" + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [integrations.traefik.middlewares] to be a [slice], but got [string] + + - it: should fail with fixedMiddlewares not a slice + set: + operator: *operator + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + traefik: + enabled: true + fixedMiddlewares: "not a slice" + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [integrations.traefik.fixedMiddlewares] to be a [slice], but got [string] + + - it: should fail with duplicate middlewares + set: + operator: *operator + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + traefik: + enabled: true + middlewares: + - chain-basic + asserts: + - failedTemplate: + errorMessage: Ingress - Combined traefik middlewares contain duplicates [chain-basic, chain-basic] + + - it: should fail with duplicate entrypoints + set: + operator: *operator + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + traefik: + enabled: true + entrypoints: + - websecure + - websecure + asserts: + - failedTemplate: + errorMessage: Ingress - Combined traefik entrypoints contain duplicates [websecure, websecure] diff --git a/library/common/templates/class/_ingress.tpl b/library/common/templates/class/_ingress.tpl index b17583df..fe40ecdd 100644 --- a/library/common/templates/class/_ingress.tpl +++ b/library/common/templates/class/_ingress.tpl @@ -20,7 +20,7 @@ objectData: The object data to be used to render the Ingress. {{- $_ := set $objectData "annotations" dict -}} {{- end -}} - {{- include "tc.v1.common.lib.ingress.integration.certManager" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} + {{- /* include "tc.v1.common.lib.ingress.integration.certManager" (dict "rootCtx" $rootCtx "objectData" $objectData) */ -}} {{- include "tc.v1.common.lib.ingress.integration.traefik" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} {{- include "tc.v1.common.lib.ingress.integration.homepage" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} {{/* TODO: fix last - */}} diff --git a/library/common/templates/lib/ingress/integrations/_traefik.tpl b/library/common/templates/lib/ingress/integrations/_traefik.tpl new file mode 100644 index 00000000..d40d9e0d --- /dev/null +++ b/library/common/templates/lib/ingress/integrations/_traefik.tpl @@ -0,0 +1,121 @@ +{{- define "tc.v1.common.lib.ingress.integration.traefik" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + + {{- $traefik := $objectData.integrations.traefik -}} + + {{- $enabled := false -}} + {{- if (hasKey $rootCtx.Values.global "traefik") -}} + {{- $enabled = $rootCtx.Values.global.traefik.addIngressAnnotations -}} + {{- end -}} + + {{- if and $traefik (kindIs "bool" $traefik.enabled) -}} + {{- $enabled = $traefik.enabled -}} + {{- end -}} + + {{- if $enabled -}} + {{- include "tc.v1.common.lib.ingress.integration.traefik.validate" (dict "objectData" $objectData) -}} + + {{- $fixedMiddlewares := list -}} + {{- $enableFixed := false -}} + {{- if (hasKey $rootCtx.Values.global "traefik") -}} + {{- $fixedMiddlewares = $rootCtx.Values.global.traefik.fixedMiddlewares -}} + {{- $enableFixed = $rootCtx.Values.global.traefik.enableFixedMiddlewares -}} + {{- end -}} + + {{/* Override global (enable)fixedMiddlewares with local */}} + {{- if $traefik.fixedMiddlewares -}} + {{- $fixedMiddlewares = $traefik.fixedMiddlewares -}} + {{- end -}} + + {{/* Replace global fixed with local fixed */}} + {{- if and (hasKey $traefik "enableFixedMiddlewares") (kindIs "bool" $traefik.enableFixedMiddlewares) -}} + {{- $enableFixed = $traefik.enableFixedMiddlewares -}} + {{- end -}} + + {{/* Replace global and local fixed middlewares with the opencors-chain */}} + {{- if $traefik.allowCors -}} + {{- $fixedMiddlewares = list "tc-opencors-chain" -}} + {{- end -}} + + {{- $entrypoints := $traefik.entrypoints | default (list "websecure") -}} + {{- $middlewares := list -}} + + {{/* Add the fixedMiddlewares */}} + {{- if and $enableFixed $fixedMiddlewares -}} + {{- $middlewares = concat $middlewares $fixedMiddlewares -}} + {{- end -}} + + {{/* Add the user middlewares */}} + {{- if $traefik.middlewares -}} + {{- $middlewares = concat $middlewares $traefik.middlewares -}} + {{- end -}} + + {{/* Make sure we dont have dupes */}} + {{- if $middlewares -}} + {{- if not (deepEqual (mustUniq $middlewares) $middlewares) -}} + {{- fail (printf "Ingress - Combined traefik middlewares contain duplicates [%s]" (join ", " $middlewares)) -}} + {{- end -}} + {{- end -}} + + {{- if not (deepEqual (mustUniq $entrypoints) $entrypoints) -}} + {{- fail (printf "Ingress - Combined traefik entrypoints contain duplicates [%s]" (join ", " $entrypoints)) -}} + {{- end -}} + + {{- $midNamespace := "tc-system" -}} + {{/* If our hook has set operator.traefik.namespace, use that */}} + {{- if (hasKey $rootCtx.Values.operator "traefik") -}} + {{- if $rootCtx.Values.operator.traefik.namespace -}} + {{- $midNamespace = $rootCtx.Values.operator.traefik.namespace -}} + {{- end -}} + {{- end -}} + + {{- if $traefik.ingressClassName -}} + {{- $midNamespace = $traefik.ingressClassName -}} + + {{/* On SCALE prepend with ix- */}} + {{- if $rootCtx.Values.global.ixChartContext -}} + {{- $midNamespace = (printf "ix-%s" $midNamespace) -}} + {{- end -}} + {{- end -}} + + {{/* Format middlewares */}} + {{- $formMiddlewares := list -}} + {{- range $mid := $middlewares -}} + {{- $formMiddlewares = mustAppend $formMiddlewares (printf "%s-%s@kubernetescrd" $mid $midNamespace) -}} + {{- end -}} + + {{- $_ := set $objectData.annotations "traefik.ingress.kubernetes.io/router.entrypoints" (join "," $entrypoints) -}} + {{- if $formMiddlewares -}} + {{- $_ := set $objectData.annotations "traefik.ingress.kubernetes.io/router.middlewares" (join "," $formMiddlewares) -}} + {{- end -}} + + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.lib.ingress.integration.traefik.validate" -}} + {{- $objectData := .objectData -}} + + {{- $traefik := $objectData.integrations.traefik -}} + + {{- if $traefik -}} + {{- if $traefik.entrypoints -}} + {{- if not (kindIs "slice" $traefik.entrypoints) -}} + {{- fail (printf "Ingress - Expected [integrations.traefik.entrypoints] to be a [slice], but got [%s]" (kindOf $traefik.entrypoints)) -}} + {{- end -}} + {{- end -}} + + {{- if $traefik.middlewares -}} + {{- if not (kindIs "slice" $traefik.middlewares) -}} + {{- fail (printf "Ingress - Expected [integrations.traefik.middlewares] to be a [slice], but got [%s]" (kindOf $traefik.middlewares)) -}} + {{- end -}} + {{- end -}} + + {{- if $traefik.fixedMiddlewares -}} + {{- if not (kindIs "slice" $traefik.fixedMiddlewares) -}} + {{- fail (printf "Ingress - Expected [integrations.traefik.fixedMiddlewares] to be a [slice], but got [%s]" (kindOf $traefik.fixedMiddlewares)) -}} + {{- end -}} + {{- end -}} + + {{- end -}} +{{- end -}} diff --git a/library/common/values.yaml b/library/common/values.yaml index ba8d43d9..3a1d03b6 100644 --- a/library/common/values.yaml +++ b/library/common/values.yaml @@ -13,6 +13,8 @@ global: traefik: # -- Adds traefik annotations to services (when needed) addServiceAnnotations: true + # -- Enable or disable traefik ingress integration, can be overruled per ingress + addIngressAnnotations: true # Enables or disables the fixed middlewares on all ingresses # Can be overrided per ingress enableFixedMiddlewares: true @@ -532,77 +534,23 @@ ingress: main: # -- Enables or disables the ingress enabled: false - - # -- Adds integrations to ingress -# integration: -# homepage: -# enabled: true -# # Default: chart name -# name: somename -# # Default: chart description -# description: some description -# group: somegroup -# # Default: chart icon -# icon: icon.png -# pod-selector: "" -# widget: -# # Default: chartname -# type: "sometype" -# # Default: host-path of first ingress -# url: "https://example.com" -# custom: -# - somesetting: some value - # -- Make this the primary ingress (used in probes, notes, etc...). # If there is more than 1 ingress, make sure that only 1 ingress is marked as primary. primary: true - # -- Ensure this ingress is always enabled. required: false - - # -- Override the name suffix that is used for this ingress. - nameOverride: - - # -- Autolink the ingress to a service and port, both with the same name as the ingress. - autoLink: false - - # -- disable to ignore any default middlwares - enableFixedMiddlewares: true - - # -- set the Cert-Manager clusterissuer for this ingress - clusterIssuer: "" - - # -- List of middlewares in the traefikmiddlewares k8s namespace to add automatically - # Creates an annotation with the middlewares and appends k8s and traefik namespaces to the middleware names - # Primarily used for TrueNAS SCALE to add additional (seperate) middlewares without exposing them to the end-user - - - # -- Additional List of middlewares in the traefikmiddlewares k8s namespace to add automatically - # Creates an annotation with the middlewares and appends k8s and traefik namespaces to the middleware names - middlewares: [] - annotationsList: [] - # - name: somename - # value: somevalue - # -- Provide additional annotations which may be required. - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - - labelsList: [] - # - name: somename - # value: somevalue - # -- Set labels on the deployment/statefulset/daemonset - # -- Provide additional labels which may be required. + expandObjectName: false # -- Provide additional labels which may be required. labels: {} - + # -- Provide additional annotations which may be required. + annotations: {} # -- Set the ingressClass that is used for this ingress. # Requires Kubernetes >=1.19 ingressClassName: # "nginx" - - # Enable or disable CORS Requests to the ingress - allowCors: false - + # Defaults to primary service and primary port + # targetSelector: + # # service: port + # main: main ## Configure the hosts for the ingress hosts: - # -- Host address. Helm template can be passed. @@ -613,12 +561,12 @@ ingress: path: / # -- Ignored if not kubeVersion >= 1.14-0 pathType: Prefix - service: + # -- Overrides the service reference for this path, by default the selector is honored + overrideService: # -- Overrides the service name reference for this path name: # -- Overrides the service port reference for this path port: - # -- Configure TLS for the ingress. Both secretName and hosts can process a Helm template. # Gets ignored when clusterIssuer is filled tls: [] @@ -629,6 +577,61 @@ ingress: # scaleCert: "" # hosts: # - chart-example.local + integrations: + traefik: + enabled: true + # Default to websecure + entrypoints: + - websecure + enableFixedMiddlewares: true + # Drops both global and local fixedMiddlewares when enabled + allowCors: false + fixedMiddlewares: + - chain-basic + middlewares: [] + certManager: + enabled: true + clusterIssuer: "" + # -- Adds integrations to ingress + integration: + homepage: + # enabled: true + # # Default: chart name + # name: somename + # # Default: chart description + # description: some description + # # Default: no group + # group: somegroup + # # Default: chart icon + # icon: icon.png + # widget: + # # Default: chartname + # type: "sometype" + # # Default to ingress host 0 + # url: "https://example.com" + # custom: + # - somesetting: some value + traefik: + # -- Enables or disables the traefik integration (enabled if not defined) + enabled: true + # -- List of entrypoints to add, Defaults to websecure + entrypoints: + - websecure + # -- disable to ignore any default middelwares + enableFixedMiddlewares: true + # -- Additional List of middlewares in the traefik middlewares k8s namespace to add automatically + # Creates an annotation with the middlewares and appends k8s and traefik namespaces to the middleware names + middlewares: [] + # Replaces global fixed middlewares + fixedMiddlewares: [] + # Enable or disable CORS Requests to the ingress + # Drops both global and local fixedMiddlewares when enabled + allowCors: false + certManager: + # -- Enables or disables the cert-manager integration (enabled if not defined) + enabled: true + # -- set the Cert-Manager clusterissuer for this ingress + clusterIssuer: "" # -- BETA: Configure the gateway routes for the chart here. # Additional routes can be added by adding a dictionary key similar to the 'main' route.