blob: 4305531ec5f6e191fd32db57c0a93000c2e0ee29 [file] [log] [blame]
# 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 tests.charts.helm_template_generator import render_chart
class TestCleanupDeployment:
"""Tests cleanup pods deployments."""
def test_should_have_a_schedule_with_defaults(self):
doc = render_chart(
values={
"cleanup": {"enabled": True},
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)[0]
assert doc["spec"]["schedule"] == "*/15 * * * *"
cron_tests = [
("release-name", "*/5 * * * *", "*/5 * * * *"),
("something-else", "@hourly", "@hourly"),
(
"custom-name",
'{{- add 3 (regexFind ".$" (adler32sum .Release.Name)) -}}-59/15 * * * *',
"7-59/15 * * * *",
),
(
"airflow-rules",
'{{- add 3 (regexFind ".$" (adler32sum .Release.Name)) -}}-59/15 * * * *',
"10-59/15 * * * *",
),
]
@pytest.mark.parametrize(
"release_name,schedule_value,schedule_result",
cron_tests,
ids=[x[0] for x in cron_tests],
)
def test_should_work_with_custom_schedule_string(self, release_name, schedule_value, schedule_result):
doc = render_chart(
name=release_name,
values={
"cleanup": {
"enabled": True,
"schedule": schedule_value,
},
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)[0]
assert doc["spec"]["schedule"] == schedule_result
class TestCleanupPods:
"""Tests cleanup of pods."""
def test_should_create_cronjob_for_enabled_cleanup(self):
docs = render_chart(
values={
"cleanup": {"enabled": True},
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert "airflow-cleanup-pods" == jmespath.search(
"spec.jobTemplate.spec.template.spec.containers[0].name", docs[0]
)
assert jmespath.search("spec.jobTemplate.spec.template.spec.containers[0].image", docs[0]).startswith(
"apache/airflow"
)
assert {"name": "config", "configMap": {"name": "release-name-config"}} in jmespath.search(
"spec.jobTemplate.spec.template.spec.volumes", docs[0]
)
assert {
"name": "config",
"mountPath": "/opt/airflow/airflow.cfg",
"subPath": "airflow.cfg",
"readOnly": True,
} in jmespath.search("spec.jobTemplate.spec.template.spec.containers[0].volumeMounts", docs[0])
def test_should_pass_validation_with_v1beta1_api(self):
render_chart(
values={"cleanup": {"enabled": True}},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
kubernetes_version="1.16.0",
) # checks that no validation exception is raised
def test_should_change_image_when_set_airflow_image(self):
docs = render_chart(
values={
"cleanup": {"enabled": True},
"images": {"airflow": {"repository": "airflow", "tag": "test"}},
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert "airflow:test" == jmespath.search(
"spec.jobTemplate.spec.template.spec.containers[0].image", docs[0]
)
def test_should_create_valid_affinity_tolerations_and_node_selector(self):
docs = render_chart(
values={
"cleanup": {
"enabled": True,
"affinity": {
"nodeAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [
{
"matchExpressions": [
{"key": "foo", "operator": "In", "values": ["true"]},
]
}
]
}
}
},
"tolerations": [
{"key": "dynamic-pods", "operator": "Equal", "value": "true", "effect": "NoSchedule"}
],
"nodeSelector": {"diskType": "ssd"},
}
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert "CronJob" == jmespath.search("kind", docs[0])
assert "foo" == jmespath.search(
"spec.jobTemplate.spec.template.spec.affinity.nodeAffinity."
"requiredDuringSchedulingIgnoredDuringExecution."
"nodeSelectorTerms[0]."
"matchExpressions[0]."
"key",
docs[0],
)
assert "ssd" == jmespath.search(
"spec.jobTemplate.spec.template.spec.nodeSelector.diskType",
docs[0],
)
assert "dynamic-pods" == jmespath.search(
"spec.jobTemplate.spec.template.spec.tolerations[0].key",
docs[0],
)
def test_scheduler_name(self):
docs = render_chart(
values={"cleanup": {"enabled": True}, "schedulerName": "airflow-scheduler"},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert "airflow-scheduler" == jmespath.search(
"spec.jobTemplate.spec.template.spec.schedulerName",
docs[0],
)
def test_default_command_and_args(self):
docs = render_chart(
values={"cleanup": {"enabled": True}}, show_only=["templates/cleanup/cleanup-cronjob.yaml"]
)
assert jmespath.search("spec.jobTemplate.spec.template.spec.containers[0].command", docs[0]) is None
assert ["bash", "-c", "exec airflow kubernetes cleanup-pods --namespace=default"] == jmespath.search(
"spec.jobTemplate.spec.template.spec.containers[0].args", docs[0]
)
def test_should_add_extraEnvs(self):
docs = render_chart(
values={
"cleanup": {
"enabled": True,
"env": [{"name": "TEST_ENV_1", "value": "test_env_1"}],
},
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert {"name": "TEST_ENV_1", "value": "test_env_1"} in jmespath.search(
"spec.jobTemplate.spec.template.spec.containers[0].env", docs[0]
)
@pytest.mark.parametrize("command", [None, ["custom", "command"]])
@pytest.mark.parametrize("args", [None, ["custom", "args"]])
def test_command_and_args_overrides(self, command, args):
docs = render_chart(
values={"cleanup": {"enabled": True, "command": command, "args": args}},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert command == jmespath.search(
"spec.jobTemplate.spec.template.spec.containers[0].command", docs[0]
)
assert args == jmespath.search("spec.jobTemplate.spec.template.spec.containers[0].args", docs[0])
def test_command_and_args_overrides_are_templated(self):
docs = render_chart(
values={
"cleanup": {
"enabled": True,
"command": ["{{ .Release.Name }}"],
"args": ["{{ .Release.Service }}"],
}
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert ["release-name"] == jmespath.search(
"spec.jobTemplate.spec.template.spec.containers[0].command", docs[0]
)
assert ["Helm"] == jmespath.search("spec.jobTemplate.spec.template.spec.containers[0].args", docs[0])
def test_should_set_labels_to_jobs_from_cronjob(self):
docs = render_chart(
values={
"cleanup": {"enabled": True},
"labels": {"project": "airflow"},
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert {
"tier": "airflow",
"component": "airflow-cleanup-pods",
"release": "release-name",
"project": "airflow",
} == jmespath.search("spec.jobTemplate.spec.template.metadata.labels", docs[0])
def test_should_add_component_specific_labels(self):
docs = render_chart(
values={
"cleanup": {
"enabled": True,
"labels": {"test_label": "test_label_value"},
},
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert "test_label" in jmespath.search("spec.jobTemplate.spec.template.metadata.labels", docs[0])
assert (
jmespath.search("spec.jobTemplate.spec.template.metadata.labels", docs[0])["test_label"]
== "test_label_value"
)
def test_should_add_component_specific_annotations(self):
docs = render_chart(
values={
"cleanup": {
"enabled": True,
"jobAnnotations": {"test_cronjob_annotation": "test_cronjob_annotation_value"},
"podAnnotations": {"test_pod_annotation": "test_pod_annotation_value"},
},
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert "test_cronjob_annotation" in jmespath.search("metadata.annotations", docs[0])
assert (
"test_cronjob_annotation_value"
== jmespath.search("metadata.annotations", docs[0])["test_cronjob_annotation"]
)
assert "test_pod_annotation" in jmespath.search(
"spec.jobTemplate.spec.template.metadata.annotations", docs[0]
)
assert (
"test_pod_annotation_value"
== jmespath.search("spec.jobTemplate.spec.template.metadata.annotations", docs[0])[
"test_pod_annotation"
]
)
def test_cleanup_resources_are_configurable(self):
resources = {
"requests": {
"cpu": "128m",
"memory": "256Mi",
},
"limits": {
"cpu": "256m",
"memory": "512Mi",
},
}
docs = render_chart(
values={
"cleanup": {
"enabled": True,
"resources": resources,
},
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert resources == jmespath.search(
"spec.jobTemplate.spec.template.spec.containers[0].resources", docs[0]
)
def test_should_set_job_history_limits(self):
docs = render_chart(
values={
"cleanup": {
"enabled": True,
"failedJobsHistoryLimit": 2,
"successfulJobsHistoryLimit": 4,
},
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert 2 == jmespath.search("spec.failedJobsHistoryLimit", docs[0])
assert 4 == jmespath.search("spec.successfulJobsHistoryLimit", docs[0])
def test_no_airflow_local_settings(self):
docs = render_chart(
values={
"cleanup": {"enabled": True},
"airflowLocalSettings": None,
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
volume_mounts = jmespath.search(
"spec.jobTemplate.spec.template.spec.containers[0].volumeMounts", docs[0]
)
assert "airflow_local_settings.py" not in str(volume_mounts)
def test_airflow_local_settings(self):
docs = render_chart(
values={
"cleanup": {"enabled": True},
"airflowLocalSettings": "# Well hello!",
},
show_only=["templates/cleanup/cleanup-cronjob.yaml"],
)
assert {
"name": "config",
"mountPath": "/opt/airflow/config/airflow_local_settings.py",
"subPath": "airflow_local_settings.py",
"readOnly": True,
} in jmespath.search("spec.jobTemplate.spec.template.spec.containers[0].volumeMounts", docs[0])
class TestCleanupServiceAccount:
"""Tests cleanup of service accounts."""
def test_should_add_component_specific_labels(self):
docs = render_chart(
values={
"cleanup": {
"enabled": True,
"labels": {"test_label": "test_label_value"},
},
},
show_only=["templates/cleanup/cleanup-serviceaccount.yaml"],
)
assert "test_label" in jmespath.search("metadata.labels", docs[0])
assert jmespath.search("metadata.labels", docs[0])["test_label"] == "test_label_value"
def test_default_automount_service_account_token(self):
docs = render_chart(
values={
"cleanup": {
"enabled": True,
},
},
show_only=["templates/cleanup/cleanup-serviceaccount.yaml"],
)
assert jmespath.search("automountServiceAccountToken", docs[0]) is True
def test_overridden_automount_service_account_token(self):
docs = render_chart(
values={
"cleanup": {"enabled": True, "serviceAccount": {"automountServiceAccountToken": False}},
},
show_only=["templates/cleanup/cleanup-serviceaccount.yaml"],
)
assert jmespath.search("automountServiceAccountToken", docs[0]) is False