blob: 3a1be1371e1f98f5b861bf3dc3b15f4f7f96ec59 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from __future__ import annotations
import jmespath
import pytest
from chart_utils.helm_template_generator import render_chart
class TestSCBackwardsCompatibility:
"""Tests SC Backward Compatibility."""
@pytest.mark.parametrize("executor", ["CeleryExecutor", "KubernetesExecutor"])
def test_uid_gid_set(self, executor):
docs = render_chart(
values={
"uid": 3000,
"gid": 30,
"flower": {"enabled": True},
"executor": executor,
},
show_only=[
"templates/dag-processor/dag-processor-deployment.yaml",
"templates/flower/flower-deployment.yaml",
"templates/scheduler/scheduler-deployment.yaml",
"templates/triggerer/triggerer-deployment.yaml",
"templates/api-server/api-server-deployment.yaml",
"templates/workers/worker-deployment.yaml",
"templates/jobs/create-user-job.yaml",
"templates/jobs/migrate-database-job.yaml",
],
)
for doc in docs:
assert jmespath.search("spec.template.spec.securityContext.runAsUser", doc) == 3000
assert jmespath.search("spec.template.spec.securityContext.fsGroup", doc) == 30
def test_check_statsd_uid(self):
docs = render_chart(
values={"statsd": {"enabled": True, "uid": 3000}},
show_only=["templates/statsd/statsd-deployment.yaml"],
)
assert jmespath.search("spec.template.spec.securityContext.runAsUser", docs[0]) == 3000
def test_check_pgbouncer_uid(self):
docs = render_chart(
values={"pgbouncer": {"enabled": True, "uid": 3000}},
show_only=["templates/pgbouncer/pgbouncer-deployment.yaml"],
)
assert jmespath.search("spec.template.spec.securityContext.runAsUser", docs[0]) == 3000
@pytest.mark.parametrize("executor", ["CeleryExecutor,KubernetesExecutor", "KubernetesExecutor"])
def test_check_cleanup_job(self, executor):
docs = render_chart(
values={
"uid": 3000,
"gid": 30,
"executor": executor,
"cleanup": {"enabled": True},
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert (
jmespath.search("spec.jobTemplate.spec.template.spec.securityContext.runAsUser", docs[0]) == 3000
)
assert jmespath.search("spec.jobTemplate.spec.template.spec.securityContext.fsGroup", docs[0]) == 30
def test_gitsync_sidecar_and_init_container(self):
docs = render_chart(
values={
"dags": {"gitSync": {"enabled": True, "uid": 3000}},
},
show_only=[
"templates/workers/worker-deployment.yaml",
"templates/triggerer/triggerer-deployment.yaml",
"templates/dag-processor/dag-processor-deployment.yaml",
],
)
for doc in docs:
assert "git-sync" in [c["name"] for c in jmespath.search("spec.template.spec.containers", doc)]
assert "git-sync-init" in [
c["name"] for c in jmespath.search("spec.template.spec.initContainers", doc)
]
assert (
jmespath.search(
"spec.template.spec.initContainers[?name=='git-sync-init'].securityContext.runAsUser | [0]",
doc,
)
== 3000
)
assert (
jmespath.search(
"spec.template.spec.containers[?name=='git-sync'].securityContext.runAsUser | [0]",
doc,
)
== 3000
)
class TestSecurityContext:
"""Tests security context."""
# Test priority:
# <local>.securityContexts > securityContexts > uid + gid
def test_check_local_setting_default_version(self):
component_contexts = {"securityContexts": {"pod": {"runAsUser": 9000, "fsGroup": 90}}}
docs = render_chart(
values={
"uid": 3000,
"gid": 30,
"securityContexts": {"pod": {"runAsUser": 6000, "fsGroup": 60}},
"apiServer": component_contexts,
"workers": {"celery": component_contexts},
"dagProcessor": component_contexts,
"flower": {"enabled": True, **component_contexts},
"scheduler": component_contexts,
"createUserJob": component_contexts,
"migrateDatabaseJob": component_contexts,
"triggerer": component_contexts,
"redis": component_contexts,
"statsd": {"enabled": True, **component_contexts},
},
show_only=[
"templates/flower/flower-deployment.yaml",
"templates/scheduler/scheduler-deployment.yaml",
"templates/triggerer/triggerer-deployment.yaml",
"templates/dag-processor/dag-processor-deployment.yaml",
"templates/api-server/api-server-deployment.yaml",
"templates/workers/worker-deployment.yaml",
"templates/jobs/create-user-job.yaml",
"templates/jobs/migrate-database-job.yaml",
"templates/statsd/statsd-deployment.yaml",
"templates/redis/redis-statefulset.yaml",
],
)
for doc in docs:
assert jmespath.search("spec.template.spec.securityContext.runAsUser", doc) == 9000
assert jmespath.search("spec.template.spec.securityContext.fsGroup", doc) == 90
# Test securityContexts for main containers
def test_global_security_context(self):
ctx_value_pod = {"runAsUser": 7000}
ctx_value_container = {"allowPrivilegeEscalation": False}
docs = render_chart(
values={
"securityContexts": {"containers": ctx_value_container, "pod": ctx_value_pod},
"executor": "CeleryExecutor,KubernetesExecutor",
"cleanup": {"enabled": True},
"flower": {"enabled": True},
"pgbouncer": {"enabled": True},
},
show_only=[
"templates/cleanup/cleanup-cronjob.yaml",
"templates/flower/flower-deployment.yaml",
"templates/scheduler/scheduler-deployment.yaml",
"templates/workers/worker-deployment.yaml",
"templates/jobs/create-user-job.yaml",
"templates/jobs/migrate-database-job.yaml",
"templates/triggerer/triggerer-deployment.yaml",
"templates/pgbouncer/pgbouncer-deployment.yaml",
"templates/statsd/statsd-deployment.yaml",
"templates/redis/redis-statefulset.yaml",
],
)
assert ctx_value_container == jmespath.search(
"spec.jobTemplate.spec.template.spec.containers[0].securityContext", docs[0]
)
assert ctx_value_pod == jmespath.search(
"spec.jobTemplate.spec.template.spec.securityContext", docs[0]
)
for doc in docs[1:-3]:
assert ctx_value_container == jmespath.search(
"spec.template.spec.containers[0].securityContext", doc
)
assert ctx_value_pod == jmespath.search("spec.template.spec.securityContext", doc)
# Global security context is not propagated to pgbouncer, redis and statsd, so we test default value
default_ctx_value_container = {"allowPrivilegeEscalation": False, "capabilities": {"drop": ["ALL"]}}
default_ctx_value_pod_pgbouncer = {"runAsUser": 65534}
default_ctx_value_pod_statsd = {"runAsUser": 65534}
default_ctx_value_pod_redis = {"runAsUser": 0}
for doc in docs[-3:]:
assert default_ctx_value_container == jmespath.search(
"spec.template.spec.containers[0].securityContext", doc
)
# Test pgbouncer metrics-exporter container
assert default_ctx_value_container == jmespath.search(
"spec.template.spec.containers[1].securityContext", docs[-3]
)
assert default_ctx_value_pod_pgbouncer == jmespath.search(
"spec.template.spec.securityContext", docs[-3]
)
assert default_ctx_value_pod_statsd == jmespath.search("spec.template.spec.securityContext", docs[-2])
assert default_ctx_value_pod_redis == jmespath.search("spec.template.spec.securityContext", docs[-1])
# Test securityContexts for main containers
def test_main_container_setting(self):
ctx_value = {"allowPrivilegeEscalation": False}
security_context = {"securityContexts": {"container": ctx_value}}
docs = render_chart(
values={
"executor": "CeleryExecutor,KubernetesExecutor",
"cleanup": {"enabled": True, **security_context},
"scheduler": security_context,
"dagProcessor": security_context,
"apiServer": security_context,
"workers": {"celery": security_context},
"flower": {"enabled": True, **security_context},
"statsd": security_context,
"createUserJob": security_context,
"migrateDatabaseJob": security_context,
"triggerer": security_context,
"pgbouncer": {"enabled": True, **security_context},
"redis": security_context,
},
show_only=[
"templates/cleanup/cleanup-cronjob.yaml",
"templates/flower/flower-deployment.yaml",
"templates/scheduler/scheduler-deployment.yaml",
"templates/api-server/api-server-deployment.yaml",
"templates/dag-processor/dag-processor-deployment.yaml",
"templates/workers/worker-deployment.yaml",
"templates/statsd/statsd-deployment.yaml",
"templates/jobs/create-user-job.yaml",
"templates/jobs/migrate-database-job.yaml",
"templates/triggerer/triggerer-deployment.yaml",
"templates/pgbouncer/pgbouncer-deployment.yaml",
"templates/redis/redis-statefulset.yaml",
],
)
assert ctx_value == jmespath.search(
"spec.jobTemplate.spec.template.spec.containers[0].securityContext", docs[0]
)
for doc in docs[1:]:
assert ctx_value == jmespath.search("spec.template.spec.containers[0].securityContext", doc)
# Test securityContexts for log-groomer-sidecar main container
def test_log_groomer_sidecar_container_setting(self):
ctx_value = {"allowPrivilegeEscalation": False}
spec = {"logGroomerSidecar": {"securityContexts": {"container": ctx_value}}}
docs = render_chart(
values={
"scheduler": spec,
"workers": {"celery": spec},
"dagProcessor": spec,
"triggerer": spec,
},
show_only=[
"templates/scheduler/scheduler-deployment.yaml",
"templates/workers/worker-deployment.yaml",
"templates/triggerer/triggerer-deployment.yaml",
"templates/dag-processor/dag-processor-deployment.yaml",
],
)
for doc in docs:
assert ctx_value == jmespath.search("spec.template.spec.containers[1].securityContext", doc)
# Test securityContexts for metrics-explorer main container
def test_metrics_explorer_container_setting(self):
ctx_value = {"allowPrivilegeEscalation": False}
docs = render_chart(
values={
"pgbouncer": {
"enabled": True,
"metricsExporterSidecar": {"securityContexts": {"container": ctx_value}},
},
},
show_only=["templates/pgbouncer/pgbouncer-deployment.yaml"],
)
assert ctx_value == jmespath.search("spec.template.spec.containers[1].securityContext", docs[0])
# Test securityContexts for worker-kerberos main container
def test_worker_kerberos_container_security_context(self):
docs = render_chart(
values={
"workers": {
"celery": {
"kerberosSidecar": {
"enabled": True,
"securityContexts": {"container": {"allowPrivilegeEscalation": False}},
}
}
}
},
show_only=["templates/workers/worker-deployment.yaml"],
)
assert jmespath.search("spec.template.spec.containers[2].securityContext", docs[0]) == {
"allowPrivilegeEscalation": False
}
def test_worker_kerberos_init_container_security_contexts(self):
docs = render_chart(
values={
"workers": {
"celery": {
"kerberosInitContainer": {
"enabled": True,
"securityContexts": {"container": {"runAsUser": 2000}},
}
}
}
},
show_only=["templates/workers/worker-deployment.yaml"],
)
assert jmespath.search(
"spec.template.spec.initContainers[?name=='kerberos-init'] | [0].securityContext", docs[0]
) == {"runAsUser": 2000}
def test_wait_for_migrations_init_container_setting(self):
ctx_value = {"allowPrivilegeEscalation": False}
spec = {
"waitForMigrations": {
"enabled": True,
"securityContexts": {"container": ctx_value},
}
}
docs = render_chart(
values={
"scheduler": spec,
"apiServer": spec,
"triggerer": spec,
"dagProcessor": spec,
"workers": {"celery": spec},
},
show_only=[
"templates/scheduler/scheduler-deployment.yaml",
"templates/api-server/api-server-deployment.yaml",
"templates/triggerer/triggerer-deployment.yaml",
"templates/workers/worker-deployment.yaml",
"templates/dag-processor/dag-processor-deployment.yaml",
],
)
for doc in docs:
assert ctx_value == jmespath.search("spec.template.spec.initContainers[0].securityContext", doc)
# Test securityContexts for volume-permissions init container
def test_volume_permissions_init_container_setting(self):
docs = render_chart(
values={
"workers": {
"celery": {
"persistence": {
"enabled": True,
"fixPermissions": True,
"securityContexts": {"container": {"allowPrivilegeEscalation": False}},
}
}
}
},
show_only=["templates/workers/worker-deployment.yaml"],
)
assert jmespath.search("spec.template.spec.initContainers[0].securityContext", docs[0]) == {
"allowPrivilegeEscalation": False
}
def test_main_pod_setting(self):
ctx_value = {"runAsUser": 7000}
security_context = {"securityContexts": {"pod": ctx_value}}
docs = render_chart(
values={
"executor": "CeleryExecutor,KubernetesExecutor",
"cleanup": {"enabled": True, **security_context},
"scheduler": security_context,
"apiServer": security_context,
"workers": {"celery": security_context},
"flower": {"enabled": True, **security_context},
"statsd": security_context,
"createUserJob": security_context,
"migrateDatabaseJob": security_context,
"triggerer": security_context,
"pgbouncer": {"enabled": True, **security_context},
"redis": security_context,
"dagProcessor": security_context,
},
show_only=[
"templates/cleanup/cleanup-cronjob.yaml",
"templates/flower/flower-deployment.yaml",
"templates/scheduler/scheduler-deployment.yaml",
"templates/api-server/api-server-deployment.yaml",
"templates/workers/worker-deployment.yaml",
"templates/statsd/statsd-deployment.yaml",
"templates/jobs/create-user-job.yaml",
"templates/jobs/migrate-database-job.yaml",
"templates/triggerer/triggerer-deployment.yaml",
"templates/pgbouncer/pgbouncer-deployment.yaml",
"templates/redis/redis-statefulset.yaml",
"templates/dag-processor/dag-processor-deployment.yaml",
],
)
assert ctx_value == jmespath.search("spec.jobTemplate.spec.template.spec.securityContext", docs[0])
for doc in docs[1:]:
assert ctx_value == jmespath.search("spec.template.spec.securityContext", doc)
def test_disable_defaults_pod_and_container(self):
docs = render_chart(
values={
"securityContexts": {"disableDefaults": True},
"flower": {"enabled": True},
"pgbouncer": {"enabled": True},
"statsd": {"enabled": True},
},
show_only=[
"templates/flower/flower-deployment.yaml",
"templates/scheduler/scheduler-deployment.yaml",
"templates/api-server/api-server-deployment.yaml",
"templates/dag-processor/dag-processor-deployment.yaml",
"templates/workers/worker-deployment.yaml",
"templates/jobs/create-user-job.yaml",
"templates/jobs/migrate-database-job.yaml",
"templates/triggerer/triggerer-deployment.yaml",
"templates/pgbouncer/pgbouncer-deployment.yaml",
"templates/statsd/statsd-deployment.yaml",
"templates/redis/redis-statefulset.yaml",
],
)
assert all(v is None for v in jmespath.search("[].spec.template.spec.securityContext", docs))
assert all(
v is None for v in jmespath.search("[].spec.template.spec.containers[0].securityContext", docs)
)
def test_disable_defaults_pod_and_container_cronjob(self):
docs = render_chart(
values={
"securityContexts": {"disableDefaults": True},
"executor": "CeleryExecutor,KubernetesExecutor",
"cleanup": {"enabled": True},
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert jmespath.search("spec.jobTemplate.spec.template.spec.securityContext", docs[0]) is None
assert (
jmespath.search("spec.jobTemplate.spec.template.spec.containers[0].securityContext", docs[0])
is None
)
def test_disable_defaults_gitsync_containers(self):
docs = render_chart(
values={
"securityContexts": {"disableDefaults": True},
"dags": {"gitSync": {"enabled": True}},
},
show_only=[
"templates/workers/worker-deployment.yaml",
"templates/triggerer/triggerer-deployment.yaml",
"templates/dag-processor/dag-processor-deployment.yaml",
],
)
for doc in docs:
assert (
jmespath.search(
"spec.template.spec.initContainers[?name=='git-sync-init'].securityContext | [0]",
doc,
)
is None
)
assert (
jmespath.search(
"spec.template.spec.containers[?name=='git-sync'].securityContext | [0]",
doc,
)
is None
)
def test_disable_defaults_volume_permissions_init_container_skipped(self):
"""With no explicit uid/gid override, the volume-permissions init container is omitted entirely."""
docs = render_chart(
values={
"securityContexts": {"disableDefaults": True},
"workers": {
"celery": {
"persistence": {"enabled": True, "fixPermissions": True},
}
},
},
show_only=["templates/workers/worker-deployment.yaml"],
)
assert (
jmespath.search("spec.template.spec.initContainers[?name=='volume-permissions']", docs[0]) == []
)
def test_disable_defaults_explicit_override_still_applied(self):
ctx_value = {"runAsUser": 7000}
docs = render_chart(
values={
"securityContexts": {"disableDefaults": True, "pod": ctx_value},
"workers": {
"celery": {
"persistence": {"enabled": True, "fixPermissions": True},
}
},
},
show_only=[
"templates/scheduler/scheduler-deployment.yaml",
"templates/workers/worker-deployment.yaml",
],
)
for doc in docs:
assert ctx_value == jmespath.search("spec.template.spec.securityContext", doc)
assert (
jmespath.search("spec.template.spec.initContainers[?name=='volume-permissions']", docs[1]) != []
)
@pytest.mark.parametrize(
("component_values", "show_only", "security_context_path"),
[
(
{"scheduler": {"securityContexts": {"pod": {"runAsUser": 8000}}}},
"templates/scheduler/scheduler-deployment.yaml",
"spec.template.spec.securityContext",
),
(
{"apiServer": {"securityContexts": {"pod": {"runAsUser": 8000}}}},
"templates/api-server/api-server-deployment.yaml",
"spec.template.spec.securityContext",
),
(
{"dagProcessor": {"securityContexts": {"pod": {"runAsUser": 8000}}}},
"templates/dag-processor/dag-processor-deployment.yaml",
"spec.template.spec.securityContext",
),
(
{"triggerer": {"securityContexts": {"pod": {"runAsUser": 8000}}}},
"templates/triggerer/triggerer-deployment.yaml",
"spec.template.spec.securityContext",
),
(
{"workers": {"celery": {"securityContexts": {"pod": {"runAsUser": 8000}}}}},
"templates/workers/worker-deployment.yaml",
"spec.template.spec.securityContext",
),
(
{"flower": {"enabled": True, "securityContexts": {"pod": {"runAsUser": 8000}}}},
"templates/flower/flower-deployment.yaml",
"spec.template.spec.securityContext",
),
(
{"statsd": {"securityContexts": {"pod": {"runAsUser": 8000}}}},
"templates/statsd/statsd-deployment.yaml",
"spec.template.spec.securityContext",
),
(
{"pgbouncer": {"enabled": True, "securityContexts": {"pod": {"runAsUser": 8000}}}},
"templates/pgbouncer/pgbouncer-deployment.yaml",
"spec.template.spec.securityContext",
),
(
{"redis": {"securityContexts": {"pod": {"runAsUser": 8000}}}},
"templates/redis/redis-statefulset.yaml",
"spec.template.spec.securityContext",
),
(
{"createUserJob": {"securityContexts": {"pod": {"runAsUser": 8000}}}},
"templates/jobs/create-user-job.yaml",
"spec.template.spec.securityContext",
),
(
{"migrateDatabaseJob": {"securityContexts": {"pod": {"runAsUser": 8000}}}},
"templates/jobs/migrate-database-job.yaml",
"spec.template.spec.securityContext",
),
(
{
"executor": "CeleryExecutor,KubernetesExecutor",
"cleanup": {"enabled": True, "securityContexts": {"pod": {"runAsUser": 8000}}},
},
"templates/cleanup/cleanup-cronjob.yaml",
"spec.jobTemplate.spec.template.spec.securityContext",
),
(
{"databaseCleanup": {"enabled": True, "securityContexts": {"pod": {"runAsUser": 8000}}}},
"templates/database-cleanup/database-cleanup-cronjob.yaml",
"spec.jobTemplate.spec.template.spec.securityContext",
),
],
ids=[
"scheduler",
"apiServer",
"dagProcessor",
"triggerer",
"workers.celery",
"flower",
"statsd",
"pgbouncer",
"redis",
"createUserJob",
"migrateDatabaseJob",
"cleanup",
"databaseCleanup",
],
)
def test_disable_defaults_component_level_override_still_applied(
self, component_values, show_only, security_context_path
):
docs = render_chart(
values={"securityContexts": {"disableDefaults": True}, **component_values},
show_only=[show_only],
)
assert jmespath.search(security_context_path, docs[0]) == {"runAsUser": 8000}
def test_disable_defaults_workers_volume_permissions_init_container_still_added(self):
docs = render_chart(
values={
"securityContexts": {"disableDefaults": True},
"workers": {
"celery": {
"securityContexts": {"pod": {"runAsUser": 8000}},
"persistence": {"enabled": True, "fixPermissions": True},
}
},
},
show_only=["templates/workers/worker-deployment.yaml"],
)
assert (
jmespath.search("spec.template.spec.initContainers[?name=='volume-permissions']", docs[0]) != []
)
def test_workers_overwrite_local(self):
docs = render_chart(
values={
"workers": {
"celery": {
"enableDefault": False,
"securityContexts": {"pod": {"runAsUser": 6000, "fsGroup": 60}},
"sets": [
{"name": "test", "securityContexts": {"pod": {"runAsUser": 9000, "fsGroup": 90}}}
],
},
}
},
show_only=["templates/workers/worker-deployment.yaml"],
)
assert jmespath.search("spec.template.spec.securityContext.runAsUser", docs[0]) == 9000
assert jmespath.search("spec.template.spec.securityContext.fsGroup", docs[0]) == 90
def test_workers_sets_overwrite_local_container(self):
docs = render_chart(
values={
"workers": {
"celery": {
"enableDefault": False,
"securityContexts": {"container": {"allowPrivilegeEscalation": True}},
"sets": [
{
"name": "test",
"securityContexts": {"container": {"allowPrivilegeEscalation": False}},
}
],
},
}
},
show_only=["templates/workers/worker-deployment.yaml"],
)
assert jmespath.search("spec.template.spec.containers[0].securityContext", docs[0]) == {
"allowPrivilegeEscalation": False
}
@pytest.mark.parametrize(
"values",
[
{
"celery": {
"enableDefault": False,
"sets": [
{
"name": "set1",
"persistence": {
"fixPermissions": True,
"securityContexts": {"container": {"allowPrivilegeEscalation": False}},
},
}
],
}
},
{
"celery": {
"enableDefault": False,
"persistence": {"securityContexts": {"container": {"allowPrivilegeEscalation": True}}},
"sets": [
{
"name": "set1",
"persistence": {
"fixPermissions": True,
"securityContexts": {"container": {"allowPrivilegeEscalation": False}},
},
}
],
}
},
],
)
def test_workers_sets_volume_permissions_init_container_setting(self, values):
docs = render_chart(
values={"workers": values},
show_only=["templates/workers/worker-deployment.yaml"],
)
assert jmespath.search("spec.template.spec.initContainers[0].securityContext", docs[0]) == {
"allowPrivilegeEscalation": False
}