Skip to content

Commit 4bccdd4

Browse files
authored
Merge pull request #583 from element-hq/bbz/nodeSelector-tests
nodeSelector tests
2 parents 822d522 + 0b7c65d commit 4bccdd4

7 files changed

Lines changed: 100 additions & 19 deletions

File tree

charts/matrix-stack/source/common/sub_schema_values.yaml.j2

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,12 @@ imagePullSecrets: []
175175

176176
{% macro labels(global=false, key='labels') %}
177177
## Labels to add to all manifest {{ 'for all components in this chart' if global else 'for this component' }}
178-
labels: {}
178+
{{ key }}: {}
179179
{%- endmacro %}
180180

181181
{% macro nodeSelector(key='nodeSelector') %}
182182
## 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/
183-
# {{ key }}: {}
183+
{{ key }}: {}
184184
{%- endmacro %}
185185

186186
{% macro persistentVolumeClaim(key) %}
@@ -194,9 +194,10 @@ labels: {}
194194
size: 10Gi
195195

196196
## The StorageClass to be used by the constructed PersistentVolumeClaim.
197-
## Will use the cluster default if not provided
197+
## Will use the cluster default if not provided.
198+
## If changed after initial usage the PersistentVolumeClaim could be recreated causing data-loss.
198199
## Ignored if existingClaim is provided
199-
# storageClass:
200+
# storageClassName:
200201

201202
## Whether to instruct Helm to keep or delete the constructed PersistentVolumeClaim when uninstalling the chart
202203
## Ignored if existingClaim is provided

charts/matrix-stack/values.yaml

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ initSecrets:
149149
## value: "bar"
150150
extraEnv: []
151151
## 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/
152-
# nodeSelector: {}
152+
nodeSelector: {}
153153
## A subset of PodSecurityContext. PodSecurityContext holds pod-level security attributes and common container settings
154154
podSecurityContext:
155155
## A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to
@@ -276,7 +276,7 @@ deploymentMarkers:
276276
## value: "bar"
277277
extraEnv: []
278278
## 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/
279-
# nodeSelector: {}
279+
nodeSelector: {}
280280
## A subset of PodSecurityContext. PodSecurityContext holds pod-level security attributes and common container settings
281281
podSecurityContext:
282282
## A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to
@@ -491,7 +491,7 @@ matrixRTC:
491491
## value: "bar"
492492
extraEnv: []
493493
## 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/
494-
# nodeSelector: {}
494+
nodeSelector: {}
495495
## A subset of PodSecurityContext. PodSecurityContext holds pod-level security attributes and common container settings
496496
podSecurityContext:
497497
## A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to
@@ -743,7 +743,7 @@ matrixRTC:
743743
# seccompProfile:
744744
# type: RuntimeDefault
745745
## 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/
746-
# nodeSelector: {}
746+
nodeSelector: {}
747747
## A subset of PodSecurityContext. PodSecurityContext holds pod-level security attributes and common container settings
748748
podSecurityContext:
749749
## A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to
@@ -977,7 +977,7 @@ elementWeb:
977977
# seccompProfile:
978978
# type: RuntimeDefault
979979
## 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/
980-
# nodeSelector: {}
980+
nodeSelector: {}
981981
## A subset of PodSecurityContext. PodSecurityContext holds pod-level security attributes and common container settings
982982
podSecurityContext:
983983
## A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to
@@ -1188,7 +1188,7 @@ haproxy:
11881188
## value: "bar"
11891189
extraEnv: []
11901190
## 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/
1191-
# nodeSelector: {}
1191+
nodeSelector: {}
11921192
## A subset of PodSecurityContext. PodSecurityContext holds pod-level security attributes and common container settings
11931193
podSecurityContext:
11941194
## A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to
@@ -1560,7 +1560,7 @@ matrixAuthenticationService:
15601560
annotations: {}
15611561

15621562
## 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/
1563-
# nodeSelector: {}
1563+
nodeSelector: {}
15641564

15651565
## Workload tolerations allows Pods that are part of this (sub)component to 'tolerate' any taint that matches the triple <key,value,effect> using the matching operator <operator>.
15661566
##
@@ -1818,7 +1818,7 @@ matrixAuthenticationService:
18181818
# seccompProfile:
18191819
# type: RuntimeDefault
18201820
## 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/
1821-
# nodeSelector: {}
1821+
nodeSelector: {}
18221822
## A subset of PodSecurityContext. PodSecurityContext holds pod-level security attributes and common container settings
18231823
podSecurityContext:
18241824
## A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to
@@ -2083,9 +2083,10 @@ postgres:
20832083
size: 10Gi
20842084

