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 diff --git a/ignition/templates/_helpers.tpl b/ignition/templates/_helpers.tpl index 6ce7cdf..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 -}} @@ -738,6 +744,126 @@ 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 }} + +{{/* +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 +*/}} +{{- 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 +879,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 +889,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..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 @@ -309,9 +313,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 +346,269 @@ 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' +- 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: