diff --git a/charts/matrix-stack/ci/fragments/matrix-authentication-service-pytest-extras.yaml b/charts/matrix-stack/ci/fragments/matrix-authentication-service-pytest-extras.yaml index 2f9843875..cbe06ceb6 100644 --- a/charts/matrix-stack/ci/fragments/matrix-authentication-service-pytest-extras.yaml +++ b/charts/matrix-stack/ci/fragments/matrix-authentication-service-pytest-extras.yaml @@ -19,6 +19,11 @@ matrixAuthenticationService: configSecret: "{{ $.Release.Name }}-pytest-admin" configSecretKey: "admin.yaml" + syn2mas: + extraEnv: + - name: DEBUG_RENDERING + value: "1" + postgres: podSecurityContext: runAsGroup: 0 diff --git a/charts/matrix-stack/ci/fragments/matrix-authentication-service-syn2mas-dryrun.yaml b/charts/matrix-stack/ci/fragments/matrix-authentication-service-syn2mas-dryrun.yaml new file mode 100644 index 000000000..f0132667a --- /dev/null +++ b/charts/matrix-stack/ci/fragments/matrix-authentication-service-syn2mas-dryrun.yaml @@ -0,0 +1,16 @@ +# Copyright 2025 New Vector Ltd +# +# SPDX-License-Identifier: AGPL-3.0-only + +matrixAuthenticationService: + syn2mas: + enabled: true + dryRun: true + + additional: + password-scheme.yml: + config: | + passwords: + schemes: + - version: 1 + algorithm: bcrypt diff --git a/charts/matrix-stack/ci/fragments/matrix-authentication-service-auth-synapse.yaml b/charts/matrix-stack/ci/fragments/matrix-authentication-service-syn2mas-migrate.yaml similarity index 72% rename from charts/matrix-stack/ci/fragments/matrix-authentication-service-auth-synapse.yaml rename to charts/matrix-stack/ci/fragments/matrix-authentication-service-syn2mas-migrate.yaml index a7d3e2a88..ab5f2523a 100644 --- a/charts/matrix-stack/ci/fragments/matrix-authentication-service-auth-synapse.yaml +++ b/charts/matrix-stack/ci/fragments/matrix-authentication-service-syn2mas-migrate.yaml @@ -3,4 +3,5 @@ # SPDX-License-Identifier: AGPL-3.0-only matrixAuthenticationService: - preMigrationSynapseHandlesAuth: true + syn2mas: + dryRun: false diff --git a/charts/matrix-stack/ci/matrix-authentication-service-keep-auth-in-synapse-values.yaml b/charts/matrix-stack/ci/matrix-authentication-service-keep-auth-in-synapse-values.yaml deleted file mode 100644 index d85e0a8de..000000000 --- a/charts/matrix-stack/ci/matrix-authentication-service-keep-auth-in-synapse-values.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2024-2025 New Vector Ltd -# -# SPDX-License-Identifier: AGPL-3.0-only -# -# source_fragments: matrix-authentication-service-minimal.yaml matrix-authentication-service-auth-synapse.yaml synapse-minimal.yaml -# DO NOT EDIT DIRECTLY. Edit the fragment files to add / modify / remove values - -# initSecrets, postgres don't have any required properties to be set and defaults to enabled -elementWeb: - enabled: false -matrixAuthenticationService: - ingress: - host: mas.ess.localhost - preMigrationSynapseHandlesAuth: true -matrixRTC: - enabled: false -serverName: ess.localhost -synapse: - ingress: - host: synapse.ess.localhost -wellKnownDelegation: - enabled: false diff --git a/charts/matrix-stack/ci/matrix-authentication-service-synapse-secrets-externally-values.yaml b/charts/matrix-stack/ci/matrix-authentication-service-synapse-syn2mas-dry-run-secrets-externally-values.yaml similarity index 89% rename from charts/matrix-stack/ci/matrix-authentication-service-synapse-secrets-externally-values.yaml rename to charts/matrix-stack/ci/matrix-authentication-service-synapse-syn2mas-dry-run-secrets-externally-values.yaml index c2fcb146d..a95c5fa9d 100644 --- a/charts/matrix-stack/ci/matrix-authentication-service-synapse-secrets-externally-values.yaml +++ b/charts/matrix-stack/ci/matrix-authentication-service-synapse-syn2mas-dry-run-secrets-externally-values.yaml @@ -2,13 +2,20 @@ # # SPDX-License-Identifier: AGPL-3.0-only # -# source_fragments: matrix-authentication-service-minimal.yaml matrix-authentication-service-postgres.yaml matrix-authentication-service-postgres-secrets-externally.yaml matrix-authentication-service-secrets-externally.yaml synapse-minimal.yaml synapse-postgres.yaml synapse-postgres-secrets-externally.yaml synapse-secrets-externally.yaml +# source_fragments: matrix-authentication-service-minimal.yaml matrix-authentication-service-postgres.yaml matrix-authentication-service-postgres-secrets-externally.yaml matrix-authentication-service-secrets-externally.yaml synapse-minimal.yaml synapse-postgres.yaml synapse-postgres-secrets-externally.yaml synapse-secrets-externally.yaml matrix-authentication-service-syn2mas-dryrun.yaml # DO NOT EDIT DIRECTLY. Edit the fragment files to add / modify / remove values # initSecrets, postgres don't have any required properties to be set and defaults to enabled elementWeb: enabled: false matrixAuthenticationService: + additional: + password-scheme.yml: + config: | + passwords: + schemes: + - version: 1 + algorithm: bcrypt encryptionSecret: secret: '{{ $.Release.Name }}-mas-external' secretKey: encryption @@ -34,6 +41,9 @@ matrixAuthenticationService: rsa: secret: '{{ $.Release.Name }}-mas-external' secretKey: keysRSA + syn2mas: + dryRun: true + enabled: true synapseOIDCClientSecret: secret: '{{ $.Release.Name }}-mas-external' secretKey: synapseOIDC diff --git a/charts/matrix-stack/ci/matrix-authentication-service-synapse-secrets-in-helm-values.yaml b/charts/matrix-stack/ci/matrix-authentication-service-synapse-syn2mas-dry-run-secrets-in-helm-values.yaml similarity index 91% rename from charts/matrix-stack/ci/matrix-authentication-service-synapse-secrets-in-helm-values.yaml rename to charts/matrix-stack/ci/matrix-authentication-service-synapse-syn2mas-dry-run-secrets-in-helm-values.yaml index 9b6269685..f35173fbb 100644 --- a/charts/matrix-stack/ci/matrix-authentication-service-synapse-secrets-in-helm-values.yaml +++ b/charts/matrix-stack/ci/matrix-authentication-service-synapse-syn2mas-dry-run-secrets-in-helm-values.yaml @@ -2,13 +2,20 @@ # # SPDX-License-Identifier: AGPL-3.0-only # -# source_fragments: matrix-authentication-service-minimal.yaml matrix-authentication-service-postgres.yaml matrix-authentication-service-postgres-secrets-in-helm.yaml matrix-authentication-service-secrets-in-helm.yaml synapse-minimal.yaml synapse-postgres.yaml synapse-postgres-secrets-in-helm.yaml synapse-secrets-in-helm.yaml +# source_fragments: matrix-authentication-service-minimal.yaml matrix-authentication-service-postgres.yaml matrix-authentication-service-postgres-secrets-in-helm.yaml matrix-authentication-service-secrets-in-helm.yaml synapse-minimal.yaml synapse-postgres.yaml synapse-postgres-secrets-in-helm.yaml synapse-secrets-in-helm.yaml matrix-authentication-service-syn2mas-dryrun.yaml # DO NOT EDIT DIRECTLY. Edit the fragment files to add / modify / remove values # initSecrets, postgres don't have any required properties to be set and defaults to enabled elementWeb: enabled: false matrixAuthenticationService: + additional: + password-scheme.yml: + config: | + passwords: + schemes: + - version: 1 + algorithm: bcrypt encryptionSecret: value: CHANGEME-ahohhohgiavee5Koh8ahwo ingress: @@ -42,6 +49,9 @@ matrixAuthenticationService: -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA6521bYjZ789034nLz+oXJyVWqgUdDmRlKxvTfHsBhFtGpOaAoGCCqGSM49AwEHoUQDQgAE6521bYjZ789034nLz+oXJyVWqgUdDmRlKxvTfHsBhFtGpOaAoGCCqGSM49 ------END RSA PRIVATE KEY----- + syn2mas: + dryRun: true + enabled: true synapseOIDCClientSecret: value: CHANGEME-eiv6wae8shooPhie4ief8ru2egahbah0 synapseSharedSecret: diff --git a/charts/matrix-stack/ci/matrix-authentication-service-synapse-syn2mas-migrate-secrets-externally-values.yaml b/charts/matrix-stack/ci/matrix-authentication-service-synapse-syn2mas-migrate-secrets-externally-values.yaml new file mode 100644 index 000000000..1e10dd780 --- /dev/null +++ b/charts/matrix-stack/ci/matrix-authentication-service-synapse-syn2mas-migrate-secrets-externally-values.yaml @@ -0,0 +1,79 @@ +# Copyright 2024-2025 New Vector Ltd +# +# SPDX-License-Identifier: AGPL-3.0-only +# +# source_fragments: matrix-authentication-service-minimal.yaml matrix-authentication-service-postgres.yaml matrix-authentication-service-postgres-secrets-externally.yaml matrix-authentication-service-secrets-externally.yaml synapse-minimal.yaml synapse-postgres.yaml synapse-postgres-secrets-externally.yaml synapse-secrets-externally.yaml matrix-authentication-service-syn2mas-dryrun.yaml matrix-authentication-service-syn2mas-migrate.yaml +# DO NOT EDIT DIRECTLY. Edit the fragment files to add / modify / remove values + +# initSecrets, postgres don't have any required properties to be set and defaults to enabled +elementWeb: + enabled: false +matrixAuthenticationService: + additional: + password-scheme.yml: + config: | + passwords: + schemes: + - version: 1 + algorithm: bcrypt + encryptionSecret: + secret: '{{ $.Release.Name }}-mas-external' + secretKey: encryption + ingress: + host: mas.ess.localhost + postgres: + database: mas + host: postgres + password: + secret: '{{ $.Release.Name }}-mas-external' + secretKey: postgresPassword + user: mas + privateKeys: + ecdsaPrime256v1: + secret: '{{ $.Release.Name }}-mas-external' + secretKey: keysEcdsaPrime256v1 + ecdsaSecp256k1: + secret: '{{ $.Release.Name }}-mas-external' + secretKey: keysEcdsaSecp256k1 + ecdsaSecp384r1: + secret: '{{ $.Release.Name }}-mas-external' + secretKey: keysEcdsaSecp384r1 + rsa: + secret: '{{ $.Release.Name }}-mas-external' + secretKey: keysRSA + syn2mas: + dryRun: false + enabled: true + synapseOIDCClientSecret: + secret: '{{ $.Release.Name }}-mas-external' + secretKey: synapseOIDC + synapseSharedSecret: + secret: '{{ $.Release.Name }}-mas-external' + secretKey: synapseShared +matrixRTC: + enabled: false +serverName: ess.localhost +synapse: + appservices: + - secret: '{{ $.Release.Name }}-synapse-external' + secretKey: bridge_registration.yaml + ingress: + host: synapse.ess.localhost + macaroon: + secret: '{{ $.Release.Name }}-synapse-external' + secretKey: macaroon + postgres: + database: synapse + host: ess-postgres + password: + secret: '{{ $.Release.Name }}-synapse-external' + secretKey: postgresPassword + user: synapse_user + registrationSharedSecret: + secret: '{{ $.Release.Name }}-synapse-external' + secretKey: registrationSharedSecret + signingKey: + secret: '{{ $.Release.Name }}-synapse-external' + secretKey: signingKey +wellKnownDelegation: + enabled: false diff --git a/charts/matrix-stack/ci/matrix-authentication-service-synapse-syn2mas-migrate-secrets-in-helm-values.yaml b/charts/matrix-stack/ci/matrix-authentication-service-synapse-syn2mas-migrate-secrets-in-helm-values.yaml new file mode 100644 index 000000000..87abc0b7e --- /dev/null +++ b/charts/matrix-stack/ci/matrix-authentication-service-synapse-syn2mas-migrate-secrets-in-helm-values.yaml @@ -0,0 +1,78 @@ +# Copyright 2024-2025 New Vector Ltd +# +# SPDX-License-Identifier: AGPL-3.0-only +# +# source_fragments: matrix-authentication-service-minimal.yaml matrix-authentication-service-postgres.yaml matrix-authentication-service-postgres-secrets-in-helm.yaml matrix-authentication-service-secrets-in-helm.yaml synapse-minimal.yaml synapse-postgres.yaml synapse-postgres-secrets-in-helm.yaml synapse-secrets-in-helm.yaml matrix-authentication-service-syn2mas-dryrun.yaml matrix-authentication-service-syn2mas-migrate.yaml +# DO NOT EDIT DIRECTLY. Edit the fragment files to add / modify / remove values + +# initSecrets, postgres don't have any required properties to be set and defaults to enabled +elementWeb: + enabled: false +matrixAuthenticationService: + additional: + password-scheme.yml: + config: | + passwords: + schemes: + - version: 1 + algorithm: bcrypt + encryptionSecret: + value: CHANGEME-ahohhohgiavee5Koh8ahwo + ingress: + host: mas.ess.localhost + postgres: + database: mas + host: postgres + password: + value: CHANGEME-ooWo6jeidahhei3Hae0eer9U + user: mas + privateKeys: + ecdsaPrime256v1: + value: | + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEIYjZ789034nLz+oXJyVWqgUdDmRlKxvTfHsBhFtGpOaAoGCCqGSM49 + AwEHoUQDQgAE6521bYjZ789034nLz+oXJyVWqgUdDmRlKxvTfHsBhFtGpOaAoGCCqGSM49 + AwEAAKBcZW5jb2duZXQwgggYMIINL6Ado018734nLz+oXJyVWqgUdDmRlKxvTfHsBhFtGpOaAoGCCqGSM49AwEH + ------END EC PRIVATE KEY----- + ecdsaSecp256k1: + value: | + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEZFQZ789034nLz+oXJyVWqgUdDmRlKxvTfHsBhFtGpOaAoGCCqGSM49AwEHoUQDQgAE6521bYjZ789034nLz+oXJyVWqgUdDmRlKxvTfHsBhFtGpOaAoGCCqGSM49 + ------END EC PRIVATE KEY----- + ecdsaSecp384r1: + value: | + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEZFQZ789034nLz+oXJyVWqgUdDmRlKxvTfHsBhFtGpOaAoGCCqGSM49AwEHoUQDQgAE6521bYjZ789034nLz+oXJyVWqgUdDmRlKxvTfHsBhFtGpOaAoGCCqGSM49 + ------END EC PRIVATE KEY----- + rsa: + value: | + -----BEGIN RSA PRIVATE KEY----- + MIIEowIBAAKCAQEA6521bYjZ789034nLz+oXJyVWqgUdDmRlKxvTfHsBhFtGpOaAoGCCqGSM49AwEHoUQDQgAE6521bYjZ789034nLz+oXJyVWqgUdDmRlKxvTfHsBhFtGpOaAoGCCqGSM49 + ------END RSA PRIVATE KEY----- + syn2mas: + dryRun: false + enabled: true + synapseOIDCClientSecret: + value: CHANGEME-eiv6wae8shooPhie4ief8ru2egahbah0 + synapseSharedSecret: + value: CHANGEME-iaw8eeSef4zeefie8ii3akien9tiaYah +matrixRTC: + enabled: false +serverName: ess.localhost +synapse: + ingress: + host: synapse.ess.localhost + macaroon: + value: CHANGEME-eek3Eigoh8ux8laeTingeej1 + postgres: + database: synapse + host: ess-postgres + password: + value: CHANGEME-ooWo6jeidahhei3Hae0eer9U + user: synapse_user + registrationSharedSecret: + value: CHANGEME-ooWo6jeidahhei3Hae0eer9U + signingKey: + value: ed25519 0 bNQOzBUDszff7Ax81z6w0uZ1IPWoxYaazT7emaZEfpw +wellKnownDelegation: + enabled: false diff --git a/charts/matrix-stack/ci/pytest-matrix-authentication-service-syn2mas-values.yaml b/charts/matrix-stack/ci/pytest-matrix-authentication-service-syn2mas-values.yaml new file mode 100644 index 000000000..76c689ea9 --- /dev/null +++ b/charts/matrix-stack/ci/pytest-matrix-authentication-service-syn2mas-values.yaml @@ -0,0 +1,68 @@ +# Copyright 2024-2025 New Vector Ltd +# +# SPDX-License-Identifier: AGPL-3.0-only +# +# source_fragments: synapse-minimal.yaml synapse-pytest-base-extras.yaml matrix-authentication-service-pytest-extras.yaml matrix-authentication-service-syn2mas-dryrun.yaml init-secrets-minimal.yaml init-secrets-pytest-extras.yaml postgres-minimal.yaml +# DO NOT EDIT DIRECTLY. Edit the fragment files to add / modify / remove values + +elementWeb: + enabled: false +haproxy: + podSecurityContext: + runAsGroup: 0 +initSecrets: + annotations: + has-no-service-monitor: "true" + podSecurityContext: + runAsGroup: 0 +matrixAuthenticationService: + additional: + 000-pytest-admin: + configSecret: '{{ $.Release.Name }}-pytest-admin' + configSecretKey: admin.yaml + password-scheme.yml: + config: | + passwords: + schemes: + - version: 1 + algorithm: bcrypt + enabled: false + extraEnv: + - name: DEBUG_RENDERING + value: "1" + ingress: + host: mas.{{ $.Values.serverName }} + tlsSecret: '{{ $.Release.Name }}-mas-web-tls' + podSecurityContext: + runAsGroup: 0 + syn2mas: + dryRun: true + enabled: true + extraEnv: + - name: DEBUG_RENDERING + value: "1" +matrixRTC: + enabled: false +postgres: + podSecurityContext: + runAsGroup: 0 +serverName: ess.localhost +synapse: + checkConfigHook: + annotations: + has-no-service-monitor: "true" + extraArgs: + # Validate that any Synapse config that has a _path equivalent uses it + - --no-secrets-in-config + ingress: + host: synapse.{{ $.Values.serverName }} + tlsSecret: '{{ $.Release.Name }}-synapse-web-tls' + podSecurityContext: + runAsGroup: 0 + redis: + annotations: + has-no-service-monitor: "true" + podSecurityContext: + runAsGroup: 0 +wellKnownDelegation: + enabled: false diff --git a/charts/matrix-stack/ci/pytest-matrix-authentication-service-values.yaml b/charts/matrix-stack/ci/pytest-matrix-authentication-service-values.yaml index 492d9bd5e..bf9362f9e 100644 --- a/charts/matrix-stack/ci/pytest-matrix-authentication-service-values.yaml +++ b/charts/matrix-stack/ci/pytest-matrix-authentication-service-values.yaml @@ -28,6 +28,10 @@ matrixAuthenticationService: tlsSecret: '{{ $.Release.Name }}-mas-web-tls' podSecurityContext: runAsGroup: 0 + syn2mas: + extraEnv: + - name: DEBUG_RENDERING + value: "1" matrixRTC: enabled: false postgres: diff --git a/charts/matrix-stack/configs/element-web/config.json.tpl b/charts/matrix-stack/configs/element-web/config.json.tpl index 1c6d649a2..33e98bd81 100644 --- a/charts/matrix-stack/configs/element-web/config.json.tpl +++ b/charts/matrix-stack/configs/element-web/config.json.tpl @@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{- $_ := set $config "features" (dict "feature_video_rooms" true "feature_group_calls" true "feature_new_room_decoration_ui" true "feature_element_call_video_rooms" true) -}} {{- $_ := set $config "element_call" (dict "use_exclusively" true) -}} {{- end }} -{{- if (and $root.Values.matrixAuthenticationService.enabled (not $root.Values.matrixAuthenticationService.preMigrationSynapseHandlesAuth)) }} +{{- if include "element-io.matrix-authentication-service.readyToHandleAuth" (dict "root" $root) }} {{- $embeddedPages := dict "login_for_welcome" true -}} {{- $ssoRedirectOptions := dict "immediate" false -}} {{- $_ := set $settingDefaults "UIFeature.registration" false -}} diff --git a/charts/matrix-stack/configs/matrix-authentication-service/config.yaml.tpl b/charts/matrix-stack/configs/matrix-authentication-service/config.yaml.tpl index be5c9442d..275a259bb 100644 --- a/charts/matrix-stack/configs/matrix-authentication-service/config.yaml.tpl +++ b/charts/matrix-stack/configs/matrix-authentication-service/config.yaml.tpl @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{- $root := .root }} {{- with required "matrix-authentication-service/config.yaml.tpl missing context" .context }} - +{{- $context := . -}} http: public_base: "https://{{ tpl .ingress.host $root }}" listeners: @@ -90,7 +90,7 @@ secrets: "context" (dict "secretPath" "matrixAuthenticationService.privateKeys.rsa" "initSecretKey" "MAS_RSA_PRIVATE_KEY" - "defaultSecretName" (printf "%s-matrix-authentication-service" $root.Release.Name) + "defaultSecretName" (include "element-io.matrix-authentication-service.secret-name" (dict "root" $root "context" $context)) "defaultSecretKey" "RSA_PRIVATE_KEY" ) ) }} @@ -101,7 +101,7 @@ secrets: "context" (dict "secretPath" "matrixAuthenticationService.privateKeys.ecdsaPrime256v1" "initSecretKey" "MAS_ECDSA_PRIME256V1_PRIVATE_KEY" - "defaultSecretName" (printf "%s-matrix-authentication-service" $root.Release.Name) + "defaultSecretName" (include "element-io.matrix-authentication-service.secret-name" (dict "root" $root "context" $context)) "defaultSecretKey" "ECDSA_PRIME256V1_PRIVATE_KEY" ) ) }} @@ -112,7 +112,7 @@ secrets: dict "root" $root "context" (dict "secretPath" "matrixAuthenticationService.privateKeys.ecdsaSecp256k1" - "defaultSecretName" (printf "%s-matrix-authentication-service" $root.Release.Name) + "defaultSecretName" (include "element-io.matrix-authentication-service.secret-name" (dict "root" $root "context" $context)) "defaultSecretKey" "ECDSA_SECP256K1_PRIVATE_KEY" ) ) }} @@ -124,7 +124,7 @@ secrets: dict "root" $root "context" (dict "secretPath" "matrixAuthenticationService.privateKeys.ecdsaSecp384r1" - "defaultSecretName" (printf "%s-matrix-authentication-service" $root.Release.Name) + "defaultSecretName" (include "element-io.matrix-authentication-service.secret-name" (dict "root" $root "context" $context)) "defaultSecretKey" "ECDSA_SECP384R1_PRIVATE_KEY" ) ) }} diff --git a/charts/matrix-stack/configs/synapse/partial-haproxy.cfg.tpl b/charts/matrix-stack/configs/synapse/partial-haproxy.cfg.tpl index 940ab35a4..ba2b39b12 100644 --- a/charts/matrix-stack/configs/synapse/partial-haproxy.cfg.tpl +++ b/charts/matrix-stack/configs/synapse/partial-haproxy.cfg.tpl @@ -1,5 +1,5 @@ {{- /* -Copyright 2024 New Vector Ltd +Copyright 2024-2025 New Vector Ltd SPDX-License-Identifier: AGPL-3.0-only */ -}} @@ -73,7 +73,7 @@ frontend synapse-http-in http-request set-var(req.backend) path,map_reg(/synapse/path_map_file_get,main) if has_get_map METH_GET http-request set-var(req.backend) path,map_reg(/synapse/path_map_file,main) unless { var(req.backend) -m found } -{{- if $root.Values.matrixAuthenticationService.enabled }} +{{- if include "element-io.matrix-authentication-service.readyToHandleAuth" (dict "root" $root) }} acl rendezvous path_beg /_matrix/client/unstable/org.matrix.msc4108/rendezvous acl rendezvous path_beg /_synapse/client/rendezvous use_backend return_204_rendezvous if { method OPTIONS } rendezvous @@ -229,7 +229,7 @@ backend synapse-be_{{ $additionalPathId }} backend return_204_synapse http-request return status 204 hdr "Access-Control-Allow-Origin" "*" hdr "Access-Control-Allow-Methods" "GET, HEAD, POST, PUT, DELETE, OPTIONS" hdr "Access-Control-Allow-Headers" "Origin, X-Requested-With, Content-Type, Accept, Authorization, Date" hdr "Access-Control-Expose-Headers" "Synapse-Trace-Id, Server" -{{- if $root.Values.matrixAuthenticationService.enabled }} +{{- if include "element-io.matrix-authentication-service.readyToHandleAuth" (dict "root" $root) }} backend return_204_rendezvous http-request return status 204 hdr "Access-Control-Allow-Origin" "*" hdr "Access-Control-Allow-Methods" "GET, HEAD, POST, PUT, DELETE, OPTIONS" hdr "Access-Control-Allow-Headers" "Origin, Content-Type, Accept, Content-Type, If-Match, If-None-Match" hdr "Access-Control-Expose-Headers" "Synapse-Trace-Id, Server, ETag" diff --git a/charts/matrix-stack/configs/synapse/synapse-04-homeserver-overrides.yaml.tpl b/charts/matrix-stack/configs/synapse/synapse-04-homeserver-overrides.yaml.tpl index 7350625b6..1d557e536 100644 --- a/charts/matrix-stack/configs/synapse/synapse-04-homeserver-overrides.yaml.tpl +++ b/charts/matrix-stack/configs/synapse/synapse-04-homeserver-overrides.yaml.tpl @@ -88,7 +88,7 @@ ip_range_blacklist: - 'ff00::/8' - 'fec0::/10' -{{- if (and $root.Values.matrixAuthenticationService.enabled (not $root.Values.matrixAuthenticationService.preMigrationSynapseHandlesAuth)) }} +{{- if include "element-io.matrix-authentication-service.readyToHandleAuth" (dict "root" $root) }} experimental_features: msc3861: enabled: true diff --git a/charts/matrix-stack/source/matrixAuthenticationService.json b/charts/matrix-stack/source/matrixAuthenticationService.json index 7c3646a3d..703a8bbde 100644 --- a/charts/matrix-stack/source/matrixAuthenticationService.json +++ b/charts/matrix-stack/source/matrixAuthenticationService.json @@ -7,9 +7,6 @@ "type": "boolean", "description": "Enable the Matrix Authentication Service (MAS) service." }, - "preMigrationSynapseHandlesAuth": { - "type": "boolean" - }, "additional": { "$ref": "file://common/additional.json" }, @@ -44,6 +41,49 @@ } } }, + "syn2mas": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable synapse to Matrix Authentication Service migration." + }, + "image": { + "$ref": "file://common/image.json" + }, + "dryRun": { + "type": "boolean", + "description": "Run the migration job in dry-run mode. Do not actually migrate the data." + }, + "labels": { + "$ref": "file://common/labels.json" + }, + "annotations": { + "$ref": "file://common/workloadAnnotations.json" + }, + "extraEnv": { + "$ref": "file://common/extraEnv.json" + }, + "containersSecurityContext": { + "$ref": "file://common/containersSecurityContext.json" + }, + "nodeSelector": { + "$ref": "file://common/nodeSelector.json" + }, + "podSecurityContext": { + "$ref": "file://common/podSecurityContext.json" + }, + "resources": { + "$ref": "file://common/resources.json" + }, + "serviceAccount": { + "$ref": "file://common/serviceAccount.json" + }, + "tolerations": { + "$ref": "file://common/tolerations.json" + } + } + }, "ingress": { "$ref": "file://common/ingress.json" }, diff --git a/charts/matrix-stack/source/matrixAuthenticationService.yaml.j2 b/charts/matrix-stack/source/matrixAuthenticationService.yaml.j2 index 98a2098fb..3f2d2d05a 100644 --- a/charts/matrix-stack/source/matrixAuthenticationService.yaml.j2 +++ b/charts/matrix-stack/source/matrixAuthenticationService.yaml.j2 @@ -8,12 +8,6 @@ SPDX-License-Identifier: AGPL-3.0-only enabled: true {{- sub_schema_values.image(registry='ghcr.io', repository='element-hq/matrix-authentication-service', tag='0.16.0') }} -## Force the authentication to happen with legacy authentication. -## This can be used to deploy Matrix Authentication Service and keeping auth on Synapse. -## Once MAS is deployed, you can run the syn2mas tool to migrate the data from Synapse to MAS. -## This should be set back to false and never switch again after the migration to MAS has been run. -preMigrationSynapseHandlesAuth: false - {{ sub_schema_values.postgresLibPQ() }} {{ sub_schema_values.credential("Encryption secret", "encryptionSecret", initIfAbsent=True) }} @@ -36,6 +30,7 @@ privateKeys: {{ sub_schema_values.serviceAccount() }} {{ sub_schema_values.nodeSelector() }} {{ sub_schema_values.tolerations() }} +{{ sub_schema_values.hostAliases() }} {{ sub_schema_values.topologySpreadConstraints() }} {{ sub_schema_values.podSecurityContext(user_id=10005, group_id=10005) }} {{ sub_schema_values.containersSecurityContext() }} @@ -45,3 +40,28 @@ privateKeys: {{ sub_schema_values.probe("liveness") }} {{ sub_schema_values.probe("readiness") }} {{ sub_schema_values.probe("startup", failureThreshold=4) }} + +## Enables synapse to Matrix Authentication Service migration +## The migration process must follow the following steps: +## 1. Set `matrixAuthenticationService.enabled: true` and `matrixAuthenticationService.syn2mas.enable: true`, run the helm upgrade command, check the result of the pre-upgrade hook job +## 2. Set `matrixAuthenticationService.syn2mas.dryRun`: false, run the helm upgrade command, check the result of the pre-upgrade hook job +## 3. To disable the job scaling-down Synapse in future helm-upgrades, set `matrixAuthenticationService.syn2mas.enabled: false` +syn2mas: + enabled: false + + # Syn2Mas relies on the debug image to copy mas-cli to the matrix-tools container + {{- sub_schema_values.image(registry='ghcr.io', repository='element-hq/matrix-authentication-service', tag='0.16.0-debug') | indent(2) }} + {{- sub_schema_values.labels() | indent(2) -}} + {{- sub_schema_values.workloadAnnotations() | indent(2) -}} + {{- sub_schema_values.containersSecurityContext() | indent(2) -}} + {{- sub_schema_values.nodeSelector() | indent(2) -}} + {{- sub_schema_values.podSecurityContext(user_id='10005', group_id='10005') | indent(2) -}} + {{- sub_schema_values.resources(requests_memory='50Mi', requests_cpu='50m', limits_memory='350Mi') | indent(2) -}} + {{- sub_schema_values.serviceAccount() | indent(2) -}} + {{- sub_schema_values.tolerations() | indent(2) }} + + ## Runs the syn2mas process in dryRun mode. + ## Force the authentication to happen with legacy authentication. + ## This can be used to deploy Matrix Authentication Service and keeping auth on Synapse. + ## This should be set back to false and never switch again after the migration to MAS has been run. + dryRun: true diff --git a/charts/matrix-stack/templates/ess-library/_labels.tpl b/charts/matrix-stack/templates/ess-library/_labels.tpl index 16458b2ab..7f2a074e9 100644 --- a/charts/matrix-stack/templates/ess-library/_labels.tpl +++ b/charts/matrix-stack/templates/ess-library/_labels.tpl @@ -1,5 +1,5 @@ {{- /* -Copyright 2024 New Vector Ltd +Copyright 2024-2025 New Vector Ltd SPDX-License-Identifier: AGPL-3.0-only */ -}} @@ -39,7 +39,7 @@ app.kubernetes.io/part-of: matrix-stack {{- with required "element-io.ess-library.postgres-label requires context" .context -}} {{- $essPassword := required "element-io.ess-library.postgres-label context missing essPassword" .essPassword -}} {{- $postgresProperty := required "elment-io.ess-library.postgres-label context missing postgresProperty" .postgresProperty -}} -k8s.element.io/postgres-password-hash: {{ if $postgresProperty -}} +k8s.element.io/postgres-password-{{ $essPassword | lower }}-hash: {{ if $postgresProperty -}} {{- if $postgresProperty.password.value -}} {{- $postgresProperty.password.value | sha1sum -}} {{- else -}} diff --git a/charts/matrix-stack/templates/matrix-authentication-service/_helpers.tpl b/charts/matrix-stack/templates/matrix-authentication-service/_helpers.tpl index 6b3412f91..28c1fda69 100644 --- a/charts/matrix-stack/templates/matrix-authentication-service/_helpers.tpl +++ b/charts/matrix-stack/templates/matrix-authentication-service/_helpers.tpl @@ -15,6 +15,18 @@ app.kubernetes.io/version: {{ include "element-io.ess-library.labels.makeSafe" . {{- end }} {{- end }} +{{- define "element-io.matrix-authentication-service-syn2mas.labels" -}} +{{- $root := .root -}} +{{- with required "element-io.matrix-authentication-service.labels missing context" .context -}} +{{ include "element-io.ess-library.labels.common" (dict "root" $root "context" (dict "labels" .labels "withChartVersion" .withChartVersion)) }} +app.kubernetes.io/component: matrix-authentication +app.kubernetes.io/name: matrix-authentication-service-syn2mas +app.kubernetes.io/instance: {{ $root.Release.Name }}-matrix-authentication-service-syn2mas +app.kubernetes.io/version: {{ include "element-io.ess-library.labels.makeSafe" .image.tag }} +{{- end }} +{{- end }} + + {{- define "element-io.matrix-authentication-service.config" }} {{- $root := .root -}} {{- with required "element-io.matrix-authentication-service.config missing context" .context -}} @@ -25,7 +37,7 @@ app.kubernetes.io/version: {{ include "element-io.ess-library.labels.makeSafe" . {{- define "element-io.matrix-authentication-service.configSecrets" -}} {{- $root := .root -}} {{- with required "element-io.matrix-authentication-service.configSecrets missing context" .context -}} -{{ $configSecrets := list (printf "%s-matrix-authentication-service" $root.Release.Name) }} +{{ $configSecrets := list (include "element-io.matrix-authentication-service.secret-name" (dict "root" $root "context" .)) }} {{- if and $root.Values.initSecrets.enabled (include "element-io.init-secrets.generated-secrets" (dict "root" $root)) }} {{ $configSecrets = append $configSecrets (printf "%s-generated" $root.Release.Name) }} {{- end }} @@ -33,7 +45,7 @@ app.kubernetes.io/version: {{ include "element-io.ess-library.labels.makeSafe" . (dict "root" $root "context" (dict "essPassword" "matrixAuthenticationService" "componentPasswordPath" "matrixAuthenticationService.postgres.password" - "defaultSecretName" (printf "%s-matrix-authentication-service" $root.Release.Name) + "defaultSecretName" (include "element-io.matrix-authentication-service.secret-name" (dict "root" $root "context" .)) "isHook" false ) ) @@ -100,7 +112,7 @@ app.kubernetes.io/version: {{ include "element-io.ess-library.labels.makeSafe" . "essPassword" "matrixAuthenticationService" "initSecretKey" "POSTGRES_MATRIX_AUTHENTICATION_SERVICE_PASSWORD" "componentPasswordPath" "matrixAuthenticationService.postgres.password" - "defaultSecretName" (printf "%s-matrix-authentication-service" $root.Release.Name) + "defaultSecretName" (include "element-io.matrix-authentication-service.secret-name" (dict "root" $root "context" .)) "defaultSecretKey" "POSTGRES_PASSWORD" "isHook" false ) @@ -116,7 +128,7 @@ app.kubernetes.io/version: {{ include "element-io.ess-library.labels.makeSafe" . "context" (dict "secretPath" "matrixAuthenticationService.encryptionSecret" "initSecretKey" "MAS_ENCRYPTION_SECRET" - "defaultSecretName" (printf "%s-matrix-authentication-service" $root.Release.Name) + "defaultSecretName" (include "element-io.matrix-authentication-service.secret-name" (dict "root" $root "context" .)) "defaultSecretKey" "ENCRYPTION_SECRET" ) ) @@ -135,7 +147,7 @@ app.kubernetes.io/version: {{ include "element-io.ess-library.labels.makeSafe" . "context" (dict "secretPath" "matrixAuthenticationService.synapseSharedSecret" "initSecretKey" "MAS_SYNAPSE_SHARED_SECRET" - "defaultSecretName" (printf "%s-matrix-authentication-service" $root.Release.Name) + "defaultSecretName" (include "element-io.matrix-authentication-service.secret-name" (dict "root" $root "context" .)) "defaultSecretKey" "SYNAPSE_SHARED_SECRET" ) ) @@ -150,7 +162,7 @@ app.kubernetes.io/version: {{ include "element-io.ess-library.labels.makeSafe" . "context" (dict "secretPath" "matrixAuthenticationService.synapseOIDCClientSecret" "initSecretKey" "MAS_SYNAPSE_OIDC_CLIENT_SECRET" - "defaultSecretName" (printf "%s-matrix-authentication-service" $root.Release.Name) + "defaultSecretName" (include "element-io.matrix-authentication-service.secret-name" (dict "root" $root "context" .)) "defaultSecretKey" "SYNAPSE_OIDC_CLIENT_SECRET" ) ) @@ -164,7 +176,20 @@ app.kubernetes.io/version: {{ include "element-io.ess-library.labels.makeSafe" . {{- define "element-io.matrix-authentication-service.secret-name" }} {{- $root := .root }} {{- with required "element-io.matrix-authentication-service.secret-name requires context" .context }} -{{- $isHook := required "element-io.matrix-authentication-service.secret-name requires context.isHook" .isHook }} +{{- $isHook := .isHook }} +{{- if $isHook }} +{{- $root.Release.Name }}-matrix-authentication-service-hook +{{- else }} +{{- $root.Release.Name }}-matrix-authentication-service +{{- end }} +{{- end }} +{{- end }} + + +{{- define "element-io.matrix-authentication-service.configmap-name" }} +{{- $root := .root }} +{{- with required "element-io.matrix-authentication-service.configmap-name requires context" .context }} +{{- $isHook := required "element-io.matrix-authentication-service.configmap-name requires context.isHook" .isHook }} {{- if $isHook }} {{- $root.Release.Name }}-matrix-authentication-service-hook {{- else }} @@ -254,3 +279,50 @@ config.yaml: | {{- end -}} {{- end }} {{- end -}} + + +{{- define "element-io.matrix-authentication-service.render-config" -}} +{{- $root := .root -}} +{{- with required "element-io.matrix-authentication-service.render-config missing context" .context -}} +{{- $context := . -}} +- "/matrix-tools" +- render-config +- -output +- /conf/config.yaml + {{- range $key := (.additional | keys | uniq | sortAlpha) -}} + {{- $prop := index $root.Values.matrixAuthenticationService.additional $key }} + {{- if $prop.config }} +- /secrets/{{ include "element-io.matrix-authentication-service.secret-name" (dict "root" $root "context" $context) }}/user-{{ $key }} + {{- end }} + {{- if $prop.configSecret }} +- /secrets/{{ tpl $prop.configSecret $root }}/{{ $prop.configSecretKey }} + {{- end }} + {{- end }} +- /config-templates/config.yaml +{{- end }} +{{- end }} + + +{{- define "element-io.matrix-authentication-service.syn2masConfigSecrets" -}} +{{- $root := .root -}} +{{- with required "element-io.matrix-authentication-service.syn2masConfigSecrets missing context" .context -}} +{{- $masSecrets := include "element-io.matrix-authentication-service.configSecrets" (dict "root" $root "context" .masContext) | fromJsonArray }} +{{- $synapseSecrets := include "element-io.synapse.configSecrets" (dict "root" $root "context" .synapseContext) | fromJsonArray }} +{{- $syn2masSecrets := concat $masSecrets $synapseSecrets | uniq | sortAlpha }} +{{- $syn2masSecrets | toJson -}} +{{- end -}} +{{- end -}} + +{{- define "element-io.matrix-authentication-service.readyToHandleAuth" -}} +{{- $root := .root -}} +{{- /* + If MAS is enabled, and the migration is disabled, it is ready to handle auth + If MAS is enabled, and the migration is enabled, but not running in dryRun, once the migration is complete + it will be ready to handle auth (after the pre-upgrade hooks) +*/}} +{{- if (and $root.Values.matrixAuthenticationService.enabled + (or (not $root.Values.matrixAuthenticationService.syn2mas.enabled) + (not $root.Values.matrixAuthenticationService.syn2mas.dryRun))) -}} +true +{{- end -}} +{{- end -}} diff --git a/charts/matrix-stack/templates/matrix-authentication-service/configmap.yaml b/charts/matrix-stack/templates/matrix-authentication-service/configmap.yaml index 7a6d4ca8b..edba7e1ee 100644 --- a/charts/matrix-stack/templates/matrix-authentication-service/configmap.yaml +++ b/charts/matrix-stack/templates/matrix-authentication-service/configmap.yaml @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only apiVersion: v1 kind: ConfigMap metadata: - name: {{ $.Release.Name }}-matrix-authentication-service + name: {{ include "element-io.matrix-authentication-service.configmap-name" (dict "root" $ "context" (dict "isHook" false)) }} namespace: {{ $.Release.Namespace }} labels: {{- include "element-io.matrix-authentication-service.labels" (dict "root" $ "context" .) | nindent 4 }} diff --git a/charts/matrix-stack/templates/matrix-authentication-service/configmap_hook.yaml b/charts/matrix-stack/templates/matrix-authentication-service/configmap_hook.yaml new file mode 100644 index 000000000..ce39ddfe9 --- /dev/null +++ b/charts/matrix-stack/templates/matrix-authentication-service/configmap_hook.yaml @@ -0,0 +1,23 @@ +{{- /* +Copyright 2025 New Vector Ltd + +SPDX-License-Identifier: AGPL-3.0-only +*/ -}} + +{{- with .Values.matrixAuthenticationService -}} +{{- if and .enabled .syn2mas.enabled -}} +{{- $masContext := (mustMergeOverwrite ($.Values.matrixAuthenticationService | deepCopy) (dict "isHook" true)) -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "element-io.matrix-authentication-service.configmap-name" (dict "root" $ "context" (dict "isHook" true)) }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "element-io.matrix-authentication-service.labels" (dict "root" $ "context" $masContext) | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-5" +data: + {{- include "element-io.matrix-authentication-service.configmap-data" (dict "root" $ "context" $masContext) | nindent 2 -}} +{{ end -}} +{{- end -}} diff --git a/charts/matrix-stack/templates/matrix-authentication-service/deployment.yaml b/charts/matrix-stack/templates/matrix-authentication-service/deployment.yaml index e4dc65f8e..4eb1add9e 100644 --- a/charts/matrix-stack/templates/matrix-authentication-service/deployment.yaml +++ b/charts/matrix-stack/templates/matrix-authentication-service/deployment.yaml @@ -32,7 +32,7 @@ spec: strategy: type: RollingUpdate rollingUpdate: - maxUnavailable: 1 + maxUnavailable: {{ min (max 0 (sub .replicas 1)) 1 }} maxSurge: 2 template: metadata: diff --git a/charts/matrix-stack/templates/matrix-authentication-service/ingress.yaml b/charts/matrix-stack/templates/matrix-authentication-service/ingress.yaml index 2af5b9ff0..e8768e843 100644 --- a/charts/matrix-stack/templates/matrix-authentication-service/ingress.yaml +++ b/charts/matrix-stack/templates/matrix-authentication-service/ingress.yaml @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only */ -}} {{- with .Values.matrixAuthenticationService -}} -{{- if .enabled -}} +{{- if include "element-io.matrix-authentication-service.readyToHandleAuth" (dict "root" $) -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/charts/matrix-stack/templates/matrix-authentication-service/secret_hook.yaml b/charts/matrix-stack/templates/matrix-authentication-service/secret_hook.yaml index 5c473968c..c9cbcff5f 100644 --- a/charts/matrix-stack/templates/matrix-authentication-service/secret_hook.yaml +++ b/charts/matrix-stack/templates/matrix-authentication-service/secret_hook.yaml @@ -5,23 +5,29 @@ SPDX-License-Identifier: AGPL-3.0-only */ -}} {{- with .Values.matrixAuthenticationService -}} -{{- if .enabled -}} -{{- if and $.Values.synapse.enabled $.Values.synapse.checkConfigHook.enabled -}} -{{- if include "element-io.matrix-authentication-service.synapse-secret-data" (dict "root" $ "context" .) -}} +{{- if and .enabled $.Values.synapse.enabled + (or (and $.Values.synapse.checkConfigHook.enabled + (include "element-io.matrix-authentication-service.synapse-secret-data" (dict "root" $ "context" .)) + ) + .syn2mas.enabled + ) -}} +{{- $masContext := (mustMergeOverwrite ($.Values.matrixAuthenticationService | deepCopy) (dict "isHook" true)) -}} apiVersion: v1 kind: Secret metadata: name: {{ include "element-io.matrix-authentication-service.secret-name" (dict "root" $ "context" (dict "isHook" true)) }} namespace: {{ $.Release.Namespace }} labels: - {{- include "element-io.matrix-authentication-service.labels" (dict "root" $ "context" .) | nindent 4 }} + {{- include "element-io.matrix-authentication-service.labels" (dict "root" $ "context" $masContext) | nindent 4 }} annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-weight": "-5" type: Opaque data: -{{- include "element-io.matrix-authentication-service.synapse-secret-data" (dict "root" $ "context" .) | nindent 2 }} -{{- end -}} +{{- if .syn2mas.enabled }} +{{- include "element-io.matrix-authentication-service.secret-data" (dict "root" $ "context" $masContext) | nindent 2 }} +{{- else }} +{{- include "element-io.matrix-authentication-service.synapse-secret-data" (dict "root" $ "context" $masContext) | nindent 2 }} {{- end -}} {{- end -}} {{- end -}} diff --git a/charts/matrix-stack/templates/matrix-authentication-service/syn2mas_job.yaml b/charts/matrix-stack/templates/matrix-authentication-service/syn2mas_job.yaml new file mode 100644 index 000000000..c9c8ae124 --- /dev/null +++ b/charts/matrix-stack/templates/matrix-authentication-service/syn2mas_job.yaml @@ -0,0 +1,332 @@ +{{- /* +Copyright 2025 New Vector Ltd + +SPDX-License-Identifier: AGPL-3.0-only +*/ -}} + +{{- with .Values.matrixAuthenticationService -}} +{{- if and .enabled .syn2mas.enabled $.Values.synapse.enabled -}} +{{/* dryRun mode runs as a post-upgrade hook, and can use the existing configmaps and secrets */}} +{{- $isHook := (not .syn2mas.dryRun) -}} +{{- $synapseContext := (mustMergeOverwrite ($.Values.synapse | deepCopy) (dict "processType" "main" "isHook" $isHook)) -}} +{{- $masContext := (mustMergeOverwrite ($.Values.matrixAuthenticationService | deepCopy) (dict "isHook" $isHook)) -}} +{{- with (mustMergeOverwrite ($.Values.matrixAuthenticationService.syn2mas | deepCopy) (dict "isHook" $isHook)) -}} +apiVersion: batch/v1 +kind: Job +metadata: +{{- with .annotations }} + annotations: + {{- toYaml . | nindent 4 }} +{{- end }} + labels: + {{- include "element-io.matrix-authentication-service-syn2mas.labels" (dict "root" $ "context" .) | nindent 4 }} + k8s.element.io/matrix-authentication-service-config-hash: {{ include "element-io.matrix-authentication-service.configmap-data" (dict "root" $ "context" $masContext) | sha1sum }} + k8s.element.io/matrix-authentication-service-secret-hash: {{ include "element-io.matrix-authentication-service.secret-data" (dict "root" $ "context" $masContext) | sha1sum }} + k8s.element.io/synapse-config-hash: {{ include "element-io.synapse.configmap-data" (dict "root" $ "context" $synapseContext) | sha1sum }} + k8s.element.io/synapse-secret-hash: {{ include "element-io.synapse.secret-data" (dict "root" $ "context" $synapseContext) | sha1sum }} + {{ include "element-io.ess-library.postgres-label" (dict "root" $ "context" (dict + "essPassword" "matrixAuthenticationService" + "postgresProperty" $masContext.postgres + ) + ) }} + {{ include "element-io.ess-library.postgres-label" (dict "root" $ "context" (dict + "essPassword" "synapse" + "postgresProperty" $synapseContext.postgres + ) + ) }} + annotations: +{{- if .dryRun }} + "helm.sh/hook": post-install,post-upgrade +{{- else }} + "helm.sh/hook": pre-install,pre-upgrade +{{- /* +Hook Weights are +- -10 : The initSecret hook generating secrets used by the check config job +- -5 : The synapse secret & configMap for the hook, so that they are created before the job +- 0 : The job itself, so that it is run after the secrets and configs are created & +*/}} + "helm.sh/hook-weight": "0" +{{- end }} + name: {{ $.Release.Name }}-matrix-authentication-service-syn2mas + namespace: {{ $.Release.Namespace }} +spec: + backoffLimit: 0 + completionMode: NonIndexed + completions: 1 + manualSelector: false + parallelism: 1 + podReplacementPolicy: TerminatingOrFailed + template: + metadata: + annotations: +{{- with .annotations }} + {{- toYaml . | nindent 8 }} +{{- end }} + labels: + {{- include "element-io.matrix-authentication-service-syn2mas.labels" (dict "root" $ "context" (dict "image" .image "labels" .labels "withChartVersion" false)) | nindent 8 }} + k8s.element.io/matrix-authentication-service-config-hash: {{ include "element-io.matrix-authentication-service.configmap-data" (dict "root" $ "context" $masContext) | sha1sum }} + k8s.element.io/matrix-authentication-service-secret-hash: {{ include "element-io.matrix-authentication-service.secret-data" (dict "root" $ "context" $masContext) | sha1sum }} + k8s.element.io/synapse-config-hash: {{ include "element-io.synapse.configmap-data" (dict "root" $ "context" $synapseContext) | sha1sum }} + k8s.element.io/synapse-secret-hash: {{ include "element-io.synapse.secret-data" (dict "root" $ "context" $synapseContext) | sha1sum }} + {{ include "element-io.ess-library.postgres-label" (dict "root" $ "context" (dict + "essPassword" "matrixAuthenticationService" + "postgresProperty" $masContext.postgres + ) + ) }} + {{ include "element-io.ess-library.postgres-label" (dict "root" $ "context" (dict + "essPassword" "synapse" + "postgresProperty" $synapseContext.postgres + ) + ) }} + spec: + restartPolicy: Never +{{- include "element-io.ess-library.pods.commonSpec" (dict "root" $ "context" (dict "componentValues" . "instanceSuffix" "matrix-authentication-service-syn2mas" "deployment" false "usesMatrixTools" true "mountServiceAccountToken" true)) | nindent 6 }} + initContainers: + - name: copy-mas-cli +{{- with .image -}} +{{- if .digest }} + image: "{{ .registry }}/{{ .repository }}@{{ .digest }}" + imagePullPolicy: {{ .pullPolicy | default "IfNotPresent" }} +{{- else }} + image: "{{ .registry }}/{{ .repository }}:{{ required "matrixAuthenticationService.syn2mas.image.tag is required if no digest" .tag }}" + imagePullPolicy: {{ .pullPolicy | default "Always" }} +{{- end }} +{{- end }} + command: + - sh + - -c + - cp -v /usr/local/bin/mas-cli /tmp-mas-cli/mas-cli +{{- with .containersSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} +{{- end }} +{{- with .resources }} + resources: + {{- toYaml . | nindent 10 }} +{{- end }} + volumeMounts: + - mountPath: /tmp-mas-cli + name: tmp-mas-cli + - name: render-config-mas +{{- with $.Values.matrixTools.image -}} +{{- if .digest }} + image: "{{ .registry }}/{{ .repository }}@{{ .digest }}" + imagePullPolicy: {{ .pullPolicy | default "IfNotPresent" }} +{{- else }} + image: "{{ .registry }}/{{ .repository }}:{{ required "matrixTools.image.tag is required if no digest" .tag }}" + imagePullPolicy: {{ .pullPolicy | default "Always" }} +{{- end }} +{{- end }} +{{- with .containersSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} +{{- end }} + command: + {{- include "element-io.matrix-authentication-service.render-config" (dict "root" $ "context" $masContext) | nindent 8 }} + env: + {{- include "element-io.matrix-authentication-service.matrixToolsEnv" (dict "root" $ "context" $masContext) | nindent 8 }} + {{- include "element-io.matrix-authentication-service.env" (dict "root" $ "context" $masContext) | nindent 8 }} +{{- with .extraEnv }} + {{- toYaml . | nindent 8 }} +{{- end }} +{{- with .resources }} + resources: + {{- toYaml . | nindent 10 }} +{{- end }} + volumeMounts: + - mountPath: /config-templates + name: plain-mas-config + readOnly: true +{{- range $secret := include "element-io.matrix-authentication-service.configSecrets" (dict "root" $ "context" $masContext) | fromJsonArray }} + - mountPath: /secrets/{{ tpl $secret $ }} + name: "secret-{{ tpl $secret $ }}" + readOnly: true +{{- end }} + - mountPath: /conf + name: rendered-config + readOnly: false + - name: render-config-syn +{{- with $.Values.matrixTools.image -}} +{{- if .digest }} + image: "{{ .registry }}/{{ .repository }}@{{ .digest }}" + imagePullPolicy: {{ .pullPolicy | default "IfNotPresent" }} +{{- else }} + image: "{{ .registry }}/{{ .repository }}:{{ required "matrixTools.image.tag is required if no digest" .tag }}" + imagePullPolicy: {{ .pullPolicy | default "Always" }} +{{- end }} +{{- end }} +{{- with .containersSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} +{{- end }} + command: + {{- include "element-io.synapse.render-config" (dict "root" $ "context" $synapseContext) | nindent 8 }} + env: + {{- include "element-io.synapse.matrixToolsEnv" (dict "root" $ "context" $synapseContext) | nindent 8 }} + {{- include "element-io.synapse.env" (dict "root" $ "context" $synapseContext) | nindent 8 }} +{{- with .extraEnv }} + {{- toYaml . | nindent 8 }} +{{- end }} +{{- with .resources }} + resources: + {{- toYaml . | nindent 10 }} +{{- end }} + volumeMounts: + - mountPath: /config-templates + name: plain-syn-config + readOnly: true +{{- range $secret := include "element-io.synapse.configSecrets" (dict "root" $ "context" $synapseContext) | fromJsonArray }} + - mountPath: /secrets/{{ tpl $secret $ }} + name: "secret-{{ tpl $secret $ }}" + readOnly: true +{{- end }} + - mountPath: /conf + name: rendered-config + readOnly: false + - name: db-wait-mas +{{- with $.Values.matrixTools.image -}} +{{- if .digest }} + image: "{{ .registry }}/{{ .repository }}@{{ .digest }}" + imagePullPolicy: {{ .pullPolicy | default "IfNotPresent" }} +{{- else }} + image: "{{ .registry }}/{{ .repository }}:{{ required "matrixTools.image.tag is required if no digest" .tag }}" + imagePullPolicy: {{ .pullPolicy | default "Always" }} +{{- end }} +{{- end }} +{{- with .containersSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} +{{- end }} + command: + - "/matrix-tools" + - tcpwait + - -address + - {{ include "element-io.ess-library.postgres-host-port" (dict "root" $ "context" (dict "postgres" $masContext.postgres)) | quote }} +{{- with .resources }} + resources: + {{- toYaml . | nindent 10 }} +{{- end }} + - name: db-wait-syn +{{- with $.Values.matrixTools.image -}} +{{- if .digest }} + image: "{{ .registry }}/{{ .repository }}@{{ .digest }}" + imagePullPolicy: {{ .pullPolicy | default "IfNotPresent" }} +{{- else }} + image: "{{ .registry }}/{{ .repository }}:{{ required "matrixTools.image.tag is required if no digest" .tag }}" + imagePullPolicy: {{ .pullPolicy | default "Always" }} +{{- end }} +{{- end }} +{{- with .containersSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} +{{- end }} + command: + - "/matrix-tools" + - tcpwait + - -address + - {{ include "element-io.ess-library.postgres-host-port" (dict "root" $ "context" (dict "postgres" $synapseContext.postgres)) | quote }} +{{- with .resources }} + resources: + {{- toYaml . | nindent 10 }} +{{- end }} + - name: syn2mas-check + args: ["syn2mas", "check", "--config", "/conf/config.yaml", "--synapse-config", "/conf/homeserver.yaml"] +{{- with .image -}} +{{- if .digest }} + image: "{{ .registry }}/{{ .repository }}@{{ .digest }}" + imagePullPolicy: {{ .pullPolicy | default "IfNotPresent" }} +{{- else }} + image: "{{ .registry }}/{{ .repository }}:{{ required "matrixAuthenticationService.image.tag is required if no digest" .tag }}" + imagePullPolicy: {{ .pullPolicy | default "Always" }} +{{- end }} +{{- end }} +{{- with .containersSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} +{{- end }} +{{- with .resources }} + resources: + {{- toYaml . | nindent 10 }} +{{- end }} + volumeMounts: + - mountPath: "/conf" + name: rendered-config + readOnly: true +{{- range $secret := include "element-io.matrix-authentication-service.syn2masConfigSecrets" (dict "root" $ "context" (dict "synapseContext" $synapseContext "masContext" $masContext)) | fromJsonArray }} + - mountPath: /secrets/{{ tpl $secret $ }} + name: "secret-{{ tpl $secret $ }}" + readOnly: true +{{- end }} + containers: + - name: syn2mas-migrate +{{- if .dryRun }} + args: ["syn2mas", "migrate", "--config", "/conf/config.yaml", "--synapse-config", "/conf/homeserver.yaml", "--dry-run"] + {{- with .image -}} + {{- if .digest }} + image: "{{ .registry }}/{{ .repository }}@{{ .digest }}" + imagePullPolicy: {{ .pullPolicy | default "IfNotPresent" }} + {{- else }} + image: "{{ .registry }}/{{ .repository }}:{{ required "matrixAuthenticationService.image.tag is required if no digest" .tag }}" + imagePullPolicy: {{ .pullPolicy | default "Always" }} + {{- end }} + {{- end }} +{{- else }} + command: ["/matrix-tools", "syn2mas", "--config", "/conf/config.yaml", "--synapse-config", "/conf/homeserver.yaml"] + {{- with $.Values.matrixTools.image -}} + {{- if .digest }} + image: "{{ .registry }}/{{ .repository }}@{{ .digest }}" + imagePullPolicy: {{ .pullPolicy | default "IfNotPresent" }} + {{- else }} + image: "{{ .registry }}/{{ .repository }}:{{ required "matrixTools.image.tag is required if no digest" .tag }}" + imagePullPolicy: {{ .pullPolicy | default "Always" }} + {{- end }} + {{- end }} +{{- end }} +{{- with .containersSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} +{{- end }} + env: + - name: NAMESPACE + value: {{ $.Release.Namespace }} +{{- with .extraEnv }} + {{- toYaml . | nindent 8 }} +{{- end }} +{{- with .resources }} + resources: + {{- toYaml . | nindent 10 }} +{{- end }} + volumeMounts: + - mountPath: "/conf" + name: rendered-config + readOnly: true + - mountPath: "/tmp-mas-cli" + name: tmp-mas-cli + readOnly: true +{{- range $secret := include "element-io.matrix-authentication-service.syn2masConfigSecrets" (dict "root" $ "context" (dict "synapseContext" $synapseContext "masContext" $masContext)) | fromJsonArray }} + - mountPath: /secrets/{{ tpl $secret $ }} + name: "secret-{{ tpl $secret $ }}" + readOnly: true +{{- end }} + volumes: + - name: plain-syn-config + configMap: + defaultMode: 420 + name: {{ include "element-io.synapse.configmap-name" (dict "root" $ "context" (dict "isHook" $isHook)) }} + - name: plain-mas-config + configMap: + name: {{ include "element-io.matrix-authentication-service.configmap-name" (dict "root" $ "context" (dict "isHook" $isHook)) }} +{{- range $secret := include "element-io.matrix-authentication-service.syn2masConfigSecrets" (dict "root" $ "context" (dict "synapseContext" $synapseContext "masContext" $masContext)) | fromJsonArray }} + - secret: + secretName: {{ tpl $secret $ }} + name: secret-{{ tpl $secret $ }} +{{- end }} + - emptyDir: + medium: Memory + name: "rendered-config" + - emptyDir: + medium: Memory + name: "tmp-mas-cli" +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/matrix-stack/templates/matrix-authentication-service/syn2mas_role.yaml b/charts/matrix-stack/templates/matrix-authentication-service/syn2mas_role.yaml new file mode 100644 index 000000000..ea9a5543b --- /dev/null +++ b/charts/matrix-stack/templates/matrix-authentication-service/syn2mas_role.yaml @@ -0,0 +1,44 @@ +{{- /* +Copyright 2025 New Vector Ltd + +SPDX-License-Identifier: AGPL-3.0-only +*/ -}} +{{- with .Values.matrixAuthenticationService -}} +{{- if and .enabled .syn2mas.enabled -}} +{{- with .syn2mas -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ $.Release.Name }}-matrix-authentication-service-syn2mas + namespace: {{ $.Release.Namespace }} + labels: + {{- include "element-io.matrix-authentication-service-syn2mas.labels" (dict "root" $ "context" .) | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade +{{- /* +Hook Weights are +- -10 : The initSecret hook generating secrets used by the check config job +- -5 : The synapse secret & configMap for the hook, so that they are created before the job +- 0 : The job itself, so that it is run after the secrets and configs are created & +*/}} + "helm.sh/hook-weight": "0" +rules: +{{/* + https://kubernetes.io/docs/reference/access-authn-authz/rbac/#referring-to-resources + You cannot restrict create or deletecollection requests by resourceName. + For create, this limitation is because the object name is not known at authorization time. +*/}} +- apiGroups: ["apps"] + resources: ["statefulsets"] + verbs: ["list"] +- apiGroups: ["apps"] + resources: ["statefulsets"] + resourceNames: +{{- $enabledWorkers := (include "element-io.synapse.enabledWorkers" (dict "root" $)) | fromJson }} +{{- range $processType, $unmergedProcessDetails := mustMergeOverwrite (dict "main" dict) $enabledWorkers }} + - "{{ $.Release.Name }}-synapse-{{ $processType }}" +{{- end }} + verbs: ["get", "update"] +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/matrix-stack/templates/matrix-authentication-service/syn2mas_rolebinding.yaml b/charts/matrix-stack/templates/matrix-authentication-service/syn2mas_rolebinding.yaml new file mode 100644 index 000000000..820274e5a --- /dev/null +++ b/charts/matrix-stack/templates/matrix-authentication-service/syn2mas_rolebinding.yaml @@ -0,0 +1,36 @@ +{{- /* +Copyright 2025 New Vector Ltd + +SPDX-License-Identifier: AGPL-3.0-only +*/ -}} + +{{- with .Values.matrixAuthenticationService -}} +{{- if and .enabled .syn2mas.enabled -}} +{{- with .syn2mas -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ $.Release.Name }}-matrix-authentication-service-syn2mas + namespace: {{ $.Release.Namespace }} + labels: + {{- include "element-io.matrix-authentication-service-syn2mas.labels" (dict "root" $ "context" .) | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade +{{- /* +Hook Weights are +- -10 : The initSecret hook generating secrets used by the check config job +- -5 : The synapse secret & configMap for the hook, so that they are created before the job +- 0 : The job itself, so that it is run after the secrets and configs are created & +*/}} + "helm.sh/hook-weight": "0" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ $.Release.Name }}-matrix-authentication-service-syn2mas +subjects: +- kind: ServiceAccount + name: {{ include "element-io.ess-library.serviceAccountName" (dict "root" $ "context" (dict "serviceAccount" .serviceAccount "nameSuffix" "matrix-authentication-service-syn2mas")) }} + namespace: {{ $.Release.Namespace }} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/matrix-stack/templates/matrix-authentication-service/syn2mas_serviceaccount.yaml b/charts/matrix-stack/templates/matrix-authentication-service/syn2mas_serviceaccount.yaml new file mode 100644 index 000000000..31ee8770d --- /dev/null +++ b/charts/matrix-stack/templates/matrix-authentication-service/syn2mas_serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- /* +Copyright 2025 New Vector Ltd + +SPDX-License-Identifier: AGPL-3.0-only +*/ -}} +{{- with .Values.matrixAuthenticationService -}} +{{- if and .enabled .syn2mas.enabled $.Values.synapse.enabled -}} +{{- with .syn2mas -}} +{{- include "element-io.ess-library.serviceAccount" (dict "root" $ "context" (dict "componentValues" . "nameSuffix" "matrix-authentication-service-syn2mas" "extraAnnotations" (dict "helm.sh/hook" "pre-install,pre-upgrade" "helm.sh/hook-weight" "0"))) }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/matrix-stack/templates/synapse/_helpers.tpl b/charts/matrix-stack/templates/synapse/_helpers.tpl index 03435e5d9..601958c98 100644 --- a/charts/matrix-stack/templates/synapse/_helpers.tpl +++ b/charts/matrix-stack/templates/synapse/_helpers.tpl @@ -1,5 +1,5 @@ {{- /* -Copyright 2024 New Vector Ltd +Copyright 2024-2025 New Vector Ltd SPDX-License-Identifier: AGPL-3.0-only */ -}} @@ -115,7 +115,7 @@ app.kubernetes.io/version: {{ include "element-io.ess-library.labels.makeSafe" . {{- define "element-io.synapse.ingress.additionalPaths" -}} {{- $root := .root -}} {{- with required "element-io.synapse.ingress.additionalPaths missing context" .context -}} -{{- if (and $root.Values.matrixAuthenticationService.enabled (not $root.Values.matrixAuthenticationService.preMigrationSynapseHandlesAuth)) }} +{{- if include "element-io.matrix-authentication-service.readyToHandleAuth" (dict "root" $root) }} {{- range $apiVersion := list "api/v1" "r0" "v3" "unstable" }} {{- range $apiSubpath := list "login" "refresh" "logout" }} - path: "/_matrix/client/{{ $apiVersion }}/{{ $apiSubpath }}" @@ -219,3 +219,31 @@ path_map_file: | path_map_file_get: | {{- (tpl ($root.Files.Get "configs/synapse/path_map_file_get.tpl") (dict "root" $root)) | nindent 2 -}} {{- end -}} + +{{- define "element-io.synapse.render-config" -}} +{{- $root := .root -}} +{{- with required "element-io.synapse.render-config missing context" .context }} +{{- $isHook := required "element-io.synapse.render-config requires context.isHook" .isHook }} +{{- $processType := required "element-io.synapse.render-config requires context.processType" .processType }} +- "/matrix-tools" +- render-config +- -output +- /conf/homeserver.yaml +- /config-templates/01-homeserver-underrides.yaml + {{- range $key := (.additional | keys | uniq | sortAlpha) -}} + {{- $prop := index $root.Values.synapse.additional $key }} + {{- if $prop.config }} +- /secrets/{{ (include "element-io.synapse.secret-name" (dict "root" $root "context" (dict "isHook" $isHook))) }}/user-{{ $key }} + {{- end }} + {{- if $prop.configSecret }} +- /secrets/{{ tpl $prop.configSecret $root }}/{{ $prop.configSecretKey }} + {{- end }} + {{- end }} +- /config-templates/04-homeserver-overrides.yaml +{{- if eq $processType "check-config-hook" }} +- /config-templates/05-main.yaml +{{- else }} +- /config-templates/05-{{ $processType }}.yaml +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/matrix-stack/templates/synapse/_synapse_details.tpl b/charts/matrix-stack/templates/synapse/_synapse_details.tpl index 8d96fb9f7..ecce82635 100644 --- a/charts/matrix-stack/templates/synapse/_synapse_details.tpl +++ b/charts/matrix-stack/templates/synapse/_synapse_details.tpl @@ -1,5 +1,5 @@ {{- /* -Copyright 2024 New Vector Ltd +Copyright 2024-2025 New Vector Ltd SPDX-License-Identifier: AGPL-3.0-only */ -}} @@ -161,7 +161,7 @@ responsibleForMedia ) ) ) -}} -{{- if (and $root.Values.matrixAuthenticationService.enabled (not $root.Values.matrixAuthenticationService.preMigrationSynapseHandlesAuth)) }} +{{- if include "element-io.matrix-authentication-service.readyToHandleAuth" (dict "root" $root) }} {{- with $root.Values.matrixAuthenticationService -}} {{- with .synapseSharedSecret -}} {{- with .value -}} diff --git a/charts/matrix-stack/templates/synapse/_synapse_pod.tpl b/charts/matrix-stack/templates/synapse/_synapse_pod.tpl index 023738288..7a96db799 100644 --- a/charts/matrix-stack/templates/synapse/_synapse_pod.tpl +++ b/charts/matrix-stack/templates/synapse/_synapse_pod.tpl @@ -75,26 +75,7 @@ We have an init container to render & merge the config for several reasons: {{- toYaml . | nindent 8 }} {{- end }} command: - - "/matrix-tools" - - render-config - - -output - - /conf/homeserver.yaml - - /config-templates/01-homeserver-underrides.yaml - {{- range $key := (.additional | keys | uniq | sortAlpha) -}} - {{- $prop := index $root.Values.synapse.additional $key }} - {{- if $prop.config }} - - /secrets/{{ (include "element-io.synapse.secret-name" (dict "root" $root "context" (dict "isHook" $isHook))) }}/user-{{ $key }} - {{- end }} - {{- if $prop.configSecret }} - - /secrets/{{ tpl $prop.configSecret $root }}/{{ $prop.configSecretKey }} - {{- end }} - {{- end }} - - /config-templates/04-homeserver-overrides.yaml -{{- if eq $processType "check-config-hook" }} - - /config-templates/05-main.yaml -{{- else }} - - /config-templates/05-{{ $processType }}.yaml -{{- end }} + {{- include "element-io.synapse.render-config" (dict "root" $root "context" .) | nindent 6 }} env: {{- include "element-io.synapse.matrixToolsEnv" (dict "root" $root "context" .) | nindent 8 }} {{- include "element-io.synapse.env" (dict "root" $root "context" .) | nindent 8 }} diff --git a/charts/matrix-stack/templates/synapse/synapse_configmap_hook.yaml b/charts/matrix-stack/templates/synapse/synapse_configmap_hook.yaml index 8bf25eedc..ac15bd337 100644 --- a/charts/matrix-stack/templates/synapse/synapse_configmap_hook.yaml +++ b/charts/matrix-stack/templates/synapse/synapse_configmap_hook.yaml @@ -5,7 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only */ -}} {{- with .Values.synapse -}} -{{- if and .enabled .checkConfigHook.enabled -}} +{{- if and .enabled (or .checkConfigHook.enabled + (and $.Values.matrixAuthenticationService.enabled + $.Values.matrixAuthenticationService.syn2mas.enabled)) -}} apiVersion: v1 kind: ConfigMap metadata: diff --git a/charts/matrix-stack/templates/synapse/synapse_secret_hook.yaml b/charts/matrix-stack/templates/synapse/synapse_secret_hook.yaml index d9d95cbe0..07b87b512 100644 --- a/charts/matrix-stack/templates/synapse/synapse_secret_hook.yaml +++ b/charts/matrix-stack/templates/synapse/synapse_secret_hook.yaml @@ -5,7 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only */ -}} {{- with .Values.synapse -}} -{{- if and .enabled .checkConfigHook.enabled -}} +{{- if and .enabled (or .checkConfigHook.enabled + (and $.Values.matrixAuthenticationService.enabled + $.Values.matrixAuthenticationService.syn2mas.enabled)) -}} apiVersion: v1 kind: Secret type: Opaque diff --git a/charts/matrix-stack/templates/well-known/_helpers.tpl b/charts/matrix-stack/templates/well-known/_helpers.tpl index 989c73ed6..7a4050743 100644 --- a/charts/matrix-stack/templates/well-known/_helpers.tpl +++ b/charts/matrix-stack/templates/well-known/_helpers.tpl @@ -1,5 +1,5 @@ {{- /* -Copyright 2024 New Vector Ltd +Copyright 2024-2025 New Vector Ltd SPDX-License-Identifier: AGPL-3.0-only */ -}} @@ -39,7 +39,7 @@ k8s.element.io/target-instance: {{ $root.Release.Name }}-haproxy {{- $_ := set $config "m.homeserver" $mHomeserver -}} {{- end -}} {{- end -}} -{{- if (and $root.Values.matrixAuthenticationService.enabled (not $root.Values.matrixAuthenticationService.preMigrationSynapseHandlesAuth)) }} +{{- if include "element-io.matrix-authentication-service.readyToHandleAuth" (dict "root" $root) }} {{- with required "WellKnownDelegation requires matrixAuthenticationService.ingress.host set" $root.Values.matrixAuthenticationService.ingress.host -}} {{- $msc2965 := dict "issuer" (printf "https://%s/" .) "account" (printf "https://%s/account" .) diff --git a/charts/matrix-stack/values.schema.json b/charts/matrix-stack/values.schema.json index fc8207f25..21eeb2c72 100644 --- a/charts/matrix-stack/values.schema.json +++ b/charts/matrix-stack/values.schema.json @@ -2815,9 +2815,6 @@ "type": "boolean", "description": "Enable the Matrix Authentication Service (MAS) service." }, - "preMigrationSynapseHandlesAuth": { - "type": "boolean" - }, "additional": { "type": "object", "additionalProperties": { @@ -3012,6 +3009,295 @@ }, "additionalProperties": false }, + "syn2mas": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable synapse to Matrix Authentication Service migration." + }, + "image": { + "type": "object", + "required": [ + "repository" + ], + "properties": { + "registry": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "digest": { + "type": "string" + }, + "pullPolicy": { + "type": "string", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ] + }, + "pullSecrets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, + "dryRun": { + "type": "boolean", + "description": "Run the migration job in dry-run mode. Do not actually migrate the data." + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + } + }, + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "extraEnv": { + "type": "array", + "items": { + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "containersSecurityContext": { + "properties": { + "allowPrivilegeEscalation": { + "type": "boolean" + }, + "capabilities": { + "properties": { + "add": { + "items": { + "type": "string" + }, + "type": "array" + }, + "drop": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "additionalProperties": false + }, + "readOnlyRootFilesystem": { + "type": "boolean" + }, + "seccompProfile": { + "properties": { + "localhostProfile": { + "type": "string" + }, + "type": { + "enum": [ + "RuntimeDefault", + "Unconfined", + "Localhost" + ], + "type": "string" + } + }, + "type": "object", + "additionalProperties": false + } + }, + "type": "object", + "additionalProperties": false + }, + "nodeSelector": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "podSecurityContext": { + "properties": { + "fsGroup": { + "format": "int64", + "type": "integer" + }, + "fsGroupChangePolicy": { + "type": "string" + }, + "runAsGroup": { + "format": "int64", + "type": "integer" + }, + "runAsNonRoot": { + "type": "boolean" + }, + "runAsUser": { + "format": "int64", + "type": "integer" + }, + "seLinuxOptions": { + "properties": { + "level": { + "type": "string" + }, + "role": { + "type": "string" + }, + "type": { + "type": "string" + }, + "user": { + "type": "string" + } + }, + "type": "object", + "additionalProperties": false + }, + "seccompProfile": { + "properties": { + "localhostProfile": { + "type": "string" + }, + "type": { + "enum": [ + "RuntimeDefault", + "Unconfined", + "Localhost" + ], + "type": "string" + } + }, + "type": "object", + "additionalProperties": false + }, + "supplementalGroups": { + "items": { + "format": "int64", + "type": "integer" + }, + "type": "array" + } + }, + "type": "object", + "additionalProperties": false + }, + "resources": { + "properties": { + "limits": { + "additionalProperties": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$" + }, + "type": "object" + }, + "requests": { + "additionalProperties": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$" + }, + "type": "object" + } + }, + "type": "object", + "additionalProperties": false + }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "tolerations": { + "type": "array", + "items": { + "properties": { + "effect": { + "type": "string", + "enum": [ + "NoSchedule", + "PreferNoSchedule", + "NoExecute" + ] + }, + "key": { + "type": "string" + }, + "operator": { + "type": "string" + }, + "tolerationSeconds": { + "type": "number" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, "ingress": { "type": "object", "properties": { diff --git a/charts/matrix-stack/values.yaml b/charts/matrix-stack/values.yaml index 9a04d827d..5cdfcbd4f 100644 --- a/charts/matrix-stack/values.yaml +++ b/charts/matrix-stack/values.yaml @@ -1218,12 +1218,6 @@ matrixAuthenticationService: ## - name: dockerhub pullSecrets: [] - ## Force the authentication to happen with legacy authentication. - ## This can be used to deploy Matrix Authentication Service and keeping auth on Synapse. - ## Once MAS is deployed, you can run the syn2mas tool to migrate the data from Synapse to MAS. - ## This should be set back to false and never switch again after the migration to MAS has been run. - preMigrationSynapseHandlesAuth: false - ## Details of the Postgres Database to use postgres: {} @@ -1428,6 +1422,19 @@ matrixAuthenticationService: tolerations: [] + ## The list of hosts aliases to configure on the pod spec. + ## It should be avoid as much as possible to use this feature. + ## Please prefer using an DNS entry to resolve your hostnames. + ## This can be used as a workaround when entries cannot be resolved using DNS, for example for our automated testings. + ## e.g. + ## hostAliases: + ## - ip: 192.0.2.1 # An IP resolution to add to /etc/hosts + ## # A list of hostnames to be associated with the above IP + ## hostnames: + ## - ess.localhost + ## - synapse.ess.localhost + hostAliases: [] + ## TopologySpreadConstraints describes how Pods for this component should be spread between nodes. ## https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/ for in-depth details ## labelSelector can be omitted and the chart will populate a sensible value for this component. @@ -1583,6 +1590,163 @@ matrixAuthenticationService: ## Number of seconds after which the probe times out timeoutSeconds: 1 + ## Enables synapse to Matrix Authentication Service migration + ## The migration process must follow the following steps: + ## 1. Set `matrixAuthenticationService.enabled: true` and `matrixAuthenticationService.syn2mas.enable: true`, run the helm upgrade command, check the result of the pre-upgrade hook job + ## 2. Set `matrixAuthenticationService.syn2mas.dryRun`: false, run the helm upgrade command, check the result of the pre-upgrade hook job + ## 3. To disable the job scaling-down Synapse in future helm-upgrades, set `matrixAuthenticationService.syn2mas.enabled: false` + syn2mas: + enabled: false + + # Syn2Mas relies on the debug image to copy mas-cli to the matrix-tools container + # Details of the image to be used + image: + ## The host and (optional) port of the container image registry for this component. + ## If not specified Docker Hub is implied + registry: ghcr.io + + ## The path in the registry where the container image is located + repository: element-hq/matrix-authentication-service + + ## The tag of the container image to use. + ## Defaults to the Chart's appVersion if not set + tag: "0.16.0-debug" + + ## Container digest to use. Used to pull the image instead of the image tag / Chart appVersion if set + # digest: + + ## Whether the image should be pulled on container startup. Valid values are Always, IfNotPresent and Never + ## If this isn't provided it defaults to Always when using the image tag / Chart appVersion or + ## IfNotPresent if using a digest + # pullPolicy: + + ## A list of pull secrets to use for this image + ## e.g. + ## pullSecrets: + ## - name: dockerhub + pullSecrets: [] + ## Labels to add to all manifest for this component + labels: {} + ## Defines the annotations to add to the workload + # annotations: {} + ## A subset of SecurityContext. ContainersSecurityContext holds pod-level security attributes and common container settings + containersSecurityContext: + ## Controls whether a process can gain more privileges than its parent process. + ## This bool directly controls whether the no_new_privs flag gets set on the container process. + ## allowPrivilegeEscalation is always true when the container is run as privileged, or has CAP_SYS_ADMIN + allowPrivilegeEscalation: false + + ## Give a process some privileges, but not all the privileges of the root user. + capabilities: + ## Privileges to add. + # add: [] + ## Privileges to drop. + drop: + - ALL + + ## Mounts the container's root filesystem as read-only. + readOnlyRootFilesystem: true + + ## To set the Seccomp profile for a Container, include the seccompProfile field in the securityContext section of your Pod or Container manifest. + ## The seccompProfile field is a SeccompProfile object consisting of type and localhostProfile. Valid options for type include RuntimeDefault, Unconfined, and Localhost. + ## localhostProfile must only be set set if type Localhost. It indicates the path of the pre-configured profile on the node, relative to the kubelet's configured Seccomp profile location (configured with the --root-dir flag). + # seccompProfile: + # type: RuntimeDefault + ## NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + # nodeSelector: {} + ## A subset of PodSecurityContext. PodSecurityContext holds pod-level security attributes and common container settings + podSecurityContext: + ## A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to + ## change the ownership of that volume to be owned by the pod: + ## + ## 1. The owning GID will be the FSGroup + ## 2. The setgid bit is set (new files created in the volume will be owned by FSGroup)## 3. The permission bits are OR'd with rw-rw---- + ## + ## If unset, the Kubelet will not modify the ownership and permissions of any volume. + fsGroup: 10005 + + ## fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod. + ## This field will only apply to volume types which support fsGroup based ownership(and permissions). + ## It will have no effect on ephemeral volume types such as: secret, configmaps and emptydir. Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + # fsGroupChangePolicy: + + ## The GID to run the entrypoint of the container process. Uses runtime default if unset. + runAsGroup: 10005 + + ## Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. + runAsNonRoot: true + + ## The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. + runAsUser: 10005 + + ## SELinuxOptions are the labels to be applied to all the pod containers + # seLinuxOptions: + ## Level is SELinux level label that applies to the container. + # level: + + ## Role is a SELinux role label that applies to the container. + # role: + + ## Type is a SELinux type label that applies to the container. + # type: + + ## User is a SELinux user label that applies to the container. + # user: + + ## "To set the Seccomp profile for a Container, include the seccompProfile field in the securityContext section of your Pod or Container manifest. + ## The seccompProfile field is a SeccompProfile object consisting of type and localhostProfile. + ## Valid options for type include RuntimeDefault, Unconfined, and Localhost. localhostProfile must only be set set if type Localhost. + ## It indicates the path of the pre-configured profile on the node, relative to the kubelet's configured Seccomp profile location (configured with the --root-dir flag). + seccompProfile: + # localhostProfile: + type: RuntimeDefault + + ## A list of groups applied to the first process run in each container, in addition to the container's primary GID. + ## If unspecified, no groups will be added to any container. + supplementalGroups: [] + ## Kubernetes resources to allocate to each instance. + resources: + ## Requests describes the minimum amount of compute resources required. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + requests: + memory: 50Mi + cpu: 50m + + ## Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + limits: + memory: 350Mi + ## Controls configuration of the ServiceAccount for this component + serviceAccount: + ## Whether a ServiceAccount should be created by the chart or not + create: true + + ## What name to give the ServiceAccount. If not provided the chart will provide the name automatically + name: "" + + ## Annotations to add to the service account + annotations: {} + ## Workload tolerations allows Pods that are part of this (sub)component to 'tolerate' any taint that matches the triple using the matching operator . + ## + ## * effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + ## * key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. + ## * operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + ## * value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. + ## + ## * tolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. + ## e.g. + ## tolerations: + ## - effect: + ## key: + ## operator: + ## value: + + tolerations: [] + + ## Runs the syn2mas process in dryRun mode. + ## Force the authentication to happen with legacy authentication. + ## This can be used to deploy Matrix Authentication Service and keeping auth on Synapse. + ## This should be set back to false and never switch again after the migration to MAS has been run. + dryRun: true + postgres: enabled: true diff --git a/matrix-tools/Dockerfile b/matrix-tools/Dockerfile index 20a186c61..bba584b6f 100644 --- a/matrix-tools/Dockerfile +++ b/matrix-tools/Dockerfile @@ -12,7 +12,7 @@ RUN go mod download ARG TARGETOS TARGETARCH RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o /app/matrix-tools cmd/main.go -FROM gcr.io/distroless/static-debian12 +FROM gcr.io/distroless/cc-debian12 WORKDIR / COPY --from=buildstage --chmod=0755 /app/matrix-tools / diff --git a/matrix-tools/cmd/main.go b/matrix-tools/cmd/main.go index 0d8625bbb..1a2a9c6c0 100644 --- a/matrix-tools/cmd/main.go +++ b/matrix-tools/cmd/main.go @@ -6,14 +6,15 @@ package main import ( "fmt" - "os" "io" + "os" "flag" "github.com/element-hq/ess-helm/matrix-tools/internal/pkg/args" "github.com/element-hq/ess-helm/matrix-tools/internal/pkg/renderer" "github.com/element-hq/ess-helm/matrix-tools/internal/pkg/secret" + "github.com/element-hq/ess-helm/matrix-tools/internal/pkg/syn2mas" "github.com/element-hq/ess-helm/matrix-tools/internal/pkg/tcpwait" "github.com/pkg/errors" "gopkg.in/yaml.v3" @@ -96,6 +97,18 @@ func main() { } case args.TCPWait: tcpwait.WaitForTCP(options.Address) + case args.Syn2Mas: + clientset, err := getKubernetesClient() + if err != nil { + fmt.Println("Error getting Kubernetes client: ", err) + os.Exit(1) + } + namespace := os.Getenv("NAMESPACE") + if namespace == "" { + fmt.Println("Error, $NAMESPACE is not defined") + os.Exit(1) + } + syn2mas.RunSyn2MAS(clientset, namespace, options.SynapseConfig, options.MASConfig) case args.GenerateSecrets: clientset, err := getKubernetesClient() if err != nil { diff --git a/matrix-tools/internal/pkg/args/args.go b/matrix-tools/internal/pkg/args/args.go index c7a7f3e0b..c604fd59d 100644 --- a/matrix-tools/internal/pkg/args/args.go +++ b/matrix-tools/internal/pkg/args/args.go @@ -15,6 +15,7 @@ type CommandType int const ( RenderConfig CommandType = iota GenerateSecrets + Syn2Mas TCPWait ) @@ -65,6 +66,8 @@ type Options struct { Address string GeneratedSecrets []GeneratedSecret SecretLabels map[string]string + SynapseConfig string + MASConfig string } func ParseArgs(args []string) (*Options, error) { @@ -76,6 +79,10 @@ func ParseArgs(args []string) (*Options, error) { tcpWaitSet := flag.NewFlagSet("tcpwait", flag.ExitOnError) tcpWait := tcpWaitSet.String("address", "", "Address to listen on for TCP connections") + syn2MasSet := flag.NewFlagSet("syn2mas", flag.ExitOnError) + masConfig := syn2MasSet.String("config", "", "Path to MAS config file") + synapseConfig := syn2MasSet.String("synapse-config", "", "Path to Synapse config file") + generateSecretsSet := flag.NewFlagSet("generate-secrets", flag.ExitOnError) secrets := generateSecretsSet.String("secrets", "", "Comma-separated list of secrets to generate, in the format of `name:key:type`, where `type` is one of: rand32") secretsLabels := generateSecretsSet.String("labels", "", "Comma-separated list of labels for generated secrets, in the format of `key=value`") @@ -103,6 +110,14 @@ func ParseArgs(args []string) (*Options, error) { options.Address = *tcpWait } options.Command = TCPWait + case "syn2mas": + err := syn2MasSet.Parse(args[2:]) + if err != nil { + return nil, err + } + options.MASConfig = *masConfig + options.SynapseConfig = *synapseConfig + options.Command = Syn2Mas case "generate-secrets": err := generateSecretsSet.Parse(args[2:]) if err != nil { diff --git a/matrix-tools/internal/pkg/syn2mas/syn2mas.go b/matrix-tools/internal/pkg/syn2mas/syn2mas.go new file mode 100644 index 000000000..36eff8e1e --- /dev/null +++ b/matrix-tools/internal/pkg/syn2mas/syn2mas.go @@ -0,0 +1,111 @@ +// Copyright 2025 New Vector Ltd +// +// SPDX-License-Identifier: AGPL-3.0-only +// internal/pkg/secret/secret.go + +package syn2mas + +import ( + "context" + "fmt" + "os" + "os/exec" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +func scaleDownSynapse(client kubernetes.Interface, namespace string) map[string]int32 { + ctx := context.Background() + + stsClient := client.AppsV1().StatefulSets(namespace) + sts, err := stsClient.List(ctx, metav1.ListOptions{ + LabelSelector: "app.kubernetes.io/component=matrix-server", + }) + stsReplicas := make(map[string]int32) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + for _, s := range sts.Items { + stsReplicas[s.Name] = *s.Spec.Replicas + noReplicas := int32(0) + s.Spec.Replicas = &noReplicas + fmt.Printf("Setting replicas 0 on %s\n", s.Name) + if _, err := stsClient.Update(ctx, &s, metav1.UpdateOptions{}); err != nil { + fmt.Println(err) + } + } + + for { + fmt.Println("Waiting for replicas to be 0 on all synapse replicas" ) + sts, err := stsClient.List(ctx, metav1.ListOptions{ + LabelSelector: "app.kubernetes.io/component=matrix-server", + }) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + allStsDown := true + for _, s := range sts.Items { + fmt.Printf("%s replicas are %d\n", s.Name, s.Status.AvailableReplicas) + if s.Status.AvailableReplicas != 0 { + time.Sleep(time.Second) + allStsDown = false + } + } + if (allStsDown) { + break + } + } + return stsReplicas +} + +func scaleBack(client kubernetes.Interface, namespace string, scaledSts map[string]int32) { + ctx := context.Background() + fmt.Println("Scaling back synapse") + stsClient := client.AppsV1().StatefulSets(namespace) + for stsName, replicas := range scaledSts{ + fmt.Printf("Scaling back to %d replicas on %s\n", replicas, stsName) + sts, err := stsClient.Get(ctx, stsName, metav1.GetOptions{}) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + sts.Spec.Replicas = &replicas + _, err = stsClient.Update(ctx, sts, metav1.UpdateOptions{}) + if err != nil { + fmt.Println(err) + } + } +} + +func RunSyn2MAS(client kubernetes.Interface, namespace string, synapseConfigPath string, masConfigMap string) { + originStsReplicas := scaleDownSynapse(client, namespace) + // Run syn2mas cli, and in case of failure, scale back synapse up + fmt.Println("Running syn2mas") + cmd := exec.Command("/tmp-mas-cli/mas-cli", "syn2mas", "migrate", "--config", masConfigMap, "--synapse-config", synapseConfigPath) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + fmt.Println("syn2mas run ended") + var exitError *exec.ExitError + var ok bool + if err != nil { + // Detailed error handling + if exitError, ok = err.(*exec.ExitError); ok { + fmt.Printf("Command failed with status: %v\n", exitError.ExitCode()) + } else { + fmt.Println(err) + } + } + scaleBack(client, namespace, originStsReplicas) + if exitError != nil { + os.Exit(exitError.ExitCode()) + } else if err != nil { + os.Exit(1) + } else { + os.Exit(0) + } +} diff --git a/matrix-tools/internal/pkg/syn2mas/syn2mas_test.go b/matrix-tools/internal/pkg/syn2mas/syn2mas_test.go new file mode 100644 index 000000000..d3077fdea --- /dev/null +++ b/matrix-tools/internal/pkg/syn2mas/syn2mas_test.go @@ -0,0 +1,70 @@ +// Copyright 2025 New Vector Ltd +// +// SPDX-License-Identifier: AGPL-3.0-only +// internal/pkg/secret/secret.go + +package syn2mas + +import ( + "context" + "reflect" + "testing" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + testclient "k8s.io/client-go/kubernetes/fake" +) + +func TestScaleDown(t *testing.T) { + ctx := context.Background() + namespace := "test" + client := testclient.NewClientset() + // Create a namespace + _, err := client.CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create namespace: %v", err) + } + oneReplica := int32(1) + _, err = client.AppsV1().StatefulSets(namespace).Create(ctx, &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "synapse", + Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/component": "matrix-server", + }, + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &oneReplica, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "synapse", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "synapse", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "synapse", + Image: "synapse:latest", + }, + }, + }, + }, + }, + }, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create StatefulSet: %v", err) + } + scaledSts := scaleDownSynapse(client, namespace) + + expectedSts := map[string]int32{"synapse": 1} + if !reflect.DeepEqual(scaledSts, expectedSts) { + t.Errorf("Expected %v, got %v", expectedSts, scaledSts) + } +} diff --git a/newsfragments/454.added.md b/newsfragments/454.added.md new file mode 100644 index 000000000..2b3385cf2 --- /dev/null +++ b/newsfragments/454.added.md @@ -0,0 +1 @@ +Add support for Syn2Mas migration. See `matrixAuthenticationService.syn2mas` documentation in values file for more information. diff --git a/newsfragments/470.internal.md b/newsfragments/470.internal.md index ecd3c5431..54fcce885 100644 --- a/newsfragments/470.internal.md +++ b/newsfragments/470.internal.md @@ -1 +1 @@ -Rename exemple values files named `*-test-postgres-*` to `*-postgres-*`. +Rename exemple values files named `*-postgres-*` to `*-postgres-*`. diff --git a/tests/integration/env/matrix-authentication-service-syn2mas.rc b/tests/integration/env/matrix-authentication-service-syn2mas.rc new file mode 100644 index 000000000..2c0bdd15e --- /dev/null +++ b/tests/integration/env/matrix-authentication-service-syn2mas.rc @@ -0,0 +1,6 @@ +# Copyright 2025 New Vector Ltd +# +# SPDX-License-Identifier: AGPL-3.0-only + +export TEST_VALUES_FILE=charts/matrix-stack/ci/pytest-matrix-authentication-service-syn2mas-values.yaml +export BUILD_MATRIX_TOOLS=1 \ No newline at end of file diff --git a/tests/integration/fixtures/helm.py b/tests/integration/fixtures/helm.py index 544363337..97bd3e1a6 100644 --- a/tests/integration/fixtures/helm.py +++ b/tests/integration/fixtures/helm.py @@ -66,7 +66,11 @@ async def helm_prerequisites( ) ) - if value_file_has("matrixAuthenticationService.enabled", True): + # if MAS is disabled but syn2mas is enabled, we are going to enable MAS later on during the test + # So let's initilize everything it needs + if value_file_has("matrixAuthenticationService.enabled", True) or value_file_has( + "matrixAuthenticationService.syn2mas.enabled", True + ): resources.append( kubernetes_tls_secret( f"{generated_data.release_name}-mas-web-tls", diff --git a/tests/integration/fixtures/syn2mas.py b/tests/integration/fixtures/syn2mas.py new file mode 100644 index 000000000..7c92dd974 --- /dev/null +++ b/tests/integration/fixtures/syn2mas.py @@ -0,0 +1,123 @@ +# Copyright 2025 New Vector Ltd +# +# SPDX-License-Identifier: AGPL-3.0-only + +import os + +import pyhelm3 +import pytest +from lightkube.resources.core_v1 import Namespace + +from .data import ESSData + + +@pytest.fixture(autouse=True, scope="session") +async def upgrade_enable_syn2mas( + helm_client: pyhelm3.Client, + ingress, + helm_prerequisites, + ess_namespace: Namespace, + generated_data: ESSData, + loaded_matrix_tools: dict, +): + revision = await helm_client.get_current_revision( + generated_data.release_name, namespace=generated_data.ess_namespace + ) + values = revision.values + values.setdefault("matrixAuthenticationService", {})["enabled"] = True + values["matrixAuthenticationService"].setdefault("syn2mas", {})["enabled"] = True + chart = await helm_client.get_chart("charts/matrix-stack") + # Install or upgrade a release + revision = await helm_client.install_or_upgrade_release( + generated_data.release_name, + chart, + values, + namespace=generated_data.ess_namespace, + atomic="CI" not in os.environ, + wait=True, + ) + assert revision.status == pyhelm3.ReleaseRevisionStatus.DEPLOYED + + +@pytest.fixture(autouse=True, scope="session") +async def upgrade_migrate_dryrun_syn2mas( + helm_client: pyhelm3.Client, + ingress, + helm_prerequisites, + upgrade_enable_syn2mas, + ess_namespace: Namespace, + generated_data: ESSData, + loaded_matrix_tools: dict, +): + revision = await helm_client.get_current_revision( + generated_data.release_name, namespace=generated_data.ess_namespace + ) + values = revision.values + values["matrixAuthenticationService"]["syn2mas"].setdefault("migrate")["enabled"] = True + chart = await helm_client.get_chart("charts/matrix-stack") + # Install or upgrade a release + revision = await helm_client.install_or_upgrade_release( + generated_data.release_name, + chart, + values, + namespace=generated_data.ess_namespace, + atomic="CI" not in os.environ, + wait=True, + ) + assert revision.status == pyhelm3.ReleaseRevisionStatus.DEPLOYED + + +@pytest.fixture(autouse=True, scope="session") +async def upgrade_migrate_syn2mas( + helm_client: pyhelm3.Client, + ingress, + helm_prerequisites, + upgrade_enable_syn2mas, + ess_namespace: Namespace, + generated_data: ESSData, + loaded_matrix_tools: dict, +): + revision = await helm_client.get_current_revision( + generated_data.release_name, namespace=generated_data.ess_namespace + ) + values = revision.values + values["matrixAuthenticationService"]["syn2mas"].setdefault("migrate")["dryRun"] = False + chart = await helm_client.get_chart("charts/matrix-stack") + # Install or upgrade a release + revision = await helm_client.install_or_upgrade_release( + generated_data.release_name, + chart, + values, + namespace=generated_data.ess_namespace, + atomic="CI" not in os.environ, + wait=True, + ) + assert revision.status == pyhelm3.ReleaseRevisionStatus.DEPLOYED + + +@pytest.fixture(autouse=True, scope="session") +async def upgrade_final_syn2mas( + helm_client: pyhelm3.Client, + ingress, + helm_prerequisites, + upgrade_enable_syn2mas, + ess_namespace: Namespace, + generated_data: ESSData, + loaded_matrix_tools: dict, +): + revision = await helm_client.get_current_revision( + generated_data.release_name, namespace=generated_data.ess_namespace + ) + values = revision.values + values["matrixAuthenticationService"]["syn2mas"]["enabled"] = False + chart = await helm_client.get_chart("charts/matrix-stack") + # Install or upgrade a release + revision = await helm_client.install_or_upgrade_release( + generated_data.release_name, + chart, + values, + namespace=generated_data.ess_namespace, + atomic="CI" not in os.environ, + wait=True, + ) + assert revision.status == pyhelm3.ReleaseRevisionStatus.DEPLOYED diff --git a/tests/integration/test_matrix_authentication_service.py b/tests/integration/test_matrix_authentication_service.py index 5b90b80c6..009955767 100644 --- a/tests/integration/test_matrix_authentication_service.py +++ b/tests/integration/test_matrix_authentication_service.py @@ -9,6 +9,7 @@ @pytest.mark.skipif(value_file_has("matrixAuthenticationService.enabled", False), reason="MAS not deployed") +@pytest.mark.skipif(value_file_has("matrixAuthenticationService.syn2mas.enabled", False), reason="Syn2Mas not deployed") @pytest.mark.asyncio_cooperative async def test_matrix_authentication_service_graphql_endpoint(ingress_ready, generated_data: ESSData, ssl_context): await ingress_ready("matrix-authentication-service") diff --git a/tests/integration/test_syn2mas.py b/tests/integration/test_syn2mas.py new file mode 100644 index 000000000..cce2f73e5 --- /dev/null +++ b/tests/integration/test_syn2mas.py @@ -0,0 +1,76 @@ +# Copyright 2025 New Vector Ltd +# +# SPDX-License-Identifier: AGPL-3.0-only + +import os + +import pyhelm3 +import pytest + +from .fixtures import ESSData +from .lib.utils import aiohttp_post_json, value_file_has +from .test_matrix_authentication_service import test_matrix_authentication_service_graphql_endpoint + + +@pytest.mark.skipif(value_file_has("synapse.enabled", False), reason="Synapse not deployed") +@pytest.mark.skipif(value_file_has("matrixAuthenticationService.syn2mas.enabled", False), reason="Syn2Mas not deployed") +@pytest.mark.parametrize("users", [("syn2mas-user",)], indirect=True) +@pytest.mark.asyncio_cooperative +async def test_run_syn2mas_upgrade( + helm_client: pyhelm3.Client, + users, + ingress_ready, + ssl_context, + generated_data: ESSData, +): + access_token = users[0] + await ingress_ready("synapse") + # After the base chart is setup, we enable MAS to run the syn2mas dry run job + + revision = await helm_client.get_current_revision( + generated_data.release_name, namespace=generated_data.ess_namespace + ) + values = await revision.values() + values["matrixAuthenticationService"]["enabled"] = True + chart = await helm_client.get_chart("charts/matrix-stack") + # Install or upgrade a release + revision = await helm_client.install_or_upgrade_release( + generated_data.release_name, + chart, + values, + namespace=generated_data.ess_namespace, + atomic="CI" not in os.environ, + wait=True, + ) + assert revision.status == pyhelm3.ReleaseRevisionStatus.DEPLOYED + # We should still be able to reach synapse ingress + await ingress_ready("synapse") + # We then run the final migration + revision = await helm_client.get_current_revision( + generated_data.release_name, namespace=generated_data.ess_namespace + ) + values = await revision.values() + values["matrixAuthenticationService"]["syn2mas"]["dryRun"] = False + chart = await helm_client.get_chart("charts/matrix-stack") + # Install or upgrade a release + revision = await helm_client.install_or_upgrade_release( + generated_data.release_name, + chart, + values, + namespace=generated_data.ess_namespace, + atomic="CI" not in os.environ, + wait=True, + ) + assert revision.status == pyhelm3.ReleaseRevisionStatus.DEPLOYED + + # MAS should be available + await test_matrix_authentication_service_graphql_endpoint(ingress_ready, generated_data, ssl_context) + + sync_result = await aiohttp_post_json( + f"https://synapse.{generated_data.server_name}/_matrix/client/unstable/org.matrix.simplified_msc3575/sync", + {}, + {"Authorization": f"Bearer {access_token}"}, + ssl_context, + ) + + assert "pos" in sync_result diff --git a/tests/manifests/__init__.py b/tests/manifests/__init__.py index dd86e5f70..09259b662 100644 --- a/tests/manifests/__init__.py +++ b/tests/manifests/__init__.py @@ -446,6 +446,30 @@ def make_synapse_worker_sub_component(worker_name: str) -> SubComponentDetails: helm_keys=("matrixAuthenticationService",), has_db=True, shared_component_names=("init-secrets", "postgres"), + sub_components=( + SubComponentDetails( + name="matrix-authentication-service-syn2mas", + helm_keys=("matrixAuthenticationService", "syn2mas"), + paths_consistency_noqa=( + "/conf/log_config.yaml", + "/media_store", + "/media/media_store", + "/as/0/bridge_registration.yaml", + "/usr/local/bin/mas-cli", + ), + helm_keys_overrides={ + # Job so no livenessProbe + PropertyType.LivenessProbe: None, + # Job so no readinessProbe + PropertyType.ReadinessProbe: None, + # Job so no startupProbe + PropertyType.StartupProbe: None, + }, + has_ingress=False, + has_service_monitor=False, + has_topology_spread_constraints=False, + ), + ), ), ComponentDetails( name="synapse", @@ -516,14 +540,20 @@ def _get_all_deployables_details() -> set[DeployableDetails]: all_deployables_details = _get_all_deployables_details() -_extra_values_files_to_test: list[str] = [ - "example-default-enabled-components-values.yaml", - "matrix-authentication-service-keep-auth-in-synapse-values.yaml", +_extra_values_files_to_test: list[str] = ["example-default-enabled-components-values.yaml"] + +_extra_workloads_files_to_test = [ + "matrix-authentication-service-synapse-syn2mas-dry-run-secrets-in-helm-values.yaml", + "matrix-authentication-service-synapse-syn2mas-dry-run-secrets-externally-values.yaml", + "matrix-authentication-service-synapse-syn2mas-migrate-secrets-in-helm-values.yaml", + "matrix-authentication-service-synapse-syn2mas-migrate-secrets-externally-values.yaml", ] _extra_secret_values_files_to_test = [ - "matrix-authentication-service-synapse-secrets-in-helm-values.yaml", - "matrix-authentication-service-synapse-secrets-externally-values.yaml", + "matrix-authentication-service-synapse-syn2mas-dry-run-secrets-in-helm-values.yaml", + "matrix-authentication-service-synapse-syn2mas-dry-run-secrets-externally-values.yaml", + "matrix-authentication-service-synapse-syn2mas-migrate-secrets-in-helm-values.yaml", + "matrix-authentication-service-synapse-syn2mas-migrate-secrets-externally-values.yaml", ] _extra_services_values_files_to_test = [ @@ -539,5 +569,9 @@ def _get_all_deployables_details() -> set[DeployableDetails]: sum([component_details.values_files for component_details in all_components_details], tuple()) ) | set(_extra_values_files_to_test) +workloads_values_files_to_test = set( + sum([component_details.values_files for component_details in all_components_details], tuple()) +) | set(_extra_workloads_files_to_test) + services_values_files_to_test = values_files_to_test | set(_extra_services_values_files_to_test) diff --git a/tests/manifests/test_configs_and_mounts_consistency.py b/tests/manifests/test_configs_and_mounts_consistency.py index 03db3389b..710db3dde 100644 --- a/tests/manifests/test_configs_and_mounts_consistency.py +++ b/tests/manifests/test_configs_and_mounts_consistency.py @@ -82,13 +82,17 @@ def find_paths_in_contents(container, mounted_config_maps, deployable_details): return paths_found -def find_mount_paths_and_assert_key_is_consistent(container_name, mounted_keys, mount_path, matches_in): +def find_mount_paths_and_assert_key_is_consistent( + container_name, mounted_keys, mount_path, matches_in, deployable_details +): found_mount = False for match_in in matches_in: for match in re.findall(rf"(?:^|\s|\"){mount_path}/([^\s\n\")`;,]+(?!.*noqa))", match_in): + if f"{mount_path}/{match}" in deployable_details.paths_consistency_noqa: + continue assert f"{mount_path}/{match}" in mounted_keys, ( f"{mount_path}/{match} used in {container_name} but it is not found " - f"from any mounted secret or configmap" + f"from any mounted secret or configmap ({deployable_details.name})" ) found_mount = True return found_mount @@ -102,12 +106,15 @@ def find_keys_mounts_in_content(mounted_key, matches_in): return False -def get_key_from_render_config(template): +def get_keys_from_render_config(template): + keys = [] for container in template["spec"]["template"]["spec"]["initContainers"]: - if container["name"] == "render-config": + if container["name"].startswith("render-config"): for idx, cmd in enumerate(container["command"]): if cmd == "-output": - return container["command"][idx + 1].split("/")[-1] + keys.append(container["command"][idx + 1].split("/")[-1]) + if keys: + return keys raise AssertionError( f"{template['kind']}/{template['metadata']['name']} has a rendered-config volume, " "but no render-config output file could be found" @@ -159,7 +166,8 @@ def get_virtual_config_map_from_render_config(template, templates): in the rendered config files """ for container in template["spec"]["template"]["spec"]["initContainers"]: - if container["name"] == "render-config": + data = {} + if container["name"].startswith("render-config"): paths_to_keys = {} for volume_mount in container["volumeMounts"]: current_volume = get_volume_from_mount(template, volume_mount) @@ -173,7 +181,10 @@ def get_virtual_config_map_from_render_config(template, templates): for key in current_config_map["data"]: paths_to_keys[volume_mount["mountPath"] + "/" + key] = current_config_map["data"][key] source_files = container["command"][4:] - return {"data": {p: k for p, k in paths_to_keys.items() if p in source_files}} + for p, k in paths_to_keys.items(): + if p in source_files: + data[p] = k + return {"data": data} raise RuntimeError("No render-config container found") @@ -282,9 +293,10 @@ async def test_secrets_consistency(templates, other_secrets, template_to_deploya # We can't verify rendered-config, it's generated at runtime uses_rendered_config = True mount_paths.append(volume_mount["mountPath"]) - key = f"{volume_mount['mountPath']}/{get_key_from_render_config(template)}" - mounted_keys.append(key) - mounted_keys_to_parents[key] = volume_mount["mountPath"] + keys = [f"{volume_mount['mountPath']}/{k}" for k in get_keys_from_render_config(template)] + mounted_keys += keys + for k in keys: + mounted_keys_to_parents[k] = volume_mount["mountPath"] assert len(mounted_keys) == len(set(mounted_keys)), ( f"Mounted key paths are not unique in {template['metadata']['name']}: {mounted_keys}" @@ -295,7 +307,7 @@ async def test_secrets_consistency(templates, other_secrets, template_to_deploya # If we are checking render-config, # we need to look up mounted_keys in the container using the rendered-config - if container["name"] == "render-config": + if container["name"].startswith("render-config"): rendered_mounted_keys, rendered_mounted_keys_to_parents = get_keys_from_container_using_rendered_config( template, templates, other_secrets ) @@ -350,6 +362,7 @@ async def test_secrets_consistency(templates, other_secrets, template_to_deploya [e.get("value", "") for e in container.get("env", [])] + container.get("command", []) + container.get("args", []), + deployable_details, ): mount_path_found = True @@ -362,6 +375,7 @@ async def test_secrets_consistency(templates, other_secrets, template_to_deploya mounted_keys, mounted_keys_to_parents[mounted_key], [content], + deployable_details, ): mount_path_found = True if not mount_path_found and not uses_rendered_config: diff --git a/tests/manifests/test_labels.py b/tests/manifests/test_labels.py index 78919a7d6..fbd912ec6 100644 --- a/tests/manifests/test_labels.py +++ b/tests/manifests/test_labels.py @@ -7,11 +7,11 @@ import pytest -from . import PropertyType, secret_values_files_to_test, values_files_to_test +from . import PropertyType, secret_values_files_to_test, workloads_values_files_to_test from .utils import template_id -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_templates_have_expected_labels(release_name, templates): expected_labels = [ @@ -65,7 +65,9 @@ async def test_templates_have_postgres_hash_label(release_name, templates, value if not deployable_details.has_db: continue - assert "k8s.element.io/postgres-password-hash" in labels, f"{id} does not have postgres password hash label" + assert any(re.match("k8s.element.io/postgres-password-[a-z]+-hash", label) for label in labels), ( + f"{id} does not have postgres password hash label" + ) assert ( len(deployable_details.helm_keys) == 1 ) # We currently assume that Postgres is for top-level components only @@ -84,12 +86,13 @@ async def test_templates_have_postgres_hash_label(release_name, templates, value else: expected = f"{release_name}-generated" expected = expected.replace("{{ $.Release.Name }}", release_name) - assert labels["k8s.element.io/postgres-password-hash"] == sha1(expected.encode()).hexdigest(), ( - f"{id} has incorrect postgres password hash, expect {expected} hashed as sha1" - ) + assert ( + labels[f"k8s.element.io/postgres-password-{helm_key.lower()}-hash"] + == sha1(expected.encode()).hexdigest() + ), f"{id} has incorrect postgres password hash, expect {expected} hashed as sha1" -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_pod_spec_labels_are_consistent_with_parent_labels(templates): for template in templates: @@ -108,13 +111,13 @@ async def test_pod_spec_labels_are_consistent_with_parent_labels(templates): ) -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_our_labels_are_named_consistently(templates): acceptable_matches = [ "k8s.element.io/as-registration-[0-9]+-hash", "k8s.element.io/([a-z0-9-]+)-(config|secret)-hash", - "k8s.element.io/postgres-password-hash", + "k8s.element.io/postgres-password-([a-z]+)-hash", "k8s.element.io/synapse-instance", "k8s.element.io/target-(instance|name)", ] diff --git a/tests/manifests/test_pod_containers_ports.py b/tests/manifests/test_pod_containers_ports.py index 652ce2cbb..052984669 100644 --- a/tests/manifests/test_pod_containers_ports.py +++ b/tests/manifests/test_pod_containers_ports.py @@ -4,10 +4,10 @@ import pytest -from . import values_files_to_test +from . import workloads_values_files_to_test -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_unique_ports_in_containers(templates): for template in templates: @@ -19,7 +19,7 @@ async def test_unique_ports_in_containers(templates): assert len(ports) == len(set(ports)), f"Ports are not unique: {id}, {ports}" -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_ports_in_containers_are_named(templates): for template in templates: @@ -36,7 +36,7 @@ async def test_ports_in_containers_are_named(templates): assert len(port_names) == len(set(port_names)), f"Port names are not unique: {id}, {port_names}" -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_no_ports_in_jobs(templates): for template in templates: diff --git a/tests/manifests/test_pod_env.py b/tests/manifests/test_pod_env.py index 45b1ff5c5..76c059a69 100644 --- a/tests/manifests/test_pod_env.py +++ b/tests/manifests/test_pod_env.py @@ -4,13 +4,13 @@ import pytest -from . import DeployableDetails, PropertyType, values_files_to_test +from . import DeployableDetails, PropertyType, workloads_values_files_to_test from .utils import iterate_deployables_workload_parts, template_id extra_env = {"a_string": "a", "b_boolean": True, "c_integer": 1, "d_float": 1.1} -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_unique_env_name_in_containers(values, make_templates): def set_extra_env(deployable_details: DeployableDetails): @@ -35,7 +35,7 @@ def set_extra_env(deployable_details: DeployableDetails): ) -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_env_values_are_strings_in_containers(values, make_templates): def set_extra_env(deployable_details: DeployableDetails): diff --git a/tests/manifests/test_pod_idempotency.py b/tests/manifests/test_pod_idempotency.py index bcebdcdd4..78137a57e 100644 --- a/tests/manifests/test_pod_idempotency.py +++ b/tests/manifests/test_pod_idempotency.py @@ -1,15 +1,15 @@ -# Copyright 2024 New Vector Ltd +# Copyright 2024-2025 New Vector Ltd # # SPDX-License-Identifier: AGPL-3.0-only import pytest import yaml -from . import values_files_to_test +from . import workloads_values_files_to_test from .utils import helm_template, template_id -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_values_file_renders_idempotent_pods(release_name, values, helm_client, temp_chart): async def _patch_version_chart(): diff --git a/tests/manifests/test_pod_probes.py b/tests/manifests/test_pod_probes.py index 1c28d078b..684eac79f 100644 --- a/tests/manifests/test_pod_probes.py +++ b/tests/manifests/test_pod_probes.py @@ -5,11 +5,11 @@ import pytest -from . import DeployableDetails, PropertyType, values_files_to_test +from . import DeployableDetails, PropertyType, workloads_values_files_to_test from .utils import iterate_deployables_workload_parts, template_id -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_no_probes_for_jobs(templates): for template in templates: @@ -26,7 +26,7 @@ async def test_no_probes_for_jobs(templates): ) -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_no_probes_for_initContainers(templates): for template in templates: @@ -139,7 +139,7 @@ def assert_matching_probe(template, probe_type, deployable_details_to_probe_deta ) -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_sensible_livenessProbes_by_default(templates): for template in templates: @@ -147,7 +147,7 @@ async def test_sensible_livenessProbes_by_default(templates): assert_sensible_default_probe(template, "livenessProbe") -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_livenessProbes_are_configurable(values, make_templates, template_to_deployable_details): deployable_details_to_probe_details = set_probe_details(values, PropertyType.LivenessProbe) @@ -161,7 +161,7 @@ async def test_livenessProbes_are_configurable(values, make_templates, template_ ) -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_sensible_readinessProbes_by_default(templates): for template in templates: @@ -169,7 +169,7 @@ async def test_sensible_readinessProbes_by_default(templates): assert_sensible_default_probe(template, "readinessProbe") -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_readinessProbes_are_configurable(values, make_templates, template_to_deployable_details): deployable_details_to_probe_details = set_probe_details(values, PropertyType.ReadinessProbe) @@ -183,7 +183,7 @@ async def test_readinessProbes_are_configurable(values, make_templates, template ) -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_sensible_startupProbes_by_default(templates): for template in templates: @@ -191,7 +191,7 @@ async def test_sensible_startupProbes_by_default(templates): assert_sensible_default_probe(template, "startupProbe") -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_startupProbes_are_configurable(values, make_templates, template_to_deployable_details): deployable_details_to_probe_details = set_probe_details(values, PropertyType.StartupProbe) diff --git a/tests/manifests/test_pod_pull_secrets.py b/tests/manifests/test_pod_pull_secrets.py index 63001a2c3..f273bb0a4 100644 --- a/tests/manifests/test_pod_pull_secrets.py +++ b/tests/manifests/test_pod_pull_secrets.py @@ -4,11 +4,11 @@ import pytest -from . import PropertyType, values_files_to_test +from . import PropertyType, workloads_values_files_to_test from .utils import iterate_deployables_parts -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_sets_global_pull_secrets(values, make_templates): values["imagePullSecrets"] = [ @@ -26,7 +26,7 @@ async def test_sets_global_pull_secrets(values, make_templates): ) -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_local_pull_secrets(values, base_values, make_templates): values["imagePullSecrets"] = [ diff --git a/tests/manifests/test_pod_resources.py b/tests/manifests/test_pod_resources.py index 77d1a6772..7ad170d80 100644 --- a/tests/manifests/test_pod_resources.py +++ b/tests/manifests/test_pod_resources.py @@ -4,11 +4,11 @@ import pytest -from . import DeployableDetails, PropertyType, values_files_to_test +from . import DeployableDetails, PropertyType, workloads_values_files_to_test from .utils import iterate_deployables_workload_parts, template_id -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_pod_resources_are_configurable(values, make_templates, template_to_deployable_details): deployable_details_to_resources = {} diff --git a/tests/manifests/test_pod_securityContext.py b/tests/manifests/test_pod_securityContext.py index 9e33abe32..64a67a889 100644 --- a/tests/manifests/test_pod_securityContext.py +++ b/tests/manifests/test_pod_securityContext.py @@ -4,11 +4,11 @@ import pytest -from . import PropertyType, values_files_to_test +from . import PropertyType, workloads_values_files_to_test from .utils import iterate_deployables_workload_parts -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_sets_nonRoot_uids_gids_in_pod_securityContext_by_default(templates): for template in templates: @@ -36,7 +36,7 @@ async def test_sets_nonRoot_uids_gids_in_pod_securityContext_by_default(template ) -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_can_nuke_pod_securityContext_ids(values, make_templates): iterate_deployables_workload_parts( @@ -59,7 +59,7 @@ async def test_can_nuke_pod_securityContext_ids(values, make_templates): assert idKey not in pod_securityContext, f"{idKey} set in {id}'s Pod securityContext" -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_sets_seccompProfile_in_pod_securityContext_by_default(templates): for template in templates: @@ -81,7 +81,7 @@ async def test_sets_seccompProfile_in_pod_securityContext_by_default(templates): ) -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_can_nuke_pod_securityContext_seccompProfile(values, make_templates): iterate_deployables_workload_parts( diff --git a/tests/manifests/test_serviceaccounts.py b/tests/manifests/test_serviceaccounts.py index fb4cd3aee..b240b423b 100644 --- a/tests/manifests/test_serviceaccounts.py +++ b/tests/manifests/test_serviceaccounts.py @@ -6,11 +6,11 @@ import pytest -from . import DeployableDetails, PropertyType, all_deployables_details, values_files_to_test +from . import DeployableDetails, PropertyType, all_deployables_details, workloads_values_files_to_test from .utils import iterate_deployables_workload_parts -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_dont_automount_serviceaccount_tokens(templates): for template in templates: @@ -22,7 +22,7 @@ async def test_dont_automount_serviceaccount_tokens(templates): ) -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_uses_serviceaccount_named_as_per_pod_controller_by_default(templates): workloads_by_id = {} @@ -55,7 +55,7 @@ async def test_uses_serviceaccount_named_as_per_pod_controller_by_default(templa assert serviceaccount_names == covered_serviceaccount_names, f"{id} created ServiceAccounts that it shouldn't have" -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_uses_serviceaccount_named_as_values_if_specified(values, make_templates): def service_account_name(deployable_details: DeployableDetails): @@ -89,7 +89,7 @@ def service_account_name(deployable_details: DeployableDetails): ), f"{id} uses unexpected ServiceAccount" -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_does_not_create_serviceaccounts_if_configured_not_to(values, make_templates): for deployable_details in all_deployables_details: diff --git a/tests/manifests/test_topology_spread_constraints.py b/tests/manifests/test_topology_spread_constraints.py index 0cb573ba1..70d3874b3 100644 --- a/tests/manifests/test_topology_spread_constraints.py +++ b/tests/manifests/test_topology_spread_constraints.py @@ -4,11 +4,11 @@ import pytest -from . import DeployableDetails, PropertyType, values_files_to_test +from . import DeployableDetails, PropertyType, workloads_values_files_to_test from .utils import iterate_deployables_parts -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_sets_no_topology_spread_constraint_default(templates): for template in templates: @@ -20,7 +20,7 @@ async def test_sets_no_topology_spread_constraint_default(templates): ) -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_topology_spread_constraint_has_default(values, make_templates, template_to_deployable_details): def set_topology_spread_constraints(deployable_details: DeployableDetails): @@ -66,7 +66,7 @@ def set_topology_spread_constraints(deployable_details: DeployableDetails): ) -@pytest.mark.parametrize("values_file", values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_can_nuke_topology_spread_constraint_defaults(values, make_templates, template_to_deployable_details): def set_topology_spread_constraints(deployable_details: DeployableDetails): diff --git a/tests/manifests/test_volumes_mounts.py b/tests/manifests/test_volumes_mounts.py index 8a38d6a90..ab03dbb67 100644 --- a/tests/manifests/test_volumes_mounts.py +++ b/tests/manifests/test_volumes_mounts.py @@ -4,10 +4,10 @@ import pytest -from . import secret_values_files_to_test, values_files_to_test +from . import secret_values_files_to_test, workloads_values_files_to_test -@pytest.mark.parametrize("values_file", values_files_to_test | secret_values_files_to_test) +@pytest.mark.parametrize("values_file", workloads_values_files_to_test | secret_values_files_to_test) @pytest.mark.asyncio_cooperative async def test_volumes_mounts_exists(templates, other_secrets): configmaps_names = [t["metadata"]["name"] for t in templates if t["kind"] == "ConfigMap"]