20852085
## The StorageClass to be used by the constructed PersistentVolumeClaim.
2086-
## Will use the cluster default if not provided
2086+
## Will use the cluster default if not provided.
2087+
## If changed after initial usage the PersistentVolumeClaim could be recreated causing data-loss.
20872088
## Ignored if existingClaim is provided
2088-
# storageClass:
2089+
# storageClassName:
20892090

20902091
## Whether to instruct Helm to keep or delete the constructed PersistentVolumeClaim when uninstalling the chart
20912092
## Ignored if existingClaim is provided
@@ -2150,7 +2151,7 @@ postgres:
21502151
# seccompProfile:
21512152
# type: RuntimeDefault
21522153
## 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/
2153-
# nodeSelector: {}
2154+
nodeSelector: {}
21542155
## A subset of PodSecurityContext. PodSecurityContext holds pod-level security attributes and common container settings
21552156
podSecurityContext:
21562157
## A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to
@@ -2363,9 +2364,10 @@ synapse:
23632364
size: 10Gi
23642365

23652366
## The StorageClass to be used by the constructed PersistentVolumeClaim.
2366-
## Will use the cluster default if not provided
2367+
## Will use the cluster default if not provided.
2368+
## If changed after initial usage the PersistentVolumeClaim could be recreated causing data-loss.
23672369
## Ignored if existingClaim is provided
2368-
# storageClass:
2370+
# storageClassName:
23692371

23702372
## Whether to instruct Helm to keep or delete the constructed PersistentVolumeClaim when uninstalling the chart
23712373
## Ignored if existingClaim is provided
@@ -3682,7 +3684,7 @@ synapse:
36823684
## - synapse.ess.localhost
36833685
hostAliases: []
36843686
## 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/
3685-
# nodeSelector: {}
3687+
nodeSelector: {}
36863688
## A subset of PodSecurityContext. PodSecurityContext holds pod-level security attributes and common container settings
36873689
podSecurityContext:
36883690
## A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to
@@ -3899,7 +3901,7 @@ synapse:
38993901
## value: "bar"
39003902
extraEnv: []
39013903
## 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/
3902-
# nodeSelector: {}
3904+
nodeSelector: {}
39033905
## A subset of PodSecurityContext. PodSecurityContext holds pod-level security attributes and common container settings
39043906
podSecurityContext:
39053907
## A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to

newsfragments/583.fixed.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Fix Postgres and Synapse Media `storageClassName` configuration not being respected.
2+
3+
**Warning** Previously `synapse.media.storage.storageClass` and `postgres.storage.storageClass`
4+
were in the values file and associated schema. These values were accidentally silently ignored
5+
and all chart-managed `PersistentVolumeClaims` were constructed without `spec.storageClassName`
6+
set, using the cluster default `StorageClass`.
7+
8+
The values file and associated schema have been updated so that the values are now
9+
`synapse.media.storage.storageClassName` and `postgres.storage.storageClassName`. The previous
10+
values are disallowed by the schema. Setting these values after the initial install could
11+
cause the `PersistentVolumeClaims` to be recreated, with associated data-loss. Only set
12+
`synapse.media.storage.storageClassName` or `postgres.storage.storageClassName` on initial
13+
installation.

newsfragments/583.internal.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CI: test nodeSelectors are appropriately configured.

tests/manifests/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class PropertyType(Enum):
1616
Ingress = "ingress"
1717
Labels = "labels"
1818
LivenessProbe = "livenessProbe"
19+
NodeSelector = "nodeSelector"
1920
PodSecurityContext = "podSecurityContext"
2021
Postgres = "postgres"
2122
Replicas = "replicas"
@@ -215,6 +216,7 @@ def __post_init__(self):
215216

