diff --git a/.github/scripts/test-cilium-envoy-toggle.py b/.github/scripts/test-cilium-envoy-toggle.py new file mode 100644 index 0000000..1b04178 --- /dev/null +++ b/.github/scripts/test-cilium-envoy-toggle.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +"""Regression test for the Cilium Envoy toggle. + +The `cilium_envoy` variable lets users enable or disable the Cilium Envoy +proxy. The Install/upgrade Cilium task in +roles/k3s_server_post/tasks/cilium.yml passes the value through to Helm as +`envoy.enabled`. This test: + + - loads the real "Install Cilium" task and confirms the install/upgrade + command actually contains the `envoy.enabled` Helm value, + - renders the conditional that computes the Helm value and confirms it + produces `true` when cilium_envoy is enabled and `false` when disabled, + - confirms the task stays forward/backward compatible (no raw `true` / + `false` hardcoded in place of the conditional). +""" + +from __future__ import print_function + +import os +import re +import subprocess + +import yaml +from jinja2 import Environment + +ENVOY_EXPRESSION = '{{ "true" if cilium_envoy else "false" }}' + + +def repo_root(): + return subprocess.check_output( + ["git", "rev-parse", "--show-toplevel"], text=True + ).strip() + + +def fail(message): + raise SystemExit("Cilium Envoy toggle test failed: " + message) + + +def extract_install_command(path): + """Return the command string for the 'Install Cilium' task. + + Walks both top-level tasks and tasks nested inside a `block`/`always`/ + `rescue` list, since the Cilium deploy steps are grouped under the + 'Prepare Cilium CLI on first master and deploy CNI' block. + """ + with open(path, encoding="utf-8") as handle: + doc = yaml.safe_load(handle) + + def find_command(tasks): + for task in tasks: + if not isinstance(task, dict): + continue + if task.get("name") == "Install Cilium": + command = task.get("ansible.builtin.command") + if command is None: + raise SystemExit( + "Cilium Envoy toggle test failed: " + "'Install Cilium' task has no ansible.builtin.command" + ) + return command + # Recurse into block/always/rescue sub-lists. + for key in ("block", "always", "rescue"): + nested = task.get(key) + if isinstance(nested, list): + found = find_command(nested) + if found is not None: + return found + return None + + command = find_command(doc) + if command is None: + raise SystemExit( + "Cilium Envoy toggle test failed: could not find 'Install Cilium' task" + ) + return command + + +def assert_envoy_in_command(command): + if "envoy.enabled" not in command: + fail("install command is missing --helm-set envoy.enabled") + if ENVOY_EXPRESSION not in command: + fail( + "install command does not use the cilium_envoy conditional: " + "expected {0!r}".format(ENVOY_EXPRESSION) + ) + # The conditional must be a WYSIWYG helm-set value, not a pre-rendered + # true/false literal (which would ignore the cilium_envoy variable). + if re.search(r"--helm-set envoy\.enabled=true(?:$|\s)", command): + fail("install command hardcodes envoy.enabled=true") + if re.search(r"--helm-set envoy\.enabled=false(?:$|\s)", command): + fail("install command hardcodes envoy.enabled=false") + + +def assert_render(): + env = Environment() + + def render_for(value): + template = env.from_string(ENVOY_EXPRESSION) + return template.render(cilium_envoy=value) + + if render_for(True) != "true": + fail("envoy conditional did not render 'true' when enabled") + if render_for(False) != "false": + fail("envoy conditional did not render 'false' when disabled") + + +def main(): + root = repo_root() + cilium_tasks = os.path.join( + root, "roles", "k3s_server_post", "tasks", "cilium.yml" + ) + command = extract_install_command(cilium_tasks) + assert_envoy_in_command(command) + assert_render() + + print("Cilium Envoy toggle regression test passed") + + +if __name__ == "__main__": + main() diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 871a131..0b80f6d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -70,6 +70,15 @@ repos: - Jinja2>=3.1 pass_filenames: false files: ^roles/k3s_server_post/templates/cilium\.crs\.j2$|^\.github/scripts/test-cilium-bgp-manifest\.py$ + - id: cilium-envoy-toggle-test + name: Cilium Envoy toggle test + entry: python3 .github/scripts/test-cilium-envoy-toggle.py + language: python + additional_dependencies: + - Jinja2>=3.1 + - PyYAML + pass_filenames: false + files: ^roles/k3s_server_post/tasks/cilium\.yml$|^\.github/scripts/test-cilium-envoy-toggle\.py$ - id: kube-vip-manifest-test name: kube-vip manifest test entry: python3 .github/scripts/test-kube-vip-manifest.py diff --git a/inventory/sample/group_vars/all.yml b/inventory/sample/group_vars/all.yml index 4e461b6..26295ab 100644 --- a/inventory/sample/group_vars/all.yml +++ b/inventory/sample/group_vars/all.yml @@ -24,6 +24,10 @@ cilium_mode: native # native when nodes are on the same subnet or use BGP, other cilium_tag: v1.20.0 # cilium version tag cilium_cli_tag: v0.19.7 # cilium cli version tag cilium_hubble: true # enable hubble observability relay and ui +cilium_envoy: true # enable the Envoy proxy for Cilium L7 policies + +# disable cilium_envoy to skip the Envoy proxy entirely (e.g. no L7 policies) +# cilium_envoy: false # if using calico or cilium, you may specify the cluster pod cidr pool cluster_cidr: 10.52.0.0/16 diff --git a/roles/k3s_server_post/defaults/main.yml b/roles/k3s_server_post/defaults/main.yml index ec03c3b..0d64546 100644 --- a/roles/k3s_server_post/defaults/main.yml +++ b/roles/k3s_server_post/defaults/main.yml @@ -18,6 +18,7 @@ cilium_bgp_peer_asn: 64512 cilium_bgp_neighbors: [] cilium_bgp_neighbors_groups: ['k3s_all'] cilium_bgp_lb_cidr: 192.168.31.0/24 +cilium_envoy: true cilium_hubble: true cilium_mode: native cilium_tag: v1.20.0 diff --git a/roles/k3s_server_post/tasks/cilium.yml b/roles/k3s_server_post/tasks/cilium.yml index 78fd24e..46bca62 100644 --- a/roles/k3s_server_post/tasks/cilium.yml +++ b/roles/k3s_server_post/tasks/cilium.yml @@ -178,6 +178,7 @@ --helm-set hubble.enabled={{ "true" if cilium_hubble else "false" }} --helm-set hubble.relay.enabled={{ "true" if cilium_hubble else "false" }} --helm-set hubble.ui.enabled={{ "true" if cilium_hubble else "false" }} + --helm-set envoy.enabled={{ "true" if cilium_envoy else "false" }} {% if kube_proxy_replacement is not false %} --helm-set loadBalancer.algorithm={{ bpf_lb_algorithm }} --helm-set loadBalancer.mode={{ bpf_lb_mode }}