From ed7d67d60a90464547bb0beabd52596478e365ca Mon Sep 17 00:00:00 2001 From: Stavros Kois <47820033+stavros-k@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:45:20 +0200 Subject: [PATCH] feat(certificate): refactor + tests (#626) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Description** ⚒️ Fixes # **⚙️ Type of change** - [x] ⚙️ Feature/App addition - [x] 🪛 Bugfix - [ ] ⚠️ Breaking change (fix or feature that would cause existing functionality to not work as expected) - [x] 🔃 Refactor of current code **🧪 How Has This Been Tested?** **📃 Notes:** **✔️ Checklist:** - [x] ⚖️ My code follows the style guidelines of this project - [x] 👀 I have performed a self-review of my own code - [x] #️⃣ I have commented my code, particularly in hard-to-understand areas - [ ] 📄 I have made corresponding changes to the documentation - [x] ⚠️ My changes generate no new warnings - [x] 🧪 I have added tests to this description that prove my fix is effective or that my feature works - [ ] ⬆️ I increased versions for any altered app according to semantic versioning **➕ App addition** If this PR is an app addition please make sure you have done the following. - [ ] 🪞 I have opened a PR on [truecharts/containers](https://github.com/truecharts/containers) adding the container to TrueCharts mirror repo. - [ ] 🖼️ I have added an icon in the Chart's root directory called `icon.png` --- _Please don't blindly check all the boxes. Read them and only check those that apply. Those checkboxes are there for the reviewer to see what is this all about and the status of this PR with a quick glance._ --- .../tests/certificate/data_test.yaml | 106 ++++++++-- .../tests/certificate/metadata_test.yaml | 193 ++++++++++++++++-- .../tests/certificate/name_test.yaml | 44 ++-- .../tests/certificate/validation_test.yaml | 183 +++++++++++------ .../tests/scaleCertificate/data_test.yaml | 33 +++ .../tests/scaleCertificate/metadata_test.yaml | 64 ++++++ .../tests/scaleCertificate/name_test.yaml | 44 ++++ .../scaleCertificate/validation_test.yaml | 146 +++++++++++++ library/common/Chart.yaml | 2 +- .../common/templates/class/_certificate.tpl | 45 ---- .../class/cert-manager/_certificate.tpl | 59 ++++++ .../common/templates/lib/_tc_capabilities.tpl | 5 - .../templates/lib/certificate/_validation.tpl | 49 +++++ .../common/templates/spawner/_certificate.tpl | 27 --- .../spawner/cert-manager/_certificate.tpl | 44 ++++ library/common/values.yaml | 11 + 16 files changed, 860 insertions(+), 195 deletions(-) create mode 100644 library/common-test/tests/scaleCertificate/data_test.yaml create mode 100644 library/common-test/tests/scaleCertificate/metadata_test.yaml create mode 100644 library/common-test/tests/scaleCertificate/name_test.yaml create mode 100644 library/common-test/tests/scaleCertificate/validation_test.yaml delete mode 100644 library/common/templates/class/_certificate.tpl create mode 100644 library/common/templates/class/cert-manager/_certificate.tpl create mode 100644 library/common/templates/lib/certificate/_validation.tpl delete mode 100644 library/common/templates/spawner/_certificate.tpl create mode 100644 library/common/templates/spawner/cert-manager/_certificate.tpl diff --git a/library/common-test/tests/certificate/data_test.yaml b/library/common-test/tests/certificate/data_test.yaml index daaa7faa..06620e44 100644 --- a/library/common-test/tests/certificate/data_test.yaml +++ b/library/common-test/tests/certificate/data_test.yaml @@ -7,27 +7,99 @@ release: name: test-release-name namespace: test-release-namespace tests: - - it: should pass with secret created for certificate + - it: should pass with certificate created set: - ixCertificates: - "1": - certificate: some_cert - privatekey: some_key - scaleCertificate: - my-cert: + issuer: some-issuer + host: host1 + certificate: + my-certificate1: enabled: true - id: 1 + hosts: + - "{{ .Values.host }}" + certificateIssuer: "{{ .Values.issuer }}" + my-certificate2: + enabled: true + hosts: + - host2 + certificateIssuer: some-other-issuer + certificateSecretTemplate: + labels: + label1: label1 + label2: label2 + annotations: + annotation1: annotation1 + annotation2: annotation2 asserts: - - documentIndex: &secretDoc 0 + - documentIndex: &certDoc 0 isKind: - of: Secret - - documentIndex: *secretDoc + of: Certificate + - documentIndex: *certDoc + isAPIVersion: + of: cert-manager.io/v1 + - documentIndex: *certDoc equal: - path: data + path: metadata.name + value: test-release-name-common-test-my-certificate1 + - documentIndex: *certDoc + equal: + path: metadata.namespace + value: test-release-namespace + - documentIndex: *certDoc + equal: + path: spec value: - tls.crt: c29tZV9jZXJ0 - tls.key: c29tZV9rZXk= - - documentIndex: *secretDoc + secretName: test-release-name-common-test-my-certificate1 + dnsNames: + - host1 + issuerRef: + name: some-issuer + kind: ClusterIssuer + group: cert-manager.io + privateKey: + algorithm: ECDSA + size: 256 + rotationPolicy: Always + - documentIndex: &otherCertDoc 1 + isKind: + of: Certificate + - documentIndex: *otherCertDoc + isAPIVersion: + of: cert-manager.io/v1 + - documentIndex: *otherCertDoc equal: - path: type - value: kubernetes.io/tls + path: spec + value: + secretName: certificate-issuer-my-certificate2 + dnsNames: + - host2 + issuerRef: + name: some-other-issuer + kind: ClusterIssuer + group: cert-manager.io + privateKey: + algorithm: ECDSA + size: 256 + rotationPolicy: Always + secretTemplate: + labels: + label1: label1 + label2: label2 + 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: v9.9.9 + helm-revision: "0" + helm.sh/chart: common-test-1.0.0 + release: test-release-name + annotations: + annotation1: annotation1 + annotation2: annotation2 + - documentIndex: *otherCertDoc + equal: + path: metadata.name + value: certificate-issuer-my-certificate2 + - documentIndex: *otherCertDoc + equal: + path: metadata.namespace + value: test-release-namespace diff --git a/library/common-test/tests/certificate/metadata_test.yaml b/library/common-test/tests/certificate/metadata_test.yaml index ff620ca1..cbc59307 100644 --- a/library/common-test/tests/certificate/metadata_test.yaml +++ b/library/common-test/tests/certificate/metadata_test.yaml @@ -11,8 +11,10 @@ tests: set: label1: label1 label2: global_label2 + label3: label3 annotation1: annotation1 annotation2: global_annotation2 + annotation3: annotation3 global: labels: g_label1: global_label1 @@ -20,25 +22,44 @@ tests: annotations: g_annotation1: global_annotation1 g_annotation2: "{{ .Values.annotation2 }}" - ixCertificates: - "1": - certificate: some_cert - privatekey: some_key - scaleCertificate: - my-cert: + certificate: + my-certificate1: enabled: true - id: 1 - labels: - label1: "{{ .Values.label1 }}" - label2: label2 annotations: annotation1: "{{ .Values.annotation1 }}" annotation2: annotation2 + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + hosts: + - host1 + certificateIssuer: some-issuer + my-certificate2: + enabled: true + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + hosts: + - host1 + certificateIssuer: some-issuer + certificateSecretTemplate: + labels: + label3: "{{ .Values.label3 }}" + label4: label4 + annotations: + annotation3: "{{ .Values.annotation3 }}" + annotation4: annotation4 asserts: - - documentIndex: &secretDoc 0 + - documentIndex: &certDoc 0 isKind: - of: Secret - - documentIndex: *secretDoc + of: Certificate + - documentIndex: *certDoc + isAPIVersion: + of: cert-manager.io/v1 + - documentIndex: *certDoc equal: path: metadata.annotations value: @@ -46,7 +67,7 @@ tests: annotation2: annotation2 g_annotation1: global_annotation1 g_annotation2: global_annotation2 - - documentIndex: *secretDoc + - documentIndex: *certDoc equal: path: metadata.labels value: @@ -54,11 +75,151 @@ 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 + 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: *certDoc + equal: + path: metadata.name + value: test-release-name-common-test-my-certificate1 + - documentIndex: *certDoc + equal: + path: metadata.namespace + value: test-release-namespace + + - documentIndex: &otherCertDoc 1 + isKind: + of: Certificate + - documentIndex: *otherCertDoc + isAPIVersion: + of: cert-manager.io/v1 + - documentIndex: *otherCertDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *otherCertDoc + 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/managed-by: Helm + app.kubernetes.io/version: *appVer + 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: *otherCertDoc + equal: + path: metadata.name + value: certificate-issuer-my-certificate2 + - documentIndex: *otherCertDoc + equal: + path: metadata.namespace + value: test-release-namespace + - documentIndex: *otherCertDoc + equal: + path: spec.secretTemplate.annotations + value: + annotation3: annotation3 + annotation4: annotation4 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *otherCertDoc + equal: + path: spec.secretTemplate.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/managed-by: Helm + app.kubernetes.io/version: *appVer + app.kubernetes.io/instance: test-release-name + app.kubernetes.io/name: common-test + g_label1: global_label1 + g_label2: global_label2 + label3: label3 + label4: label4 + + - it: should pass with certificate created with namespace from tpl + set: + key: some-namespace + certificate: + my-cert1: + enabled: true + namespace: "{{ .Values.key }}" + hosts: + - host1 + certificateIssuer: some-issuer + asserts: + - documentIndex: *certDoc + equal: + path: metadata.namespace + value: some-namespace + + - it: should pass with certificate created with global namespace from tpl + set: + key: global-namespace + global: + namespace: "{{ .Values.key }}" + certificate: + my-cert1: + enabled: true + hosts: + - host1 + certificateIssuer: some-issuer + asserts: + - documentIndex: *certDoc + equal: + path: metadata.namespace + value: global-namespace + + - it: should pass with certificate created with root namespace from tpl + set: + key: local-namespace + namespace: "{{ .Values.key }}" + global: + namespace: global-namespace + certificate: + my-cert1: + enabled: true + hosts: + - host1 + certificateIssuer: some-issuer + asserts: + - documentIndex: *certDoc + equal: + path: metadata.namespace + value: local-namespace + + - it: should pass with certificate created with namespace in TrueNAS SCALE + set: + global: + ixChartContext: + iAmNotEmpty: true + namespace: ix-namespace + certificate: + my-cert1: + enabled: true + hosts: + - host1 + certificateIssuer: some-issuer + asserts: + - documentIndex: *certDoc + equal: + path: metadata.namespace + value: ix-namespace diff --git a/library/common-test/tests/certificate/name_test.yaml b/library/common-test/tests/certificate/name_test.yaml index a3402018..31ff78c3 100644 --- a/library/common-test/tests/certificate/name_test.yaml +++ b/library/common-test/tests/certificate/name_test.yaml @@ -7,38 +7,40 @@ release: tests: - it: should generate correct name set: - ixCertificates: - "1": - certificate: some_cert - privatekey: some_key - "2": - certificate: some_cert - privatekey: some_key - scaleCertificate: + certificate: my-cert1: enabled: true - id: 1 + hosts: + - host1 + certificateIssuer: some-issuer my-cert2: enabled: true - id: 2 + hosts: + - host1 + certificateIssuer: some-issuer + certificateSecretTemplate: + labels: + some-label: my-cert2 + annotations: + some-annotation: my-cert2 asserts: - - documentIndex: &secretDoc 0 + - documentIndex: &certDoc 0 isKind: - of: Secret - - documentIndex: *secretDoc + of: Certificate + - documentIndex: *certDoc isAPIVersion: - of: v1 - - documentIndex: *secretDoc + of: cert-manager.io/v1 + - documentIndex: *certDoc equal: path: metadata.name value: test-release-name-common-test-my-cert1 - - documentIndex: &otherSecretDoc 1 + - documentIndex: &otherCertDoc 1 isKind: - of: Secret - - documentIndex: *otherSecretDoc + of: Certificate + - documentIndex: *otherCertDoc isAPIVersion: - of: v1 - - documentIndex: *otherSecretDoc + of: cert-manager.io/v1 + - documentIndex: *otherCertDoc equal: path: metadata.name - value: test-release-name-common-test-my-cert2 + value: certificate-issuer-my-cert2 diff --git a/library/common-test/tests/certificate/validation_test.yaml b/library/common-test/tests/certificate/validation_test.yaml index 8b5bccd2..5ed4b27f 100644 --- a/library/common-test/tests/certificate/validation_test.yaml +++ b/library/common-test/tests/certificate/validation_test.yaml @@ -7,140 +7,197 @@ release: tests: - it: should fail with name longer than 253 characters set: - scaleCertificate: - my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-long-long-long-long-long-name: + certificate: + my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-long-long-long-long-long-long-long-name: enabled: true - id: 1 asserts: - failedTemplate: - errorMessage: Name [test-release-name-common-test-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-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. + errorMessage: Name [test-release-name-common-test-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-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: - scaleCertificate: + certificate: _my-cert: enabled: true - id: 1 asserts: - failedTemplate: errorMessage: Name [test-release-name-common-test-_my-cert] 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: + certificate: + my-certificate: + enabled: true + namespace: my-extra-super-duper-long-name-that-is-longer-than-63-characters + certificateIssuer: some-issuer + hosts: + - test-host + asserts: + - failedTemplate: + errorMessage: Cert Manager Certificate - 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 + certificate: + my-certificate: + enabled: true + namespace: my-namespace + certificateIssuer: some-issuer + hosts: + - test-host + asserts: + - failedTemplate: + errorMessage: Cert Manager Certificate - Namespace [my-namespace] expected to have [ix-] prefix when installed in TrueNAS SCALE + - it: should fail with labels not a dict set: - scaleCertificate: + certificate: my-cert: enabled: true labels: "not a dict" - id: 1 asserts: - failedTemplate: - errorMessage: Certificate - Expected [labels] to be a dictionary, but got [string] + errorMessage: Cert Manager Certificate - Expected [labels] to be a dictionary, but got [string] - it: should fail with annotations not a dict set: - scaleCertificate: - my-cert: + certificate: + my-certificate: enabled: true annotations: "not a dict" - id: 1 asserts: - failedTemplate: - errorMessage: Certificate - Expected [annotations] to be a dictionary, but got [string] + errorMessage: Cert Manager Certificate - Expected [annotations] to be a dictionary, but got [string] - - it: should fail without id + - it: should fail with empty enabled set: - scaleCertificate: + certificate: + my-certificate: + enabled: + asserts: + - failedTemplate: + errorMessage: Cert Manager Certificate - Expected the defined key [enabled] in [certificate.my-certificate] to not be empty + + - it: should fail if certificateIssuer is missing + set: + certificate: my-cert: enabled: true - id: "" asserts: - failedTemplate: - errorMessage: Certificate - Expected non-empty [id] + errorMessage: Cert Manager Certificate - Expected non-empty [certificateIssuer] - - it: should fail with targetSelector not a dict + - it: should fail if hosts are empty set: - scaleCertificate: + certificate: my-cert: enabled: true - id: 1 - targetSelector: "not a dict" + certificateIssuer: some-issuer + hosts: [] asserts: - failedTemplate: - errorMessage: Certificate - Expected [targetSelector] to be a [map], but got [string] + errorMessage: Cert Manager Certificate - Expected non-empty [hosts] - - it: should fail with empty ixCertificates when cert is defined + - it: should fail if hosts is not slice set: - ixCertificates: [] - scaleCertificate: + certificate: my-cert: enabled: true - id: 1 + certificateIssuer: some-issuer + hosts: not-a-slice asserts: - failedTemplate: - errorMessage: Certificate - Expected non-empty [ixCertificates] + errorMessage: Cert Manager Certificate - Expected [hosts] to be a [slice], but got [string] - - it: should fail with not defined id in ixCertificates when cert is defined + - it: should fail if hosts entry is empty set: - ixCertificates: - "2": - key: value - scaleCertificate: + certificate: my-cert: enabled: true - id: 1 + certificateIssuer: some-issuer + hosts: + - "" asserts: - failedTemplate: - errorMessage: Certificate - Expected certificate with [id] ["1"] to exist in [ixCertificates] + errorMessage: Cert Manager Certificate - Expected non-empty entry in [hosts] - - it: should fail with with revoked cert + - it: should fail if hosts entry starts with https:// set: - ixCertificates: - "1": - revoked: true - scaleCertificate: + certificate: my-cert: enabled: true - id: 1 + certificateIssuer: some-issuer + hosts: + - https://test-host asserts: - failedTemplate: - errorMessage: Certificate - Expected non-revoked certificate with [id] ["1"] + errorMessage: Cert Manager Certificate - Expected entry in [hosts] to not start with [https://], but got [https://test-host] - - it: should fail with with expired cert + - it: should fail if hosts entry starts with http:// set: - ixCertificates: - "1": - expired: true - scaleCertificate: + certificate: my-cert: enabled: true - id: 1 + certificateIssuer: some-issuer + hosts: + - http://test-host asserts: - failedTemplate: - errorMessage: Certificate - Expected non-expired certificate with [id] ["1"] + errorMessage: Cert Manager Certificate - Expected entry in [hosts] to not start with [http://], but got [http://test-host] - - it: should fail with with empty certificate + - it: should fail if hosts entry contains ":" set: - ixCertificates: - "1": - certificate: "" - scaleCertificate: + certificate: my-cert: enabled: true - id: 1 + certificateIssuer: some-issuer + hosts: + - test-host:123 asserts: - failedTemplate: - errorMessage: Certificate - Expected non-empty [certificate] in certificate with [id] ["1"] in [ixCertificates] + errorMessage: Cert Manager Certificate - Expected entry in [hosts] to not contain [:], but got [test-host:123] - - it: should fail with with empty privatekey + - it: should fail if certificateSecretTemplate missing both labels and annotations set: - ixCertificates: - "1": - certificate: some_value - privatekey: "" - scaleCertificate: + certificate: my-cert: enabled: true - id: 1 + certificateIssuer: some-issuer + hosts: + - test-host + certificateSecretTemplate: + some-key: some-value asserts: - failedTemplate: - errorMessage: Certificate - Expected non-empty [privatekey] in certificate with [id] ["1"] in [ixCertificates] + errorMessage: Cert Manager Certificate - Expected [certificateSecretTemplate] to have at least one of [labels, annotations] + + - it: should fail if certificateSecretTemplate labels are not a map + set: + certificate: + my-cert: + enabled: true + certificateIssuer: some-issuer + hosts: + - test-host + certificateSecretTemplate: + labels: "not a map" + asserts: + - failedTemplate: + errorMessage: Cert Manager Certificate (certificateSecretTemplate) - Expected [labels] to be a dictionary, but got [string] + + - it: should fail if certificateSecretTemplate annotations are not a map + set: + certificate: + my-cert: + enabled: true + certificateIssuer: some-issuer + hosts: + - test-host + certificateSecretTemplate: + annotations: "not a map" + asserts: + - failedTemplate: + errorMessage: Cert Manager Certificate (certificateSecretTemplate) - Expected [annotations] to be a dictionary, but got [string] diff --git a/library/common-test/tests/scaleCertificate/data_test.yaml b/library/common-test/tests/scaleCertificate/data_test.yaml new file mode 100644 index 00000000..6f942b74 --- /dev/null +++ b/library/common-test/tests/scaleCertificate/data_test.yaml @@ -0,0 +1,33 @@ +suite: scale certificate data test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +release: + name: test-release-name + namespace: test-release-namespace +tests: + - it: should pass with secret created for certificate + set: + ixCertificates: + "1": + certificate: some_cert + privatekey: some_key + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - documentIndex: &secretDoc 0 + isKind: + of: Secret + - documentIndex: *secretDoc + equal: + path: data + value: + tls.crt: c29tZV9jZXJ0 + tls.key: c29tZV9rZXk= + - documentIndex: *secretDoc + equal: + path: type + value: kubernetes.io/tls diff --git a/library/common-test/tests/scaleCertificate/metadata_test.yaml b/library/common-test/tests/scaleCertificate/metadata_test.yaml new file mode 100644 index 00000000..e769afbb --- /dev/null +++ b/library/common-test/tests/scaleCertificate/metadata_test.yaml @@ -0,0 +1,64 @@ +suite: scale certificate 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 certificate created with labels and annotations + set: + 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 }}" + ixCertificates: + "1": + certificate: some_cert + privatekey: some_key + scaleCertificate: + my-cert: + enabled: true + id: 1 + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + asserts: + - documentIndex: &secretDoc 0 + isKind: + of: Secret + - documentIndex: *secretDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *secretDoc + 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 + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 diff --git a/library/common-test/tests/scaleCertificate/name_test.yaml b/library/common-test/tests/scaleCertificate/name_test.yaml new file mode 100644 index 00000000..754fbc10 --- /dev/null +++ b/library/common-test/tests/scaleCertificate/name_test.yaml @@ -0,0 +1,44 @@ +suite: scale certificate name test +templates: + - common.yaml +release: + name: test-release-name + namespace: test-release-namespace +tests: + - it: should generate correct name + set: + ixCertificates: + "1": + certificate: some_cert + privatekey: some_key + "2": + certificate: some_cert + privatekey: some_key + scaleCertificate: + my-cert1: + enabled: true + id: 1 + my-cert2: + enabled: true + id: 2 + asserts: + - documentIndex: &secretDoc 0 + isKind: + of: Secret + - documentIndex: *secretDoc + isAPIVersion: + of: v1 + - documentIndex: *secretDoc + equal: + path: metadata.name + value: test-release-name-common-test-my-cert1 + - documentIndex: &otherSecretDoc 1 + isKind: + of: Secret + - documentIndex: *otherSecretDoc + isAPIVersion: + of: v1 + - documentIndex: *otherSecretDoc + equal: + path: metadata.name + value: test-release-name-common-test-my-cert2 diff --git a/library/common-test/tests/scaleCertificate/validation_test.yaml b/library/common-test/tests/scaleCertificate/validation_test.yaml new file mode 100644 index 00000000..443d514e --- /dev/null +++ b/library/common-test/tests/scaleCertificate/validation_test.yaml @@ -0,0 +1,146 @@ +suite: scale certificate validation test +templates: + - common.yaml +release: + name: test-release-name + namespace: test-release-namespace +tests: + - it: should fail with name longer than 253 characters + set: + scaleCertificate: + my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-long-long-long-long-long-name: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Name [test-release-name-common-test-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-long-name-that-is-longer-than-253-characters-my-certificate-super-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: + scaleCertificate: + _my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Name [test-release-name-common-test-_my-cert] 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 labels not a dict + set: + scaleCertificate: + my-cert: + enabled: true + labels: "not a dict" + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected [labels] to be a dictionary, but got [string] + + - it: should fail with annotations not a dict + set: + scaleCertificate: + my-cert: + enabled: true + annotations: "not a dict" + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected [annotations] to be a dictionary, but got [string] + + - it: should fail without id + set: + scaleCertificate: + my-cert: + enabled: true + id: "" + asserts: + - failedTemplate: + errorMessage: Certificate - Expected non-empty [id] + + - it: should fail with targetSelector not a dict + set: + scaleCertificate: + my-cert: + enabled: true + id: 1 + targetSelector: "not a dict" + asserts: + - failedTemplate: + errorMessage: Certificate - Expected [targetSelector] to be a [map], but got [string] + + - it: should fail with empty ixCertificates when cert is defined + set: + ixCertificates: [] + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected non-empty [ixCertificates] + + - it: should fail with not defined id in ixCertificates when cert is defined + set: + ixCertificates: + "2": + key: value + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected certificate with [id] ["1"] to exist in [ixCertificates] + + - it: should fail with with revoked cert + set: + ixCertificates: + "1": + revoked: true + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected non-revoked certificate with [id] ["1"] + + - it: should fail with with expired cert + set: + ixCertificates: + "1": + expired: true + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected non-expired certificate with [id] ["1"] + + - it: should fail with with empty certificate + set: + ixCertificates: + "1": + certificate: "" + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected non-empty [certificate] in certificate with [id] ["1"] in [ixCertificates] + + - it: should fail with with empty privatekey + set: + ixCertificates: + "1": + certificate: some_value + privatekey: "" + scaleCertificate: + my-cert: + enabled: true + id: 1 + asserts: + - failedTemplate: + errorMessage: Certificate - Expected non-empty [privatekey] in certificate with [id] ["1"] in [ixCertificates] diff --git a/library/common/Chart.yaml b/library/common/Chart.yaml index 377e2622..24a1ebea 100644 --- a/library/common/Chart.yaml +++ b/library/common/Chart.yaml @@ -15,4 +15,4 @@ maintainers: name: common sources: null type: library -version: 16.1.0 +version: 16.2.0 diff --git a/library/common/templates/class/_certificate.tpl b/library/common/templates/class/_certificate.tpl deleted file mode 100644 index 5178dc22..00000000 --- a/library/common/templates/class/_certificate.tpl +++ /dev/null @@ -1,45 +0,0 @@ -{{/* -This template serves as a blueprint for all Cert-Manager Certificate objects that are created -within the common library. -*/}} -{{- define "tc.v1.common.class.certificate" -}} -{{- $root := .root -}} -{{- $name := .name -}} -{{- $hosts := .hosts -}} -{{- $certificateIssuer := .certificateIssuer -}} -{{- $certificateSecretTemplate := .secretTemplate }} ---- -apiVersion: {{ include "tc.v1.common.capabilities.cert-manager.certificate.apiVersion" $ }} -kind: Certificate -metadata: - name: {{ $name }} - namespace: {{ $root.Values.namespace | default $root.Values.global.namespace | default $root.Release.Namespace }} -spec: - secretName: {{ $name }} - dnsNames: - {{- range $hosts }} - - {{ tpl . $root | quote }} - {{- end }} - privateKey: - algorithm: ECDSA - size: 256 - rotationPolicy: Always - issuerRef: - name: {{ tpl $certificateIssuer $root | quote }} - kind: ClusterIssuer - group: cert-manager.io - {{- if $certificateSecretTemplate }} - secretTemplate: - {{- $labels := (mustMerge ($certificateSecretTemplate.labels | default dict) (include "tc.v1.common.lib.metadata.allLabels" $root | fromYaml)) -}} - {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $root "labels" $labels) | trim) }} - labels: - {{- . | nindent 6 }} - {{- end -}} - {{- $annotations := (mustMerge ($certificateSecretTemplate.annotations | default dict) (include "tc.v1.common.lib.metadata.allAnnotations" $root | fromYaml)) -}} - {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $root "annotations" $annotations) | trim) }} - annotations: - {{- . | nindent 6 }} - {{- end -}} - {{- end -}} - -{{- end -}} diff --git a/library/common/templates/class/cert-manager/_certificate.tpl b/library/common/templates/class/cert-manager/_certificate.tpl new file mode 100644 index 00000000..d4a41687 --- /dev/null +++ b/library/common/templates/class/cert-manager/_certificate.tpl @@ -0,0 +1,59 @@ +{{/* Certificate Class */}} +{{/* Call this template: +{{ include "tc.v1.common.class.certificate" (dict "rootCtx" $ "objectData" $objectData) }} + +rootCtx: The root context of the chart. +objectData: + name: The name of the certificate. + labels: The labels of the certificate. + annotations: The annotations of the certificate. + namespace: The namespace of the certificate. (Optional) +*/}} +{{- define "tc.v1.common.class.certificate" -}} + + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ $objectData.name }} + namespace: {{ include "tc.v1.common.lib.metadata.namespace" (dict "rootCtx" $rootCtx "objectData" $objectData "caller" "Cert Manager Certificate") }} + {{- $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 ($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: + {{- . | nindent 4 }} + {{- end }} +spec: + secretName: {{ $objectData.name }} + dnsNames: + {{- range $h := $objectData.hosts }} + - {{ tpl $h $rootCtx }} + {{- end }} + privateKey: + algorithm: ECDSA + size: 256 + rotationPolicy: Always + issuerRef: + name: {{ tpl $objectData.certificateIssuer $rootCtx }} + kind: ClusterIssuer + group: cert-manager.io + {{- if $objectData.certificateSecretTemplate }} + secretTemplate: + {{- $labels := (mustMerge ($objectData.certificateSecretTemplate.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 6 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.certificateSecretTemplate.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: + {{- . | nindent 6 }} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/templates/lib/_tc_capabilities.tpl b/library/common/templates/lib/_tc_capabilities.tpl index 4d911dd8..c3e61706 100644 --- a/library/common/templates/lib/_tc_capabilities.tpl +++ b/library/common/templates/lib/_tc_capabilities.tpl @@ -22,8 +22,3 @@ {{- define "tc.v1.common.capabilities.hpa.apiVersion" -}} {{- print "autoscaling/v2" -}} {{- end -}} - -{{/* Return the appropriate apiVersion for Cert-Manager certificates */}} -{{- define "tc.v1.common.capabilities.cert-manager.certificate.apiVersion" -}} - {{- print "cert-manager.io/v1" -}} -{{- end -}} diff --git a/library/common/templates/lib/certificate/_validation.tpl b/library/common/templates/lib/certificate/_validation.tpl new file mode 100644 index 00000000..9e84d102 --- /dev/null +++ b/library/common/templates/lib/certificate/_validation.tpl @@ -0,0 +1,49 @@ +{{/* Certificate Validation */}} +{{/* Call this template: +{{ include "tc.v1.common.lib.certificate.validation" (dict "rootCtx" $ "objectData" $objectData) -}} +objectData: + rootCtx: The root context of the chart. + objectData: The Certificate object. +*/}} + +{{- define "tc.v1.common.lib.certificate.validation" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- if not $objectData.certificateIssuer -}} + {{- fail "Cert Manager Certificate - Expected non-empty [certificateIssuer]" -}} + {{- end -}} + + {{- if not $objectData.hosts -}} + {{- fail "Cert Manager Certificate - Expected non-empty [hosts]" -}} + {{- end -}} + + {{- if not (kindIs "slice" $objectData.hosts) -}} + {{- fail (printf "Cert Manager Certificate - Expected [hosts] to be a [slice], but got [%s]" (kindOf $objectData.hosts)) -}} + {{- end -}} + + {{- range $h := $objectData.hosts -}} + {{- if not $h -}} + {{- fail "Cert Manager Certificate - Expected non-empty entry in [hosts]" -}} + {{- end -}} + + {{- $host := tpl $h $rootCtx -}} + {{- if (hasPrefix "http://" $host) -}} + {{- fail (printf "Cert Manager Certificate - Expected entry in [hosts] to not start with [http://], but got [%s]" $host) -}} + {{- end -}} + {{- if (hasPrefix "https://" $host) -}} + {{- fail (printf "Cert Manager Certificate - Expected entry in [hosts] to not start with [https://], but got [%s]" $host) -}} + {{- end -}} + {{- if (contains ":" $host) -}} + {{- fail (printf "Cert Manager Certificate - Expected entry in [hosts] to not contain [:], but got [%s]" $host) -}} + {{- end -}} + + {{- with $objectData.certificateSecretTemplate -}} + {{- if and (not .labels) (not .annotations) -}} + {{- fail "Cert Manager Certificate - Expected [certificateSecretTemplate] to have at least one of [labels, annotations]" -}} + {{- end -}} + + {{- include "tc.v1.common.lib.metadata.validation" (dict "objectData" $objectData.certificateSecretTemplate "caller" "Cert Manager Certificate (certificateSecretTemplate)") -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/templates/spawner/_certificate.tpl b/library/common/templates/spawner/_certificate.tpl deleted file mode 100644 index 0dd53528..00000000 --- a/library/common/templates/spawner/_certificate.tpl +++ /dev/null @@ -1,27 +0,0 @@ -{{/* Renders the certificate objects required by the chart */}} -{{- define "tc.v1.common.spawner.certificate" -}} - {{- $fullname := include "tc.v1.common.lib.chart.names.fullname" $ -}} - - {{/* Generate named certs as required */}} - {{- range $name, $cert := .Values.cert -}} - {{- if $cert.enabled -}} - {{- $certValues := $cert -}} - {{- $certName := $fullname -}} - - {{/* set defaults */}} {{/* FIXME: the primary template does not exist */}} - {{- if and (not $certValues.nameOverride) (ne $name (include "tc.v1.common.lib.util.cert.primary" $)) -}} - {{- $_ := set $certValues "nameOverride" $name -}} - {{- end -}} - - {{- if $certValues.nameOverride -}} - {{- $certName = printf "%v-%v" $certName $certValues.nameOverride -}} - {{- end -}} - - {{- if $certValues.secretTemplate -}} - {{- $certName = printf "%v-%v" "clusterissuer-templated" $name -}} - {{- end -}} - - {{- include "tc.v1.common.class.certificate" (dict "root" $ "name" $certName "certificateIssuer" $cert.certificateIssuer "hosts" $cert.hosts "secretTemplate" $cert.secretTemplate ) -}} - {{- end -}} - {{- end -}} -{{- end -}} diff --git a/library/common/templates/spawner/cert-manager/_certificate.tpl b/library/common/templates/spawner/cert-manager/_certificate.tpl new file mode 100644 index 00000000..92389382 --- /dev/null +++ b/library/common/templates/spawner/cert-manager/_certificate.tpl @@ -0,0 +1,44 @@ +{{/* Certificate Spawner */}} +{{/* Call this template: +{{ include "tc.v1.common.spawner.priorityclass" $ -}} +*/}} + +{{- define "tc.v1.common.spawner.certificate" -}} + {{- $fullname := include "tc.v1.common.lib.chart.names.fullname" $ -}} + + {{- range $name, $cert := .Values.certificate -}} + + {{- $enabled := (include "tc.v1.common.lib.util.enabled" (dict + "rootCtx" $ "objectData" $cert + "name" $name "caller" "Cert Manager Certificate" + "key" "certificate")) -}} + {{- if eq $enabled "true" -}} + {{- $objectData := (mustDeepCopy $cert) -}} + + {{- $objectName := (printf "%s-%s" $fullname $name) -}} + {{- if hasKey $objectData "expandObjectName" -}} + {{- if not $objectData.expandObjectName -}} + {{- $objectName = $name -}} + {{- end -}} + {{- end -}} + + {{/* If a certificateSecretTemplate is defined, adjust name */}} + {{- if $objectData.certificateSecretTemplate }} + {{- $objectName = printf "certificate-issuer-%s" $name -}} + {{- end -}} + + {{/* 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" "Cert Manager Certificate") -}} + {{- include "tc.v1.common.lib.certificate.validation" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{/* Set the name of the secret */}} + {{- $_ := set $objectData "name" $objectName -}} + {{- $_ := set $objectData "shortName" $name -}} + + {{/* Call class to create the object */}} + {{- include "tc.v1.common.class.certificate" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/library/common/values.yaml b/library/common/values.yaml index ffa44111..b1abdcda 100644 --- a/library/common/values.yaml +++ b/library/common/values.yaml @@ -607,6 +607,17 @@ ingress: custom: - somesetting: some value +certificate: {} + # main: + # enabled: false + # certificateIssuer: someissuer + # hosts: + # - somehost + # # Optional + # certificateSecretTemplate: + # labels: {} + # annotations: {} + # -- BETA: Configure the gateway routes for the chart here. # Additional routes can be added by adding a dictionary key similar to the 'main' route. # Please be aware that this is an early beta of this feature, TrueCharts does not guarantee this actually works.