diff --git a/library/common-test/Chart.yaml b/library/common-test/Chart.yaml index 34a2e909..31dc8d5e 100644 --- a/library/common-test/Chart.yaml +++ b/library/common-test/Chart.yaml @@ -3,7 +3,7 @@ appVersion: "" dependencies: - name: common repository: file://../common - version: ~16.0.0 + version: ~16.1.0 deprecated: false description: Helper chart to test different use cases of the common library home: https://github.com/truecharts/apps/tree/master/charts/library/common-test diff --git a/library/common-test/ci/ingress-values.yaml b/library/common-test/ci/ingress-values.yaml index ac6f17ef..2cb68375 100644 --- a/library/common-test/ci/ingress-values.yaml +++ b/library/common-test/ci/ingress-values.yaml @@ -53,9 +53,6 @@ ingress: paths: - path: / pathType: Prefix - service: - name: - port: tls: [] scalecert: diff --git a/library/common-test/tests/ingress/cert_manager_test.yaml b/library/common-test/tests/ingress/cert_manager_test.yaml new file mode 100644 index 00000000..62c8b52f --- /dev/null +++ b/library/common-test/tests/ingress/cert_manager_test.yaml @@ -0,0 +1,115 @@ +suite: ingress - cert manager 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 cert manager + set: + operator: &operator + verify: + enabled: false + service: &service + my-service: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + port: 80 + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: &traefik + enabled: false + certManager: + enabled: true + certificateIssuer: some-issuer + hosts: &hosts + - host: test-host + paths: + - path: /test-path + asserts: + - documentIndex: &ingressDoc 1 + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + cert-manager.io/cluster-issuer: some-issuer + cert-manager.io/private-key-rotation-policy: Always + - documentIndex: *ingressDoc + equal: + path: metadata.namespace + value: test-release-namespace + + - it: should pass with ingress created without cert manager annotations when cert manager false + set: + operator: *operator + service: *service + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: *traefik + certManager: + enabled: false + hosts: *hosts + 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 missing certificateIssuer + set: + operator: *operator + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + certManager: + enabled: true + hosts: *hosts + asserts: + - failedTemplate: + errorMessage: Ingress - Expected a non-empty [integrations.certManager.certificateIssuer] + + - it: should fail with certificateIssuer not a string + set: + operator: *operator + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + certManager: + enabled: true + certificateIssuer: + - some-issuer + hosts: *hosts + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [integrations.certManager.certificateIssuer] to be a [string], but got [slice] diff --git a/library/common-test/tests/ingress/homepage_test.yaml b/library/common-test/tests/ingress/homepage_test.yaml new file mode 100644 index 00000000..1ad45c69 --- /dev/null +++ b/library/common-test/tests/ingress/homepage_test.yaml @@ -0,0 +1,152 @@ +suite: ingress - homepage 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 homepage enabled + set: + k1: some-name + k2: some-desc + k3: some-icon + k4: some-url + k5: some-type + k6: some-group + operator: &operator + verify: + enabled: false + service: &service + my-service: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + port: 80 + someHost: test-host + somePath: /test-path + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + traefik: &traefik + enabled: false + homepage: + enabled: true + hosts: + - host: "{{ .Values.someHost }}" + paths: + - path: "{{ .Values.somePath }}" + my-ingress2: + enabled: true + integrations: + traefik: *traefik + homepage: + enabled: true + name: "{{ .Values.k1 }}" + description: "{{ .Values.k2 }}" + icon: "{{ .Values.k3 }}" + group: "{{ .Values.k6 }}" + weight: 1 + podSelector: + - main + - other + widget: + url: "{{ .Values.k4 }}" + type: "{{ .Values.k5 }}" + custom: + some-key: some-value + some-other-key: some-other-value + hosts: &hosts + - host: test-host + paths: + - path: /test-path + asserts: + - documentIndex: &ingressDoc 1 + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.annotations + value: + gethomepage.dev/enabled: "true" + gethomepage.dev/name: CommonTest + gethomepage.dev/description: Helper chart to test different use cases of the common library + gethomepage.dev/icon: https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png + gethomepage.dev/widget.type: common-test + gethomepage.dev/widget.url: "https://test-host/test-path" + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.namespace + value: test-release-namespace + - documentIndex: &otherIngressDoc 2 + isKind: + of: Ingress + - documentIndex: *otherIngressDoc + equal: + path: metadata.annotations + value: + gethomepage.dev/enabled: "true" + gethomepage.dev/name: some-name + gethomepage.dev/description: some-desc + gethomepage.dev/group: some-group + gethomepage.dev/icon: some-icon + gethomepage.dev/widget.url: some-url + gethomepage.dev/weight: "1" + gethomepage.dev/widget.type: some-type + gethomepage.dev/pod-selector: pod.name in (main,other) + gethomepage.dev/widget.some-key: some-value + gethomepage.dev/widget.some-other-key: some-other-value + - documentIndex: *otherIngressDoc + equal: + path: metadata.name + value: test-release-name-common-test-my-ingress2 + - documentIndex: *otherIngressDoc + equal: + path: metadata.namespace + value: test-release-namespace + + # Failures + - it: should fail with podSelector not a slice + set: + operator: *operator + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + homepage: + enabled: true + podSelector: main + hosts: *hosts + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [integrations.homepage.podSelector] to be a [slice], but got [string] + + - it: should fail with custom widget not a map + set: + operator: *operator + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + homepage: + enabled: true + widget: + custom: "not a map" + hosts: *hosts + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [integrations.homepage.widget.custom] to be a [map], but got [string] diff --git a/library/common-test/tests/ingress/metadata_test.yaml b/library/common-test/tests/ingress/metadata_test.yaml index 5c4cfeae..ad3fc5f7 100644 --- a/library/common-test/tests/ingress/metadata_test.yaml +++ b/library/common-test/tests/ingress/metadata_test.yaml @@ -1,4 +1,4 @@ -suite: ingress metadata +suite: ingress metadata test templates: - common.yaml chart: @@ -7,135 +7,66 @@ release: name: test-release-name namespace: test-release-namespace tests: - - it: default metadata should pass + - it: should pass with ingress created with labels and annotations set: - operator: + label1: label1 + label2: global_label2 + annotation1: annotation1 + annotation2: global_annotation2 + global: + labels: + g_label1: global_label1 + g_label2: "{{ .Values.label2 }}" + annotations: + g_annotation1: global_annotation1 + g_annotation2: "{{ .Values.annotation2 }}" + operator: &operator verify: enabled: false - ingress.main.enabled: true - service: - main: + service: &service + my-service: enabled: true + primary: true ports: main: enabled: true primary: true - port: 12345 - workload: - my-workload: - enabled: true - primary: true - type: Deployment - podSpec: {} - asserts: - - documentIndex: &ingressDocument 2 - isKind: - of: Ingress - - documentIndex: *ingressDocument - equal: - path: metadata.annotations - value: - traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.middlewares: tc-system-chain-basic@kubernetescrd - - - documentIndex: *ingressDocument - equal: - path: metadata.labels - value: - app: common-test-1.0.0 - release: test-release-name - helm-revision: "0" - helm.sh/chart: common-test-1.0.0 - app.kubernetes.io/name: common-test - app.kubernetes.io/instance: test-release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/version: *appVer - - - it: cors metadata should pass - set: - operator: - verify: - enabled: false + port: 80 ingress: - main: - enabled: true - allowCors: true - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: + my-ingress1: enabled: true primary: true - type: Deployment - podSpec: {} - asserts: - - documentIndex: &ingressDocument 2 - isKind: - of: Ingress - - documentIndex: *ingressDocument - equal: - path: metadata.annotations - value: - traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.middlewares: tc-system-tc-opencors-chain@kubernetescrd - - - documentIndex: *ingressDocument - equal: - path: metadata.labels - value: - app: common-test-1.0.0 - release: test-release-name - helm-revision: "0" - helm.sh/chart: common-test-1.0.0 - app.kubernetes.io/name: common-test - app.kubernetes.io/instance: test-release-name - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/version: *appVer - - - it: custom metadata should pass - set: - operator: - verify: - enabled: false - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: - enabled: true - primary: true - type: Deployment - podSpec: {} - ingress: - main: - enabled: true - annotations: - test_annotation: test labels: - test_label: test + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + integrations: &integrations + traefik: + enabled: false + hosts: &hosts + - host: test-host + paths: + - path: /test-path + my-ingress2: + enabled: true + primary: false + integrations: *integrations + hosts: *hosts asserts: - - documentIndex: &ingressDocument 2 + - documentIndex: &ingressDoc 1 isKind: of: Ingress - - documentIndex: *ingressDocument + - documentIndex: *ingressDoc equal: path: metadata.annotations value: - test_annotation: test - traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.middlewares: tc-system-chain-basic@kubernetescrd - - documentIndex: *ingressDocument + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *ingressDoc equal: path: metadata.labels value: @@ -143,8 +74,121 @@ tests: release: test-release-name helm-revision: "0" helm.sh/chart: common-test-1.0.0 - app.kubernetes.io/name: common-test - app.kubernetes.io/instance: test-release-name app.kubernetes.io/managed-by: Helm app.kubernetes.io/version: *appVer - test_label: test + app.kubernetes.io/instance: test-release-name + app.kubernetes.io/name: common-test + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: metadata.namespace + value: test-release-namespace + - documentIndex: &otherIngressDoc 2 + isKind: + of: Ingress + - documentIndex: *otherIngressDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + app.kubernetes.io/instance: test-release-name + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: common-test + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + release: test-release-name + - documentIndex: *otherIngressDoc + equal: + path: metadata.name + value: test-release-name-common-test-my-ingress2 + - documentIndex: *otherIngressDoc + equal: + path: metadata.namespace + value: test-release-namespace + + - it: should pass with ingress created with object namespace from tpl + set: + key: some-namespace + operator: *operator + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + namespace: "{{ .Values.key }}" + integrations: *integrations + hosts: *hosts + asserts: + - documentIndex: *ingressDoc + equal: + path: metadata.namespace + value: some-namespace + + - it: should pass with ingress created with global namespace from tpl + set: + key: global-namespace + global: + namespace: "{{ .Values.key }}" + operator: *operator + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + integrations: *integrations + hosts: *hosts + asserts: + - documentIndex: *ingressDoc + equal: + path: metadata.namespace + value: global-namespace + + - it: should pass with ingress created with root namespace from tpl + set: + key: local-namespace + namespace: "{{ .Values.key }}" + global: + namespace: global-namespace + operator: *operator + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + integrations: *integrations + hosts: *hosts + asserts: + - documentIndex: *ingressDoc + equal: + path: metadata.namespace + value: local-namespace + + - it: should pass with ingress created with namespace in TrueNAS SCALE + set: + global: + ixChartContext: + iAmNotEmpty: true + namespace: ix-namespace + operator: *operator + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + integrations: *integrations + hosts: *hosts + asserts: + - documentIndex: *ingressDoc + equal: + path: metadata.namespace + value: ix-namespace diff --git a/library/common-test/tests/ingress/name_test.yaml b/library/common-test/tests/ingress/name_test.yaml new file mode 100644 index 00000000..6f36ded8 --- /dev/null +++ b/library/common-test/tests/ingress/name_test.yaml @@ -0,0 +1,67 @@ +suite: ingress name test +templates: + - common.yaml +release: + name: test-release-name + namespace: test-release-namespace +tests: + - it: should generate correct name + set: + operator: + verify: + enabled: false + service: + my-service: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + port: 80 + ingress: + my-ingress1: + enabled: true + primary: true + hosts: &hosts + - host: test-host + paths: + - path: /test-path + my-ingress2: + enabled: true + hosts: *hosts + my-ingress3: + enabled: true + expandObjectName: false + hosts: *hosts + asserts: + - documentIndex: &ingressDoc 1 + isKind: + of: Ingress + - documentIndex: *ingressDoc + isAPIVersion: + of: networking.k8s.io/v1 + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: &otherIngressDoc 2 + isKind: + of: Ingress + - documentIndex: *otherIngressDoc + isAPIVersion: + of: networking.k8s.io/v1 + - documentIndex: *otherIngressDoc + equal: + path: metadata.name + value: test-release-name-common-test-my-ingress2 + - documentIndex: &thirdIngressDoc 3 + isKind: + of: Ingress + - documentIndex: *thirdIngressDoc + isAPIVersion: + of: networking.k8s.io/v1 + - documentIndex: *thirdIngressDoc + equal: + path: metadata.name + value: my-ingress3 diff --git a/library/common-test/tests/ingress/presence_test.yaml b/library/common-test/tests/ingress/presence_test.yaml deleted file mode 100644 index 43328c63..00000000 --- a/library/common-test/tests/ingress/presence_test.yaml +++ /dev/null @@ -1,93 +0,0 @@ -suite: ingress presence -templates: - - common.yaml -release: - name: test-release-name - namespace: test-release-namespace -tests: - - it: default should pass - asserts: - - hasDocuments: - count: 0 - - - - it: explicitly disabled should pass - set: - ingress.main.enabled: false - asserts: - - hasDocuments: - count: 0 - - - it: explicitly enabled should pass - set: - operator: - verify: - enabled: false - ingress.main.enabled: true - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: - enabled: true - primary: true - type: Deployment - podSpec: {} - asserts: - - hasDocuments: - count: 3 - - documentIndex: 0 - not: true - isKind: - of: Ingress - - documentIndex: 1 - not: true - isKind: - of: Ingress - - documentIndex: 2 - isKind: - of: Ingress - - - it: multiple enabled should pass - set: - operator: - verify: - enabled: false - ingress.main.enabled: true - ingress.test.enabled: true - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: - enabled: true - primary: true - type: Deployment - podSpec: {} - asserts: - - hasDocuments: - count: 4 - - documentIndex: 0 - not: true - isKind: - of: Ingress - - documentIndex: 1 - not: true - isKind: - of: Ingress - - documentIndex: 2 - isKind: - of: Ingress - - documentIndex: 3 - isKind: - of: Ingress diff --git a/library/common-test/tests/ingress/rules_test.yaml b/library/common-test/tests/ingress/rules_test.yaml new file mode 100644 index 00000000..eda68c2e --- /dev/null +++ b/library/common-test/tests/ingress/rules_test.yaml @@ -0,0 +1,199 @@ +suite: ingress - rules 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 ingressClassName + set: + operator: &operator + verify: + enabled: false + service: + my-service: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + port: 80 + ingress: + my-ingress: + enabled: true + primary: true + ingressClassName: some-class + hosts: + - host: test-host + paths: + - path: /test-path + integrations: &integrations + traefik: + enabled: false + asserts: + - documentIndex: &ingressDoc 1 + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: spec.ingressClassName + value: some-class + + - it: should pass with ingress created with rules + set: + operator: &operator + verify: + enabled: false + service: + my-service: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + port: 80 + someHost: test-host + somePath: /test-path + someType: Exact + ingress: + my-ingress: + enabled: true + primary: true + hosts: &hosts + - host: "{{ .Values.someHost }}" + paths: + - path: "{{ .Values.somePath }}" + pathType: "{{ .Values.someType }}" + - host: test-other-host + paths: + - path: /test-other-path + - path: /test-other-path2 + overrideService: + name: other-service + port: 8080 + integrations: *integrations + asserts: + - documentIndex: &ingressDoc 1 + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: spec.rules + value: + - host: test-host + http: + paths: + - path: /test-path + pathType: Exact + backend: + service: + name: test-release-name-common-test + port: + number: 80 + - host: test-other-host + http: + paths: + - path: /test-other-path + pathType: Prefix + backend: + service: + name: test-release-name-common-test + port: + number: 80 + - path: /test-other-path2 + pathType: Prefix + backend: + service: + name: other-service + port: + number: 8080 + + - it: should pass with ingress created with rules with targetSelector + set: + operator: &operator + verify: + enabled: false + service: + my-service: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + port: 80 + my-service2: + enabled: true + ports: + my-port2: + enabled: true + port: 9000 + ingress: + my-ingress: + enabled: true + primary: true + targetSelector: + my-service2: my-port2 + hosts: &hosts + - host: test-host + paths: + - path: /test-path + - host: test-other-host + paths: + - path: /test-other-path + - path: /test-other-path2 + overrideService: + name: other-service + port: 8080 + integrations: *integrations + asserts: + - documentIndex: &ingressDoc 2 + isKind: + of: Ingress + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: spec.rules + value: + - host: test-host + http: + paths: + - path: /test-path + pathType: Prefix + backend: + service: + name: test-release-name-common-test-my-service2 + port: + number: 9000 + - host: test-other-host + http: + paths: + - path: /test-other-path + pathType: Prefix + backend: + service: + name: test-release-name-common-test-my-service2 + port: + number: 9000 + - path: /test-other-path2 + pathType: Prefix + backend: + service: + name: other-service + port: + number: 8080 diff --git a/library/common-test/tests/ingress/service_reference_test.yaml b/library/common-test/tests/ingress/service_reference_test.yaml deleted file mode 100644 index 0fe2042f..00000000 --- a/library/common-test/tests/ingress/service_reference_test.yaml +++ /dev/null @@ -1,79 +0,0 @@ -suite: ingress service reference -templates: - - common.yaml -release: - name: test-release-name - namespace: test-release-namespace -tests: - - it: default should pass - set: - operator: - verify: - enabled: false - ingress.main.enabled: true - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: - enabled: true - primary: true - type: Deployment - podSpec: {} - asserts: - - documentIndex: &ingressDocument 2 - isKind: - of: Ingress - - documentIndex: *ingressDocument - equal: - path: spec.rules[0].http.paths[0].backend.service - value: - name: test-release-name-common-test - port: - number: 12345 - - - - it: custom service reference should pass - set: - operator: - verify: - enabled: false - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: - enabled: true - primary: true - type: Deployment - podSpec: {} - ingress.main: - enabled: true - hosts: - - host: chart-test.local - paths: - - path: / - service: - name: pathService - port: 1234 - asserts: - - documentIndex: &ingressDocument 2 - isKind: - of: Ingress - - documentIndex: *ingressDocument - equal: - path: spec.rules[0].http.paths[0].backend.service - value: - name: pathService - port: - number: 1234 diff --git a/library/common-test/tests/ingress/tls_test.yaml b/library/common-test/tests/ingress/tls_test.yaml index d07b8bbc..089ef69f 100644 --- a/library/common-test/tests/ingress/tls_test.yaml +++ b/library/common-test/tests/ingress/tls_test.yaml @@ -1,143 +1,195 @@ -suite: ingress tls +suite: ingress - tls test templates: - common.yaml +chart: + appVersion: &appVer v9.9.9 release: name: test-release-name namespace: test-release-namespace tests: - - it: default should pass + - it: should pass with ingress created with certManager integration set: - operator: + operator: &operator verify: enabled: false - service: - main: + service: &service + my-service: enabled: true + primary: true ports: main: enabled: true primary: true - port: 12345 - workload: - my-workload: + port: 80 + ingress: + my-ingress: enabled: true primary: true - type: Deployment - podSpec: {} - ingress.main.enabled: true + hosts: + - host: test-host + paths: + - path: /test-path + - host: other-test-host + paths: + - path: /other-test-path + integrations: &integrations + traefik: + enabled: false + certManager: + enabled: true + certificateIssuer: some-issuer asserts: - - documentIndex: &ingressDocument 2 + - documentIndex: &ingressDoc 1 isKind: of: Ingress - - documentIndex: *ingressDocument - isNull: + - documentIndex: *ingressDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: path: spec.tls + value: + - hosts: + - test-host + secretName: test-release-name-common-test-tls-0 + - hosts: + - other-test-host + secretName: test-release-name-common-test-tls-1 - - it: tls enabled should pass + - it: should pass with ingress created with tls with scaleCert set: - operator: - verify: - enabled: false - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: + operator: *operator + service: *service + namespace: ix-test-namespace + global: + ixChartContext: + imNotEmpty: true + ixCertificates: + "1": + certificate: some_cert + privatekey: some_key + "2": + certificate: some_other_cert + privatekey: some_other_key + ingress: + my-ingress: enabled: true primary: true - type: Deployment - podSpec: {} - ingress.main: - enabled: true - tls: - - secretName: test - hosts: - - hostname + hosts: + - host: test-host + paths: + - path: /test-path + - host: other-test-host + paths: + - path: /other-test-path + tls: + - hosts: + - test-host + - other-test-host + scaleCert: "1" + - hosts: + - some-other-test-host + scaleCert: "2" + integrations: &integrations + traefik: + enabled: false asserts: - - documentIndex: &ingressDocument 2 + - documentIndex: &secretDoc 2 + isKind: + of: Secret + - documentIndex: *secretDoc + equal: + path: metadata.name + value: test-release-name-common-test-scale-tls-0 + - documentIndex: *secretDoc + equal: + path: data + value: + tls.crt: c29tZV9jZXJ0 + tls.key: c29tZV9rZXk= + - documentIndex: *secretDoc + equal: + path: type + value: kubernetes.io/tls + - documentIndex: &secretDoc 3 + isKind: + of: Secret + - documentIndex: *secretDoc + equal: + path: metadata.name + value: test-release-name-common-test-scale-tls-1 + - documentIndex: *secretDoc + equal: + path: data + value: + tls.crt: c29tZV9vdGhlcl9jZXJ0 + tls.key: c29tZV9vdGhlcl9rZXk= + - documentIndex: *secretDoc + equal: + path: type + value: kubernetes.io/tls + - documentIndex: &ingressDoc 1 isKind: of: Ingress - - documentIndex: *ingressDocument + - documentIndex: *ingressDoc equal: - path: spec.tls[0] + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc + equal: + path: spec.tls value: - secretName: test - hosts: - - hostname + - hosts: + - test-host + - other-test-host + secretName: test-release-name-common-test-scale-tls-0 + - hosts: + - some-other-test-host + secretName: test-release-name-common-test-scale-tls-1 - - it: tls enabled without secret should pass + - it: should pass with ingress created with tls with certificateIssuer set: - operator: - verify: - enabled: false - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: + operator: *operator + service: *service + ingress: + my-ingress: enabled: true primary: true - type: Deployment - podSpec: {} - ingress.main: - enabled: true - tls: - - hosts: - - hostname + hosts: + - host: test-host + paths: + - path: /test-path + - host: other-test-host + paths: + - path: /other-test-path + tls: + - hosts: + - test-host + - other-test-host + certificateIssuer: some-issuer + - hosts: + - some-other-test-host + certificateIssuer: some-other-issuer + integrations: &integrations + traefik: + enabled: false asserts: - - documentIndex: &ingressDocument 2 + - documentIndex: &ingressDoc 1 isKind: of: Ingress - - documentIndex: *ingressDocument + - documentIndex: *ingressDoc equal: - path: spec.tls[0] - value: - hosts: - - hostname - - - it: tls enabled with secret template should pass - set: - operator: - verify: - enabled: false - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: - enabled: true - primary: true - type: Deployment - podSpec: {} - ingress.main: - enabled: true - tls: - - secretName: "{{ .Release.Name }}-secret" - hosts: - - hostname - asserts: - - documentIndex: &ingressDocument 2 - isKind: - of: Ingress - - documentIndex: *ingressDocument + path: metadata.name + value: test-release-name-common-test + - documentIndex: *ingressDoc equal: - path: spec.tls[0] + path: spec.tls value: - secretName: test-release-name-secret - hosts: - - hostname + - hosts: + - test-host + - other-test-host + secretName: test-release-name-common-test-tls-0 + - hosts: + - some-other-test-host + secretName: test-release-name-common-test-tls-1 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..2eb69a5f --- /dev/null +++ b/library/common-test/tests/ingress/traefik_test.yaml @@ -0,0 +1,466 @@ +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 + set: + operator: &operator + verify: + enabled: false + service: &service + my-service: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + port: 80 + ingress: + my-ingress: + enabled: true + primary: true + hosts: &hosts + - host: test-host + paths: + - path: /test-path + asserts: + - documentIndex: &ingressDoc 1 + 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 + service: *service + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: false + hosts: *hosts + 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 + service: *service + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + allowCors: true + fixedMiddlewares: + - some-fixed-middleware + hosts: *hosts + 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 + service: *service + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + allowCors: true + hosts: *hosts + 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 + service: *service + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + fixedMiddlewares: + - some-fixed-middleware + hosts: *hosts + 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 + service: *service + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + entrypoints: + - web + - websecure + hosts: *hosts + 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 + service: *service + global: + traefik: + enableFixedMiddlewares: false + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + hosts: *hosts + 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 + service: *service + global: + traefik: + enableFixedMiddlewares: true + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + enableFixedMiddlewares: false + hosts: *hosts + 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 + service: *service + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + entrypoints: + - web + - websecure + hosts: *hosts + 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 + someclass: some-class + service: *service + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + ingressClassName: "{{ .Values.someclass }}" + hosts: *hosts + 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 + service: *service + global: + ixChartContext: + imNotEmpty: true + namespace: ix-namespace + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + ingressClassName: some-class + hosts: *hosts + 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 + service: *service + ingress: + my-ingress: + enabled: true + primary: true + integrations: + traefik: + enabled: true + middlewares: + - some-middleware + - some-other-middleware + hosts: *hosts + 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 + + # Failures + - it: should fail with entrypoint not a slice + set: + operator: *operator + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + traefik: + enabled: true + entrypoints: "not a string" + hosts: *hosts + 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 + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + traefik: + enabled: true + middlewares: "not a slice" + hosts: *hosts + 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 + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + traefik: + enabled: true + fixedMiddlewares: "not a slice" + hosts: *hosts + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [integrations.traefik.fixedMiddlewares] to be a [slice], but got [string] + + - it: should fail with duplicate middlewares + set: + operator: *operator + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + traefik: + enabled: true + middlewares: + - chain-basic + hosts: *hosts + asserts: + - failedTemplate: + errorMessage: Ingress - Combined traefik middlewares contain duplicates [chain-basic, chain-basic] + + - it: should fail with duplicate entrypoints + set: + operator: *operator + service: *service + ingress: + my-ingress1: + enabled: true + primary: true + integrations: + traefik: + enabled: true + entrypoints: + - websecure + - websecure + hosts: *hosts + asserts: + - failedTemplate: + errorMessage: Ingress - Combined traefik entrypoints contain duplicates [websecure, websecure] diff --git a/library/common-test/tests/ingress/validation_test.yaml b/library/common-test/tests/ingress/validation_test.yaml new file mode 100644 index 00000000..bd7dd62c --- /dev/null +++ b/library/common-test/tests/ingress/validation_test.yaml @@ -0,0 +1,607 @@ +suite: ingress validation test +templates: + - common.yaml +release: + name: test-release-name + namespace: test-release-namespace +tests: + - it: should fail if required is true but enabled is false + set: + operator: &operator + verify: + enabled: false + service: &service + my-service: + enabled: true + primary: true + ports: + main: + enabled: true + primary: true + port: 80 + ingress: + my-ingress: + enabled: false + required: true + asserts: + - failedTemplate: + errorMessage: Ingress - Expected ingress [my-ingress] to be enabled. This chart is designed to work only with ingress enabled. + + - it: should fail with name longer than 253 characters + set: + operator: *operator + service: *service + ingress: + my-ing: + enabled: true + primary: true + hosts: &hosts + - host: test-host + paths: + - path: /test-path + my-ingress-super-long-name-that-is-longer-than-253-characters-my-ingress-super-long-name-that-is-longer-than-253-characters-my-ingress-super-long-name-that-is-longer-than-253-characters-my-ingress-super-long-long-long-long-long-long-long-long-name: + enabled: true + asserts: + - failedTemplate: + errorMessage: Name [test-release-name-common-test-my-ingress-super-long-name-that-is-longer-than-253-characters-my-ingress-super-long-name-that-is-longer-than-253-characters-my-ingress-super-long-name-that-is-longer-than-253-characters-my-ingress-super-long-long-long-long-long-long-long-long-name] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 253 characters. + + - it: should fail with name starting with underscore + set: + operator: *operator + service: *service + ingress: + my-ing: + enabled: true + primary: true + hosts: *hosts + _my-ingress: + enabled: true + asserts: + - failedTemplate: + errorMessage: Name [test-release-name-common-test-_my-ingress] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 253 characters. + + - it: should fail with namespace longer than 63 characters + set: + operator: *operator + service: *service + ingress: + my-ingress: + enabled: true + primary: true + hosts: *hosts + namespace: my-extra-super-duper-long-name-that-is-longer-than-63-characters + asserts: + - failedTemplate: + errorMessage: Ingress - Namespace [my-extra-super-duper-long-name-that-is-longer-than-63-characters] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with namespace not starting with [ix-] in TrueNAS SCALE + set: + global: + ixChartContext: + iAmNotEmpty: true + operator: *operator + service: + my-service: + enabled: true + primary: true + namespace: ix-namespace + ports: + main: + enabled: true + primary: true + port: 80 + ingress: + my-ingress: + enabled: true + primary: true + hosts: *hosts + namespace: my-namespace + asserts: + - failedTemplate: + errorMessage: Ingress - Namespace [my-namespace] expected to have [ix-] prefix when installed in TrueNAS SCALE + + - it: should fail with labels not a dict + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + labels: "not a dict" + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [labels] to be a dictionary, but got [string] + + - it: should fail with annotations not a dict + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + annotations: "not a dict" + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [annotations] to be a dictionary, but got [string] + + - it: should fail with empty enabled + set: + operator: *operator + ingress: + my-ingress: + enabled: + asserts: + - failedTemplate: + errorMessage: Ingress - Expected the defined key [enabled] in [ingress.my-ingress] to not be empty + + - it: should fail with targetSelector not a map + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + targetSelector: "not a map" + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [targetSelector] to be a [map], but got [string] + + - it: should fail with targetSelector having more than one key + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + targetSelector: + main: main + other: other + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [targetSelector] to have exactly one key, but got [2] + + - it: should fail with targetSelector key missing value + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + targetSelector: + main: + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [targetSelector.main] to have a value + + - it: should fail with targetSelector key having non-string value + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + targetSelector: + main: + - not a string + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [targetSelector.main] to be a [string], but got [slice] + + - it: should fail with no hosts + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: [] + asserts: + - failedTemplate: + errorMessage: Ingress - Expected non-empty [hosts] + + - it: should fail with hosts not a slice + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: not-a-slice + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [hosts] to be a [slice], but got [string] + + - it: should fail with no hosts.host + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: + - paths: + - path: /test-path + asserts: + - failedTemplate: + errorMessage: Ingress - Expected non-empty [hosts.host] + + - it: should fail with host starting with https:// + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: + - host: https://test-host + paths: + - path: /test-path + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [hosts.host] to not start with [https://], but got [https://test-host] + + - it: should fail with host starting with http:// + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: + - host: http://test-host + paths: + - path: /test-path + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [hosts.host] to not start with [http://], but got [http://test-host] + + - it: should fail with host contain ":" + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: + - host: test-host:123 + paths: + - path: /test-path + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [hosts.host] to not contain [:], but got [test-host:123] + + - it: should fail without paths + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: + - host: test-host + asserts: + - failedTemplate: + errorMessage: Ingress - Expected non-empty [hosts.paths] + + - it: should fail with paths not a slice + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: + - host: test-host + paths: not-a-slice + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [hosts.paths] to be a [slice], but got [string] + + - it: should fail with invalid pathType + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: + - host: test-host + paths: + - path: /test-path + pathType: not-a-valid-path-type + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [hosts.paths.pathType] to be one of [Prefix, Exact, ImplementationSpecific], but got [not-a-valid-path-type] + + - it: should fail with path not starting with / (only on pathType Exact and Prefix) + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: + - host: test-host + paths: + - path: test-path + pathType: Prefix + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [hosts.paths.path] to start with [/], but got [test-path] + + - it: should fail if name in the overrideService is missing + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: + - host: test-host + paths: + - path: /test-path + overrideService: + port: 80 + asserts: + - failedTemplate: + errorMessage: Ingress - Expected non-empty [hosts.paths.overrideService.name] + + - it: should fail if port in the overrideService is missing + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: + - host: test-host + paths: + - path: /test-path + overrideService: + name: test-service + asserts: + - failedTemplate: + errorMessage: Ingress - Expected non-empty [hosts.paths.overrideService.port] + + - it: should fail if targeted service does not exist + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + targetSelector: + my-service: my-port + hosts: + - host: test-host + paths: + - path: /test-path + asserts: + - failedTemplate: + errorMessage: Ingress - Expected targeted service [my-service] to exist + + - it: should fail if targeted service is not enabled + set: + operator: *operator + service: + my-service: + enabled: false + primary: true + ports: + my-port: + enabled: true + primary: true + port: 80 + ingress: + my-ingress: + enabled: true + primary: true + targetSelector: + my-service: my-port + hosts: + - host: test-host + paths: + - path: /test-path + asserts: + - failedTemplate: + errorMessage: Ingress - Expected targeted service [my-service] to be enabled + + - it: should fail without primary service or a targetSelector + set: + operator: *operator + ingress: + my-ingress: + enabled: true + primary: true + hosts: + - host: test-host + paths: + - path: /test-path + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [targetSelector] or a primary service to exist + + - it: should fail if the targeted service does not have the targeted port + set: + operator: *operator + service: + my-service: + enabled: true + primary: true + ports: + my-other-port: + enabled: true + primary: true + port: 80 + ingress: + my-ingress: + enabled: true + primary: true + targetSelector: + my-service: my-port + hosts: + - host: test-host + paths: + - path: /test-path + asserts: + - failedTemplate: + errorMessage: Ingress - Expected targeted service [my-service] to have port [my-port] + + - it: should fail if the targeted service port is not enabled + set: + operator: *operator + service: + my-service: + enabled: true + primary: true + ports: + my-port: + enabled: false + primary: true + port: 80 + my-other-port: + enabled: true + primary: true + port: 80 + ingress: + my-ingress: + enabled: true + primary: true + targetSelector: + my-service: my-port + hosts: + - host: test-host + paths: + - path: /test-path + asserts: + - failedTemplate: + errorMessage: Ingress - Expected targeted service port [my-port] to be enabled + + - it: should fail if tls.hosts are empty + set: + operator: *operator + service: *service + ingress: + my-ingress: + enabled: true + primary: true + hosts: *hosts + tls: + - hosts: [] + asserts: + - failedTemplate: + errorMessage: Ingress - Expected non-empty [tls.hosts] + + - it: should fail if tls.hosts is not a slice + set: + operator: *operator + service: *service + ingress: + my-ingress: + enabled: true + primary: true + hosts: *hosts + tls: + - hosts: not-a-slice + asserts: + - failedTemplate: + errorMessage: Ingress - Expected [tls.hosts] to be a [slice], but got [string] + + - it: should fail if tls.hosts.host is empty + set: + operator: *operator + service: *service + ingress: + my-ingress: + enabled: true + primary: true + hosts: *hosts + tls: + - hosts: + - "" + asserts: + - failedTemplate: + errorMessage: Ingress - Expected non-empty entry in [tls.hosts] + + - it: should fail if tls.hosts.host starts with https:// + set: + operator: *operator + service: *service + ingress: + my-ingress: + enabled: true + primary: true + hosts: *hosts + tls: + - hosts: + - https://test-host + asserts: + - failedTemplate: + errorMessage: Ingress - Expected entry in [tls.hosts] to not start with [https://], but got [https://test-host] + + - it: should fail if tls.hosts.host starts with http:// + set: + operator: *operator + service: *service + ingress: + my-ingress: + enabled: true + primary: true + hosts: *hosts + tls: + - hosts: + - http://test-host + asserts: + - failedTemplate: + errorMessage: Ingress - Expected entry in [tls.hosts] to not start with [http://], but got [http://test-host] + + - it: should fail if tls.hosts.host contains ":" + set: + operator: *operator + service: *service + ingress: + my-ingress: + enabled: true + primary: true + hosts: *hosts + tls: + - hosts: + - test-host:123 + asserts: + - failedTemplate: + errorMessage: Ingress - Expected entry in [tls.hosts] to not contain [:], but got [test-host:123] + + - it: should fail if more than 1 cert option is set under tls + set: + operator: *operator + service: *service + ingress: + my-ingress: + enabled: true + primary: true + hosts: *hosts + tls: + - hosts: + - test-host + secretName: test-secret + scaleCert: "1" + asserts: + - failedTemplate: + errorMessage: Ingress - Expected only one of [scaleCert, secretName] to be set, but got [scaleCert, secretName] + + - it: should fail with scaleCert outside of SCALE + set: + operator: *operator + service: *service + ingress: + my-ingress: + enabled: true + primary: true + hosts: *hosts + tls: + - hosts: + - test-host + scaleCert: "1" + asserts: + - failedTemplate: + errorMessage: Ingress - [tls.scalecert] can only be used in TrueNAS SCALE diff --git a/library/common-test/tests/ingress/values_test.yaml b/library/common-test/tests/ingress/values_test.yaml deleted file mode 100644 index ef06584e..00000000 --- a/library/common-test/tests/ingress/values_test.yaml +++ /dev/null @@ -1,143 +0,0 @@ -suite: ingress values -templates: - - common.yaml -release: - name: test-release-name - namespace: test-release-namespace -tests: - - it: default should pass - set: - operator: - verify: - enabled: false - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: - enabled: true - primary: true - type: Deployment - podSpec: {} - ingress.main.enabled: true - asserts: - - documentIndex: &ingressDocument 2 - isKind: - of: Ingress - - documentIndex: *ingressDocument - equal: - path: spec.rules[0].host - value: chart-example.local - - documentIndex: *ingressDocument - equal: - path: spec.rules[0].http.paths[0].path - value: "/" - - - it: custom host and path should pass - set: - operator: - verify: - enabled: false - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: - enabled: true - primary: true - type: Deployment - podSpec: {} - ingress.main: - enabled: true - hosts: - - host: chart-test.local - paths: - - path: /test - asserts: - - documentIndex: &ingressDocument 2 - isKind: - of: Ingress - - documentIndex: *ingressDocument - equal: - path: spec.rules[0].host - value: chart-test.local - - documentIndex: *ingressDocument - equal: - path: spec.rules[0].http.paths[0].path - value: "/test" - - - it: host with template should pass - set: - operator: - verify: - enabled: false - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: - enabled: true - primary: true - type: Deployment - podSpec: {} - ingress.main: - enabled: true - hosts: - - host: "{{ .Release.Name }}.hostname" - asserts: - - documentIndex: &ingressDocument 2 - isKind: - of: Ingress - - documentIndex: *ingressDocument - equal: - path: spec.rules[0].host - value: test-release-name.hostname - - - it: path with template should pass - set: - operator: - verify: - enabled: false - service: - main: - enabled: true - ports: - main: - enabled: true - primary: true - port: 12345 - workload: - my-workload: - enabled: true - primary: true - type: Deployment - podSpec: {} - ingress.main: - enabled: true - hosts: - - host: chart-test.local - paths: - - path: "/{{ .Release.Name }}.path" - asserts: - - documentIndex: &ingressDocument 2 - isKind: - of: Ingress - - documentIndex: *ingressDocument - equal: - path: spec.rules[0].http.paths[0].path - value: "/test-release-name.path" diff --git a/library/common-test/tests/service/metadata_test.yaml b/library/common-test/tests/service/metadata_test.yaml index b3af6915..681e57c7 100644 --- a/library/common-test/tests/service/metadata_test.yaml +++ b/library/common-test/tests/service/metadata_test.yaml @@ -113,11 +113,13 @@ tests: path: metadata.namespace value: test-release-namespace - - it: should pass with service type LoadBalancer, with https port and addMetalLBAnnotations/Traefik true + - it: should pass with service type LoadBalancer, with https port and metallb.addServiceAnnotations/Traefik true set: global: - addMetalLBAnnotations: true - addTraefikAnnotations: true + metallb: + addServiceAnnotations: true + traefik: + addServiceAnnotations: true proto: https service: my-service1: diff --git a/library/common/Chart.yaml b/library/common/Chart.yaml index c49add2c..377e2622 100644 --- a/library/common/Chart.yaml +++ b/library/common/Chart.yaml @@ -15,4 +15,4 @@ maintainers: name: common sources: null type: library -version: 16.0.0 +version: 16.1.0 diff --git a/library/common/templates/class/_ingress.tpl b/library/common/templates/class/_ingress.tpl index f85fe63b..6e925721 100644 --- a/library/common/templates/class/_ingress.tpl +++ b/library/common/templates/class/_ingress.tpl @@ -1,157 +1,93 @@ -{{/* -This template serves as a blueprint for all Ingress objects that are created -within the common library. +{{/* Ingress Class */}} +{{/* Call this template: +{{ include "tc.v1.common.class.ingress" (dict "rootCtx" $ "objectData" $objectData) }} + +rootCtx: The root context of the chart. +objectData: The object data to be used to render the Ingress. */}} + {{- define "tc.v1.common.class.ingress" -}} - {{- $fullName := include "tc.v1.common.lib.chart.names.fullname" . -}} - {{- $ingressName := $fullName -}} - {{- $values := .Values.ingress -}} - {{- if hasKey . "ObjectValues" -}} - {{- with .ObjectValues.ingress -}} - {{- $values = . -}} - {{- end -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $svcData := (include "tc.v1.common.lib.ingress.targetSelector" (dict "rootCtx" $rootCtx "objectData" $objectData) | fromYaml) -}} + + {{- if not (hasKey $objectData "integrations") -}} + {{- $_ := set $objectData "integrations" dict -}} {{- end -}} - {{- $ingressLabels := $values.labels -}} - {{- $ingressAnnotations := $values.annotations -}} - - {{- $ingressName = $values.name -}} - - {{/* Get the name of the primary service, if any */}} - {{- $primaryServiceName := (include "tc.v1.common.lib.util.service.primary" (dict "services" .Values.service "root" .)) -}} - {{/* Get service values of the primary service, if any */}} - {{- $primaryService := get .Values.service $primaryServiceName -}} - {{- $defaultServiceName := $fullName -}} - - {{- if and (hasKey $primaryService "nameOverride") $primaryService.nameOverride -}} - {{- $defaultServiceName = printf "%v-%v" $defaultServiceName $primaryService.nameOverride -}} - {{- end -}} - {{- $defaultServicePort := get $primaryService.ports (include "tc.v1.common.lib.util.service.ports.primary" (dict "svcValues" $primaryService "svcName" $primaryServiceName )) -}} - - {{- $mddwrNamespace := "tc-system" -}} - {{- if $.Values.operator.traefik -}} - {{- if $.Values.operator.traefik.namespace -}} - {{- $mddwrNamespace = $.Values.operator.traefik.namespace -}} - {{- end -}} + {{- if not (hasKey $objectData "annotations") -}} + {{- $_ := set $objectData "annotations" dict -}} {{- end -}} - {{- if $values.ingressClassName -}} - - {{- if $.Values.global.ixChartContext -}} - {{- $mddwrNamespace = (printf "ix-%s" $values.ingressClassName) -}} - {{- else -}} - {{- $mddwrNamespace = $values.ingressClassName -}} - {{- end -}} - {{- end -}} - - {{- $fixedMiddlewares := "" -}} - {{- if $values.enableFixedMiddlewares -}} - - {{/* If cors is enabled, replace the default fixedMiddleware with the opencors chain */}} - {{- if $values.allowCors -}} - {{- $corsMiddlewares := list "tc-opencors-chain" }} - {{- $_ := set $values "fixedMiddlewares" $corsMiddlewares -}} - {{- end -}} - - {{- range $index, $fixedMiddleware := $values.fixedMiddlewares -}} - {{- if $index -}} - {{- $fixedMiddlewares = ( printf "%v, %v-%v@%v" $fixedMiddlewares $mddwrNamespace $fixedMiddleware "kubernetescrd" ) -}} - {{- else -}} - {{- $fixedMiddlewares = ( printf "%v-%v@%v" $mddwrNamespace $fixedMiddleware "kubernetescrd" ) -}} - {{- end -}} - {{- end -}} - {{- end -}} - - {{- $middlewares := "" -}} - {{- range $index, $middleware := $values.middlewares -}} - {{- if $index -}} - {{- $middlewares = ( printf "%v, %v-%v@%v" $middlewares $mddwrNamespace $middleware "kubernetescrd" ) -}} - {{- else -}} - {{- $middlewares = ( printf "%v-%v@%v" $mddwrNamespace $middleware "kubernetescrd" ) -}} - {{- end -}} - {{ end }} - - {{- if and ( $fixedMiddlewares ) ( $middlewares ) -}} - {{- $middlewares = ( printf "%v, %v" $fixedMiddlewares $middlewares ) -}} - {{- else if $fixedMiddlewares -}} - {{- $middlewares = ( printf "%s" $fixedMiddlewares ) -}} - {{- end }} + {{- 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) }} --- -apiVersion: {{ include "tc.v1.common.capabilities.ingress.apiVersion" $ }} +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: {{ $ingressName }} - namespace: {{ $.Values.namespace | default $.Values.global.namespace | default $.Release.Namespace }} - {{- $labels := (mustMerge ($ingressLabels | default dict) (include "tc.v1.common.lib.metadata.allLabels" $ | fromYaml)) -}} - {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $ "labels" $labels) | trim) }} + name: {{ $objectData.name }} + namespace: {{ include "tc.v1.common.lib.metadata.namespace" (dict "rootCtx" $rootCtx "objectData" $objectData "caller" "Ingress") }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "tc.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} + {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} labels: {{- . | nindent 4 }} {{- end -}} - {{- $annotations := (mustMerge ($ingressAnnotations | default dict) (include "tc.v1.common.lib.metadata.allAnnotations" $ | fromYaml) (include "tc.v1.common.lib.ingress.integration.homepage" (dict "objectData" $values "rootCtx" $) | fromYaml)) }} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "tc.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} annotations: - {{- with $values.certificateIssuer }} - cert-manager.io/cluster-issuer: {{ tpl ( toYaml . ) $ }} - cert-manager.io/private-key-rotation-policy: Always - {{- end }} - "traefik.ingress.kubernetes.io/router.entrypoints": {{ $values.entrypoint | default "websecure" }} - "traefik.ingress.kubernetes.io/router.middlewares": {{ $middlewares | quote }} - {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $ "annotations" $annotations) | trim) }} {{- . | nindent 4 }} {{- end }} spec: - {{- if $values.ingressClassName }} - ingressClassName: {{ $values.ingressClassName }} - {{- end -}} - {{- if $values.certificateIssuer }} - tls: - {{- range $index, $hostsValues := $values.hosts }} - - hosts: - - {{ tpl $hostsValues.host $ | quote }} - secretName: {{ ( printf "%v-%v-%v" $ingressName "tls" $index ) }} - {{- end -}} - {{- else if $values.tls }} - tls: - {{- range $index, $tlsValues := $values.tls }} - {{- $tlsName := ( printf "%v-%v" "tls" $index ) }} - - hosts: - {{- range $tlsValues.hosts }} - - {{ tpl . $ | quote }} - {{- end -}} - {{- if $tlsValues.certificateIssuer }} - secretName: {{ printf "%v-%v" $ingressName $tlsName }} - {{- else if and ($tlsValues.scaleCert) ($.Values.global.ixChartContext) -}} - {{- $cert := dict }} - {{- $_ := set $cert "id" $tlsValues.scaleCert }} - {{- $_ := set $cert "nameOverride" $tlsName }} - secretName: {{ printf "%s-tls-%v" (include "tc.v1.common.lib.chart.names.fullname" $) $index }} - {{- else if .clusterCertificate }} - secretName: clusterissuer-templated-{{ tpl .clusterCertificate $ }} - {{- else if .secretName }} - secretName: {{ tpl .secretName $ | quote }} - {{- end -}} - {{- end -}} + {{- if $objectData.ingressClassName }} + ingressClassName: {{ tpl $objectData.ingressClassName $rootCtx }} {{- end }} rules: - {{- range $values.hosts }} - - host: {{ tpl .host $ | quote }} + {{- range $h := $objectData.hosts }} + - host: {{ tpl $h.host $rootCtx }} http: paths: - {{- range .paths -}} - {{- $service := $defaultServiceName -}} - {{- $port := $defaultServicePort.port -}} - {{- if .service -}} - {{- $service = default $service .service.name -}} - {{- $port = default $port .service.port -}} + {{- range $p := $h.paths -}} + {{- with $p.overrideService -}} + {{- $svcData = (dict "name" .name "port" .port) -}} {{- end }} - - path: {{ tpl .path $ | quote }} - pathType: {{ default "Prefix" .pathType }} + - path: {{ tpl $p.path $rootCtx }} + pathType: {{ tpl ($p.pathType | default "Prefix") $rootCtx }} backend: service: - name: {{ $service }} + name: {{ $svcData.name }} port: - number: {{ $port }} + number: {{ $svcData.port }} {{- end -}} + {{- end -}} + {{/* If a certificateIssuer is defined in the whole ingress, use that */}} + {{- if and $objectData.integrations.certManager $objectData.integrations.certManager.enabled }} + tls: + {{- range $idx, $h := $objectData.hosts }} + - secretName: {{ printf "%s-tls-%d" $objectData.name ($idx | int) }} + hosts: + - {{ tpl $h.host $rootCtx }} + {{- end -}} + {{/* else if a tls section is defined use the configuration from there */}} + {{- else if $objectData.tls }} + tls: + {{- range $idx, $t := $objectData.tls -}} + {{- $secretName := "" -}} + {{- if $t.secretName -}} + {{- $secretName = tpl $t.secretName $rootCtx -}} + {{- else if $t.scaleCert -}} + {{- $secretName = printf "%s-scale-tls-%d" $objectData.name ($idx | int) -}} + {{- else if $t.certificateIssuer -}} + {{- $secretName = printf "%s-tls-%d" $objectData.name ($idx | int) -}} + {{- else if $t.clusterCertificate -}} + {{/* TODO: Needs the refactor of Certificate object */}} + {{- end }} + - secretName: {{ $secretName }} + hosts: + {{- range $h := $t.hosts }} + - {{ tpl $h $rootCtx }} + {{- end -}} + {{- end -}} {{- end -}} - - {{- end -}} diff --git a/library/common/templates/class/_priorityClass.tpl b/library/common/templates/class/_priorityClass.tpl index 4789f9b8..3b4b8450 100644 --- a/library/common/templates/class/_priorityClass.tpl +++ b/library/common/templates/class/_priorityClass.tpl @@ -22,7 +22,7 @@ apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: {{ $objectData.name }} - namespace: {{ include "tc.v1.common.lib.metadata.namespace" (dict "rootCtx" $rootCtx "objectData" $objectData "caller" "priorityclass") }} + namespace: {{ include "tc.v1.common.lib.metadata.namespace" (dict "rootCtx" $rootCtx "objectData" $objectData "caller" "Priority Class") }} {{- $labels := (mustMerge ($objectData.labels | default dict) (include "tc.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} labels: diff --git a/library/common/templates/class/cnpg/_cluster.tpl b/library/common/templates/class/cnpg/_cluster.tpl index 7be41c44..a0bf8834 100644 --- a/library/common/templates/class/cnpg/_cluster.tpl +++ b/library/common/templates/class/cnpg/_cluster.tpl @@ -158,7 +158,7 @@ spec: {{- end -}} {{- with $preloadLibraries }} shared_preload_libraries: - {{- range $lib := (. | uniq) }} + {{- range $lib := (. | mustUniq) }} - {{ $lib | quote }} {{- end -}} {{- end -}} diff --git a/library/common/templates/lib/_tc_capabilities.tpl b/library/common/templates/lib/_tc_capabilities.tpl index e4187638..4d911dd8 100644 --- a/library/common/templates/lib/_tc_capabilities.tpl +++ b/library/common/templates/lib/_tc_capabilities.tpl @@ -13,11 +13,6 @@ {{- print "monitoring.coreos.com/v1" -}} {{- end -}} -{{/* Return the appropriate apiVersion for Ingress */}} -{{- define "tc.v1.common.capabilities.ingress.apiVersion" -}} - {{- print "networking.k8s.io/v1" -}} -{{- end -}} - {{/* Return the appropriate apiVersion for NetworkPolicy*/}} {{- define "tc.v1.common.capabilities.networkpolicy.apiVersion" -}} {{- print "networking.k8s.io/v1" -}} diff --git a/library/common/templates/lib/container/_securityContext.tpl b/library/common/templates/lib/container/_securityContext.tpl index 4736a2f5..3552a2f8 100644 --- a/library/common/templates/lib/container/_securityContext.tpl +++ b/library/common/templates/lib/container/_securityContext.tpl @@ -172,7 +172,7 @@ objectData: The object data to be used to render the container. {{- end -}} {{- end -}} - {{- if not (deepEqual (uniq $item) $item) -}} + {{- if not (deepEqual (mustUniq $item) $item) -}} {{- fail (printf "Container - Expected items of [securityContext.capabilities.%s] to be unique, but got [%s]" $key (join ", " $item)) -}} {{- end -}} {{- end -}} diff --git a/library/common/templates/lib/ingress/_targetSelector.tpl b/library/common/templates/lib/ingress/_targetSelector.tpl new file mode 100644 index 00000000..17115631 --- /dev/null +++ b/library/common/templates/lib/ingress/_targetSelector.tpl @@ -0,0 +1,85 @@ +{{/* Returns the selected service or fallback to primary */}} +{{- define "tc.v1.common.lib.ingress.targetSelector" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $selectedService := (dict "name" "" "port" 0) -}} + {{- $svcData := dict -}} + {{- $portData := dict -}} + {{- $svcName := "" -}} + {{- $portName := "" -}} + + {{- if $objectData.targetSelector -}} + {{/* We have validation that only 1 key is allowed */}} + {{- $svcName = ($objectData.targetSelector | keys | mustFirst) -}} + {{- $portName = (get $objectData.targetSelector $svcName) -}} + {{- $svcData = (get $rootCtx.Values.service $svcName) -}} + + {{- if not $svcData -}} + {{- fail (printf "Ingress - Expected targeted service [%s] to exist" $svcName) -}} + {{- end -}} + + {{- $enabled := (include "tc.v1.common.lib.util.enabled" (dict + "rootCtx" $rootCtx "objectData" $svcData + "name" $svcName "caller" "Ingress" + "key" "ingress")) -}} + + {{- if ne $enabled "true" -}} + {{- fail (printf "Ingress - Expected targeted service [%s] to be enabled" $svcName) -}} + {{- end -}} + + {{- else -}} + {{/* Find the primary service */}} + {{- range $name, $service := $rootCtx.Values.service -}} + + {{- $enabled := (include "tc.v1.common.lib.util.enabled" (dict + "rootCtx" $rootCtx "objectData" $service + "name" $name "caller" "Ingress" + "key" "ingress")) -}} + + {{/* Check if its enabled */}} + {{- if eq $enabled "true" -}} + + {{- if $service.primary -}} + {{- $svcName = $name -}} + {{- $svcData = $service -}} + + {{/* Find the primary port */}} + {{- range $name, $port := $svcData.ports -}} + {{- if $port.primary -}} + {{- $portName = $name -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if not $svcData -}} + {{- fail "Ingress - Expected [targetSelector] or a primary service to exist" -}} + {{- end -}} + + {{- end -}} + + {{- $portData = (get $svcData.ports $portName) -}} + {{- if not $portData -}} + {{- fail (printf "Ingress - Expected targeted service [%s] to have port [%s]" $svcName $portName) -}} + {{- end -}} + + {{- $enabled := (include "tc.v1.common.lib.util.enabled" (dict + "rootCtx" $rootCtx "objectData" $portData + "name" $portName "caller" "Ingress" + "key" "ingress")) -}} + + {{- if ne $enabled "true" -}} + {{- fail (printf "Ingress - Expected targeted service port [%s] to be enabled" $portName) -}} + {{- end -}} + + {{- $expandedSvcName := include "tc.v1.common.lib.chart.names.fullname" $rootCtx -}} + {{- if not $svcData.primary -}} + {{- $expandedSvcName = printf "%s-%s" $expandedSvcName $svcName -}} + {{- end -}} + + {{- $selectedService = (dict "name" $expandedSvcName "port" (tpl ($portData.port | toString) $rootCtx)) -}} + + {{- $selectedService | toYaml -}} +{{- end -}} diff --git a/library/common/templates/lib/ingress/_validation.tpl b/library/common/templates/lib/ingress/_validation.tpl index a63bc583..216ef5e9 100644 --- a/library/common/templates/lib/ingress/_validation.tpl +++ b/library/common/templates/lib/ingress/_validation.tpl @@ -1,14 +1,182 @@ {{/* Ingress Validation */}} {{/* Call this template: -{{ include "tc.v1.common.lib.ingress.validation" (dict "objectData" $objectData) -}} +{{ include "tc.v1.common.lib.ingress.validation" (dict "rootCtx" $ "objectData" $objectData) -}} objectData: rootCtx: The root context of the chart. - objectData: The ingress object. + objectData: The Ingress object. */}} {{- define "tc.v1.common.lib.ingress.validation" -}} {{- $rootCtx := .rootCtx -}} {{- $objectData := .objectData -}} + {{- if $objectData.targetSelector -}} + {{- if not (kindIs "map" $objectData.targetSelector) -}} + {{- fail (printf "Ingress - Expected [targetSelector] to be a [map], but got [%s]" (kindOf $objectData.targetSelector)) -}} + {{- end -}} + + {{- $selectors := $objectData.targetSelector | keys | len -}} + {{- if (gt $selectors 1) -}} + {{ fail (printf "Ingress - Expected [targetSelector] to have exactly one key, but got [%d]" $selectors) -}} + {{- end -}} + + {{- range $k, $v := $objectData.targetSelector -}} + {{- if not $v -}} + {{- fail (printf "Ingress - Expected [targetSelector.%s] to have a value" $k) -}} + {{- end -}} + + {{- if not (kindIs "string" $v) -}} + {{- fail (printf "Ingress - Expected [targetSelector.%s] to be a [string], but got [%s]" $k (kindOf $v)) -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if not $objectData.hosts -}} + {{- fail "Ingress - Expected non-empty [hosts]" -}} + {{- end -}} + + {{- if not (kindIs "slice" $objectData.hosts) -}} + {{- fail (printf "Ingress - Expected [hosts] to be a [slice], but got [%s]" (kindOf $objectData.hosts)) -}} + {{- end -}} + + {{- range $h := $objectData.hosts -}} + {{- if not $h.host -}} + {{- fail "Ingress - Expected non-empty [hosts.host]" -}} + {{- end -}} + + {{- $host := tpl $h.host $rootCtx -}} + {{- if (hasPrefix "http://" $host) -}} + {{- fail (printf "Ingress - Expected [hosts.host] to not start with [http://], but got [%s]" $host) -}} + {{- end -}} + {{- if (hasPrefix "https://" $host) -}} + {{- fail (printf "Ingress - Expected [hosts.host] to not start with [https://], but got [%s]" $host) -}} + {{- end -}} + {{- if (contains ":" $host) -}} + {{- fail (printf "Ingress - Expected [hosts.host] to not contain [:], but got [%s]" $host) -}} + {{- end -}} + + {{- if not $h.paths -}} + {{- fail "Ingress - Expected non-empty [hosts.paths]" -}} + {{- end -}} + + {{- if not (kindIs "slice" $h.paths) -}} + {{- fail (printf "Ingress - Expected [hosts.paths] to be a [slice], but got [%s]" (kindOf $h.paths)) -}} + {{- end -}} + + {{- range $p := $h.paths -}} + {{- $pathType := "Prefix" -}} + {{- if $p.pathType -}} + {{- $pathType = tpl $p.pathType $rootCtx -}} + {{- end -}} + + {{- $validPathTypes := (list "Prefix" "Exact" "ImplementationSpecific") -}} + {{- if not (mustHas $pathType $validPathTypes) -}} + {{- fail (printf "Ingress - Expected [hosts.paths.pathType] to be one of [%s], but got [%s]" (join ", " $validPathTypes) $pathType) -}} + {{- end -}} + + {{- $path := tpl $p.path $rootCtx -}} + {{- $prefixSlashTypes := (list "Prefix" "Exact") -}} + {{- if (mustHas $pathType $prefixSlashTypes) -}} + {{- if not (hasPrefix "/" $path) -}} + {{- fail (printf "Ingress - Expected [hosts.paths.path] to start with [/], but got [%s]" $path) -}} + {{- end -}} + {{- end -}} + + {{/* If at least one thing in overrideService is defined... */}} + {{- with $p.overrideService -}} + {{- if not .name -}} + {{- fail "Ingress - Expected non-empty [hosts.paths.overrideService.name]" -}} + {{- end -}} + {{- if not .port -}} + {{- fail "Ingress - Expected non-empty [hosts.paths.overrideService.port]" -}} + {{- end -}} + {{- end -}} + + {{- end -}} + {{- end -}} + + {{- range $t := $objectData.tls -}} + {{- if not $t.hosts -}} + {{- fail "Ingress - Expected non-empty [tls.hosts]" -}} + {{- end -}} + + {{- if not (kindIs "slice" $t.hosts) -}} + {{- fail (printf "Ingress - Expected [tls.hosts] to be a [slice], but got [%s]" (kindOf $t.hosts)) -}} + {{- end -}} + + {{- range $h := $t.hosts -}} + {{- if not $h -}} + {{- fail "Ingress - Expected non-empty entry in [tls.hosts]" -}} + {{- end -}} + + {{- $host := tpl $h $rootCtx -}} + {{- if (hasPrefix "http://" $host) -}} + {{- fail (printf "Ingress - Expected entry in [tls.hosts] to not start with [http://], but got [%s]" $host) -}} + {{- end -}} + {{- if (hasPrefix "https://" $host) -}} + {{- fail (printf "Ingress - Expected entry in [tls.hosts] to not start with [https://], but got [%s]" $host) -}} + {{- end -}} + {{- if (contains ":" $host) -}} + {{- fail (printf "Ingress - Expected entry in [tls.hosts] to not contain [:], but got [%s]" $host) -}} + {{- end -}} + {{- end -}} + + {{/* TODO: Add the rest of the options?! */}} + {{- $certOptions := (list "scaleCert" "secretName") -}} + {{- $optsSet := list -}} + {{- range $opt := $certOptions -}} + {{- if (get $t $opt) -}} + {{- $optsSet = mustAppend $optsSet $opt -}} + {{- end -}} + {{- end -}} + + {{- if gt ($optsSet | len) 1 -}} + {{- fail (printf "Ingress - Expected only one of [%s] to be set, but got [%s]" (join ", " $certOptions) (join ", " $optsSet)) -}} + {{- end -}} + + {{- end -}} + +{{- end -}} + +{{/* Ingress Primary Validation */}} +{{/* Call this template: +{{ include "tc.v1.common.lib.ingress.primaryValidation" $ -}} +*/}} + +{{- define "tc.v1.common.lib.ingress.primaryValidation" -}} + + {{/* Initialize values */}} + {{- $hasPrimary := false -}} + {{- $hasEnabled := false -}} + + {{- range $name, $ingress := $.Values.ingress -}} + + {{- $enabled := (include "tc.v1.common.lib.util.enabled" (dict + "rootCtx" $ "objectData" $ingress + "name" $name "caller" "Ingress" + "key" "ingress")) -}} + + {{/* If ingress is enabled */}} + {{- if eq $enabled "true" -}} + {{- $hasEnabled = true -}} + + {{/* And ingress is primary */}} + {{- if and (hasKey $ingress "primary") ($ingress.primary) -}} + {{/* Fail if there is already a primary ingress */}} + {{- if $hasPrimary -}} + {{- fail "Ingress - Only one ingress can be primary" -}} + {{- end -}} + + {{- $hasPrimary = true -}} + + {{- end -}} + + {{- end -}} + {{- end -}} + + {{/* Require at least one primary ingress, if any enabled */}} + {{- if and $hasEnabled (not $hasPrimary) -}} + {{- fail "Ingress - At least one enabled ingress must be primary" -}} + {{- end -}} {{- end -}} diff --git a/library/common/templates/lib/ingress/integrations/_certManager.tpl b/library/common/templates/lib/ingress/integrations/_certManager.tpl new file mode 100644 index 00000000..2df0cdbf --- /dev/null +++ b/library/common/templates/lib/ingress/integrations/_certManager.tpl @@ -0,0 +1,29 @@ +{{- define "tc.v1.common.lib.ingress.integration.certManager" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + + {{- $certManager := $objectData.integrations.certManager -}} + + {{- if $certManager.enabled -}} + {{- include "tc.v1.common.lib.ingress.integration.certManager.validate" (dict "objectData" $objectData) -}} + + {{- $_ := set $objectData.annotations "cert-manager.io/cluster-issuer" $certManager.certificateIssuer -}} + {{- $_ := set $objectData.annotations "cert-manager.io/private-key-rotation-policy" "Always" -}} + + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.lib.ingress.integration.certManager.validate" -}} + {{- $objectData := .objectData -}} + + {{- $certManager := $objectData.integrations.certManager -}} + + {{- if not $certManager.certificateIssuer -}} + {{- fail "Ingress - Expected a non-empty [integrations.certManager.certificateIssuer]" -}} + {{- end -}} + + {{- if not (kindIs "string" $certManager.certificateIssuer) -}} + {{- fail (printf "Ingress - Expected [integrations.certManager.certificateIssuer] to be a [string], but got [%s]" (kindOf $certManager.certificateIssuer)) -}} + {{- end -}} + +{{- end -}} diff --git a/library/common/templates/lib/ingress/integrations/_homepage.tpl b/library/common/templates/lib/ingress/integrations/_homepage.tpl index 73692bb5..94863efb 100644 --- a/library/common/templates/lib/ingress/integrations/_homepage.tpl +++ b/library/common/templates/lib/ingress/integrations/_homepage.tpl @@ -1,35 +1,75 @@ -{{/* Ingress Homepage Integration */}} -{{/* Call this template: -{{ include "tc.v1.common.lib.ingress.integration.homepage" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} -objectData: - rootCtx: The root context of the chart. - objectData: The ingress object. -*/}} - {{- define "tc.v1.common.lib.ingress.integration.homepage" -}} + {{- $objectData := .objectData -}} {{- $rootCtx := .rootCtx -}} + + {{- $homepage := $objectData.integrations.homepage -}} + {{- if and $homepage $homepage.enabled -}} + {{- if not (hasKey $homepage "widget") -}} + {{- $_ := set $objectData.integrations.homepage "widget" dict -}} + {{- end -}} + + {{- include "tc.v1.common.lib.ingress.integration.homepage.validation" (dict "objectData" $objectData) -}} + + {{- $name := $homepage.name | default ($rootCtx.Chart.Name | camelcase) -}} + {{- $desc := $homepage.description | default $rootCtx.Chart.Description -}} + {{- $icon := $homepage.icon | default $rootCtx.Chart.Icon -}} + {{- $type := $homepage.widget.type | default $rootCtx.Chart.Name -}} + {{- $url := $homepage.widget.url -}} + + {{- if not $url -}} + {{- $fHost := $objectData.hosts | mustFirst -}} + {{- $fPath := $fHost.paths | mustFirst -}} + {{- $host := tpl $fHost.host $rootCtx -}} + {{- $path := tpl $fPath.path $rootCtx -}} + + {{- $url = printf "https://%s/%s" $host ($path | trimPrefix "/") -}} + {{- end -}} + + {{- $_ := set $objectData.annotations "gethomepage.dev/enabled" "true" -}} + {{- $_ := set $objectData.annotations "gethomepage.dev/name" (tpl $name $rootCtx) -}} + {{- $_ := set $objectData.annotations "gethomepage.dev/description" (tpl $desc $rootCtx) -}} + {{- $_ := set $objectData.annotations "gethomepage.dev/icon" (tpl $icon $rootCtx) -}} + {{- $_ := set $objectData.annotations "gethomepage.dev/widget.type" (tpl $type $rootCtx) -}} + {{- with $homepage.group -}} + {{- $_ := set $objectData.annotations "gethomepage.dev/group" (tpl . $rootCtx) -}} + {{- end -}} + + {{- with $homepage.weight -}} + {{- $_ := set $objectData.annotations "gethomepage.dev/weight" (. | toString) -}} + {{- end -}} + + {{- with $url -}} + {{- $_ := set $objectData.annotations "gethomepage.dev/widget.url" (tpl $url $rootCtx) -}} + {{- end -}} + + {{- if $homepage.widget.custom -}} + {{- range $k, $v := $homepage.widget.custom -}} + {{- $_ := set $objectData.annotations (printf "gethomepage.dev/widget.%s" $k) (tpl $v $rootCtx | toString) -}} + {{- end -}} + {{- end -}} + + {{- with $homepage.podSelector -}} + {{- $selector := (printf "pod.name in (%s)" (join "," .)) -}} + {{- $_ := set $objectData.annotations "gethomepage.dev/pod-selector" $selector -}} + {{- end -}} + + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.lib.ingress.integration.homepage.validation" -}} {{- $objectData := .objectData -}} -{{- if and $objectData.integration $objectData.integration.homepage $objectData.integration.homepage.enabled -}} -gethomepage.dev/enabled: "true" -gethomepage.dev/name: {{ $objectData.integration.homepage.name | default ( camelcase $rootCtx.Chart.Name ) }} -gethomepage.dev/description: {{ $objectData.integration.homepage.description | default $rootCtx.Chart.Description }} -gethomepage.dev/group: {{ $objectData.integration.homepage.group | default "default" }} -gethomepage.dev/icon: {{ $objectData.integration.homepage.icon | default $rootCtx.Chart.Icon }} -{{- if $objectData.integration.homepage.podSelector -}} -gethomepage.dev/pod-selector: {{ . }} -{{- else -}} -gethomepage.dev/pod-selector: "" -{{- end -}} -{{- with $objectData.integration.homepage.weight -}} -gethomepage.dev/weight: {{ . }} -{{- end -}} -gethomepage.dev/widget.type: {{ $objectData.integration.homepage.widget.type | default $rootCtx.Chart.Name }} -{{- with (index $objectData.hosts 0) -}} -gethomepage.dev/widget.url: {{ $objectData.integration.homepage.widget.url | default (printf "%v%v" .host ( .path | default "/")) }} -{{- end -}} -{{- range $objectData.integration.homepage.widget.custom -}} -gethomepage.dev/widget.{{ .name }}: {{ .value }} -{{- end -}} -{{- end -}} + {{- $homepage := $objectData.integrations.homepage -}} + + {{- with $homepage.podSelector -}} + {{- if not (kindIs "slice" .) -}} + {{- fail (printf "Ingress - Expected [integrations.homepage.podSelector] to be a [slice], but got [%s]" (kindOf .)) -}} + {{- end -}} + {{- end -}} + + {{- if $homepage.widget.custom -}} + {{- if not (kindIs "map" $homepage.widget.custom) -}} + {{- fail (printf "Ingress - Expected [integrations.homepage.widget.custom] to be a [map], but got [%s]" (kindOf $homepage.widget.custom)) -}} + {{- end -}} + {{- end -}} {{- end -}} 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..4dcef8dd --- /dev/null +++ b/library/common/templates/lib/ingress/integrations/_traefik.tpl @@ -0,0 +1,115 @@ +{{- define "tc.v1.common.lib.ingress.integration.traefik" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + + {{- $traefik := $objectData.integrations.traefik -}} + + {{- $enabled := true -}} + {{- if and $traefik (hasKey $traefik "enabled") (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 = tpl $traefik.ingressClassName $rootCtx -}} + + {{/* 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.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 -}} diff --git a/library/common/templates/lib/service/_additionalAnnotations.tpl b/library/common/templates/lib/service/_additionalAnnotations.tpl index ab4e8dcc..e314083c 100644 --- a/library/common/templates/lib/service/_additionalAnnotations.tpl +++ b/library/common/templates/lib/service/_additionalAnnotations.tpl @@ -18,8 +18,10 @@ annotations: The annotations variable reference, to append the MetalLB annotatio {{- $sharedKey = tpl . $rootCtx -}} {{- end -}} - {{- if $rootCtx.Values.global.addMetalLBAnnotations -}} - {{- $_ := set $annotations "metallb.universe.tf/allow-shared-ip" $sharedKey -}} + {{- if (hasKey $rootCtx.Values.global "metallb") -}} + {{- if $rootCtx.Values.global.metallb.addServiceAnnotations -}} + {{- $_ := set $annotations "metallb.universe.tf/allow-shared-ip" $sharedKey -}} + {{- end -}} {{- end -}} {{- end -}} @@ -34,7 +36,9 @@ annotations: The annotations variable reference, to append the Traefik annotatio {{- $rootCtx := .rootCtx -}} {{- $annotations := .annotations -}} - {{- if $rootCtx.Values.global.addTraefikAnnotations -}} - {{- $_ := set $annotations "traefik.ingress.kubernetes.io/service.serversscheme" "https" -}} + {{- if (hasKey $rootCtx.Values.global "traefik") -}} + {{- if $rootCtx.Values.global.traefik.addServiceAnnotations -}} + {{- $_ := set $annotations "traefik.ingress.kubernetes.io/service.serversscheme" "https" -}} + {{- end -}} {{- end -}} {{- end -}} diff --git a/library/common/templates/lib/util/_primary_service.tpl b/library/common/templates/lib/util/_primary_service.tpl index b661a250..75f57631 100644 --- a/library/common/templates/lib/util/_primary_service.tpl +++ b/library/common/templates/lib/util/_primary_service.tpl @@ -32,8 +32,8 @@ {{- end -}} {{- if $result -}} - {{- $result -}} + {{- $result -}} {{- else -}} - {{- fail "No primary and enabled service found" -}} + {{- fail "No primary and enabled service found" -}} {{- end -}} {{- end -}} diff --git a/library/common/templates/loader/_init.tpl b/library/common/templates/loader/_init.tpl index aff423a4..13111e53 100644 --- a/library/common/templates/loader/_init.tpl +++ b/library/common/templates/loader/_init.tpl @@ -8,7 +8,7 @@ {{- include "tc.v1.common.loader.lists" . -}} {{/* Ensure TrueCharts chart context information is available */}} - {{- include "tc.v1.common.lib.util.chartcontext" . -}} + {{- /* include "tc.v1.common.lib.util.chartcontext" . */ -}} {{/* Autogenerate postgresql passwords if needed */}} {{- include "tc.v1.common.spawner.cnpg" . }} diff --git a/library/common/templates/spawner/_ingress.tpl b/library/common/templates/spawner/_ingress.tpl index 07353144..cadf12ca 100644 --- a/library/common/templates/spawner/_ingress.tpl +++ b/library/common/templates/spawner/_ingress.tpl @@ -1,62 +1,95 @@ -{{/* Renders the Ingress objects required by the chart */}} +{{/* Ingress Spawwner */}} +{{/* Call this template: +{{ include "tc.v1.common.spawner.ingress" $ -}} +*/}} + {{- define "tc.v1.common.spawner.ingress" -}} {{- $fullname := include "tc.v1.common.lib.chart.names.fullname" $ -}} - {{/* Generate named ingresses as required */}} + {{/* Validate that only 1 primary exists */}} + {{- include "tc.v1.common.lib.ingress.primaryValidation" $ -}} + {{- range $name, $ingress := .Values.ingress -}} - {{- if $ingress.enabled -}} - {{- $ingressValues := $ingress -}} - {{- $ingressName := $fullname -}} - {{/* set defaults */}} - {{- if and (not $ingressValues.nameOverride) (ne $name (include "tc.v1.common.lib.util.ingress.primary" $)) -}} - {{- $_ := set $ingressValues "nameOverride" $name -}} + {{- $enabled := (include "tc.v1.common.lib.util.enabled" (dict + "rootCtx" $ "objectData" $ingress + "name" $name "caller" "Ingress" + "key" "ingress")) -}} + + {{- if and (eq $enabled "false") ($ingress.required) -}} + {{- fail (printf "Ingress - Expected ingress [%s] to be enabled. This chart is designed to work only with ingress enabled." $name) -}} + {{- end -}} + + {{- if eq $enabled "true" -}} + + {{/* Create a copy of the ingress */}} + {{- $objectData := (mustDeepCopy $ingress) -}} + + {{/* Init object name */}} + {{- $objectName := $name -}} + + {{- $expandName := (include "tc.v1.common.lib.util.expandName" (dict + "rootCtx" $ "objectData" $objectData + "name" $name "caller" "Ingress" + "key" "ingress")) -}} + + {{- if eq $expandName "true" -}} + {{/* Expand the name of the service if expandName resolves to true */}} + {{- $objectName = $fullname -}} {{- end -}} - {{- if $ingressValues.nameOverride -}} - {{- $ingressName = printf "%v-%v" $ingressName $ingressValues.nameOverride -}} + {{- if and (eq $expandName "true") (not $objectData.primary) -}} + {{/* If the ingress is not primary append its name to fullname */}} + {{- $objectName = (printf "%s-%s" $fullname $name) -}} {{- end -}} - {{- $_ := set $ingressValues "name" $ingressName -}} + {{/* Perform validations */}} + {{- include "tc.v1.common.lib.chart.names.validation" (dict "name" $objectName "length" 253) -}} + {{- include "tc.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" "Ingress") -}} + {{- include "tc.v1.common.lib.ingress.validation" (dict "rootCtx" $ "objectData" $objectData) -}} - {{- $_ := set $ "ObjectValues" (dict "ingress" $ingressValues) -}} - {{- include "tc.v1.common.class.ingress" $ -}} - {{- if and ( $ingressValues.tls ) ( not $ingressValues.clusterIssuer ) -}} - {{- range $index, $tlsValues := $ingressValues.tls -}} - {{- $tlsName := ( printf "%v-%v" "tls" $index ) -}} - {{- if $tlsValues.certificateIssuer -}} - {{- include "tc.v1.common.class.certificate" (dict "root" $ "name" ( printf "%v-%v" $ingressName $tlsName ) "certificateIssuer" $tlsValues.certificateIssuer "hosts" $tlsValues.hosts ) -}} - {{- else if and ( $tlsValues.scaleCert ) ( $.Values.global.ixChartContext ) -}} + {{/* Set the name of the ingress */}} + {{- $_ := set $objectData "name" $objectName -}} + {{- $_ := set $objectData "shortName" $name -}} - {{/* Create certificate object and use it to construct a secret */}} - {{- $objectData := dict -}} - {{- $_ := set $objectData "id" .scaleCert -}} - - {{- $objectName := (printf "%s-%s" $fullname $tlsName) -}} - {{/* Perform validations */}} - {{- include "tc.v1.common.lib.chart.names.validation" (dict "name" $objectName) -}} - {{- include "tc.v1.common.lib.scaleCertificate.validation" (dict "objectData" $objectData) -}} - {{- include "tc.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" "Certificate") -}} - - {{/* Prepare data */}} - {{- $data := fromJson (include "tc.v1.common.lib.scaleCertificate.getData" (dict "rootCtx" $ "objectData" $objectData)) -}} - {{- $_ := set $objectData "data" $data -}} - - {{/* Set the type to certificate */}} - {{- $_ := set $objectData "type" "certificate" -}} - - {{/* Set the name of the certificate */}} - {{- $_ := set $objectData "name" $objectName -}} - {{- $_ := set $objectData "shortName" $name -}} - - {{/* Call class to create the object */}} - {{- include "tc.v1.common.class.secret" (dict "rootCtx" $ "objectData" $objectData) -}} + {{/* Call class to create the object */}} + {{- include "tc.v1.common.class.ingress" (dict "rootCtx" $ "objectData" $objectData) -}} + {{/* TODO: range over TLS and do stuff */}} + {{- $hasCertIssuer := false -}} + {{- if $objectData.integrations -}} + {{- if and $objectData.integrations.certManager $objectData.integrations.certManager.enabled -}} + {{- $hasCertIssuer = true -}} {{- end -}} {{- end -}} + + {{- if not $hasCertIssuer -}} + {{- range $idx, $tlsData := $objectData.tls -}} + {{- if $tlsData.scaleCert -}} + {{- if not $.Values.global.ixChartContext -}} + {{- fail "Ingress - [tls.scalecert] can only be used in TrueNAS SCALE" -}} + {{- end -}} + + {{- $certData := (include "tc.v1.common.lib.scaleCertificate.getData" (dict "rootCtx" $ "objectData" (dict "id" $tlsData.scaleCert)) | fromJson) -}} + {{- $certName := printf "%s-scale-tls-%d" $objectData.name ($idx | int) -}} + + {{- $certObjData := (dict + "id" $tlsData.scaleCert "type" "certificate" + "name" $certName "shortName" $name + "data" $certData + ) -}} + + {{- include "tc.v1.common.lib.chart.names.validation" (dict "name" $certName) -}} + {{- include "tc.v1.common.lib.scaleCertificate.validation" (dict "objectData" $certObjData) -}} + {{- include "tc.v1.common.lib.metadata.validation" (dict "objectData" $certObjData "caller" "Ingress") -}} + + {{/* Create the secret with the certData */}} + {{- include "tc.v1.common.class.secret" (dict "rootCtx" $ "objectData" $certObjData) -}} + {{- else if $tlsData.clusterCertificate -}} + {{/* TODO: Needs the refactor of Certificate object */}} + {{- end -}} + {{- end -}} {{- end -}} - {{- else if $ingress.required -}} - {{- fail (printf "Ingress - [ingress.%s] is set to be [required] and cannot be disabled" $name) -}} {{- end -}} {{- end -}} {{- end -}} diff --git a/library/common/templates/spawner/_priorityClass.tpl b/library/common/templates/spawner/_priorityClass.tpl index a7391d5d..07ecb992 100644 --- a/library/common/templates/spawner/_priorityClass.tpl +++ b/library/common/templates/spawner/_priorityClass.tpl @@ -1,4 +1,4 @@ -{{/* priorityclass Spawwner */}} +{{/* Priority Class Spawner */}} {{/* Call this template: {{ include "tc.v1.common.spawner.priorityclass" $ -}} */}} diff --git a/library/common/templates/spawner/_service.tpl b/library/common/templates/spawner/_service.tpl index 86d40841..57081af9 100644 --- a/library/common/templates/spawner/_service.tpl +++ b/library/common/templates/spawner/_service.tpl @@ -43,7 +43,7 @@ {{- include "tc.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" "Service") -}} {{- include "tc.v1.common.lib.service.validation" (dict "rootCtx" $ "objectData" $objectData) -}} - {{/* Set the name of the service account */}} + {{/* Set the name of the service */}} {{- $_ := set $objectData "name" $objectName -}} {{- $_ := set $objectData "shortName" $name -}} diff --git a/library/common/values.yaml b/library/common/values.yaml index 0123a962..ffa44111 100644 --- a/library/common/values.yaml +++ b/library/common/values.yaml @@ -7,10 +7,19 @@ global: # -- Set a global namespace # TODO: Currently some objects do not support this namespace: "" - # -- Adds metalLB annotations to services - addMetalLBAnnotations: true - # -- Adds traefik annotations to services - addTraefikAnnotations: true + metallb: + # -- Adds metalLB annotations to services + addServiceAnnotations: true + traefik: + # -- Adds traefik annotations to services (when needed) + addServiceAnnotations: true + # Enables or disables the fixed middlewares on all ingresses + # Can be overruled per ingress + enableFixedMiddlewares: true + # Applies middleware to all ingresses + # Can be overruled per ingress + fixedMiddlewares: + - chain-basic # -- Minimum nodePort value minNodePort: 9000 # -- Enable to stop most pods and containers including cnpg @@ -515,7 +524,6 @@ wireguardImage: # -- Specify the WireGuard image pull policy pullPolicy: IfNotPresent - # -- Configure the ingresses for the chart here. # Additional ingresses can be added by adding a dictionary key similar to the 'main' ingress. # @default -- See below @@ -523,104 +531,81 @@ 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 - fixedMiddlewares: - - chain-basic - - # -- 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 - + ingressClassName: "" + # 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. - host: chart-example.local - ## Configure the paths for the host - paths: - - # -- Path. Helm template can be passed. - path: / - # -- Ignored if not kubeVersion >= 1.14-0 - pathType: Prefix - service: - # -- Overrides the service name reference for this path - name: - # -- Overrides the service port reference for this path - port: - + hosts: [] + # - # -- Host address. Helm template can be passed. + # host: chart-example.local + # ## Configure the paths for the host + # paths: + # - # -- Path. Helm template can be passed. + # path: / + # # -- Ignored if not kubeVersion >= 1.14-0 + # pathType: Prefix + # # -- 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: [] # - secretName: chart-example-tls # # Cannot be combined with scaleCert - # clusterIssuer: "" - # # Cannot be combined with clusterIssuer + # certificateIssuer: "" + # # Cannot be combined with certificateIssuer # scaleCert: "" # hosts: # - chart-example.local + integrations: + certManager: + enabled: false + certificateIssuer: "" + traefik: + enabled: true + # Default to websecure + entrypoints: + - websecure + enableFixedMiddlewares: true + # Drops both global and local fixedMiddlewares when enabled + allowCors: false + # fixedMiddlewares: + # - chain-basic + middlewares: [] + homepage: + enabled: false + # 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 # -- BETA: Configure the gateway routes for the chart here. # Additional routes can be added by adding a dictionary key similar to the 'main' route.