216217
sidecar_values_file_path_overrides = {
217218
# Not possible, will come from the parent component
219+
PropertyType.NodeSelector: ValuesFilePath.not_supported(),
218220
PropertyType.PodSecurityContext: ValuesFilePath.not_supported(),
219221
PropertyType.ServiceAccount: ValuesFilePath.not_supported(),
220222
PropertyType.Tolerations: ValuesFilePath.not_supported(),
@@ -349,6 +351,7 @@ def make_synapse_worker_sub_component(worker_name: str, worker_type: str) -> Sub
349351
PropertyType.HostAliases: ValuesFilePath.read_elsewhere("synapse", "hostAliases"),
350352
PropertyType.Image: ValuesFilePath.read_elsewhere("synapse", "image"),
351353
PropertyType.Labels: ValuesFilePath.read_elsewhere("synapse", "labels"),
354+
PropertyType.NodeSelector: ValuesFilePath.read_elsewhere("synapse", "nodeSelector"),
352355
PropertyType.PodSecurityContext: ValuesFilePath.read_elsewhere("synapse", "podSecurityContext"),
353356
PropertyType.ServiceAccount: ValuesFilePath.read_elsewhere("synapse", "serviceAccount"),
354357
PropertyType.ServiceMonitor: ValuesFilePath.read_elsewhere("synapse", "serviceMonitor"),
@@ -564,6 +567,7 @@ def make_synapse_worker_sub_component(worker_name: str, worker_type: str) -> Sub
564567
PropertyType.Image: ValuesFilePath.read_elsewhere("synapse", "image"),
565568
# Job so no livenessProbe
566569
PropertyType.LivenessProbe: ValuesFilePath.not_supported(),
570+
PropertyType.NodeSelector: ValuesFilePath.read_elsewhere("synapse", "nodeSelector"),
567571
PropertyType.PodSecurityContext: ValuesFilePath.read_elsewhere("synapse", "podSecurityContext"),
568572
PropertyType.Resources: ValuesFilePath.read_elsewhere("synapse", "resources"),
569573
# Job so no readinessProbe

tests/manifests/test_basic.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,24 @@ async def test_default_values_file_sets_stub_values(base_values):
9595
unset_marker = "XXXX unset XXX"
9696
for deployable_details in all_deployables_details:
9797
extraEnv = deployable_details.get_helm_values(base_values, PropertyType.Env, default_value=unset_marker)
98+
nodeSelector = deployable_details.get_helm_values(
99+
base_values, PropertyType.NodeSelector, default_value=unset_marker
100+
)
98101
if deployable_details.has_workloads:
99102
assert extraEnv == [], f"{deployable_details.name} has default {extraEnv=} rather than []"
103+
# Might be None iff a `not_supported` values file path override is set, e.g. for Sidecars
104+
# default_value=unset_marker means that an omitted `nodeSelector` in the values file won't
105+
# return None here
106+
assert nodeSelector == {} or nodeSelector is None, (
107+
f"{deployable_details.name} has default {nodeSelector=} rather than {{}}"
108+
)
100109
else:
101110
assert extraEnv == unset_marker, (
102111
f"{deployable_details.name} has default {extraEnv=} rather than being unset"
103112
)
113+
assert nodeSelector == unset_marker, (
114+
f"{deployable_details.name} has default {nodeSelector=} rather than being unset"
115+
)
104116

105117
hostAliases = deployable_details.get_helm_values(
106118
base_values, PropertyType.HostAliases, default_value=unset_marker
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Copyright 2025 New Vector Ltd
2+
#
3+
# SPDX-License-Identifier: AGPL-3.0-only
4+
5+
import random
6+
import string
7+
8+
import pytest
9+
10+
from . import DeployableDetails, PropertyType, values_files_to_test
11+
from .utils import (
12+
iterate_deployables_workload_parts,
13+
template_id,
14+
template_to_deployable_details,
15+
)
16+
17+
18+
@pytest.mark.parametrize("values_file", values_files_to_test)
19+
@pytest.mark.asyncio_cooperative
20+
async def test_pod_has_no_nodeSelector_by_default(templates):
21+
for template in templates:
22+
if template["kind"] in ["Deployment", "StatefulSet", "Job"]:
23+
pod_spec = template["spec"]["template"]["spec"]
24+
assert "nodeSelector" not in pod_spec, (
25+
f"{template_id(template)} has a default nodeSelector when one isn't configured"
26+
)
27+
28+
29+
@pytest.mark.parametrize("values_file", values_files_to_test)
30+
@pytest.mark.asyncio_cooperative
31+
async def test_pod_gets_configured_nodeSelector(values, make_templates, release_name):
32+
def set_nodeSelector(deployable_details: DeployableDetails):
33+
nodeSelector = {"k8s.element.io/testing": "".join(random.choices(string.ascii_lowercase))}
34+
deployable_details.set_helm_values(values, PropertyType.NodeSelector, nodeSelector)
35+
36+
iterate_deployables_workload_parts(set_nodeSelector)
37+
for template in await make_templates(values):
38+
if template["kind"] in ["Deployment", "StatefulSet", "Job"]:
39+
pod_spec = template["spec"]["template"]["spec"]
40+
assert "nodeSelector" in pod_spec, (
41+
f"{template_id(template)} doesn't have a nodeSelector when one is configured"
42+
)
43+
44+
deployable_details = template_to_deployable_details(template)
45+
expected_nodeSelector = deployable_details.get_helm_values(values, PropertyType.NodeSelector)
46+
assert pod_spec["nodeSelector"] == expected_nodeSelector, (
47+
f"{template_id(template)} has an unexpected nodeSelector"
48+
)

0 commit comments

Comments
 (0)