Compare commits

1 Commits

Author SHA1 Message Date
dependabot[bot] e3769c1c50 chore(deps): bump zgosalvez/github-actions-ensure-sha-pinned-actions
Bumps [zgosalvez/github-actions-ensure-sha-pinned-actions](https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions) from 5.0.5 to 5.0.6.
- [Release notes](https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions/releases)
- [Commits](https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions/compare/3db98c0363e2fa5df3e1c4c471777a7c10b24cc9...46cfe808a5f1588656ef299eedd0ce2fd7ec0dcc)

---
updated-dependencies:
- dependency-name: zgosalvez/github-actions-ensure-sha-pinned-actions
  dependency-version: 5.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-04 02:52:33 +00:00
32 changed files with 22 additions and 866 deletions
-120
View File
@@ -1,120 +0,0 @@
#!/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()
-37
View File
@@ -1,37 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
repo_root="$(git rev-parse --show-toplevel)"
prereq_defaults="$repo_root/roles/prereq/defaults/main.yml"
prereq_tasks="$repo_root/roles/prereq/tasks/main.yml"
# #670: k3s recommends swap be disabled on all nodes. The prereq role must expose
# a disable_swap toggle (defaulting to true) that turns swap off now and comments
# out the /etc/fstab swap entries so swap stays off across reboots.
grep -Eq -- '^disable_swap: true' "$prereq_defaults" || {
printf 'prereq defaults are missing disable_swap: true\n' >&2
exit 1
}
grep -Fq -- 'Disable swap on all cluster nodes' "$prereq_tasks" || {
printf 'prereq tasks are missing the swap-disable block\n' >&2
exit 1
}
grep -Fq -- 'swapoff -a' "$prereq_tasks" || {
printf 'swap-disable block does not run swapoff -a\n' >&2
exit 1
}
grep -Fq -- '/etc/fstab' "$prereq_tasks" || {
printf 'swap-disable block does not comment out /etc/fstab swap entries\n' >&2
exit 1
}
if ! grep -Eq -- 'when: disable_swap' "$prereq_tasks"; then
printf 'swap-disable block is not gated on the disable_swap toggle\n' >&2
exit 1
fi
printf 'Swap disable regression test passed\n'
-32
View File
@@ -123,38 +123,6 @@ def main():
if "name: bgp_peers" in output: if "name: bgp_peers" in output:
fail("bgp_peers present even though the peer list is empty") fail("bgp_peers present even though the peer list is empty")
# kube_vip_endpoint defaults to null (defined in role defaults): the
# address and subnet must fall back to the apiserver endpoint. default()
# without a truthy flag does NOT fall back on null, only on undefined, so
# this case pins the null runtime condition to prevent that regression.
output = render(
env,
{
"_kube_vip_bgp_peers": [],
"kube_vip_endpoint": None,
"kube_vip_arp": True,
"kube_vip_bgp": False,
},
)
if "value: 192.168.30.222" not in output:
fail("null kube_vip_endpoint does not fall back to apiserver_endpoint")
# kube_vip_endpoint set: overrides the internal listening address AND the
# subnet derivation while the advertised apiserver_endpoint stays separate.
output = render(
env,
{
"_kube_vip_bgp_peers": [],
"kube_vip_endpoint": "10.66.1.5",
"kube_vip_arp": True,
"kube_vip_bgp": False,
},
)
if "value: 10.66.1.5" not in output:
fail("kube_vip_endpoint did not override the address")
if "value: 192.168.30.222" in output:
fail("apiserver_endpoint leaked into address when kube_vip_endpoint set")
print("kube-vip manifest regression test passed") print("kube-vip manifest regression test passed")
@@ -1,170 +0,0 @@
#!/usr/bin/env python3
"""Regression test for the MetalLB deploy conditions.
The MetalLB manifest (roles/k3s_server/tasks/main.yml) and the MetalLB pool
(roles/k3s_server_post/tasks/main.yml) are included under a `when` condition
that decides whether MetalLB provides load balancing. A previous change (#683)
guarded `cilium_bgp` but accidentally skipped MetalLB whenever a non-BGP
Cilium CNI was in use (`cilium_iface` defined), breaking the cilium + MetalLB
scenario.
This test loads the real `when` expressions from both task files and evaluates
them against representative variable sets, asserting MetalLB is deployed in
every topology except when kube-vip owns the VIP range or Cilium BGP is enabled.
"""
from __future__ import print_function
import os
import re
import subprocess
import yaml
from jinja2 import Environment
METALLB_WHEN = (
"kube_vip_lb_ip_range is not defined and "
"not (cilium_bgp | default(false) | bool)"
)
def repo_root():
return subprocess.check_output(
["git", "rev-parse", "--show-toplevel"], text=True
).strip()
def fail(message):
raise SystemExit("MetalLB deploy condition test failed: " + message)
def extract_when(path, task_name):
"""Return the `when:` expression string for the named task."""
with open(path, encoding="utf-8") as handle:
doc = yaml.safe_load(handle)
for task in doc:
if task.get("name") == task_name:
when = task.get("when")
return (when or "").strip()
return None
def evaluate(when, variables):
"""Evaluate a `when` expression against variables using Jinja2."""
env = Environment()
def fake_bool(value):
# Minimal stand-in for Ansible's truthiness filter used by `| bool`.
if isinstance(value, bool):
return value
if value is None:
return False
return str(value).lower() in ("1", "true", "yes", "on")
env.filters["bool"] = fake_bool
template = env.from_string("{{ " + when + " }}")
rendered = template.render(**variables)
# The expression renders to the literal strings "True"/"False".
if rendered == "True":
return True
if rendered == "False":
return False
fail("condition did not render to a boolean: {0!r}".format(rendered))
def assert_deployment(when, variables, expected, label):
result = evaluate(when, variables)
verdict = "deploy" if result else "skip"
expected_verdict = "deploy" if expected else "skip"
if result != expected:
fail(
"{0}: expected to {1} MetalLB but the condition chose to {2} "
"(vars: {3})".format(label, expected_verdict, verdict, variables)
)
def scenarios():
"""Yield (variables, expected_deploy, label) pairs."""
yield (
# Default Flannel inventory (all.yml sets cilium_bgp: false).
{
"cilium_bgp": False,
"cilium_iface": None,
},
True,
"flannel default (cilium_bgp: false)",
)
yield (
# Calico CNI with no Cilium variable in scope (issue #644): cilium_bgp
# is genuinely undefined, so `default(false)` must keep MetalLB on.
{
"calico_iface": "eth1",
},
True,
"calico, cilium_bgp undefined (#644)",
)
yield (
# Cilium CNI with BGP disabled: MetalLB must still be deployed.
{
"cilium_bgp": False,
"cilium_iface": "eth1",
},
True,
"cilium non-BGP (regression catch)",
)
yield (
# Cilium CNI with BGP enabled: Cilium provides the LB, skip MetalLB.
{
"cilium_bgp": True,
"cilium_iface": "eth1",
},
False,
"cilium BGP enabled",
)
yield (
# kube-vip is the load balancer provider: skip MetalLB.
{
"kube_vip_lb_ip_range": "192.168.30.80-192.168.30.90",
"cilium_bgp": False,
},
False,
"kube-vip owns the VIP range",
)
def main():
root = repo_root()
server_tasks = os.path.join(root, "roles", "k3s_server", "tasks", "main.yml")
server_post_tasks = os.path.join(
root, "roles", "k3s_server_post", "tasks", "main.yml"
)
server_when = extract_when(server_tasks, "Deploy metallb manifest")
server_post_when = extract_when(server_post_tasks, "Deploy metallb pool")
if server_when is None:
fail("could not find 'Deploy metallb manifest' when condition")
if server_post_when is None:
fail("could not find 'Deploy metallb pool' when condition")
for when, source in (
(server_when, "k3s_server/tasks/main.yml"),
(server_post_when, "k3s_server_post/tasks/main.yml"),
):
if when != METALLB_WHEN:
fail(
"{0} when condition changed unexpectedly:\n"
" expected: {1}\n got: {2}".format(source, METALLB_WHEN, when)
)
for variables, expected, label in scenarios():
assert_deployment(server_when, variables, expected, "server " + label)
assert_deployment(
server_post_when, variables, expected, "server_post " + label
)
print("MetalLB deploy condition regression test passed for all scenarios")
if __name__ == "__main__":
main()
@@ -1,88 +0,0 @@
#!/usr/bin/env python3
"""Regression test for the MetalLB L2Advertisement interfaces.
`metal_lb_interfaces` restricts which network interfaces MetalLB announces
load balancer IPs on in layer2 mode. When the list is non-empty, the
L2Advertisement in roles/k3s_server_post/templates/metallb.crs.j2 must render
a `spec.interfaces` block; when it is empty (the default), no spec is rendered
so MetalLB announces on all interfaces.
This renders the template and asserts both cases plus the BGP path (which must
not be affected by the L2 interfaces variable).
"""
from __future__ import print_function
import os
import subprocess
from jinja2 import Environment, FileSystemLoader, StrictUndefined
def repo_root():
return subprocess.check_output(
["git", "rev-parse", "--show-toplevel"], text=True
).strip()
def fail(message):
raise SystemExit("MetalLB interfaces test failed: " + message)
def render(env, extra_vars):
base_vars = {
"metal_lb_mode": "layer2",
"metal_lb_ip_range": "192.168.30.80-192.168.30.90",
}
base_vars.update(extra_vars)
template = env.get_template("metallb.crs.j2")
return template.render(**base_vars)
def main():
root = repo_root()
template_dir = os.path.join(
root, "roles", "k3s_server_post", "templates"
)
env = Environment(
loader=FileSystemLoader(template_dir), undefined=StrictUndefined
)
# Empty list (default): no spec.interfaces in the L2Advertisement.
output = render(env, {"metal_lb_interfaces": []})
if "spec:\n interfaces:" in output:
fail("spec.interfaces rendered with an empty metal_lb_interfaces")
if "kind: L2Advertisement" not in output:
fail("L2Advertisement missing in layer2 mode")
# Single interface.
output = render(env, {"metal_lb_interfaces": ["eth1"]})
if "spec:\n interfaces:\n - eth1" not in output:
fail("single interface was not rendered in spec.interfaces")
# Multiple interfaces.
output = render(env, {"metal_lb_interfaces": ["eth1", "eth2"]})
if "spec:\n interfaces:\n - eth1\n - eth2" not in output:
fail("multiple interfaces were not rendered in spec.interfaces")
# BGP mode must not emit an L2Advertisement spec at all.
output = render(
env,
{
"metal_lb_mode": "bgp",
"metal_lb_interfaces": ["eth1"],
"metal_lb_bgp_my_asn": "64513",
"metal_lb_bgp_peer_asn": "64512",
"metal_lb_bgp_peer_address": "192.168.30.1",
},
)
if "kind: L2Advertisement" in output:
fail("L2Advertisement rendered in bgp mode")
if "interfaces:" in output:
fail("interfaces rendered in bgp mode")
print("MetalLB interfaces regression test passed")
if __name__ == "__main__":
main()
-108
View File
@@ -1,108 +0,0 @@
#!/usr/bin/env python3
"""Regression test for the MetalLB converge checks.
The MetalLB tasks in roles/k3s_server_post/tasks/metallb.yml must actually
verify resources through an explicit kubectl get, and must retry on a
transient kube API error while MetalLB converges.
The "Test metallb-system namespace" task previously ran `k3s kubectl -n
metallb-system` with no subcommand, which only printed a usage page and always
exited 0, so it always succeeded even when the namespace did not exist (issue
#350). It must instead run an explicit `get namespace metallb-system`, which
returns non-zero when the namespace is absent.
An explicit get actually contacts the API server, so these tasks need the same
retry wiring as their siblings (register, until rc == 0, retries, delay). A
bare get with no retry would otherwise abort the converge play on a transient
kube API error while MetalLB converges.
"""
from __future__ import print_function
import os
import subprocess
import yaml
def repo_root():
return subprocess.check_output(
["git", "rev-parse", "--show-toplevel"], text=True
).strip()
def fail(message):
raise SystemExit("MetalLB namespace test failed: " + message)
def find_task(tasks, name):
for entry in tasks:
if entry.get("name") == name:
return entry
fail("could not find the '{0}' task".format(name))
return None
def command_text(task):
cmd = task.get("ansible.builtin.command")
if not cmd:
cmd = task.get("command")
if not cmd:
fail("task does not use ansible.builtin.command")
return cmd if isinstance(cmd, str) else " ".join(cmd)
def check_explicit_get(task, name, needle):
text = command_text(task)
if needle not in text:
fail(
"command does not run '{0}'; the task would only print usage and "
"never verify the resource (got: {1!r})".format(needle, text)
)
def check_retry_wiring(task, name):
# The sibling k3s_server_post metallb tasks retry kubectl because the kube
# API can briefly be unavailable while MetalLB converges. Without the same
# retry, a transient API error aborts the whole converge play.
if not task.get("register"):
fail(
"{0} does not register a result; without retry wiring a transient "
"kube API error aborts the converge play".format(name)
)
if not isinstance(task.get("until"), str) or "rc == 0" not in task["until"]:
fail(
"{0} does not retry on rc == 0; the kube API can transiently fail "
"while MetalLB converges and abort the play".format(name)
)
if task.get("retries") is None:
fail("{0} is missing retries".format(name))
if task.get("delay") is None:
fail("{0} is missing delay".format(name))
def main():
task_file = os.path.join(
repo_root(), "roles", "k3s_server_post", "tasks", "metallb.yml"
)
with open(task_file, encoding="utf-8") as handle:
tasks = yaml.safe_load(handle)
namespace_task = find_task(tasks, "Test metallb-system namespace")
# A bare `-n metallb-system` with no subcommand prints kubectl usage and
# always exits 0, so it never proves the namespace exists. The fix must
# use an explicit get.
check_explicit_get(namespace_task, "Test metallb-system namespace",
"get namespace metallb-system")
check_retry_wiring(namespace_task, "Test metallb-system namespace")
webhook_task = find_task(tasks, "Test metallb-system webhook-service endpoint")
check_explicit_get(webhook_task, "Test metallb-system webhook-service endpoint",
"get endpoints")
check_retry_wiring(webhook_task, "Test metallb-system webhook-service endpoint")
print("MetalLB namespace check regression test passed")
if __name__ == "__main__":
main()
@@ -1,28 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
repo_root="$(git rev-parse --show-toplevel)"
site_play="$repo_root/site.yml"
# #636: verify the "Pre tasks" play asserts that all k3s_cluster hosts have
# unique hostnames, so a duplicate-hostname inventory fails fast instead of
# silently breaking node registration/joining.
grep -Fq -- 'Verify all cluster nodes have unique hostnames' "$site_play" || {
printf 'site.yml is missing the unique-hostname preflight check\n' >&2
exit 1
}
# The check must deduplicate the cluster hostname list via the `unique` filter
# and compare lengths, i.e. groups['k3s_cluster'] must be referenced.
grep -Fq -- "groups['k3s_cluster']" "$site_play" || {
printf 'unique-hostname check does not iterate the k3s_cluster group\n' >&2
exit 1
}
if ! grep -Eq -- 'cluster_hostnames.*\|.*unique|\| unique' "$site_play"; then
printf 'unique-hostname check does not deduplicate the hostname list\n' >&2
exit 1
fi
printf 'Unique hostname preflight regression test passed\n'
-3
View File
@@ -32,13 +32,10 @@ concurrency:
jobs: jobs:
pre: pre:
if: github.actor != 'dependabot[bot]'
uses: ./.github/workflows/cache.yml uses: ./.github/workflows/cache.yml
needs: [lint] needs: [lint]
lint: lint:
if: github.actor != 'dependabot[bot]'
uses: ./.github/workflows/lint.yml uses: ./.github/workflows/lint.yml
test: test:
if: github.actor != 'dependabot[bot]'
uses: ./.github/workflows/test.yml uses: ./.github/workflows/test.yml
needs: [pre, lint] needs: [pre, lint]
+1 -1
View File
@@ -88,7 +88,7 @@ jobs:
trap stop_monitor EXIT trap stop_monitor EXIT
/usr/bin/time -v -o "$timing_file" \ /usr/bin/time -v -o "$timing_file" \
molecule test --scenario-name ${{ matrix.scenario }} molecule test --scenario-name ${{ matrix.scenario }}
timeout-minutes: 180 timeout-minutes: 150
env: env:
ANSIBLE_K3S_LOG_DIR: ${{ runner.temp }}/logs/k3s-ansible/${{ matrix.scenario }} ANSIBLE_K3S_LOG_DIR: ${{ runner.temp }}/logs/k3s-ansible/${{ matrix.scenario }}
ANSIBLE_SSH_RETRIES: 4 ANSIBLE_SSH_RETRIES: 4
-46
View File
@@ -62,18 +62,6 @@ repos:
language: system language: system
pass_filenames: false pass_filenames: false
files: ^roles/k3s_server/tasks/(main|join_master)\.yml$|^\.github/scripts/test-k3s-server-bootstrap\.sh$ files: ^roles/k3s_server/tasks/(main|join_master)\.yml$|^\.github/scripts/test-k3s-server-bootstrap\.sh$
- id: unique-hostname-precheck-test
name: Unique hostname precheck test
entry: .github/scripts/test-unique-hostname-precheck.sh
language: system
pass_filenames: false
files: ^site\.yml$|^\.github/scripts/test-unique-hostname-precheck\.sh$
- id: disable-swap-test
name: Disable swap test
entry: .github/scripts/test-disable-swap.sh
language: system
pass_filenames: false
files: ^roles/prereq/(tasks/main|defaults/main)\.yml$|^\.github/scripts/test-disable-swap\.sh$
- id: cilium-bgp-manifest-test - id: cilium-bgp-manifest-test
name: Cilium BGP manifest test name: Cilium BGP manifest test
entry: python3 .github/scripts/test-cilium-bgp-manifest.py entry: python3 .github/scripts/test-cilium-bgp-manifest.py
@@ -82,15 +70,6 @@ repos:
- Jinja2>=3.1 - Jinja2>=3.1
pass_filenames: false pass_filenames: false
files: ^roles/k3s_server_post/templates/cilium\.crs\.j2$|^\.github/scripts/test-cilium-bgp-manifest\.py$ 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 - id: kube-vip-manifest-test
name: kube-vip manifest test name: kube-vip manifest test
entry: python3 .github/scripts/test-kube-vip-manifest.py entry: python3 .github/scripts/test-kube-vip-manifest.py
@@ -105,31 +84,6 @@ repos:
language: system language: system
pass_filenames: false pass_filenames: false
files: ^roles/k3s_server/tasks/metallb\.yml$|^\.github/scripts/test-metallb-remote-read\.sh$ files: ^roles/k3s_server/tasks/metallb\.yml$|^\.github/scripts/test-metallb-remote-read\.sh$
- id: metallb-interfaces-test
name: MetalLB interfaces test
entry: python3 .github/scripts/test-metallb-interfaces.py
language: python
additional_dependencies:
- Jinja2>=3.1
pass_filenames: false
files: ^roles/k3s_server_post/templates/metallb\.crs\.j2$|^\.github/scripts/test-metallb-interfaces\.py$
- id: metallb-namespace-test
name: MetalLB namespace test
entry: python3 .github/scripts/test-metallb-namespace.py
language: python
additional_dependencies:
- PyYAML
pass_filenames: false
files: ^roles/k3s_server_post/tasks/metallb\.yml$|^\.github/scripts/test-metallb-namespace\.py$
- id: metallb-deploy-condition-test
name: MetalLB deploy condition test
entry: python3 .github/scripts/test-metallb-deploy-condition.py
language: python
additional_dependencies:
- Jinja2>=3.1
- PyYAML
pass_filenames: false
files: ^roles/k3s_server/tasks/main\.yml$|^roles/k3s_server_post/tasks/main\.yml$|^\.github/scripts/test-metallb-deploy-condition\.py$ # noqa yaml[line-length]
- id: default-interface-test - id: default-interface-test
name: default interface test name: default interface test
entry: python3 .github/scripts/test-default-interface.py entry: python3 .github/scripts/test-default-interface.py
-26
View File
@@ -50,10 +50,6 @@ Supported processor architectures are:
- Server and agent nodes should support passwordless SSH access. Otherwise, pass `--ask-pass --ask-become-pass` to - Server and agent nodes should support passwordless SSH access. Otherwise, pass `--ask-pass --ask-become-pass` to
each playbook command. each playbook command.
- Every node in the cluster must have a **unique hostname**. k3s registers each node keyed by its hostname, so
two nodes with the same hostname cannot join the cluster. `site.yml` asserts this up front and fails fast if
any duplicate is found.
## 🚀 Getting Started ## 🚀 Getting Started
### 🍴 Preparation ### 🍴 Preparation
@@ -112,24 +108,6 @@ ansible-playbook reset.yml -i inventory/my-cluster/hosts.ini
> Reboot the nodes after reset because the virtual IP may remain configured. > Reboot the nodes after reset because the virtual IP may remain configured.
### ⏻️ Reboot Cluster Nodes
Reboot all cluster nodes at once or stage the reboot across the cluster.
```bash
ansible-playbook reboot.yml -i inventory/my-cluster/hosts.ini
```
To reboot the nodes in batches, set `concurrent_reboots` to the number of nodes
to reboot at a time (or a percentage). Optionally set `wait_seconds_after_reboot`
to pause after each batch so pods in the freshly rebooted batch can settle
before the next batch reboots.
```bash
ansible-playbook reboot.yml -i inventory/my-cluster/hosts.ini \
--extra-vars 'concurrent_reboots=2 wait_seconds_after_reboot=30'
```
## 🔁 Upgrading an existing cluster ## 🔁 Upgrading an existing cluster
These version variables select the components used for a **fresh** installation. These version variables select the components used for a **fresh** installation.
@@ -219,7 +197,6 @@ See the commands [here](https://technotim.com/posts/k3s-etcd-ansible/#testing-yo
| `k3s_server` | `kube_vip_bgp_peers` | list | `[]` | Not required | List of BGP peer ASN & address pairs | | `k3s_server` | `kube_vip_bgp_peers` | list | `[]` | Not required | List of BGP peer ASN & address pairs |
| `k3s_server` | `kube_vip_bgp_peers_groups` | list | `['k3s_master']` | Not required | Inventory group in which to search for additional `kube_vip_bgp_peers` parameters to merge. | | `k3s_server` | `kube_vip_bgp_peers_groups` | list | `['k3s_master']` | Not required | Inventory group in which to search for additional `kube_vip_bgp_peers` parameters to merge. |
| `k3s_server` | `kube_vip_iface` | string | `~` | Not required | Explicitly define an interface that ALL control nodes should use to propagate the VIP, define it here. Otherwise, kube-vip will determine the right interface automatically at runtime. | | `k3s_server` | `kube_vip_iface` | string | `~` | Not required | Explicitly define an interface that ALL control nodes should use to propagate the VIP, define it here. Otherwise, kube-vip will determine the right interface automatically at runtime. |
| `k3s_server` | `kube_vip_endpoint` | string | `~` | Not required | Overrides the internal address kube-vip binds/listens on, which can differ from the announced apiserver_endpoint for complex routing/tunnels. Defaults to apiserver_endpoint. |
| `k3s_server` | `kube_vip_tag_version` | string | `v1.2.2` | Not required | Image tag for kube-vip | | `k3s_server` | `kube_vip_tag_version` | string | `v1.2.2` | Not required | Image tag for kube-vip |
| `k3s_server` | `kube_vip_cloud_provider_tag_version` | string | `v0.0.12` | Not required | Tag for kube-vip-cloud-provider manifest when enable | | `k3s_server` | `kube_vip_cloud_provider_tag_version` | string | `v0.0.12` | Not required | Tag for kube-vip-cloud-provider manifest when enable |
| `k3s_server`, `k3_server_post` | `kube_vip_lb_ip_range` | string | `~` | Not required | IP range for kube-vip load balancer | | `k3s_server`, `k3_server_post` | `kube_vip_lb_ip_range` | string | `~` | Not required | IP range for kube-vip load balancer |
@@ -259,10 +236,7 @@ See the commands [here](https://technotim.com/posts/k3s-etcd-ansible/#testing-yo
| `k3s_server_post` | `metal_lb_bgp_peer_asn` | string | `~` | Not required | BGP peer ASN configurations | | `k3s_server_post` | `metal_lb_bgp_peer_asn` | string | `~` | Not required | BGP peer ASN configurations |
| `k3s_server_post` | `metal_lb_bgp_peer_address` | string | `~` | Not required | BGP peer address | | `k3s_server_post` | `metal_lb_bgp_peer_address` | string | `~` | Not required | BGP peer address |
| `lxc` | `custom_reboot_command` | string | `~` | Not required | Command to run on reboot | | `lxc` | `custom_reboot_command` | string | `~` | Not required | Command to run on reboot |
| `reboot` (playbook) | `concurrent_reboots` | int/string | `100%` | Not required | Number (or percentage) of nodes to reboot at a time for a staggered reboot |
| `reboot` (playbook) | `wait_seconds_after_reboot` | int | `0` | Not required | Pause in seconds between staggered reboot batches |
| `prereq` | `system_timezone` | string | `null` | Not required | Timezone to be set on all nodes | | `prereq` | `system_timezone` | string | `null` | Not required | Timezone to be set on all nodes |
| `prereq` | `disable_swap` | bool | `true` | Not required | Disable swap on all cluster nodes (swapoff + comment out /etc/fstab swap entries), all-or-nothing |
| `proxmox_lxc`, `reset_proxmox_lxc` | `proxmox_lxc_ct_ids` | list | ❌ | Required | Proxmox container ID list | | `proxmox_lxc`, `reset_proxmox_lxc` | `proxmox_lxc_ct_ids` | list | ❌ | Required | Proxmox container ID list |
| `raspberrypi` | `state` | string | `present` | Not required | Indicates whether the k3s prerequisites for Raspberry Pi should be set up (possible values are `present` and `absent`) | | `raspberrypi` | `state` | string | `present` | Not required | Indicates whether the k3s prerequisites for Raspberry Pi should be set up (possible values are `present` and `absent`) |
-19
View File
@@ -7,10 +7,6 @@ systemd_dir: /etc/systemd/system
# Set your timezone # Set your timezone
system_timezone: Your/Timezone system_timezone: Your/Timezone
# k3s recommends swap be disabled on every cluster node. Applied uniformly to all
# nodes (all-or-nothing) in the prereq role. Set to false to leave swap enabled.
disable_swap: true
# interface which will be used for flannel # interface which will be used for flannel
# Defaults to each host's default IPv4 interface (e.g. eth0, enp1s0, ens3) # Defaults to each host's default IPv4 interface (e.g. eth0, enp1s0, ens3)
# so KVM/cloud hosts without eth0 work out of the box. Override per-host if needed. # so KVM/cloud hosts without eth0 work out of the box. Override per-host if needed.
@@ -28,10 +24,6 @@ cilium_mode: native # native when nodes are on the same subnet or use BGP, other
cilium_tag: v1.20.0 # cilium version tag cilium_tag: v1.20.0 # cilium version tag
cilium_cli_tag: v0.19.7 # cilium cli version tag cilium_cli_tag: v0.19.7 # cilium cli version tag
cilium_hubble: true # enable hubble observability relay and ui 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 # if using calico or cilium, you may specify the cluster pod cidr pool
cluster_cidr: 10.52.0.0/16 cluster_cidr: 10.52.0.0/16
@@ -48,11 +40,6 @@ cilium_bgp_lb_cidr: 192.168.31.0/24 # cidr for cilium loadbalancer ipam
# enable kube-vip ARP broadcasts # enable kube-vip ARP broadcasts
kube_vip_arp: true kube_vip_arp: true
# (optional) overrides the address kube-vip binds/listens on internally, which
# can differ from the announced apiserver_endpoint for complex routing/tunnels.
# Defaults to apiserver_endpoint. Also used to derive the kube-vip subnet.
# kube_vip_endpoint: 10.66.1.5
# enable kube-vip BGP peering # enable kube-vip BGP peering
kube_vip_bgp: false kube_vip_bgp: false
@@ -129,12 +116,6 @@ metal_lb_controller_tag_version: v0.16.0
# metallb ip range for load balancer # metallb ip range for load balancer
metal_lb_ip_range: 192.168.30.80-192.168.30.90 metal_lb_ip_range: 192.168.30.80-192.168.30.90
# (optional) limit MetalLB layer2 announcements to specific network interfaces.
# Leave empty (default) to announce on all interfaces.
# metal_lb_interfaces:
# - eth1
# - eth2
# Only enable if your nodes are proxmox LXC nodes, make sure to configure your proxmox nodes # Only enable if your nodes are proxmox LXC nodes, make sure to configure your proxmox nodes
# in your hosts.ini file. # in your hosts.ini file.
# Please read https://gist.github.com/triangletodd/02f595cd4c0dc9aac5f7763ca2264185 before using this. # Please read https://gist.github.com/triangletodd/02f595cd4c0dc9aac5f7763ca2264185 before using this.
@@ -103,7 +103,7 @@
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- calico_node_ds.resources | length == 1 - calico_node_ds.resources | length == 1
- calico_node_image | regex_search(':' ~ calico_tag) is not none - calico_node_image | regex_search(':' ~ calico_tag)
success_msg: "Calico node image uses tag {{ calico_tag }}" success_msg: "Calico node image uses tag {{ calico_tag }}"
fail_msg: >- fail_msg: >-
Calico node image {{ calico_node_image }}, Calico node image {{ calico_node_image }},
@@ -169,8 +169,8 @@
- name: Assert Cilium agent and operator use the expected image tag - name: Assert Cilium agent and operator use the expected image tag
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- cilium_agent_image | regex_search(':' ~ cilium_tag) is not none - cilium_agent_image | regex_search(':' ~ cilium_tag)
- cilium_operator_image | regex_search(':' ~ cilium_tag) is not none - cilium_operator_image | regex_search(':' ~ cilium_tag)
success_msg: "Cilium agent and operator use {{ cilium_tag }}" success_msg: "Cilium agent and operator use {{ cilium_tag }}"
fail_msg: >- fail_msg: >-
Cilium agent {{ cilium_agent_image }}, Cilium agent {{ cilium_agent_image }},
@@ -265,12 +265,9 @@
- name: Assert MetalLB controller and speaker use the expected image tags - name: Assert MetalLB controller and speaker use the expected image tags
ansible.builtin.assert: ansible.builtin.assert:
# regex_search returns a string or none; check for a match with `is not
# none` so the assertion is a real boolean (ansible-core 2.19 rejects
# string conditionals and `| bool` deprecates string coercion).
that: that:
- controller_image | regex_search(metal_lb_controller_tag_version) is not none - controller_image | regex_search(metal_lb_controller_tag_version)
- speaker_image | regex_search(metal_lb_speaker_tag_version) is not none - speaker_image | regex_search(metal_lb_speaker_tag_version)
success_msg: >- success_msg: >-
MetalLB controller {{ metal_lb_controller_tag_version }}, MetalLB controller {{ metal_lb_controller_tag_version }},
speaker {{ metal_lb_speaker_tag_version }} speaker {{ metal_lb_speaker_tag_version }}
@@ -314,8 +311,8 @@
- name: Assert the kube-vip and cloud provider image tags - name: Assert the kube-vip and cloud provider image tags
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- kubevip_image | regex_search(':' ~ kube_vip_tag_version) is not none - kubevip_image | regex_search(':' ~ kube_vip_tag_version)
- cloud_provider_image | regex_search(verify_kube_vip_cloud_provider_tag) is not none - cloud_provider_image | regex_search(verify_kube_vip_cloud_provider_tag)
success_msg: >- success_msg: >-
kube-vip {{ kube_vip_tag_version }}, kube-vip {{ kube_vip_tag_version }},
cloud provider {{ verify_kube_vip_cloud_provider_tag }} cloud provider {{ verify_kube_vip_cloud_provider_tag }}
+1 -21
View File
@@ -2,29 +2,9 @@
- name: Reboot k3s_cluster - name: Reboot k3s_cluster
hosts: k3s_cluster hosts: k3s_cluster
gather_facts: true gather_facts: true
# Stagger the reboot across the cluster when concurrent_reboots is set.
# Defaults to '100%' so the whole cluster reboots at once (backward compatible).
serial: "{{ concurrent_reboots | default('100%') }}"
tasks: tasks:
- name: >- - name: Reboot the nodes (and Wait upto 5 mins max)
{{
'Reboot all nodes at once'
if (concurrent_reboots is not defined)
else 'Reboot nodes with concurrency of ' ~ concurrent_reboots
}}
become: true become: true
ansible.builtin.reboot: ansible.builtin.reboot:
reboot_command: "{{ custom_reboot_command | default(omit) }}" reboot_command: "{{ custom_reboot_command | default(omit) }}"
reboot_timeout: 300 reboot_timeout: 300
test_command: >-
{{ 'kubectl get nodes' if 'master' in group_names else 'whoami' }}
- name: Optional wait before rebooting the next batch of nodes
ansible.builtin.pause:
seconds: "{{ wait_seconds_after_reboot | int }}"
when: >-
concurrent_reboots is defined and
wait_seconds_after_reboot is defined and
wait_seconds_after_reboot | int > 0
+2 -2
View File
@@ -1,5 +1,5 @@
ansible-core>=2.19.11 ansible-core>=2.16.2
jmespath>=1.1.0 jmespath>=1.0.1
jsonpatch>=1.33 jsonpatch>=1.33
kubernetes>=29.0.0 kubernetes>=29.0.0
molecule-plugins[vagrant] molecule-plugins[vagrant]
+5 -5
View File
@@ -2,11 +2,11 @@
# This file is autogenerated by pip-compile with Python 3.11 # This file is autogenerated by pip-compile with Python 3.11
# by the following command: # by the following command:
# #
# pip-compile --output-file=requirements.txt requirements.in # pip-compile requirements.in
# #
ansible-compat==4.1.11 ansible-compat==4.1.11
# via molecule # via molecule
ansible-core==2.19.11 ansible-core==2.18.0
# via # via
# -r requirements.in # -r requirements.in
# ansible-compat # ansible-compat
@@ -53,7 +53,7 @@ jinja2==3.1.3
# via # via
# ansible-core # ansible-core
# molecule # molecule
jmespath==1.1.0 jmespath==1.0.1
# via -r requirements.in # via -r requirements.in
jsonpatch==1.33 jsonpatch==1.33
# via -r requirements.in # via -r requirements.in
@@ -77,7 +77,7 @@ molecule==6.0.3
# via # via
# -r requirements.in # -r requirements.in
# molecule-plugins # molecule-plugins
molecule-plugins[vagrant]==23.6.0 molecule-plugins[vagrant]==23.5.3
# via -r requirements.in # via -r requirements.in
netaddr==0.10.1 netaddr==0.10.1
# via -r requirements.in # via -r requirements.in
@@ -146,7 +146,7 @@ rsa==4.9
# via google-auth # via google-auth
ruamel-yaml==0.18.5 ruamel-yaml==0.18.5
# via pre-commit-hooks # via pre-commit-hooks
ruamel-yaml-clib==0.2.15 ruamel-yaml-clib==0.2.8
# via ruamel-yaml # via ruamel-yaml
six==1.16.0 six==1.16.0
# via # via
-7
View File
@@ -7,7 +7,6 @@ group_name_master: master
kube_vip_arp: true kube_vip_arp: true
kube_vip_iface: kube_vip_iface:
kube_vip_endpoint:
kube_vip_cloud_provider_tag_version: v0.0.12 kube_vip_cloud_provider_tag_version: v0.0.12
kube_vip_tag_version: v1.2.2 kube_vip_tag_version: v1.2.2
@@ -24,12 +23,6 @@ metal_lb_controller_tag_version: v0.16.0
metal_lb_speaker_tag_version: v0.16.0 metal_lb_speaker_tag_version: v0.16.0
metal_lb_type: native metal_lb_type: native
# Shared retry/delay for remote manifest and asset downloads. The CI runner's
# resolver intermittently times out on GitHub-hosted domains (helm.cilium.io,
# raw.githubusercontent.com, github.com), so retry transient DNS/network failures.
download_retries: 5
download_delay: 10
retry_count: 20 retry_count: 20
# yamllint disable rule:line-length # yamllint disable rule:line-length
-9
View File
@@ -78,15 +78,6 @@ argument_specs:
- automatically at runtime. - automatically at runtime.
default: ~ default: ~
kube_vip_endpoint:
description:
- Overrides the address kube-vip binds/listens on internally, which
- can differ from the announced apiserver_endpoint for complex
- routing and site-to-site tunnels.
- Defaults to apiserver_endpoint and is used to derive the kube-vip
- subnet.
default: ~
kube_vip_tag_version: kube_vip_tag_version:
description: Image tag for kube-vip description: Image tag for kube-vip
default: v1.2.2 default: v1.2.2
-4
View File
@@ -15,10 +15,6 @@
owner: root owner: root
group: root group: root
mode: "0644" mode: "0644"
register: kube_vip_manifest_download
retries: "{{ download_retries }}"
delay: "{{ download_delay }}"
until: kube_vip_manifest_download is succeeded
when: ansible_hostname == hostvars[groups[group_name_master | default('master')][0]]['ansible_hostname'] when: ansible_hostname == hostvars[groups[group_name_master | default('master')][0]]['ansible_hostname']
- name: Copy kubevip configMap manifest to first master - name: Copy kubevip configMap manifest to first master
+2 -9
View File
@@ -27,11 +27,7 @@
- name: Deploy metallb manifest - name: Deploy metallb manifest
ansible.builtin.include_tasks: metallb.yml ansible.builtin.include_tasks: metallb.yml
tags: metallb tags: metallb
# Deploy MetalLB unless kube-vip owns the load balancer IP range, or Cilium when: kube_vip_lb_ip_range is not defined and (cilium_bgp is not defined or cilium_iface is not defined)
# BGP is enabled (Cilium then provides its own load balancing). The cilium_bgp
# default keeps this safe when Cilium variables are not in scope at all (#644)
# while still deploying MetalLB when a non-BGP Cilium CNI is in use.
when: kube_vip_lb_ip_range is not defined and not (cilium_bgp | default(false) | bool)
- name: Deploy kube-vip manifest - name: Deploy kube-vip manifest
ansible.builtin.include_tasks: kube-vip.yml ansible.builtin.include_tasks: kube-vip.yml
@@ -122,10 +118,7 @@
always: always:
- name: Save logs of k3s-init.service - name: Save logs of k3s-init.service
ansible.builtin.include_tasks: fetch_k3s_init_logs.yml ansible.builtin.include_tasks: fetch_k3s_init_logs.yml
# ANSIBLE_K3S_LOG_DIR is a path string when set; evaluate it as a boolean when: log_destination
# so the conditional is a real boolean (ansible-core 2.19 rejects string
# conditionals derived from env vars).
when: log_destination | default('') != ''
vars: vars:
log_destination: >- log_destination: >-
{{ lookup('ansible.builtin.env', 'ANSIBLE_K3S_LOG_DIR', default=False) }} {{ lookup('ansible.builtin.env', 'ANSIBLE_K3S_LOG_DIR', default=False) }}
-4
View File
@@ -15,10 +15,6 @@
owner: root owner: root
group: root group: root
mode: "0644" mode: "0644"
register: metallb_manifest_download
retries: "{{ download_retries }}"
delay: "{{ download_delay }}"
until: metallb_manifest_download is succeeded
when: ansible_hostname == hostvars[groups[group_name_master | default('master')][0]]['ansible_hostname'] when: ansible_hostname == hostvars[groups[group_name_master | default('master')][0]]['ansible_hostname']
- name: Set image versions in manifest for metallb-{{ metal_lb_type }} - name: Set image versions in manifest for metallb-{{ metal_lb_type }}
+2 -2
View File
@@ -37,7 +37,7 @@ spec:
value: {{ kube_vip_iface }} value: {{ kube_vip_iface }}
{% endif %} {% endif %}
- name: vip_subnet - name: vip_subnet
value: "{{ (kube_vip_endpoint | default(apiserver_endpoint, true)) | ansible.utils.ipsubnet | ansible.utils.ipaddr('prefix') }}" value: "{{ apiserver_endpoint | ansible.utils.ipsubnet | ansible.utils.ipaddr('prefix') }}"
- name: cp_enable - name: cp_enable
value: "true" value: "true"
- name: cp_namespace - name: cp_namespace
@@ -55,7 +55,7 @@ spec:
- name: vip_retryperiod - name: vip_retryperiod
value: "2" value: "2"
- name: address - name: address
value: {{ kube_vip_endpoint | default(apiserver_endpoint, true) }} value: {{ apiserver_endpoint }}
{% if kube_vip_bgp | default(false) | bool %} {% if kube_vip_bgp | default(false) | bool %}
{% if kube_vip_bgp_routerid is defined %} {% if kube_vip_bgp_routerid is defined %}
- name: bgp_routerid - name: bgp_routerid
-9
View File
@@ -18,19 +18,11 @@ cilium_bgp_peer_asn: 64512
cilium_bgp_neighbors: [] cilium_bgp_neighbors: []
cilium_bgp_neighbors_groups: ['k3s_all'] cilium_bgp_neighbors_groups: ['k3s_all']
cilium_bgp_lb_cidr: 192.168.31.0/24 cilium_bgp_lb_cidr: 192.168.31.0/24
cilium_envoy: true
cilium_hubble: true cilium_hubble: true
cilium_mode: native cilium_mode: native
cilium_tag: v1.20.0 cilium_tag: v1.20.0
cilium_cli_tag: v0.19.7 cilium_cli_tag: v0.19.7
# Shared retry/delay for remote manifest, asset downloads, and waiting on
# Kubernetes resources. The CI runner's resolver intermittently times out on
# GitHub-hosted domains and the kube API can transiently return
# ServiceUnavailable, so retry transient DNS/network/API failures.
download_retries: 5
download_delay: 10
cluster_cidr: 10.52.0.0/16 cluster_cidr: 10.52.0.0/16
enable_bpf_masquerade: true enable_bpf_masquerade: true
kube_proxy_replacement: true kube_proxy_replacement: true
@@ -39,5 +31,4 @@ group_name_master: master
metal_lb_mode: layer2 metal_lb_mode: layer2
metal_lb_available_timeout: 240s metal_lb_available_timeout: 240s
metal_lb_controller_tag_version: v0.16.0 metal_lb_controller_tag_version: v0.16.0
metal_lb_interfaces: []
metal_lb_ip_range: 192.168.30.80-192.168.30.90 metal_lb_ip_range: 192.168.30.80-192.168.30.90
-8
View File
@@ -141,14 +141,6 @@ argument_specs:
description: MetalLB ip range for load balancer description: MetalLB ip range for load balancer
default: 192.168.30.80-192.168.30.90 default: 192.168.30.80-192.168.30.90
metal_lb_interfaces:
description: >-
List of network interfaces on which MetalLB should announce the
load balancer IPs in layer2 mode. When empty (default), MetalLB
announces on all interfaces.
type: list
default: []
metal_lb_controller_tag_version: metal_lb_controller_tag_version:
description: Image tag for MetalLB description: Image tag for MetalLB
default: v0.16.0 default: v0.16.0
-8
View File
@@ -18,10 +18,6 @@
owner: root owner: root
group: root group: root
mode: "0755" mode: "0755"
register: calico_crd_download
retries: "{{ download_retries }}"
delay: "{{ download_delay }}"
until: calico_crd_download is succeeded
- name: "Download to first master: manifest for Tigera Operator and Calico CRDs" - name: "Download to first master: manifest for Tigera Operator and Calico CRDs"
ansible.builtin.get_url: ansible.builtin.get_url:
@@ -30,10 +26,6 @@
owner: root owner: root
group: root group: root
mode: "0755" mode: "0755"
register: tigera_operator_download
retries: "{{ download_retries }}"
delay: "{{ download_delay }}"
until: tigera_operator_download is succeeded
- name: Apply Calico CRD bundle with server-side apply - name: Apply Calico CRD bundle with server-side apply
ansible.builtin.command: >- ansible.builtin.command: >-
-12
View File
@@ -66,10 +66,6 @@
- .tar.gz.sha256sum - .tar.gz.sha256sum
vars: vars:
cilium_base_url: https://github.com/cilium/cilium-cli/releases/download/{{ cilium_cli_tag }} cilium_base_url: https://github.com/cilium/cilium-cli/releases/download/{{ cilium_cli_tag }}
register: cilium_cli_download
retries: "{{ download_retries }}"
delay: "{{ download_delay }}"
until: cilium_cli_download is succeeded
- name: Verify the downloaded tarball - name: Verify the downloaded tarball
ansible.builtin.shell: | ansible.builtin.shell: |
@@ -178,7 +174,6 @@
--helm-set hubble.enabled={{ "true" if cilium_hubble else "false" }} --helm-set hubble.enabled={{ "true" if cilium_hubble else "false" }}
--helm-set hubble.relay.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 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 %} {% if kube_proxy_replacement is not false %}
--helm-set loadBalancer.algorithm={{ bpf_lb_algorithm }} --helm-set loadBalancer.algorithm={{ bpf_lb_algorithm }}
--helm-set loadBalancer.mode={{ bpf_lb_mode }} --helm-set loadBalancer.mode={{ bpf_lb_mode }}
@@ -187,13 +182,6 @@
KUBECONFIG: "{{ ansible_user_dir }}/.kube/config" KUBECONFIG: "{{ ansible_user_dir }}/.kube/config"
register: cilium_install_result register: cilium_install_result
changed_when: cilium_install_result.rc == 0 changed_when: cilium_install_result.rc == 0
# cilium install/upgrade fetches the Helm chart from helm.cilium.io, which is
# fronted by GitHub Pages and can transiently fail DNS resolution through the
# host resolver (intermittent "lookup helm.cilium.io ... i/o timeout"). Retry
# so a transient name/network failure does not abort the whole converge play.
until: cilium_install_result.rc == 0
retries: "{{ download_retries }}"
delay: "{{ download_delay }}"
when: cilium_installed.rc != 0 or cilium_needs_update when: cilium_installed.rc != 0 or cilium_needs_update
- name: Wait for Cilium resources - name: Wait for Cilium resources
+1 -5
View File
@@ -12,11 +12,7 @@
- name: Deploy metallb pool - name: Deploy metallb pool
ansible.builtin.include_tasks: metallb.yml ansible.builtin.include_tasks: metallb.yml
tags: metallb tags: metallb
# Deploy MetalLB unless kube-vip owns the load balancer IP range, or Cilium when: kube_vip_lb_ip_range is not defined and (cilium_bgp is not defined or cilium_iface is not defined)
# BGP is enabled (Cilium then provides its own load balancing). The cilium_bgp
# default keeps this safe when Cilium variables are not in scope at all (#644)
# while still deploying MetalLB when a non-BGP Cilium CNI is in use.
when: kube_vip_lb_ip_range is not defined and not (cilium_bgp | default(false) | bool)
- name: Remove tmp directory used for manifests - name: Remove tmp directory used for manifests
ansible.builtin.file: ansible.builtin.file:
+1 -27
View File
@@ -40,14 +40,8 @@
- name: Test metallb-system namespace - name: Test metallb-system namespace
ansible.builtin.command: >- ansible.builtin.command: >-
{{ k3s_kubectl_binary | default('k3s kubectl') }} get namespace metallb-system {{ k3s_kubectl_binary | default('k3s kubectl') }} -n metallb-system
changed_when: false changed_when: false
# The kube API can briefly return ServiceUnavailable while MetalLB converges,
# which would otherwise abort the whole converge play on a transient error.
register: metallb_namespace_result
until: metallb_namespace_result.rc == 0
retries: "{{ download_retries }}"
delay: "{{ download_delay }}"
with_items: "{{ groups[group_name_master | default('master')] }}" with_items: "{{ groups[group_name_master | default('master')] }}"
run_once: true run_once: true
@@ -60,12 +54,6 @@
{% if item.condition | default(False) -%}{{ item.condition }}{%- endif %} {% if item.condition | default(False) -%}{{ item.condition }}{%- endif %}
--timeout='{{ metal_lb_available_timeout }}' --timeout='{{ metal_lb_available_timeout }}'
changed_when: false changed_when: false
# The kube API can briefly return ServiceUnavailable while MetalLB converges,
# which would otherwise abort the whole converge play on a transient error.
register: metallb_wait_result
until: metallb_wait_result.rc == 0
retries: "{{ download_retries }}"
delay: "{{ download_delay }}"
run_once: true run_once: true
with_items: with_items:
- description: controller - description: controller
@@ -105,12 +93,6 @@
ansible.builtin.command: >- ansible.builtin.command: >-
{{ k3s_kubectl_binary | default('k3s kubectl') }} -n metallb-system get endpoints {{ metallb_webhook_service_name }} {{ k3s_kubectl_binary | default('k3s kubectl') }} -n metallb-system get endpoints {{ metallb_webhook_service_name }}
changed_when: false changed_when: false
# The kube API can briefly return ServiceUnavailable while MetalLB converges,
# which would otherwise abort the whole converge play on a transient error.
register: metallb_webhook_result
until: metallb_webhook_result.rc == 0
retries: "{{ download_retries }}"
delay: "{{ download_delay }}"
with_items: "{{ groups[group_name_master | default('master')] }}" with_items: "{{ groups[group_name_master | default('master')] }}"
run_once: true run_once: true
@@ -130,10 +112,6 @@
changed_when: false changed_when: false
run_once: true run_once: true
when: metal_lb_mode == "layer2" when: metal_lb_mode == "layer2"
register: metallb_l2_test_result
until: metallb_l2_test_result.rc == 0
retries: "{{ download_retries }}"
delay: "{{ download_delay }}"
with_items: with_items:
- IPAddressPool - IPAddressPool
- L2Advertisement - L2Advertisement
@@ -144,10 +122,6 @@
changed_when: false changed_when: false
run_once: true run_once: true
when: metal_lb_mode == "bgp" when: metal_lb_mode == "bgp"
register: metallb_bgp_test_result
until: metallb_bgp_test_result.rc == 0
retries: "{{ download_retries }}"
delay: "{{ download_delay }}"
with_items: with_items:
- IPAddressPool - IPAddressPool
- BGPPeer - BGPPeer
@@ -21,11 +21,6 @@ kind: L2Advertisement
metadata: metadata:
name: default name: default
namespace: metallb-system namespace: metallb-system
{% if metal_lb_interfaces | default([]) | length > 0 %}
spec:
interfaces:{% for iface in metal_lb_interfaces %}
- {{ iface }}{% endfor %}
{% endif %}
{% endif %} {% endif %}
{% if metal_lb_mode == "bgp" %} {% if metal_lb_mode == "bgp" %}
--- ---
-2
View File
@@ -1,6 +1,4 @@
--- ---
disable_swap: true
secure_path: secure_path:
RedHat: /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin RedHat: /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
Suse: /usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin Suse: /usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin
-22
View File
@@ -4,28 +4,6 @@
name: "{{ system_timezone }}" name: "{{ system_timezone }}"
when: (system_timezone is defined) and (system_timezone != "Your/Timezone") when: (system_timezone is defined) and (system_timezone != "Your/Timezone")
# k3s recommends swap be disabled on all nodes. Disabling swap is all-or-nothing
# across the cluster: leaving it enabled on some nodes but not others creates
# uneven scheduling/latency behavior. This block turns swap off and comments out
# the swap entries in /etc/fstab so it stays off across reboots. It is idempotent
# and a no-op when swap is already disabled or swapoff is unavailable.
- name: Disable swap on all cluster nodes
when: disable_swap
block:
- name: Turn off swap now
ansible.builtin.command: swapoff -a
register: swapoff_result
changed_when: false
failed_when: false
- name: Comment out swap entries in fstab
ansible.builtin.replace:
path: /etc/fstab
regexp: '^([^#][^\n]*\s+swap\s+)'
replace: '# \\1'
register: fstab_swap
- name: Set SELinux to disabled state - name: Set SELinux to disabled state
ansible.posix.selinux: ansible.posix.selinux:
state: disabled state: disabled
-17
View File
@@ -8,23 +8,6 @@
msg: > msg: >
"Ansible is out of date. See here for more info: https://docs.technotim.com/posts/ansible-automation/" "Ansible is out of date. See here for more info: https://docs.technotim.com/posts/ansible-automation/"
- name: Verify all cluster nodes have unique hostnames
ansible.builtin.assert:
that: (cluster_hostnames | unique | length) == (cluster_hostnames | length)
msg: >-
k3s nodes must have unique hostnames. Found a duplicate in:
{{ cluster_hostnames | unique }}. Each node registers in the cluster
keyed by its hostname, so matching hostnames prevent nodes from joining.
vars:
cluster_hostnames: >-
{{
groups['k3s_cluster']
| map('extract', hostvars, 'ansible_hostname')
| list
}}
run_once: true
when: "'k3s_cluster' in groups"
- name: Prepare Proxmox cluster - name: Prepare Proxmox cluster
hosts: proxmox hosts: proxmox
gather_facts: true gather_facts: true