blob: 68b16aed93b5346c7e1a6648c634e3ab0bcbfe04 [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.
import jmespath
import pytest
from parameterized import parameterized
from tests.charts.helm_template_generator import render_chart
class TestFlowerDeployment:
@pytest.mark.parametrize(
"executor,flower_enabled,created",
[
("CeleryExecutor", False, False),
("CeleryKubernetesExecutor", False, False),
("KubernetesExecutor", False, False),
("CeleryExecutor", True, True),
("CeleryKubernetesExecutor", True, True),
("KubernetesExecutor", True, False),
],
)
def test_create_flower(self, executor, flower_enabled, created):
docs = render_chart(
values={"executor": executor, "flower": {"enabled": flower_enabled}},
show_only=["templates/flower/flower-deployment.yaml"],
)
assert bool(docs) is created
if created:
assert "release-name-flower" == jmespath.search("metadata.name", docs[0])
assert "flower" == jmespath.search("spec.template.spec.containers[0].name", docs[0])
@parameterized.expand([(8, 10), (10, 8), (8, None), (None, 10), (None, None)])
def test_revision_history_limit(self, revision_history_limit, global_revision_history_limit):
values = {
"flower": {
"enabled": True,
}
}
if revision_history_limit:
values['flower']['revisionHistoryLimit'] = revision_history_limit
if global_revision_history_limit:
values['revisionHistoryLimit'] = global_revision_history_limit
docs = render_chart(
values=values,
show_only=["templates/flower/flower-deployment.yaml"],
)
expected_result = revision_history_limit if revision_history_limit else global_revision_history_limit
assert jmespath.search("spec.revisionHistoryLimit", docs[0]) == expected_result
@pytest.mark.parametrize(
"airflow_version, expected_arg",
[
("2.0.2", "airflow celery flower"),
("1.10.14", "airflow flower"),
("1.9.0", "airflow flower"),
("2.1.0", "airflow celery flower"),
],
)
def test_args_with_airflow_version(self, airflow_version, expected_arg):
docs = render_chart(
values={
"executor": "CeleryExecutor",
"flower": {"enabled": True},
"airflowVersion": airflow_version,
},
show_only=["templates/flower/flower-deployment.yaml"],
)
assert jmespath.search("spec.template.spec.containers[0].args", docs[0]) == [
"bash",
"-c",
f"exec \\\n{expected_arg}",
]
@pytest.mark.parametrize(
"command, args",
[
(None, None),
(None, ["custom", "args"]),
(["custom", "command"], None),
(["custom", "command"], ["custom", "args"]),
],
)
def test_command_and_args_overrides(self, command, args):
docs = render_chart(
values={"flower": {"enabled": True, "command": command, "args": args}},
show_only=["templates/flower/flower-deployment.yaml"],
)
assert command == jmespath.search("spec.template.spec.containers[0].command", docs[0])
assert args == jmespath.search("spec.template.spec.containers[0].args", docs[0])
def test_command_and_args_overrides_are_templated(self):
docs = render_chart(
values={
"flower": {
"enabled": True,
"command": ["{{ .Release.Name }}"],
"args": ["{{ .Release.Service }}"],
}
},
show_only=["templates/flower/flower-deployment.yaml"],
)
assert ["release-name"] == jmespath.search("spec.template.spec.containers[0].command", docs[0])
assert ["Helm"] == jmespath.search("spec.template.spec.containers[0].args", docs[0])
def test_should_create_flower_deployment_with_authorization(self):
docs = render_chart(
values={
"flower": {"enabled": True, "username": "flower", "password": "fl0w3r"},
"ports": {"flowerUI": 7777},
},
show_only=["templates/flower/flower-deployment.yaml"],
)
assert "AIRFLOW__CELERY__FLOWER_BASIC_AUTH" == jmespath.search(
"spec.template.spec.containers[0].env[0].name", docs[0]
)
assert ['curl', '--user', '$AIRFLOW__CELERY__FLOWER_BASIC_AUTH', 'localhost:7777'] == jmespath.search(
"spec.template.spec.containers[0].livenessProbe.exec.command", docs[0]
)
assert ['curl', '--user', '$AIRFLOW__CELERY__FLOWER_BASIC_AUTH', 'localhost:7777'] == jmespath.search(
"spec.template.spec.containers[0].readinessProbe.exec.command", docs[0]
)
def test_should_create_flower_deployment_without_authorization(self):
docs = render_chart(
values={
"flower": {"enabled": True},
"ports": {"flowerUI": 7777},
},
show_only=["templates/flower/flower-deployment.yaml"],
)
assert "AIRFLOW__CORE__FERNET_KEY" == jmespath.search(
"spec.template.spec.containers[0].env[0].name", docs[0]
)
assert ['curl', 'localhost:7777'] == jmespath.search(
"spec.template.spec.containers[0].livenessProbe.exec.command", docs[0]
)
assert ['curl', 'localhost:7777'] == jmespath.search(
"spec.template.spec.containers[0].readinessProbe.exec.command", docs[0]
)
def test_should_create_valid_affinity_tolerations_and_node_selector(self):
docs = render_chart(
values={
"flower": {
"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/flower/flower-deployment.yaml"],
)
assert "Deployment" == jmespath.search("kind", docs[0])
assert "foo" == jmespath.search(
"spec.template.spec.affinity.nodeAffinity."
"requiredDuringSchedulingIgnoredDuringExecution."
"nodeSelectorTerms[0]."
"matchExpressions[0]."
"key",
docs[0],
)
assert "ssd" == jmespath.search(
"spec.template.spec.nodeSelector.diskType",
docs[0],
)
assert "dynamic-pods" == jmespath.search(
"spec.template.spec.tolerations[0].key",
docs[0],
)
def test_flower_resources_are_configurable(self):
docs = render_chart(
values={
"flower": {
"enabled": True,
"resources": {
"limits": {"cpu": "200m", 'memory': "128Mi"},
"requests": {"cpu": "300m", 'memory': "169Mi"},
},
},
},
show_only=["templates/flower/flower-deployment.yaml"],
)
assert "128Mi" == jmespath.search("spec.template.spec.containers[0].resources.limits.memory", docs[0])
assert "169Mi" == jmespath.search(
"spec.template.spec.containers[0].resources.requests.memory", docs[0]
)
assert "300m" == jmespath.search("spec.template.spec.containers[0].resources.requests.cpu", docs[0])
def test_flower_resources_are_not_added_by_default(self):
docs = render_chart(
values={"flower": {"enabled": True}},
show_only=["templates/flower/flower-deployment.yaml"],
)
assert jmespath.search("spec.template.spec.containers[0].resources", docs[0]) == {}
def test_should_add_extra_containers(self):
docs = render_chart(
values={
"flower": {
"enabled": True,
"extraContainers": [
{"name": "test-container", "image": "test-registry/test-repo:test-tag"}
],
},
},
show_only=["templates/flower/flower-deployment.yaml"],
)
assert {
"name": "test-container",
"image": "test-registry/test-repo:test-tag",
} == jmespath.search("spec.template.spec.containers[-1]", docs[0])
def test_should_add_extra_volume_and_extra_volume_mount(self):
docs = render_chart(
values={
"flower": {
"enabled": True,
"extraVolumes": [{"name": "myvolume", "emptyDir": {}}],
"extraVolumeMounts": [{"name": "myvolume", "mountPath": "/opt/test"}],
},
},
show_only=["templates/flower/flower-deployment.yaml"],
)
assert {"name": "myvolume", "emptyDir": {}} in jmespath.search("spec.template.spec.volumes", docs[0])
assert {"name": "myvolume", "mountPath": "/opt/test"} in jmespath.search(
"spec.template.spec.containers[0].volumeMounts", docs[0]
)
class TestFlowerService:
@pytest.mark.parametrize(
"executor,flower_enabled,created",
[
("CeleryExecutor", False, False),
("CeleryKubernetesExecutor", False, False),
("KubernetesExecutor", False, False),
("CeleryExecutor", True, True),
("CeleryKubernetesExecutor", True, True),
("KubernetesExecutor", True, False),
],
)
def test_create_flower(self, executor, flower_enabled, created):
docs = render_chart(
values={"executor": executor, "flower": {"enabled": flower_enabled}},
show_only=["templates/flower/flower-service.yaml"],
)
assert bool(docs) is created
if created:
assert "release-name-flower" == jmespath.search("metadata.name", docs[0])
def test_default_service(self):
docs = render_chart(
values={"flower": {"enabled": True}},
show_only=["templates/flower/flower-service.yaml"],
)
assert "release-name-flower" == jmespath.search("metadata.name", docs[0])
assert jmespath.search("metadata.annotations", docs[0]) is None
assert {"tier": "airflow", "component": "flower", "release": "release-name"} == jmespath.search(
"spec.selector", docs[0]
)
assert "ClusterIP" == jmespath.search("spec.type", docs[0])
assert {"name": "flower-ui", "port": 5555} in jmespath.search("spec.ports", docs[0])
def test_overrides(self):
docs = render_chart(
values={
"ports": {"flowerUI": 9000},
"flower": {
"enabled": True,
"service": {
"type": "LoadBalancer",
"loadBalancerIP": "127.0.0.1",
"annotations": {"foo": "bar"},
"loadBalancerSourceRanges": ["10.123.0.0/16"],
},
},
},
show_only=["templates/flower/flower-service.yaml"],
)
assert {"foo": "bar"} == jmespath.search("metadata.annotations", docs[0])
assert "LoadBalancer" == jmespath.search("spec.type", docs[0])
assert {"name": "flower-ui", "port": 9000} in jmespath.search("spec.ports", docs[0])
assert "127.0.0.1" == jmespath.search("spec.loadBalancerIP", docs[0])
assert ["10.123.0.0/16"] == jmespath.search("spec.loadBalancerSourceRanges", docs[0])
@pytest.mark.parametrize(
"ports, expected_ports",
[
([{"port": 8888}], [{"port": 8888}]), # name is optional with a single port
(
[{"name": "{{ .Release.Name }}", "protocol": "UDP", "port": "{{ .Values.ports.flowerUI }}"}],
[{"name": "release-name", "protocol": "UDP", "port": 5555}],
),
([{"name": "only_sidecar", "port": "{{ int 9000 }}"}], [{"name": "only_sidecar", "port": 9000}]),
(
[
{"name": "flower-ui", "port": "{{ .Values.ports.flowerUI }}"},
{"name": "sidecar", "port": 80, "targetPort": "sidecar"},
],
[
{"name": "flower-ui", "port": 5555},
{"name": "sidecar", "port": 80, "targetPort": "sidecar"},
],
),
],
)
def test_ports_overrides(self, ports, expected_ports):
docs = render_chart(
values={
"flower": {"enabled": True, "service": {"ports": ports}},
},
show_only=["templates/flower/flower-service.yaml"],
)
assert expected_ports == jmespath.search("spec.ports", docs[0])
class TestFlowerNetworkPolicy:
def test_off_by_default(self):
docs = render_chart(
show_only=["templates/flower/flower-networkpolicy.yaml"],
)
assert 0 == len(docs)
def test_defaults(self):
docs = render_chart(
values={
"networkPolicies": {"enabled": True},
"flower": {
"enabled": True,
"networkPolicy": {
"ingress": {
"from": [{"namespaceSelector": {"matchLabels": {"release": "myrelease"}}}]
}
},
},
},
show_only=["templates/flower/flower-networkpolicy.yaml"],
)
assert 1 == len(docs)
assert "NetworkPolicy" == docs[0]["kind"]
assert [{"namespaceSelector": {"matchLabels": {"release": "myrelease"}}}] == jmespath.search(
"spec.ingress[0].from", docs[0]
)
assert [{"port": 5555}] == jmespath.search("spec.ingress[0].ports", docs[0])
@pytest.mark.parametrize(
"ports, expected_ports",
[
([{"port": "sidecar"}], [{"port": "sidecar"}]),
(
[
{"port": "{{ .Values.ports.flowerUI }}"},
{"port": 80},
],
[
{"port": 5555},
{"port": 80},
],
),
],
)
def test_ports_overrides(self, ports, expected_ports):
docs = render_chart(
values={
"networkPolicies": {"enabled": True},
"flower": {
"enabled": True,
"networkPolicy": {
"ingress": {
"from": [{"namespaceSelector": {"matchLabels": {"release": "myrelease"}}}],
"ports": ports,
}
},
},
},
show_only=["templates/flower/flower-networkpolicy.yaml"],
)
assert expected_ports == jmespath.search("spec.ingress[0].ports", docs[0])
def test_deprecated_from_param(self):
docs = render_chart(
values={
"networkPolicies": {"enabled": True},
"flower": {
"enabled": True,
"extraNetworkPolicies": [
{"namespaceSelector": {"matchLabels": {"release": "myrelease"}}}
],
},
},
show_only=["templates/flower/flower-networkpolicy.yaml"],
)
assert [{"namespaceSelector": {"matchLabels": {"release": "myrelease"}}}] == jmespath.search(
"spec.ingress[0].from", docs[0]
)