From 2d7fea1f300e58bf7050aa8acd44b556b047cb03 Mon Sep 17 00:00:00 2001 From: Kevin Collins Date: Thu, 2 Apr 2026 17:39:37 -0500 Subject: [PATCH 1/3] refactor: Pull Leased Licensing STS aspects into helper templates This enables coalescing with leasedActivation configurations while also simplifying the statefulset template by pulling some of the logic out into helper templates. Additionally, it is also a little smarter about shared license secret objects. Backed it all up with unit tests, including enhancing some existing ones surrounding the redundancy preparation script inclusion. --- ignition/templates/_helpers.tpl | 115 +++++++++++- ignition/templates/statefulset.yaml | 26 +-- ignition/tests/statefulset_test.yaml | 253 +++++++++++++++++++++++++++ 3 files changed, 369 insertions(+), 25 deletions(-) diff --git a/ignition/templates/_helpers.tpl b/ignition/templates/_helpers.tpl index 6ce7cdf..a0a59c4 100644 --- a/ignition/templates/_helpers.tpl +++ b/ignition/templates/_helpers.tpl @@ -738,6 +738,117 @@ Emit custom Ingress TLS settings, if defined. {{- end }} {{- end }} +{{/* +Helper template to reject based on unsupported leased licensing values configuration +*/}} +{{- define "ignition.gateway.licensing.leasedActivation.configCheck" -}} + {{- $licensing := .Values.gateway.licensing -}} + {{- $failMessage := "" -}} + + {{- $secretName := dig "leasedActivation" "secretName" nil $licensing }} + {{- $primarySecretName := dig "primaryLeasedActivation" "secretName" nil $licensing }} + {{- $backupSecretName := dig "backupLeasedActivation" "secretName" nil $licensing }} + + {{- $shouldCheck := gt (add + (len (dig "leasedActivation" dict $licensing)) + (len (dig "primaryLeasedActivation" dict $licensing)) + (len (dig "backupLeasedActivation" dict $licensing)) + ) 0 -}} + + {{- /* Check for redundancy secret names */ -}} + {{- if and .Values.gateway.redundancy.enabled $shouldCheck -}} + {{- if and (eq nil $secretName) (or (eq nil $primarySecretName) (eq nil $backupSecretName)) }} + {{- $failMessage = "Must supply primary/backup or shared licensing Secret name" }} + {{- end }} + {{- end }} + + {{- /* Check for standalone secret name */ -}} + {{- if and (not .Values.gateway.redundancy.enabled) $shouldCheck -}} + {{- if (eq nil $secretName) }} + {{- $failMessage = "Must supply licensing Secret name" }} + {{- end }} + {{- end }} + + {{- /* Throw failure if message is defined */ -}} + {{- if ne $failMessage "" -}} + {{- fail $failMessage }} + {{- end }} +{{- end }} + +{{/* +Returns "true" if leased activation licensing should use a redundancy split configuration +*/}} +{{- define "ignition.gateway.licensing.leasedActivation.useRedundancySplit" -}} + {{- $licensing := .Values.gateway.licensing -}} + {{- $shouldRender := gt (add + (len (dig "leasedActivation" dict $licensing)) + (len (dig "primaryLeasedActivation" dict $licensing)) + (len (dig "backupLeasedActivation" dict $licensing)) + ) 0 -}} + + {{- printf "%t" (and $shouldRender .Values.gateway.redundancy.enabled) }} +{{- end }} + +{{/* +Helper template to render projected secret sources for leased licensing configuration, use with indent +*/}} +{{- define "ignition.gateway.licensing.leasedActivation.projectedSecretSources" -}} + {{- $licensing := .Values.gateway.licensing -}} + + {{- $secretName := dig "leasedActivation" "secretName" nil $licensing }} + {{- $primarySecretName := dig "primaryLeasedActivation" "secretName" nil $licensing }} + {{- $backupSecretName := dig "backupLeasedActivation" "secretName" nil $licensing }} + {{- $licenseKeyKey := dig "leasedActivation" "licenseKeyKey" nil $licensing }} + {{- $primaryLicenseKeyKey := dig "primaryLeasedActivation" "licenseKeyKey" nil $licensing }} + {{- $backupLicenseKeyKey := dig "backupLeasedActivation" "licenseKeyKey" nil $licensing }} + {{- $activationTokenKey := dig "leasedActivation" "activationTokenKey" nil $licensing }} + {{- $primaryActivationTokenKey := dig "primaryLeasedActivation" "activationTokenKey" nil $licensing }} + {{- $backupActivationTokenKey := dig "backupLeasedActivation" "activationTokenKey" nil $licensing }} + + {{- $shouldRender := gt (add + (len (dig "leasedActivation" dict $licensing)) + (len (dig "primaryLeasedActivation" dict $licensing)) + (len (dig "backupLeasedActivation" dict $licensing)) + ) 0 -}} + + {{- $useRedundancySplit := eq "true" (include "ignition.gateway.licensing.leasedActivation.useRedundancySplit" .) -}} + + {{- if $useRedundancySplit -}} + {{- $primarySecretName = coalesce $primarySecretName $secretName -}} + {{- $backupSecretName = coalesce $backupSecretName $secretName -}} + {{- $primaryLicenseKeyKey = coalesce $primaryLicenseKeyKey $licenseKeyKey "ignition-license-key" -}} + {{- $backupLicenseKeyKey = coalesce $backupLicenseKeyKey $licenseKeyKey "ignition-license-key" -}} + {{- $primaryActivationTokenKey = coalesce $primaryActivationTokenKey $activationTokenKey "ignition-activation-token" -}} + {{- $backupActivationTokenKey = coalesce $backupActivationTokenKey $activationTokenKey "ignition-activation-token" -}} +- secret: + name: {{ $primarySecretName }} + items: + - key: {{ $primaryLicenseKeyKey }} + path: primary-ignition-license-key + - key: {{ $primaryActivationTokenKey }} + path: primary-ignition-activation-token + {{- if not (eq $primarySecretName $backupSecretName) }} +- secret: + name: {{ $backupSecretName }} + items: + {{- end }} + - key: {{ $backupLicenseKeyKey }} + path: backup-ignition-license-key + - key: {{ $backupActivationTokenKey }} + path: backup-ignition-activation-token + {{- end }} + + {{- if and (not $useRedundancySplit) $shouldRender -}} +- secret: + name: {{ $secretName }} + items: + - key: {{ $licenseKeyKey | default "ignition-license-key" }} + path: ignition-license-key + - key: {{ $activationTokenKey | default "ignition-activation-token" }} + path: ignition-activation-token + {{- end }} +{{- end }} + {{/* Helper template to inject default key names for leased activation licensing. */}} @@ -753,7 +864,7 @@ Helper template to inject default key names for leased activation licensing. {{/* Render an invocation of the prepare-redundancy.sh script, adding a flag for redundant licensing prep if applicable */}} -{{- define "ignition.gateway.licensing.redundancyPrepareSh" -}} +{{- define "ignition.gateway.redundancy.prepareSh" -}} {{- $args := list -}} {{- if (and .Values.gateway.redundancy .Values.gateway.redundancy.enabled) -}} {{- $args = append $args "/config/scripts/prepare-redundancy.sh" -}} @@ -763,7 +874,7 @@ Render an invocation of the prepare-redundancy.sh script, adding a flag for redu {{- $args = append $args ((print (include "ignition.fullname" .) "-gateway-0." (include "ignition.fullname" .)) | quote) -}} {{/* Optional args */}} - {{- if (and (hasKey .Values.gateway.licensing "primaryLeasedActivation") (hasKey .Values.gateway.licensing "backupLeasedActivation")) -}} + {{- if (eq "true" (include "ignition.gateway.licensing.leasedActivation.useRedundancySplit" .)) -}} {{- $args = append $args "-l" -}} {{- end -}} {{- if eq "false" (coalesce (include "ignition.gateway.envValue" (list . "GATEWAY_NETWORK_REQUIRESSL")) "true") -}} diff --git a/ignition/templates/statefulset.yaml b/ignition/templates/statefulset.yaml index 2a9dc56..08ffbd4 100644 --- a/ignition/templates/statefulset.yaml +++ b/ignition/templates/statefulset.yaml @@ -203,7 +203,7 @@ spec: {{- if (.Values.gateway.tls.enabled) }} - /config/scripts/prepare-tls-certificates.sh {{- end }} - {{- with (include "ignition.gateway.licensing.redundancyPrepareSh" .) }} + {{- with (include "ignition.gateway.redundancy.prepareSh" .) }} - {{ . }} {{- end }} {{- with .Values.gateway.preconfigure.additionalCmds }} @@ -299,28 +299,8 @@ spec: path: "gateway-admin-password" {{- end }} {{- end }} - {{- with .Values.gateway.licensing.leasedActivation }} - {{- include "ignition.gateway.licensing.setDefaults" . }} - - secret: - name: {{ required "Must supply licensing Secret name" .secretName }} - items: - - key: {{ .licenseKeyKey }} - path: "ignition-license-key" - - key: {{ .activationTokenKey }} - path: "ignition-activation-token" - {{- end }} - {{- range $redundancyMode := (.Values.gateway.redundancy.enabled | ternary (list "primary" "backup") list) -}} - {{- with (get $.Values.gateway.licensing (printf "%sLeasedActivation" $redundancyMode)) }} - {{- include "ignition.gateway.licensing.setDefaults" . }} - - secret: - name: {{ required (printf "Must supply %s licensing Secret name" $redundancyMode) .secretName }} - items: - - key: {{ .licenseKeyKey }} - path: {{ printf "%s-ignition-license-key" $redundancyMode }} - - key: {{ .activationTokenKey }} - path: {{ printf "%s-ignition-activation-token" $redundancyMode }} - {{- end }} - {{- end }} + {{- include "ignition.gateway.licensing.leasedActivation.configCheck" . }} + {{- include "ignition.gateway.licensing.leasedActivation.projectedSecretSources" . | nindent 10 }} {{- if .Values.certManager.enabled }} - name: gan-issuer-tls secret: diff --git a/ignition/tests/statefulset_test.yaml b/ignition/tests/statefulset_test.yaml index c337546..171ed72 100644 --- a/ignition/tests/statefulset_test.yaml +++ b/ignition/tests/statefulset_test.yaml @@ -309,9 +309,18 @@ tests: gateway.redundancy.enabled: true gateway.env.GATEWAY_NETWORK_REQUIRESSL: false asserts: + - lengthEqual: + path: spec.template.spec.initContainers[0].args + count: 3 - contains: path: spec.template.spec.initContainers[0].args content: '/config/scripts/prepare-redundancy.sh -g "ignition-gateway-0.ignition" -k -v' +- it: Validate Redundancy Preparation Script Not Included when Redundancy Disabled + template: statefulset.yaml + asserts: + - lengthEqual: + path: spec.template.spec.initContainers[0].args + count: 2 # Validate EmptyDir Data Volume - it: Validate Presence of Volume Claim Template by default template: statefulset.yaml @@ -333,3 +342,247 @@ tests: name: data emptyDir: sizeLimit: "3Gi" +--- +suite: StatefulSet Licensing Tests +release: + name: ignition + namespace: ignition-test +templates: +- statefulset.yaml +- configmap.yaml +tests: +# Leased Activation Licensing Tests +- it: Validate Leased License Configuration Fails without secretName (Standalone) + template: statefulset.yaml + set: + gateway.licensing.leasedActivation: + foo: bar + asserts: + - failedTemplate: + errorMessage: "Must supply licensing Secret name" +- it: Validate Leased License Configuration Fails without secretName (Redundancy, Shared) + template: statefulset.yaml + set: + gateway.redundancy.enabled: true + gateway.licensing.leasedActivation: + foo: bar + asserts: + - failedTemplate: + errorMessage: "Must supply primary/backup or shared licensing Secret name" +- it: Validate Leased License Configuration Fails without secretName on primary (Redundancy, Split) + template: statefulset.yaml + set: + gateway.redundancy.enabled: true + gateway.licensing.primaryLeasedActivation: + foo: bar + gateway.licensing.backupLeasedActivation: + secretName: leased-license2 + asserts: + - failedTemplate: + errorMessage: "Must supply primary/backup or shared licensing Secret name" +- it: Validate Leased License Configuration Fails without secretName on backup (Redundancy, Split) + template: statefulset.yaml + set: + gateway.redundancy.enabled: true + gateway.licensing.primaryLeasedActivation: + secretName: leased-license1 + gateway.licensing.backupLeasedActivation: + foo: bar + asserts: + - failedTemplate: + errorMessage: "Must supply primary/backup or shared licensing Secret name" +- it: Validate Leased License Configuration (Standalone) + template: statefulset.yaml + set: + gateway.licensing.leasedActivation: + secretName: "leased-license1" + asserts: + - lengthEqual: + path: spec.template.spec.volumes[2].projected.sources + count: 2 + - contains: + path: spec.template.spec.volumes[2].projected.sources + content: + secret: + items: + - key: ignition-license-key + path: ignition-license-key + - key: ignition-activation-token + path: ignition-activation-token + name: leased-license1 +- it: Validate Leased License Configuration (Redundancy, Shared) + template: statefulset.yaml + set: + gateway.redundancy.enabled: true + gateway.licensing.leasedActivation: + secretName: "leased-license1" + asserts: + - lengthEqual: + path: spec.template.spec.volumes[2].projected.sources + count: 2 + - contains: + path: spec.template.spec.volumes[2].projected.sources + content: + secret: + items: + - key: ignition-license-key + path: primary-ignition-license-key + - key: ignition-activation-token + path: primary-ignition-activation-token + - key: ignition-license-key + path: backup-ignition-license-key + - key: ignition-activation-token + path: backup-ignition-activation-token + name: leased-license1 +- it: Validate Leased License Configuration (Redundancy, Split) + template: statefulset.yaml + set: + gateway.redundancy.enabled: true + gateway.licensing: + primaryLeasedActivation: + secretName: "leased-license1" + backupLeasedActivation: + secretName: "leased-license2" + asserts: + - lengthEqual: + path: spec.template.spec.volumes[2].projected.sources + count: 3 + - contains: + path: spec.template.spec.volumes[2].projected.sources + content: + secret: + items: + - key: ignition-license-key + path: primary-ignition-license-key + - key: ignition-activation-token + path: primary-ignition-activation-token + name: leased-license1 + - contains: + path: spec.template.spec.volumes[2].projected.sources + content: + secret: + items: + - key: ignition-license-key + path: backup-ignition-license-key + - key: ignition-activation-token + path: backup-ignition-activation-token + name: leased-license2 +- it: Validate Leased License Configuration Coalesce on Primary (Redundancy) + template: statefulset.yaml + set: + gateway.redundancy.enabled: true + gateway.licensing.leasedActivation: + secretName: leased-license1 + gateway.licensing.backupLeasedActivation: + secretName: leased-license2 + activationTokenKey: special-ignition-activation-token + asserts: + - lengthEqual: + path: spec.template.spec.volumes[2].projected.sources + count: 3 + - contains: + path: spec.template.spec.volumes[2].projected.sources + content: + secret: + items: + - key: ignition-license-key + path: primary-ignition-license-key + - key: ignition-activation-token + path: primary-ignition-activation-token + name: leased-license1 + - contains: + path: spec.template.spec.volumes[2].projected.sources + content: + secret: + items: + - key: ignition-license-key + path: backup-ignition-license-key + - key: special-ignition-activation-token + path: backup-ignition-activation-token + name: leased-license2 +- it: Validate Leased License Configuration Coalesce on Backup (Redundancy) + template: statefulset.yaml + set: + gateway.redundancy.enabled: true + gateway.licensing.leasedActivation: + secretName: leased-license1 + licenseKeyKey: special-ignition-license-key + gateway.licensing.primaryLeasedActivation: + secretName: leased-license2 + asserts: + - lengthEqual: + path: spec.template.spec.volumes[2].projected.sources + count: 3 + - contains: + path: spec.template.spec.volumes[2].projected.sources + content: + secret: + items: + - key: special-ignition-license-key + path: primary-ignition-license-key + - key: ignition-activation-token + path: primary-ignition-activation-token + name: leased-license2 + - contains: + path: spec.template.spec.volumes[2].projected.sources + content: + secret: + items: + - key: special-ignition-license-key + path: backup-ignition-license-key + - key: ignition-activation-token + path: backup-ignition-activation-token + name: leased-license1 +- it: Validate Leased License Configuration Split Keys on Shared Secret (Redundancy) + template: statefulset.yaml + set: + gateway.redundancy.enabled: true + gateway.licensing: + leasedActivation: + secretName: leased-license + primaryLeasedActivation: + licenseKeyKey: primary-license-key + activationTokenKey: primary-activation-token + backupLeasedActivation: + licenseKeyKey: backup-license-key + activationTokenKey: backup-activation-token + asserts: + - lengthEqual: + path: spec.template.spec.volumes[2].projected.sources + count: 2 + - contains: + path: spec.template.spec.volumes[2].projected.sources + content: + secret: + items: + - key: primary-license-key + path: primary-ignition-license-key + - key: primary-activation-token + path: primary-ignition-activation-token + - key: backup-license-key + path: backup-ignition-license-key + - key: backup-activation-token + path: backup-ignition-activation-token + name: leased-license +- it: Validate Redundancy Preparation Script Inclusion with Leased Licensing (Shared) + template: statefulset.yaml + set: + gateway.redundancy.enabled: true + gateway.licensing.primaryLeasedActivation: + secretName: leased-license1 + gateway.licensing.backupLeasedActivation: + secretName: leased-license2 + asserts: + - contains: + path: spec.template.spec.initContainers[0].args + content: '/config/scripts/prepare-redundancy.sh -g "ignition-gateway-0.ignition" -l -v' +- it: Validate Redundancy Preparation Script Inclusion with Leased Licensing (Shared) + template: statefulset.yaml + set: + gateway.redundancy.enabled: true + gateway.licensing.leasedActivation: + secretName: leased-license + asserts: + - contains: + path: spec.template.spec.initContainers[0].args + content: '/config/scripts/prepare-redundancy.sh -g "ignition-gateway-0.ignition" -l -v' From 8f11de2811c866413dbf512bbf5cbd8a1748610f Mon Sep 17 00:00:00 2001 From: Kevin Collins Date: Thu, 2 Apr 2026 17:57:18 -0500 Subject: [PATCH 2/3] feat: Add easier enablement of leased license terminate-on-shutdown --- ignition/templates/_helpers.tpl | 15 +++++++++++++++ ignition/tests/statefulset_test.yaml | 26 ++++++++++++++++++++++++++ ignition/values.yaml | 2 ++ 3 files changed, 43 insertions(+) diff --git a/ignition/templates/_helpers.tpl b/ignition/templates/_helpers.tpl index a0a59c4..bddb84e 100644 --- a/ignition/templates/_helpers.tpl +++ b/ignition/templates/_helpers.tpl @@ -98,6 +98,12 @@ Emit the array elements for Ignition JVM args. {{- $jvmArgs = append $jvmArgs (printf "%s=%v" "-XX:MaxDirectMemorySize" .) -}} {{- end }} {{- end -}} + {{- if eq "true" (include "ignition.gateway.licensing.leasedActivation.terminateSessionOnShutdown" .) -}} + {{- $terminateSessionSysProp := "-Dignition.license.leased-activation-terminate-sessions-on-shutdown=true" -}} + {{- if not (has $terminateSessionSysProp .Values.gateway.jvmArgs) -}} + {{- $jvmArgs = append $jvmArgs $terminateSessionSysProp -}} + {{- end -}} + {{- end -}} {{- with .Values.gateway.loggers -}} {{- $jvmArgs = append $jvmArgs (printf "%s=%s" "-Dlogback.configurationFile" "/config/files/logback.xml") -}} {{- end -}} @@ -789,6 +795,15 @@ Returns "true" if leased activation licensing should use a redundancy split conf {{- printf "%t" (and $shouldRender .Values.gateway.redundancy.enabled) }} {{- end }} +{{/* +Returns "true" if leased activation sessions should be terminated during graceful shutdown +*/}} +{{- define "ignition.gateway.licensing.leasedActivation.terminateSessionOnShutdown" -}} + {{- $licensing := .Values.gateway.licensing -}} + {{- $terminate := dig "leasedActivation" "terminateSessionOnShutdown" false $licensing -}} + {{- printf "%t" $terminate }} +{{- end }} + {{/* Helper template to render projected secret sources for leased licensing configuration, use with indent */}} diff --git a/ignition/tests/statefulset_test.yaml b/ignition/tests/statefulset_test.yaml index 171ed72..b6c7982 100644 --- a/ignition/tests/statefulset_test.yaml +++ b/ignition/tests/statefulset_test.yaml @@ -136,6 +136,10 @@ tests: configMap: name: ignition-config-files defaultMode: 0644 + # Check default arg count + - lengthEqual: + path: spec.template.spec.containers[0].args + count: 14 # Verify StatefulSet naming - it: Validate StatefulSet name template: statefulset.yaml @@ -586,3 +590,25 @@ tests: - contains: path: spec.template.spec.initContainers[0].args content: '/config/scripts/prepare-redundancy.sh -g "ignition-gateway-0.ignition" -l -v' +- it: Validate Terminate Leased Sessions on Shutdown is applied when enabled + template: statefulset.yaml + set: + gateway.licensing.leasedActivation: + secretName: leased-license1 + terminateSessionOnShutdown: true + asserts: + - contains: + path: spec.template.spec.containers[0].args + content: '-Dignition.license.leased-activation-terminate-sessions-on-shutdown=true' +- it: Validate Terminate Leased Sessions on Shutdown deduplication of existing JVM arg + template: statefulset.yaml + set: + gateway.licensing.leasedActivation: + secretName: leased-license1 + terminateSessionOnShutdown: true + gateway.jvmArgs: + - '-Dignition.license.leased-activation-terminate-sessions-on-shutdown=true' + asserts: + - lengthEqual: + path: spec.template.spec.containers[0].args + count: 15 diff --git a/ignition/values.yaml b/ignition/values.yaml index b5158b0..63d40c1 100644 --- a/ignition/values.yaml +++ b/ignition/values.yaml @@ -69,6 +69,8 @@ gateway: # licenseKeyKey: "ignition-license-key" # -- Specify the Secret key under which the activation token is stored # activationTokenKey: "ignition-activation-token" + # -- Set to true to attempt online license session termination during graceful shutdown + # terminateSessionOnShutdown: false # -- Ignition Redundancy Configuration redundancy: From 5b6a44496bfa7146dbc20db95664142c4c1f454a Mon Sep 17 00:00:00 2001 From: Kevin Collins Date: Mon, 20 Apr 2026 17:04:23 -0500 Subject: [PATCH 3/3] Add changelog entry --- ignition/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ignition/CHANGELOG.md b/ignition/CHANGELOG.md index f4dd731..36791cf 100644 --- a/ignition/CHANGELOG.md +++ b/ignition/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added `gateway.securityContext` and `gateway.preconfigure.securityContext` with new defaults to align with "restricted" Pod Security Standards profile. +- Added `gateway.licensing.leasedActivation.terminateSessionOnShutdown` boolean to easily enable termination of leased activation license session on graceful shutdown. Defaults to base Ignition behavior of retaining the license session on-disk under the `data` volume. ## [0.2.1] - 2026-01-30