mirror of
https://github.com/techno-tim/k3s-ansible.git
synced 2026-10-23 20:22:03 +02:00
Compare commits
143
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6363cdfc5 | ||
|
|
83f205177d | ||
|
|
5c288e8f3e | ||
|
|
dfcfbc1f3f | ||
|
|
c3606a7847 | ||
|
|
35939315cd | ||
|
|
997ea63a3b | ||
|
|
68d03acc68 | ||
|
|
2babd39c89 | ||
|
|
bbca35331d | ||
|
|
4c50fbbe10 | ||
|
|
10bde4eff0 | ||
|
|
57f234c8da | ||
|
|
fb9a0bebd1 | ||
|
|
4990157d35 | ||
|
|
73fae0826c | ||
|
|
57a22e364d | ||
|
|
9b220c1629 | ||
|
|
a0d78ff317 | ||
|
|
999cf3ee05 | ||
|
|
1402f33108 | ||
|
|
94dbffaef7 | ||
|
|
a52e2ea72c | ||
|
|
bb3843dbb1 | ||
|
|
87ea8160c1 | ||
|
|
5bc347aed4 | ||
|
|
665e274820 | ||
|
|
5747bfce0e | ||
|
|
29b7aa1b72 | ||
|
|
2fad0a8db6 | ||
|
|
5cbbf7371b | ||
|
|
422621c69c | ||
|
|
39988a9bee | ||
|
|
133a84b564 | ||
|
|
6b79057f6c | ||
|
|
4c0b1ee8f3 | ||
|
|
11f9505460 | ||
|
|
850301fbc4 | ||
|
|
983e11322e | ||
|
|
a4df16cf87 | ||
|
|
f8ababb7bf | ||
|
|
90eb5e4b41 | ||
|
|
97ed29b4a2 | ||
|
|
fc2225ab8d | ||
|
|
d99f6a96f2 | ||
|
|
fab302fd91 | ||
|
|
eddbcbfb76 | ||
|
|
03ae8de0d5 | ||
|
|
d136fa4486 | ||
|
|
b906cfbf72 | ||
|
|
2c04f38e2c | ||
|
|
3435f43748 | ||
|
|
924a2f528c | ||
|
|
2892ac3858 | ||
|
|
df8e8dd591 | ||
|
|
3a0303d130 | ||
|
|
b077a49e1f | ||
|
|
635f0b21b3 | ||
|
|
4a64ad42df | ||
|
|
d0537736de | ||
|
|
2149827800 | ||
|
|
2d0596209e | ||
|
|
3a20500f9c | ||
|
|
9ce9fecc5b | ||
|
|
668d7fb896 | ||
|
|
6cee0e9051 | ||
|
|
6823ad51d5 | ||
|
|
1a521ea0d9 | ||
|
|
e48bb6df26 | ||
|
|
36893c27fb | ||
|
|
e8cd10d49b | ||
|
|
b86156b995 | ||
|
|
072f1a321d | ||
|
|
2f46a54240 | ||
|
|
bf0418d77f | ||
|
|
d88eb80df0 | ||
|
|
f50d335451 | ||
|
|
d6597150c7 | ||
|
|
353f7ab641 | ||
|
|
c7c727c3dc | ||
|
|
0422bfa2ac | ||
|
|
0333406725 | ||
|
|
f4a19d368b | ||
|
|
02d212c007 | ||
|
|
80095250e9 | ||
|
|
4fe2c92795 | ||
|
|
b3f2a4addc | ||
|
|
cb03ee829e | ||
|
|
9e2e82faeb | ||
|
|
7c1f6cbe42 | ||
|
|
604eb7a6e6 | ||
|
|
a204ed5169 | ||
|
|
b6608ca3e4 | ||
|
|
8252a45dfd | ||
|
|
c99f098c2e | ||
|
|
7867b87d85 | ||
|
|
dfe19f3731 | ||
|
|
a46d97a28d | ||
|
|
dc9d571f17 | ||
|
|
6742551e5c | ||
|
|
fb3478a086 | ||
|
|
518c5bb62a | ||
|
|
3f5d8dfe9f | ||
|
|
efbfadcb93 | ||
|
|
f81ec04ba2 | ||
|
|
8432d3bc66 | ||
|
|
14ae9df1bc | ||
|
|
f175716339 | ||
|
|
955c6f6b4a | ||
|
|
3b74985767 | ||
|
|
9ace193ade | ||
|
|
83a0be3afd | ||
|
|
029eba6102 | ||
|
|
0c8253b3a5 | ||
|
|
326b71dfa2 | ||
|
|
b95d6dd2cc | ||
|
|
e4146b4ca9 | ||
|
|
1fb10faf7f | ||
|
|
ea3b3c776a | ||
|
|
5beca87783 | ||
|
|
6ffc25dfe5 | ||
|
|
bcd37a6904 | ||
|
|
8dd3ffc825 | ||
|
|
f6ba208b5c | ||
|
|
a22d8f7aaf | ||
|
|
05fb6b566d | ||
|
|
3aeb7d69ea | ||
|
|
61bf3971ef | ||
|
|
3f06a11c8d | ||
|
|
3888a29bb1 | ||
|
|
98ef696f31 | ||
|
|
de26a79a4c | ||
|
|
ab7ca9b551 | ||
|
|
c5f71c9e2e | ||
|
|
0f23e7e258 | ||
|
|
121061d875 | ||
|
|
db53f595fd | ||
|
|
7b6b24ce4d | ||
|
|
a5728da35e | ||
|
|
cda7c92203 | ||
|
|
d910b83bf3 | ||
|
|
101313f880 | ||
|
|
12be355867 |
No files matched your search
+16
-9
@@ -1,20 +1,27 @@
|
|||||||
---
|
---
|
||||||
|
profile: production
|
||||||
exclude_paths:
|
exclude_paths:
|
||||||
# default paths
|
# default paths
|
||||||
- '.cache/'
|
- .cache/
|
||||||
- '.github/'
|
- .ansible/
|
||||||
- 'test/fixtures/formatting-before/'
|
- .github/
|
||||||
- 'test/fixtures/formatting-prettier/'
|
- test/fixtures/formatting-before/
|
||||||
|
- test/fixtures/formatting-prettier/
|
||||||
|
|
||||||
# The "converge" and "reset" playbooks use import_playbook in
|
# The "converge" and "reset" playbooks use import_playbook in
|
||||||
# conjunction with the "env" lookup plugin, which lets the
|
# conjunction with the "env" lookup plugin, which lets the
|
||||||
# syntax check of ansible-lint fail.
|
# syntax check of ansible-lint fail.
|
||||||
- 'molecule/**/converge.yml'
|
- molecule/**/converge.yml
|
||||||
- 'molecule/**/prepare.yml'
|
- molecule/**/prepare.yml
|
||||||
- 'molecule/**/reset.yml'
|
- molecule/**/reset.yml
|
||||||
|
|
||||||
# The file was generated by galaxy ansible - don't mess with it.
|
# The file was generated by galaxy ansible - don't mess with it.
|
||||||
- 'galaxy.yml'
|
- galaxy.yml
|
||||||
|
|
||||||
skip_list:
|
skip_list:
|
||||||
- 'fqcn-builtins'
|
- var-naming[no-role-prefix]
|
||||||
|
|
||||||
|
# The Molecule Vagrant driver injects this module at runtime. The custom create
|
||||||
|
# playbook is syntax-checked separately against the exact pinned plugin module.
|
||||||
|
mock_modules:
|
||||||
|
- vagrant
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
<!-- It's a good idea to check this post first for general troubleshooting https://github.com/techno-tim/k3s-ansible/discussions/19 -->
|
<!-- It's a good idea to check this post first for general troubleshooting https://github.com/timothystewart6/k3s-ansible/discussions/19 -->
|
||||||
|
|
||||||
<!--- Provide a general summary of the issue in the Title above -->
|
<!--- Provide a general summary of the issue in the Title above -->
|
||||||
|
|
||||||
@@ -37,6 +37,11 @@ systemd_dir: ""
|
|||||||
|
|
||||||
flannel_iface: ""
|
flannel_iface: ""
|
||||||
|
|
||||||
|
#calico_iface: ""
|
||||||
|
calico_ebpf: ""
|
||||||
|
calico_cidr: ""
|
||||||
|
calico_tag: ""
|
||||||
|
|
||||||
apiserver_endpoint: ""
|
apiserver_endpoint: ""
|
||||||
|
|
||||||
k3s_token: "NA"
|
k3s_token: "NA"
|
||||||
@@ -46,6 +51,9 @@ extra_agent_args: ""
|
|||||||
|
|
||||||
kube_vip_tag_version: ""
|
kube_vip_tag_version: ""
|
||||||
|
|
||||||
|
kube_vip_cloud_provider_tag_version: ""
|
||||||
|
kube_vip_lb_ip_range: ""
|
||||||
|
|
||||||
metal_lb_speaker_tag_version: ""
|
metal_lb_speaker_tag_version: ""
|
||||||
metal_lb_controller_tag_version: ""
|
metal_lb_controller_tag_version: ""
|
||||||
|
|
||||||
@@ -74,4 +82,4 @@ node
|
|||||||
## Possible Solution
|
## Possible Solution
|
||||||
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
||||||
|
|
||||||
- [ ] I've checked the [General Troubleshooting Guide](https://github.com/techno-tim/k3s-ansible/discussions/20)
|
- [ ] I've checked the [General Troubleshooting Guide](https://github.com/timothystewart6/k3s-ansible/discussions/20)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
self-hosted-runner:
|
||||||
|
labels:
|
||||||
|
- k3s-ci
|
||||||
|
- virtualbox
|
||||||
|
- nested-virt
|
||||||
@@ -9,3 +9,18 @@ updates:
|
|||||||
ignore:
|
ignore:
|
||||||
- dependency-name: "*"
|
- dependency-name: "*"
|
||||||
update-types: ["version-update:semver-major"]
|
update-types: ["version-update:semver-major"]
|
||||||
|
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
rebase-strategy: "auto"
|
||||||
|
|
||||||
|
- package-ecosystem: "docker"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
rebase-strategy: "auto"
|
||||||
|
ignore:
|
||||||
|
- dependency-name: "*"
|
||||||
|
update-types: ["version-update:semver-major"]
|
||||||
+75
-21
@@ -1,37 +1,91 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# download-boxes.sh
|
# download-boxes.sh
|
||||||
# Check all molecule.yml files for required Vagrant boxes and download the ones that are not
|
# Validate the pinned Vagrant box set and download exact versions that are not
|
||||||
# already present on the system.
|
# already present in VAGRANT_HOME.
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
GIT_ROOT=$(git rev-parse --show-toplevel)
|
GIT_ROOT=$(git rev-parse --show-toplevel)
|
||||||
PROVIDER=virtualbox
|
PROVIDER=virtualbox
|
||||||
|
LOCK_FILE="${VAGRANT_BOX_LOCK_FILE:-${GIT_ROOT}/.github/vagrant-boxes.lock}"
|
||||||
|
|
||||||
# Read all boxes for all platforms from the "molecule.yml" files
|
MOLECULE_YML_PATH=("${GIT_ROOT}"/molecule/*/molecule.yml)
|
||||||
all_boxes=$(cat "${GIT_ROOT}"/molecule/*/molecule.yml |
|
|
||||||
yq -r '.platforms[].box' | # Read the "box" property of each node under "platforms"
|
|
||||||
grep --invert-match --regexp=--- | # Filter out file separators
|
|
||||||
sort |
|
|
||||||
uniq)
|
|
||||||
|
|
||||||
# Read the boxes that are currently present on the system (for the current provider)
|
# Extract the unique boxes referenced by the scenarios.
|
||||||
|
declared_boxes=$(for file in "${MOLECULE_YML_PATH[@]}"; do
|
||||||
|
yq -r '.platforms[].box' "$file"
|
||||||
|
done | sort -u)
|
||||||
|
|
||||||
|
if [[ ! -r "$LOCK_FILE" ]]; then
|
||||||
|
printf 'Vagrant box lock file is missing or unreadable: %s\n' "$LOCK_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
lock_entries=$(awk '
|
||||||
|
/^[[:space:]]*#/ || NF == 0 { next }
|
||||||
|
NF != 3 {
|
||||||
|
printf "Invalid lock entry on line %d: expected box, version, architecture\n", NR > "/dev/stderr"
|
||||||
|
invalid = 1
|
||||||
|
next
|
||||||
|
}
|
||||||
|
{ print $1 " " $2 " " $3 }
|
||||||
|
END { exit invalid }
|
||||||
|
' "$LOCK_FILE")
|
||||||
|
|
||||||
|
duplicate_boxes=$(printf '%s\n' "$lock_entries" | awk '{ print $1 }' | sort | uniq -d)
|
||||||
|
if [[ -n "$duplicate_boxes" ]]; then
|
||||||
|
printf 'Duplicate Vagrant box lock entries:\n%s\n' "$duplicate_boxes" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
locked_boxes=$(printf '%s\n' "$lock_entries" | sort)
|
||||||
|
locked_names=$(printf '%s\n' "$locked_boxes" | awk '{ print $1 }')
|
||||||
|
missing_locks=$(comm -23 <(printf '%s\n' "$declared_boxes") <(printf '%s\n' "$locked_names"))
|
||||||
|
unused_locks=$(comm -13 <(printf '%s\n' "$declared_boxes") <(printf '%s\n' "$locked_names"))
|
||||||
|
|
||||||
|
if [[ -n "$missing_locks" || -n "$unused_locks" ]]; then
|
||||||
|
if [[ -n "$missing_locks" ]]; then
|
||||||
|
printf 'Scenario boxes missing from the lock file:\n%s\n' "$missing_locks" >&2
|
||||||
|
fi
|
||||||
|
if [[ -n "$unused_locks" ]]; then
|
||||||
|
printf 'Lock entries not referenced by a scenario:\n%s\n' "$unused_locks" >&2
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'Pinned Vagrant boxes:\n%s\n' "$locked_boxes"
|
||||||
|
|
||||||
|
# Read exact box, provider, version, and architecture tuples already present.
|
||||||
present_boxes=$(
|
present_boxes=$(
|
||||||
(vagrant box list |
|
vagrant box list --machine-readable |
|
||||||
grep "${PROVIDER}" | # Filter by boxes available for the current provider
|
awk -F, -v expected_provider="$PROVIDER" '
|
||||||
awk '{print $1;}' | # The box name is the first word in each line
|
$3 == "box-name" { name = $4; next }
|
||||||
sort |
|
$3 == "box-provider" { provider = $4; next }
|
||||||
uniq) ||
|
$3 == "box-version" { version = $4; next }
|
||||||
echo "" # In case any of these commands fails, just use an empty list
|
$3 == "box-architecture" {
|
||||||
|
architecture = $4
|
||||||
|
if (provider == expected_provider) {
|
||||||
|
print name " " version " " architecture
|
||||||
|
}
|
||||||
|
name = provider = version = architecture = ""
|
||||||
|
}
|
||||||
|
' |
|
||||||
|
sort -u
|
||||||
)
|
)
|
||||||
|
|
||||||
# The boxes that we need to download are the ones present in $all_boxes, but not $present_boxes.
|
download_boxes=$(comm -23 \
|
||||||
download_boxes=$(comm -2 -3 <(echo "${all_boxes}") <(echo "${present_boxes}"))
|
<(printf '%s\n' "$locked_boxes") \
|
||||||
|
<(printf '%s\n' "$present_boxes"))
|
||||||
|
|
||||||
# Actually download the necessary boxes
|
if [[ -n "$download_boxes" ]]; then
|
||||||
if [ -n "${download_boxes}" ]; then
|
printf '%s\n' "$download_boxes" | while read -r box version architecture; do
|
||||||
echo "${download_boxes}" | while IFS= read -r box; do
|
vagrant box add \
|
||||||
vagrant box add --provider "${PROVIDER}" "${box}"
|
--provider "$PROVIDER" \
|
||||||
|
--box-version "$version" \
|
||||||
|
--architecture "$architecture" \
|
||||||
|
"$box"
|
||||||
done
|
done
|
||||||
|
else
|
||||||
|
printf 'All pinned Vagrant boxes are already present.\n'
|
||||||
fi
|
fi
|
||||||
Executable
+249
@@ -0,0 +1,249 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
printf '%s\n' \
|
||||||
|
'Usage: cleanup-runner-resources.sh [--snapshot|--dry-run|--apply]' \
|
||||||
|
'' \
|
||||||
|
'Discover and, with --apply, remove only VirtualBox resources referenced by' \
|
||||||
|
'repository-owned Molecule Vagrant state. The default is --dry-run.'
|
||||||
|
}
|
||||||
|
|
||||||
|
mode="dry-run"
|
||||||
|
case "${1:-}" in
|
||||||
|
"") ;;
|
||||||
|
--snapshot) mode="snapshot" ;;
|
||||||
|
--dry-run) mode="dry-run" ;;
|
||||||
|
--apply) mode="apply" ;;
|
||||||
|
--help|-h) usage; exit 0 ;;
|
||||||
|
*) usage >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
home_dir="${HOME:?HOME must be set}"
|
||||||
|
molecule_root="${K3S_CI_MOLECULE_ROOT:-${home_dir}/.cache/molecule}"
|
||||||
|
repository_name="${K3S_CI_MOLECULE_PROJECT:-k3s-ansible}"
|
||||||
|
virtualbox_root="${K3S_CI_VIRTUALBOX_ROOT:-${home_dir}/VirtualBox VMs}"
|
||||||
|
hostonly_marker="${K3S_CI_HOSTONLY_MARKER:-${home_dir}/.cache/k3s-ci/hostonly-interfaces}"
|
||||||
|
|
||||||
|
record_hostonly() {
|
||||||
|
local marker_dir="${hostonly_marker%/*}"
|
||||||
|
local marker_tmp="${hostonly_marker}.tmp"
|
||||||
|
local hostonly_inventory
|
||||||
|
if ! hostonly_inventory="$(VBoxManage list hostonlyifs)"; then
|
||||||
|
fail_closed 'unable to inventory VirtualBox host-only interfaces'
|
||||||
|
fi
|
||||||
|
mkdir -p -- "$marker_dir"
|
||||||
|
awk -F': ' '
|
||||||
|
/^Name:/ { name=$2 }
|
||||||
|
/^IPAddress:/ { print name "|" $2 }
|
||||||
|
' <<< "$hostonly_inventory" > "$marker_tmp"
|
||||||
|
mv -- "$marker_tmp" "$hostonly_marker"
|
||||||
|
chmod 600 "$hostonly_marker"
|
||||||
|
printf 'Recorded host-only interface baseline: %s\n' "$hostonly_marker"
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_hostonly() {
|
||||||
|
local hostonly_inventory
|
||||||
|
if [[ ! -f "$hostonly_marker" ]]; then
|
||||||
|
printf 'No host-only interface baseline found; leaving interfaces unchanged.\n'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! hostonly_inventory="$(VBoxManage list hostonlyifs)"; then
|
||||||
|
fail_closed 'unable to inventory VirtualBox host-only interfaces'
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS='|' read -r interface_name interface_ip; do
|
||||||
|
[[ "$interface_name" == vboxnet* ]] || continue
|
||||||
|
[[ "$interface_ip" == 192.168.30.* || "$interface_ip" == fdad:bad:ba55:* ]] || continue
|
||||||
|
if grep -Fqx "${interface_name}|${interface_ip}" "$hostonly_marker"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ "$mode" == apply ]]; then
|
||||||
|
VBoxManage hostonlyif remove "$interface_name"
|
||||||
|
printf 'Removed host-only interface %s (%s)\n' "$interface_name" "$interface_ip"
|
||||||
|
else
|
||||||
|
printf 'Would remove host-only interface %s (%s)\n' "$interface_name" "$interface_ip"
|
||||||
|
fi
|
||||||
|
done < <(awk -F': ' '
|
||||||
|
/^Name:/ { name=$2 }
|
||||||
|
/^IPAddress:/ { print name "|" $2 }
|
||||||
|
' <<< "$hostonly_inventory")
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$mode" == snapshot ]]; then
|
||||||
|
record_hostonly
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
resolve_existing_dir() {
|
||||||
|
local candidate="$1"
|
||||||
|
if [[ ! -d "$candidate" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
readlink -f -- "$candidate"
|
||||||
|
}
|
||||||
|
|
||||||
|
root_contains() {
|
||||||
|
local root="$1"
|
||||||
|
local path="$2"
|
||||||
|
[[ "$path" == "$root"/* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
is_supported_scenario() {
|
||||||
|
case "$1" in
|
||||||
|
default|single_node|calico|cilium|kube-vip|ipv6) return 0 ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
is_unregistered_vm_error() {
|
||||||
|
grep -Eq 'Could not find a registered machine|VBOX_E_OBJECT_NOT_FOUND'
|
||||||
|
}
|
||||||
|
|
||||||
|
fail_closed() {
|
||||||
|
printf 'cleanup refused: %s\n' "$1" >&2
|
||||||
|
exit 3
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ! "$repository_name" =~ ^[A-Za-z0-9._-]+$ ]]; then
|
||||||
|
fail_closed 'invalid Molecule repository name'
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_inventory() {
|
||||||
|
local phase="$1"
|
||||||
|
printf '%s VirtualBox inventory:\n' "$phase"
|
||||||
|
VBoxManage list vms || true
|
||||||
|
VBoxManage list hdds || true
|
||||||
|
VBoxManage list hostonlyifs || true
|
||||||
|
}
|
||||||
|
|
||||||
|
molecule_root_real="$(resolve_existing_dir "$molecule_root" || true)"
|
||||||
|
if [[ -z "$molecule_root_real" ]]; then
|
||||||
|
printf 'No Molecule root exists: %s\n' "$molecule_root"
|
||||||
|
cleanup_hostonly
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_inventory before
|
||||||
|
|
||||||
|
repository_root_real="$(resolve_existing_dir "$molecule_root_real/$repository_name" || true)"
|
||||||
|
if [[ -z "$repository_root_real" ]]; then
|
||||||
|
printf 'No repository Molecule state root exists: %s\n' "$molecule_root_real/$repository_name"
|
||||||
|
cleanup_hostonly
|
||||||
|
print_inventory after
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if ! root_contains "$molecule_root_real" "$repository_root_real"; then
|
||||||
|
fail_closed "repository Molecule state root is outside Molecule root: $repository_root_real"
|
||||||
|
fi
|
||||||
|
|
||||||
|
declare -a state_files=()
|
||||||
|
while IFS= read -r -d '' state_file; do
|
||||||
|
state_files+=("$state_file")
|
||||||
|
done < <(find "$repository_root_real" -mindepth 6 -maxdepth 6 -type f \
|
||||||
|
-path '*/.vagrant/machines/*/virtualbox/id' -print0 2>/dev/null)
|
||||||
|
|
||||||
|
if ((${#state_files[@]} == 0)); then
|
||||||
|
printf 'No repository-owned Molecule Vagrant state found under %s\n' "$repository_root_real"
|
||||||
|
cleanup_hostonly
|
||||||
|
print_inventory after
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
virtualbox_root_real="$(resolve_existing_dir "$virtualbox_root" || true)"
|
||||||
|
|
||||||
|
declare -a vm_records=()
|
||||||
|
for state_file in "${state_files[@]}"; do
|
||||||
|
if [[ ! -f "$state_file" ]]; then
|
||||||
|
printf 'Skipping Vagrant state removed with its stale scenario directory: %s\n' "$state_file"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
state_file_real="$(readlink -f -- "$state_file")"
|
||||||
|
state_dir="${state_file_real%/.vagrant/machines/*/virtualbox/id}"
|
||||||
|
machine_dir="${state_file_real%/virtualbox/id}"
|
||||||
|
machine_name="${machine_dir##*/}"
|
||||||
|
scenario_name="${state_dir##*/}"
|
||||||
|
|
||||||
|
if ! root_contains "$repository_root_real" "$state_dir"; then
|
||||||
|
fail_closed "state path is outside the repository Molecule root: $state_file_real"
|
||||||
|
fi
|
||||||
|
if ! is_supported_scenario "$scenario_name"; then
|
||||||
|
fail_closed "unexpected Molecule scenario: $scenario_name"
|
||||||
|
fi
|
||||||
|
if [[ "$machine_name" != control* && "$machine_name" != node* ]]; then
|
||||||
|
fail_closed "unexpected Molecule machine name: $machine_name"
|
||||||
|
fi
|
||||||
|
|
||||||
|
vm_uuid="$(tr -d '[:space:]' < "$state_file_real")"
|
||||||
|
if [[ ! "$vm_uuid" =~ ^[0-9a-fA-F-]{36}$ ]]; then
|
||||||
|
fail_closed "invalid VirtualBox UUID in $state_file_real"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! vm_info="$(VBoxManage showvminfo "$vm_uuid" --machinereadable 2>&1)"; then
|
||||||
|
if is_unregistered_vm_error <<< "$vm_info"; then
|
||||||
|
printf 'Stale Vagrant state without a registered VM: %s (%s)\n' "$machine_name" "$vm_uuid"
|
||||||
|
if [[ "$mode" == apply ]]; then
|
||||||
|
rm -rf -- "${state_dir}/.vagrant"
|
||||||
|
printf 'Removed stale Vagrant state: %s\n' "${state_dir}/.vagrant"
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
fail_closed "unable to inspect VirtualBox VM $vm_uuid: $vm_info"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$virtualbox_root_real" ]]; then
|
||||||
|
fail_closed "VirtualBox VM root does not exist: $virtualbox_root"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cfg_file="$(awk -F= '$1 == "CfgFile" {gsub(/"/, "", $2); print $2; exit}' <<< "$vm_info")"
|
||||||
|
if [[ -z "$cfg_file" ]]; then
|
||||||
|
fail_closed "VirtualBox configuration path missing for $vm_uuid"
|
||||||
|
fi
|
||||||
|
cfg_file_real="$(readlink -f -- "$cfg_file")"
|
||||||
|
if ! root_contains "$virtualbox_root_real" "$cfg_file_real"; then
|
||||||
|
fail_closed "VM configuration is outside VirtualBox root: $cfg_file_real"
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r disk_path; do
|
||||||
|
[[ -z "$disk_path" ]] && continue
|
||||||
|
disk_path_real="$(readlink -f -- "$disk_path" 2>/dev/null || true)"
|
||||||
|
if [[ -z "$disk_path_real" ]] || ! root_contains "$virtualbox_root_real" "$disk_path_real"; then
|
||||||
|
fail_closed "attached disk is outside VirtualBox root: $disk_path"
|
||||||
|
fi
|
||||||
|
done < <(awk -F= '$1 ~ /^(SATA|IDE|SCSI|SAS|VirtioSCSI|NVMe)-[0-9]+-[0-9]+$/ {gsub(/"/, "", $2); print $2}' <<< "$vm_info")
|
||||||
|
|
||||||
|
vm_records+=("$vm_uuid|$machine_name|$cfg_file_real")
|
||||||
|
done
|
||||||
|
|
||||||
|
if ((${#vm_records[@]} == 0)); then
|
||||||
|
printf 'No live repository-owned VirtualBox resources found\n'
|
||||||
|
cleanup_hostonly
|
||||||
|
print_inventory after
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for record in "${vm_records[@]}"; do
|
||||||
|
IFS='|' read -r vm_uuid machine_name cfg_file_real <<< "$record"
|
||||||
|
if [[ "$mode" == dry-run ]]; then
|
||||||
|
printf 'Would remove VM %s (%s) config=%s\n' "$machine_name" "$vm_uuid" "$cfg_file_real"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
vm_state="$(VBoxManage showvminfo "$vm_uuid" --machinereadable | awk -F= '$1 == "VMState" {gsub(/"/, "", $2); print $2; exit}')"
|
||||||
|
if [[ "$vm_state" != poweroff && "$vm_state" != saved ]]; then
|
||||||
|
VBoxManage controlvm "$vm_uuid" poweroff
|
||||||
|
fi
|
||||||
|
VBoxManage unregistervm "$vm_uuid" --delete
|
||||||
|
printf 'Removed VM %s (%s)\n' "$machine_name" "$vm_uuid"
|
||||||
|
done
|
||||||
|
|
||||||
|
cleanup_hostonly
|
||||||
|
print_inventory after
|
||||||
|
|
||||||
|
if [[ "$mode" == apply ]]; then
|
||||||
|
printf 'Repository-owned VM and host-only interface cleanup complete.\n'
|
||||||
|
else
|
||||||
|
printf 'Dry run complete. No resources were modified.\n'
|
||||||
|
fi
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
output_dir="${1:-${RUNNER_TEMP:-/tmp}/k3s-ci-diagnostics}"
|
||||||
|
mkdir -p -- "$output_dir"
|
||||||
|
umask 077
|
||||||
|
|
||||||
|
run_capture() {
|
||||||
|
local output_file="$1"
|
||||||
|
shift
|
||||||
|
{
|
||||||
|
printf '$'
|
||||||
|
printf ' %q' "$@"
|
||||||
|
printf '\n'
|
||||||
|
"$@"
|
||||||
|
} > "$output_dir/$output_file" 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
run_capture system.txt uname -a
|
||||||
|
run_capture runner-user.txt id
|
||||||
|
run_capture memory.txt free -h
|
||||||
|
run_capture disk.txt df -h
|
||||||
|
run_capture virtualbox-version VBoxManage --version
|
||||||
|
run_capture virtualbox-vms VBoxManage list vms
|
||||||
|
run_capture virtualbox-running-vms VBoxManage list runningvms
|
||||||
|
run_capture virtualbox-disks VBoxManage list hdds
|
||||||
|
run_capture virtualbox-hostonlyifs VBoxManage list hostonlyifs
|
||||||
|
run_capture virtualbox-groups VBoxManage list groups
|
||||||
|
run_capture vagrant-status vagrant global-status
|
||||||
|
run_capture molecule-state find "${HOME}/.cache/molecule" -maxdepth 6 -type f -path '*/.vagrant/machines/*/virtualbox/id' -print
|
||||||
|
|
||||||
|
scenario_name="${K3S_CI_SCENARIO_NAME:-}"
|
||||||
|
if [[ "$scenario_name" =~ ^[A-Za-z0-9_-]+$ ]]; then
|
||||||
|
molecule_state_dir="${HOME}/.cache/molecule/k3s-ansible/${scenario_name}"
|
||||||
|
for log_name in vagrant.out vagrant.err; do
|
||||||
|
if [[ -r "${molecule_state_dir}/${log_name}" ]]; then
|
||||||
|
cp -- "${molecule_state_dir}/${log_name}" "$output_dir/${scenario_name}-${log_name}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -r /etc/vbox/networks.conf ]]; then
|
||||||
|
cp -- /etc/vbox/networks.conf "$output_dir/virtualbox-networks.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'Diagnostics written to %s\n' "$output_dir"
|
||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
output_dir="${1:?output directory is required}"
|
||||||
|
interval="${2:-10}"
|
||||||
|
[[ "$interval" =~ ^[1-9][0-9]*$ ]] || {
|
||||||
|
printf 'monitor interval must be a positive integer\n' >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p -- "$output_dir"
|
||||||
|
umask 077
|
||||||
|
|
||||||
|
free -h > "$output_dir/memory-before.txt"
|
||||||
|
df -h > "$output_dir/disk-before.txt"
|
||||||
|
vmstat -w "$interval" > "$output_dir/vmstat.txt" &
|
||||||
|
vmstat_pid=$!
|
||||||
|
|
||||||
|
iostat_pid=""
|
||||||
|
if command -v iostat >/dev/null 2>&1; then
|
||||||
|
iostat -dx "$interval" > "$output_dir/iostat.txt" &
|
||||||
|
iostat_pid=$!
|
||||||
|
else
|
||||||
|
printf 'iostat is not installed on this runner\n' > "$output_dir/iostat-unavailable.txt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
local rc=$?
|
||||||
|
trap - EXIT INT TERM
|
||||||
|
kill "$vmstat_pid" 2>/dev/null || true
|
||||||
|
[[ -z "$iostat_pid" ]] || kill "$iostat_pid" 2>/dev/null || true
|
||||||
|
wait "$vmstat_pid" 2>/dev/null || true
|
||||||
|
[[ -z "$iostat_pid" ]] || wait "$iostat_pid" 2>/dev/null || true
|
||||||
|
free -h > "$output_dir/memory-after.txt"
|
||||||
|
df -h > "$output_dir/disk-after.txt"
|
||||||
|
exit "$rc"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
sleep 3600 &
|
||||||
|
wait $!
|
||||||
|
done
|
||||||
+211
@@ -0,0 +1,211 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
printf 'Vagrant box master preparation refused: %s\n' "$1" >&2
|
||||||
|
exit 3
|
||||||
|
}
|
||||||
|
|
||||||
|
root_contains() {
|
||||||
|
local root="$1"
|
||||||
|
local path="$2"
|
||||||
|
[[ "$path" == "$root"/* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
read_machine_value() {
|
||||||
|
local machine_info="$1"
|
||||||
|
local key="$2"
|
||||||
|
awk -F= -v key="$key" '$1 == key {gsub(/"/, "", $2); print $2; exit}' <<< "$machine_info"
|
||||||
|
}
|
||||||
|
|
||||||
|
read_extra_data() {
|
||||||
|
local uuid="$1"
|
||||||
|
local key="$2"
|
||||||
|
local value
|
||||||
|
value="$(VBoxManage getextradata "$uuid" "$key" 2>/dev/null || true)"
|
||||||
|
[[ "$value" == 'Value: '* ]] || return 1
|
||||||
|
printf '%s\n' "${value#Value: }"
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_owned_master() {
|
||||||
|
local uuid="$1"
|
||||||
|
local box="$2"
|
||||||
|
local version="$3"
|
||||||
|
local architecture="$4"
|
||||||
|
local machine_info cfg_file cfg_file_real vm_state groups disk_path disk_path_real
|
||||||
|
|
||||||
|
[[ "$uuid" =~ ^[0-9a-fA-F-]{36}$ ]] || return 1
|
||||||
|
machine_info="$(VBoxManage showvminfo "$uuid" --machinereadable 2>/dev/null)" || return 1
|
||||||
|
vm_state="$(read_machine_value "$machine_info" VMState)"
|
||||||
|
groups="$(read_machine_value "$machine_info" groups)"
|
||||||
|
cfg_file="$(read_machine_value "$machine_info" CfgFile)"
|
||||||
|
[[ "$vm_state" == poweroff ]] || return 1
|
||||||
|
[[ ",$groups," == *,/k3s-ansible/box-masters,* ]] || return 1
|
||||||
|
[[ -n "$cfg_file" ]] || return 1
|
||||||
|
cfg_file_real="$(readlink -f -- "$cfg_file" 2>/dev/null || true)"
|
||||||
|
[[ -n "$cfg_file_real" ]] || return 1
|
||||||
|
root_contains "$virtualbox_root_real" "$cfg_file_real" || return 1
|
||||||
|
|
||||||
|
[[ "$(read_extra_data "$uuid" k3s-ansible/owner || true)" == box-master ]] || return 1
|
||||||
|
[[ "$(read_extra_data "$uuid" k3s-ansible/box || true)" == "$box" ]] || return 1
|
||||||
|
[[ "$(read_extra_data "$uuid" k3s-ansible/version || true)" == "$version" ]] || return 1
|
||||||
|
[[ "$(read_extra_data "$uuid" k3s-ansible/architecture || true)" == "$architecture" ]] || return 1
|
||||||
|
|
||||||
|
while IFS= read -r disk_path; do
|
||||||
|
[[ -z "$disk_path" || "$disk_path" == none ]] && continue
|
||||||
|
disk_path_real="$(readlink -f -- "$disk_path" 2>/dev/null || true)"
|
||||||
|
[[ -n "$disk_path_real" ]] || return 1
|
||||||
|
root_contains "$virtualbox_root_real" "$disk_path_real" || return 1
|
||||||
|
done < <(awk -F= '$1 ~ /^(SATA|IDE|SCSI|SAS|VirtioSCSI|NVMe)-[0-9]+-[0-9]+$/ {
|
||||||
|
gsub(/"/, "", $2); print $2
|
||||||
|
}' <<< "$machine_info")
|
||||||
|
}
|
||||||
|
|
||||||
|
write_prewarm_vagrantfile() {
|
||||||
|
local destination="$1"
|
||||||
|
local box="$2"
|
||||||
|
local version="$3"
|
||||||
|
{
|
||||||
|
printf '%s\n' "Vagrant.configure('2') do |config|"
|
||||||
|
printf ' config.vm.box = "%s"\n' "$box"
|
||||||
|
printf ' config.vm.box_version = "%s"\n' "$version"
|
||||||
|
printf '%s\n' \
|
||||||
|
' config.vm.synced_folder ".", "/vagrant", disabled: true' \
|
||||||
|
' config.vm.hostname = "k3s-ansible-box-prewarm"' \
|
||||||
|
' config.vm.boot_timeout = 600' \
|
||||||
|
' config.vm.provider "virtualbox" do |virtualbox|' \
|
||||||
|
' virtualbox.linked_clone = true' \
|
||||||
|
' virtualbox.memory = 1024' \
|
||||||
|
' virtualbox.cpus = 2' \
|
||||||
|
' end' \
|
||||||
|
'end'
|
||||||
|
} > "$destination"
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_prewarm() {
|
||||||
|
local rc=$?
|
||||||
|
trap - EXIT
|
||||||
|
if [[ -n "${prewarm_dir:-}" && -d "$prewarm_dir" ]]; then
|
||||||
|
VAGRANT_CWD="$prewarm_dir" vagrant destroy --force >/dev/null 2>&1 || true
|
||||||
|
rm -rf -- "$prewarm_dir"
|
||||||
|
fi
|
||||||
|
exit "$rc"
|
||||||
|
}
|
||||||
|
|
||||||
|
create_owned_master() {
|
||||||
|
local box="$1"
|
||||||
|
local version="$2"
|
||||||
|
local architecture="$3"
|
||||||
|
local master_id_file="$4"
|
||||||
|
local mapping_file="$5"
|
||||||
|
local uuid machine_info cfg_file cfg_file_real vm_state mapping_tmp
|
||||||
|
|
||||||
|
# A master_id restored from an immutable cache is only a hint. Without the
|
||||||
|
# runner-local ownership record and matching VirtualBox metadata it is not
|
||||||
|
# trusted, adopted, modified, or deleted.
|
||||||
|
rm -f -- "$master_id_file"
|
||||||
|
|
||||||
|
prewarm_dir="$(mktemp -d "${master_root}/prewarm.XXXXXX")"
|
||||||
|
trap cleanup_prewarm EXIT
|
||||||
|
write_prewarm_vagrantfile "$prewarm_dir/Vagrantfile" "$box" "$version"
|
||||||
|
|
||||||
|
printf 'Creating runner-owned linked-clone master for %s %s %s\n' \
|
||||||
|
"$box" "$version" "$architecture"
|
||||||
|
VAGRANT_CWD="$prewarm_dir" vagrant up --provider virtualbox --no-provision
|
||||||
|
|
||||||
|
[[ -r "$master_id_file" ]] || fail "Vagrant did not record a master UUID for $box"
|
||||||
|
uuid="$(tr -d '[:space:]' < "$master_id_file")"
|
||||||
|
[[ "$uuid" =~ ^[0-9a-fA-F-]{36}$ ]] || fail "Vagrant recorded an invalid master UUID for $box"
|
||||||
|
|
||||||
|
machine_info="$(VBoxManage showvminfo "$uuid" --machinereadable 2>/dev/null)" || \
|
||||||
|
fail "Vagrant master $uuid for $box is not registered"
|
||||||
|
vm_state="$(read_machine_value "$machine_info" VMState)"
|
||||||
|
cfg_file="$(read_machine_value "$machine_info" CfgFile)"
|
||||||
|
cfg_file_real="$(readlink -f -- "$cfg_file" 2>/dev/null || true)"
|
||||||
|
[[ "$vm_state" == poweroff ]] || fail "new Vagrant master $uuid is not powered off"
|
||||||
|
if [[ -z "$cfg_file_real" ]] || ! root_contains "$virtualbox_root_real" "$cfg_file_real"; then
|
||||||
|
fail "new Vagrant master $uuid is outside the runner VirtualBox root"
|
||||||
|
fi
|
||||||
|
|
||||||
|
VAGRANT_CWD="$prewarm_dir" vagrant destroy --force
|
||||||
|
VBoxManage modifyvm "$uuid" --groups /k3s-ansible/box-masters
|
||||||
|
VBoxManage setextradata "$uuid" k3s-ansible/owner box-master
|
||||||
|
VBoxManage setextradata "$uuid" k3s-ansible/box "$box"
|
||||||
|
VBoxManage setextradata "$uuid" k3s-ansible/version "$version"
|
||||||
|
VBoxManage setextradata "$uuid" k3s-ansible/architecture "$architecture"
|
||||||
|
validate_owned_master "$uuid" "$box" "$version" "$architecture" || \
|
||||||
|
fail "new Vagrant master $uuid failed ownership validation"
|
||||||
|
|
||||||
|
rm -rf -- "$prewarm_dir"
|
||||||
|
prewarm_dir=""
|
||||||
|
trap - EXIT
|
||||||
|
|
||||||
|
mapping_tmp="${mapping_file}.tmp"
|
||||||
|
printf '%s\n' "$uuid" > "$mapping_tmp"
|
||||||
|
chmod 600 "$mapping_tmp"
|
||||||
|
mv -- "$mapping_tmp" "$mapping_file"
|
||||||
|
printf '%s\n' "$uuid" > "$master_id_file"
|
||||||
|
chmod 600 "$master_id_file"
|
||||||
|
printf 'Created and recorded owned master %s for %s\n' "$uuid" "$box"
|
||||||
|
}
|
||||||
|
|
||||||
|
repository_root="${K3S_CI_REPOSITORY_ROOT:-$(git rev-parse --show-toplevel)}"
|
||||||
|
lock_file="${VAGRANT_BOX_LOCK_FILE:-${repository_root}/.github/vagrant-boxes.lock}"
|
||||||
|
vagrant_home="${VAGRANT_HOME:?VAGRANT_HOME must be set}"
|
||||||
|
master_root="${K3S_CI_VAGRANT_MASTER_ROOT:-${HOME:?HOME must be set}/.cache/k3s-ci/vagrant-masters}"
|
||||||
|
virtualbox_root="${K3S_CI_VIRTUALBOX_ROOT:-${HOME}/VirtualBox VMs}"
|
||||||
|
|
||||||
|
[[ -r "$lock_file" ]] || fail "box lock file is missing or unreadable: $lock_file"
|
||||||
|
[[ -d "$vagrant_home/boxes" ]] || fail "Vagrant box directory is missing: $vagrant_home/boxes"
|
||||||
|
[[ -d "$virtualbox_root" ]] || fail "VirtualBox root is missing: $virtualbox_root"
|
||||||
|
|
||||||
|
box_root_real="$(readlink -f -- "$vagrant_home/boxes")"
|
||||||
|
virtualbox_root_real="$(readlink -f -- "$virtualbox_root")"
|
||||||
|
mkdir -p -- "$master_root"
|
||||||
|
chmod 700 "$master_root"
|
||||||
|
|
||||||
|
exec 9> "${master_root}/prepare.lock"
|
||||||
|
flock 9
|
||||||
|
|
||||||
|
lock_entries="$(awk '
|
||||||
|
/^[[:space:]]*#/ || NF == 0 { next }
|
||||||
|
NF != 3 { invalid = 1; next }
|
||||||
|
{ print $1 " " $2 " " $3 }
|
||||||
|
END { exit invalid }
|
||||||
|
' "$lock_file")" || fail 'invalid Vagrant box lock entry'
|
||||||
|
[[ -n "$lock_entries" ]] || fail 'Vagrant box lock is empty'
|
||||||
|
|
||||||
|
while read -r box version architecture; do
|
||||||
|
[[ "$box" =~ ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$ ]] || fail "invalid box name: $box"
|
||||||
|
[[ "$version" =~ ^[A-Za-z0-9._-]+$ ]] || fail "invalid box version: $version"
|
||||||
|
[[ "$architecture" =~ ^[A-Za-z0-9._-]+$ ]] || fail "invalid box architecture: $architecture"
|
||||||
|
|
||||||
|
box_slug="${box//\//-VAGRANTSLASH-}"
|
||||||
|
record_slug="${box//\//_}-${version}-${architecture}"
|
||||||
|
box_dir="${vagrant_home}/boxes/${box_slug}/${version}/${architecture}/virtualbox"
|
||||||
|
[[ -d "$box_dir" ]] || fail "pinned box is not installed: $box $version $architecture"
|
||||||
|
box_dir_real="$(readlink -f -- "$box_dir")"
|
||||||
|
root_contains "$box_root_real" "$box_dir_real" || fail "box directory is outside VAGRANT_HOME: $box_dir_real"
|
||||||
|
|
||||||
|
master_id_file="${box_dir_real}/master_id"
|
||||||
|
mapping_file="${master_root}/${record_slug}.uuid"
|
||||||
|
uuid=""
|
||||||
|
if [[ -r "$mapping_file" ]]; then
|
||||||
|
uuid="$(tr -d '[:space:]' < "$mapping_file")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$uuid" ]] && validate_owned_master "$uuid" "$box" "$version" "$architecture"; then
|
||||||
|
printf '%s\n' "$uuid" > "$master_id_file"
|
||||||
|
chmod 600 "$master_id_file"
|
||||||
|
printf 'Reusing owned master %s for %s %s %s\n' "$uuid" "$box" "$version" "$architecture"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -e "$mapping_file" ]]; then
|
||||||
|
printf 'Owned master record is stale for %s; rebuilding without deleting any VM or disk.\n' "$box"
|
||||||
|
fi
|
||||||
|
create_owned_master "$box" "$version" "$architecture" "$master_id_file" "$mapping_file"
|
||||||
|
done <<< "$lock_entries"
|
||||||
|
|
||||||
|
printf 'All pinned Vagrant box masters are ready.\n'
|
||||||
+129
@@ -0,0 +1,129 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
test_root="$(mktemp -d "${TMPDIR:-/tmp}/k3s-ci-cleanup-test.XXXXXX")"
|
||||||
|
trap 'rm -rf -- "$test_root"' EXIT
|
||||||
|
|
||||||
|
molecule_root="$test_root/molecule"
|
||||||
|
virtualbox_root="$test_root/VirtualBox VMs"
|
||||||
|
fake_bin="$test_root/bin"
|
||||||
|
mkdir -p -- "$molecule_root/k3s-ansible/single_node/.vagrant/machines/control1/virtualbox" \
|
||||||
|
"$molecule_root/k3s-ansible/single_node/.vagrant/machines/control2/virtualbox" \
|
||||||
|
"$virtualbox_root/control1" "$virtualbox_root/unmarked" "$fake_bin"
|
||||||
|
printf '%s\n' '11111111-1111-1111-1111-111111111111' \
|
||||||
|
> "$molecule_root/k3s-ansible/single_node/.vagrant/machines/control1/virtualbox/id"
|
||||||
|
printf '%s\n' '22222222-2222-2222-2222-222222222222' \
|
||||||
|
> "$molecule_root/k3s-ansible/single_node/.vagrant/machines/control2/virtualbox/id"
|
||||||
|
touch "$virtualbox_root/control1/control1.vbox" "$virtualbox_root/control1/disk.vdi" \
|
||||||
|
"$virtualbox_root/unmarked/unmarked.vbox"
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
'#!/usr/bin/env bash' \
|
||||||
|
'set -Eeuo pipefail' \
|
||||||
|
'case "${1:-}" in' \
|
||||||
|
' list)' \
|
||||||
|
' if [[ "${2:-}" == hostonlyifs && "${FAKE_HOSTONLY_FAIL:-false}" == true ]]; then exit 1; fi' \
|
||||||
|
' exit 0' \
|
||||||
|
' ;;' \
|
||||||
|
' showvminfo)' \
|
||||||
|
' if [[ "${FAKE_VM_MODE:-normal}" == missing ]]; then' \
|
||||||
|
' printf '\''VBoxManage: error: Could not find a registered machine named "missing"\n'\'' >&2' \
|
||||||
|
' exit 1' \
|
||||||
|
' fi' \
|
||||||
|
' if [[ "${FAKE_VM_MODE:-normal}" == fault ]]; then' \
|
||||||
|
' printf '\''VBoxManage: error: VirtualBox service is unavailable\n'\'' >&2' \
|
||||||
|
' exit 1' \
|
||||||
|
' fi' \
|
||||||
|
' printf '\''CfgFile="%s"\n'\'' "${FAKE_VBOX_ROOT}/control1/control1.vbox"' \
|
||||||
|
' printf '\''SATA-0-0="%s"\n'\'' "${FAKE_VBOX_ROOT}/control1/disk.vdi"' \
|
||||||
|
' printf '\''VMState="running"\n'\''' \
|
||||||
|
' ;;' \
|
||||||
|
' controlvm) printf '\''controlvm %s\n'\'' "$*" >> "${FAKE_LOG}" ;;' \
|
||||||
|
' unregistervm)' \
|
||||||
|
' printf '\''unregistervm %s\n'\'' "$*" >> "${FAKE_LOG}"' \
|
||||||
|
' rm -f -- "${FAKE_VBOX_ROOT}/control1/control1.vbox" "${FAKE_VBOX_ROOT}/control1/disk.vdi"' \
|
||||||
|
' ;;' \
|
||||||
|
' *) : ;;' \
|
||||||
|
'esac' > "$fake_bin/VBoxManage"
|
||||||
|
chmod 700 "$fake_bin/VBoxManage"
|
||||||
|
|
||||||
|
output="$test_root/output.txt"
|
||||||
|
if PATH="$fake_bin:$PATH" \
|
||||||
|
HOME="$test_root/home" \
|
||||||
|
K3S_CI_MOLECULE_ROOT="$molecule_root" \
|
||||||
|
K3S_CI_MOLECULE_PROJECT=k3s-ansible \
|
||||||
|
K3S_CI_VIRTUALBOX_ROOT="$virtualbox_root" \
|
||||||
|
K3S_CI_HOSTONLY_MARKER="$test_root/hostonly-baseline" \
|
||||||
|
FAKE_VM_MODE=fault \
|
||||||
|
FAKE_VBOX_ROOT="$virtualbox_root" \
|
||||||
|
FAKE_LOG="$test_root/vbox.log" \
|
||||||
|
bash "$repo_root/.github/scripts/cleanup-runner-resources.sh" --apply > "$output" 2>&1; then
|
||||||
|
printf '%s\n' 'cleanup unexpectedly accepted a VirtualBox inspection failure' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fq 'cleanup refused: unable to inspect VirtualBox VM' "$output"
|
||||||
|
[[ -f "$molecule_root/k3s-ansible/single_node/.vagrant/machines/control1/virtualbox/id" ]]
|
||||||
|
|
||||||
|
printf '%s\n' 'vboxnet0|192.168.30.1' > "$test_root/hostonly-baseline"
|
||||||
|
if PATH="$fake_bin:$PATH" \
|
||||||
|
HOME="$test_root/home" \
|
||||||
|
K3S_CI_MOLECULE_ROOT="$molecule_root" \
|
||||||
|
K3S_CI_MOLECULE_PROJECT=k3s-ansible \
|
||||||
|
K3S_CI_VIRTUALBOX_ROOT="$virtualbox_root" \
|
||||||
|
K3S_CI_HOSTONLY_MARKER="$test_root/hostonly-baseline" \
|
||||||
|
FAKE_HOSTONLY_FAIL=true \
|
||||||
|
FAKE_VBOX_ROOT="$virtualbox_root" \
|
||||||
|
FAKE_LOG="$test_root/vbox.log" \
|
||||||
|
bash "$repo_root/.github/scripts/cleanup-runner-resources.sh" --dry-run > "$output" 2>&1; then
|
||||||
|
printf '%s\n' 'cleanup unexpectedly accepted a host-only inventory failure' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fq 'cleanup refused: unable to inventory VirtualBox host-only interfaces' "$output"
|
||||||
|
|
||||||
|
PATH="$fake_bin:$PATH" \
|
||||||
|
HOME="$test_root/home" \
|
||||||
|
K3S_CI_MOLECULE_ROOT="$molecule_root" \
|
||||||
|
K3S_CI_MOLECULE_PROJECT=k3s-ansible \
|
||||||
|
K3S_CI_VIRTUALBOX_ROOT="$virtualbox_root" \
|
||||||
|
K3S_CI_HOSTONLY_MARKER="$test_root/hostonly-baseline" \
|
||||||
|
FAKE_VBOX_ROOT="$virtualbox_root" \
|
||||||
|
FAKE_LOG="$test_root/vbox.log" \
|
||||||
|
bash "$repo_root/.github/scripts/cleanup-runner-resources.sh" --dry-run > "$output"
|
||||||
|
|
||||||
|
grep -Fq 'Would remove VM control1 (11111111-1111-1111-1111-111111111111)' "$output"
|
||||||
|
[[ ! -e "$test_root/vbox.log" ]]
|
||||||
|
|
||||||
|
PATH="$fake_bin:$PATH" \
|
||||||
|
HOME="$test_root/home" \
|
||||||
|
K3S_CI_MOLECULE_ROOT="$molecule_root" \
|
||||||
|
K3S_CI_MOLECULE_PROJECT=k3s-ansible \
|
||||||
|
K3S_CI_VIRTUALBOX_ROOT="$virtualbox_root" \
|
||||||
|
K3S_CI_HOSTONLY_MARKER="$test_root/hostonly-baseline" \
|
||||||
|
FAKE_VBOX_ROOT="$virtualbox_root" \
|
||||||
|
FAKE_LOG="$test_root/vbox.log" \
|
||||||
|
bash "$repo_root/.github/scripts/cleanup-runner-resources.sh" --apply > "$output"
|
||||||
|
|
||||||
|
grep -Fq 'controlvm 11111111-1111-1111-1111-111111111111 poweroff' "$test_root/vbox.log"
|
||||||
|
grep -Fq 'unregistervm unregistervm 11111111-1111-1111-1111-111111111111 --delete' "$test_root/vbox.log"
|
||||||
|
[[ ! -e "$virtualbox_root/control1/control1.vbox" ]]
|
||||||
|
[[ -e "$virtualbox_root/unmarked/unmarked.vbox" ]]
|
||||||
|
|
||||||
|
PATH="$fake_bin:$PATH" \
|
||||||
|
HOME="$test_root/home" \
|
||||||
|
K3S_CI_MOLECULE_ROOT="$molecule_root" \
|
||||||
|
K3S_CI_MOLECULE_PROJECT=k3s-ansible \
|
||||||
|
K3S_CI_VIRTUALBOX_ROOT="$virtualbox_root" \
|
||||||
|
K3S_CI_HOSTONLY_MARKER="$test_root/hostonly-baseline" \
|
||||||
|
FAKE_VM_MODE=missing \
|
||||||
|
FAKE_VBOX_ROOT="$virtualbox_root" \
|
||||||
|
FAKE_LOG="$test_root/vbox.log" \
|
||||||
|
bash "$repo_root/.github/scripts/cleanup-runner-resources.sh" --apply > "$output"
|
||||||
|
|
||||||
|
grep -Fq 'Stale Vagrant state without a registered VM' "$output"
|
||||||
|
[[ ! -d "$molecule_root/k3s-ansible/single_node/.vagrant" ]]
|
||||||
|
|
||||||
|
printf 'cleanup-runner-resources fixture test passed\n'
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
[[ "${1:-}" =~ ^[0-9]+$ ]]
|
||||||
Executable
+30
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
printf '%s\n' "$*" >> "$MOCK_VAGRANT_LOG"
|
||||||
|
case "${1:-}" in
|
||||||
|
up)
|
||||||
|
if [[ -n "${MOCK_VAGRANTFILE_CAPTURE:-}" ]]; then
|
||||||
|
cp -- "$VAGRANT_CWD/Vagrantfile" "$MOCK_VAGRANTFILE_CAPTURE"
|
||||||
|
fi
|
||||||
|
counter_file="$MOCK_VBOX_STATE/counter"
|
||||||
|
counter=0
|
||||||
|
[[ ! -r "$counter_file" ]] || counter="$(cat "$counter_file")"
|
||||||
|
counter=$((counter + 1))
|
||||||
|
printf '%s\n' "$counter" > "$counter_file"
|
||||||
|
uuid="00000000-0000-4000-8000-$(printf '%012d' "$counter")"
|
||||||
|
vm_dir="$MOCK_VBOX_ROOT/master-$counter"
|
||||||
|
mkdir -p -- "$vm_dir"
|
||||||
|
: > "$vm_dir/master.vbox"
|
||||||
|
: > "$vm_dir/master.vdi"
|
||||||
|
printf '%s\n' \
|
||||||
|
'VMState="poweroff"' \
|
||||||
|
'groups="/"' \
|
||||||
|
"CfgFile=\"$vm_dir/master.vbox\"" \
|
||||||
|
"SATA-0-0=\"$vm_dir/master.vdi\"" > "$MOCK_VBOX_STATE/vm-$uuid"
|
||||||
|
printf '%s\n' "$uuid" > "$MOCK_BOX_DIR/master_id"
|
||||||
|
;;
|
||||||
|
destroy) ;;
|
||||||
|
*) exit 2 ;;
|
||||||
|
esac
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
printf '%s\n' "$*" >> "$MOCK_VBOX_LOG"
|
||||||
|
command_name="${1:-}"
|
||||||
|
uuid="${2:-}"
|
||||||
|
|
||||||
|
case "$command_name" in
|
||||||
|
showvminfo)
|
||||||
|
[[ -r "$MOCK_VBOX_STATE/vm-$uuid" ]] || exit 1
|
||||||
|
cat "$MOCK_VBOX_STATE/vm-$uuid"
|
||||||
|
;;
|
||||||
|
getextradata)
|
||||||
|
key_slug="${3//\//_}"
|
||||||
|
if [[ ! -r "$MOCK_VBOX_STATE/extra-$uuid-$key_slug" ]]; then
|
||||||
|
printf '%s\n' 'No value set!'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
printf 'Value: '
|
||||||
|
cat "$MOCK_VBOX_STATE/extra-$uuid-$key_slug"
|
||||||
|
;;
|
||||||
|
modifyvm)
|
||||||
|
[[ "${3:-}" == --groups ]]
|
||||||
|
awk -v groups="${4:-}" '
|
||||||
|
$1 !~ /^groups=/ { print }
|
||||||
|
END { printf "groups=\"%s\"\n", groups }
|
||||||
|
' "$MOCK_VBOX_STATE/vm-$uuid" > "$MOCK_VBOX_STATE/vm-$uuid.tmp"
|
||||||
|
mv "$MOCK_VBOX_STATE/vm-$uuid.tmp" "$MOCK_VBOX_STATE/vm-$uuid"
|
||||||
|
;;
|
||||||
|
setextradata)
|
||||||
|
key_slug="${3//\//_}"
|
||||||
|
printf '%s\n' "${4:-}" > "$MOCK_VBOX_STATE/extra-$uuid-$key_slug"
|
||||||
|
;;
|
||||||
|
unregistervm|closemedium)
|
||||||
|
printf '%s\n' 'destructive VirtualBox command invoked' >&2
|
||||||
|
exit 99
|
||||||
|
;;
|
||||||
|
*) exit 2 ;;
|
||||||
|
esac
|
||||||
Executable
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
repo_root="$(git rev-parse --show-toplevel)"
|
||||||
|
main_tasks="$repo_root/roles/k3s_server/tasks/main.yml"
|
||||||
|
join_tasks="$repo_root/roles/k3s_server/tasks/join_master.yml"
|
||||||
|
|
||||||
|
for task_file in "$main_tasks" "$join_tasks"; do
|
||||||
|
for property in \
|
||||||
|
'Delegate=yes' \
|
||||||
|
'TasksMax=infinity' \
|
||||||
|
'KillMode=process' \
|
||||||
|
'LimitNOFILE=1048576' \
|
||||||
|
'LimitNPROC=infinity' \
|
||||||
|
'LimitCORE=infinity'; do
|
||||||
|
grep -Fq -- "$property" "$task_file" || {
|
||||||
|
printf '%s is missing transient K3s property %s\n' "$task_file" "$property" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
if grep -Fq -- "node-role.kubernetes.io/master=true' -o=jsonpath" "$main_tasks"; then
|
||||||
|
printf 'control-plane registration still depends on the optional legacy master role key\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
grep -Fq -- "map('extract', hostvars, 'ansible_hostname')" "$main_tasks"
|
||||||
|
grep -Fq -- 'difference(nodes.stdout.split())' "$main_tasks"
|
||||||
|
grep -Fq -- 'crd/addons.k3s.cattle.io' "$main_tasks"
|
||||||
|
grep -Fq -- 'crd/helmcharts.helm.cattle.io' "$main_tasks"
|
||||||
|
grep -Fq -- 'crd/helmchartconfigs.helm.cattle.io' "$main_tasks"
|
||||||
|
|
||||||
|
printf 'K3s transient bootstrap regression test passed\n'
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
repo_root="$(git rev-parse --show-toplevel)"
|
||||||
|
fixture="$(mktemp -d)"
|
||||||
|
trap 'rm -rf -- "$fixture"' EXIT
|
||||||
|
|
||||||
|
mock_bin="$fixture/bin"
|
||||||
|
mock_state="$fixture/state"
|
||||||
|
mock_home="$fixture/home"
|
||||||
|
mock_vagrant_home="$fixture/vagrant-home"
|
||||||
|
mock_box_dir="$mock_vagrant_home/boxes/bento-VAGRANTSLASH-ubuntu-26.04/202606.01.0/amd64/virtualbox"
|
||||||
|
mock_vbox_root="$mock_home/VirtualBox VMs"
|
||||||
|
mock_master_root="$mock_home/.cache/k3s-ci/vagrant-masters"
|
||||||
|
lock_file="$fixture/vagrant-boxes.lock"
|
||||||
|
mkdir -p -- "$mock_bin" "$mock_state" "$mock_box_dir" "$mock_vbox_root"
|
||||||
|
printf '%s\n' 'bento/ubuntu-26.04 202606.01.0 amd64' > "$lock_file"
|
||||||
|
ln -s "$repo_root/.github/scripts/test-fixtures/mock-vboxmanage" "$mock_bin/VBoxManage"
|
||||||
|
ln -s "$repo_root/.github/scripts/test-fixtures/mock-vagrant" "$mock_bin/vagrant"
|
||||||
|
ln -s "$repo_root/.github/scripts/test-fixtures/mock-flock" "$mock_bin/flock"
|
||||||
|
|
||||||
|
export PATH="$mock_bin:$PATH"
|
||||||
|
export HOME="$mock_home"
|
||||||
|
export VAGRANT_HOME="$mock_vagrant_home"
|
||||||
|
export VAGRANT_BOX_LOCK_FILE="$lock_file"
|
||||||
|
export K3S_CI_REPOSITORY_ROOT="$repo_root"
|
||||||
|
export K3S_CI_VAGRANT_MASTER_ROOT="$mock_master_root"
|
||||||
|
export K3S_CI_VIRTUALBOX_ROOT="$mock_vbox_root"
|
||||||
|
export MOCK_VBOX_STATE="$mock_state"
|
||||||
|
export MOCK_VBOX_ROOT="$mock_vbox_root"
|
||||||
|
export MOCK_BOX_DIR="$mock_box_dir"
|
||||||
|
export MOCK_VBOX_LOG="$fixture/vbox.log"
|
||||||
|
export MOCK_VAGRANT_LOG="$fixture/vagrant.log"
|
||||||
|
export MOCK_VAGRANTFILE_CAPTURE="$fixture/prewarm-Vagrantfile"
|
||||||
|
: > "$MOCK_VBOX_LOG"
|
||||||
|
: > "$MOCK_VAGRANT_LOG"
|
||||||
|
|
||||||
|
unowned_uuid='99999999-9999-4999-8999-999999999999'
|
||||||
|
printf '%s\n' "$unowned_uuid" > "$mock_box_dir/master_id"
|
||||||
|
|
||||||
|
script="$repo_root/.github/scripts/prepare-vagrant-box-masters.sh"
|
||||||
|
first_output="$fixture/first-output"
|
||||||
|
second_output="$fixture/second-output"
|
||||||
|
third_output="$fixture/third-output"
|
||||||
|
|
||||||
|
"$script" > "$first_output"
|
||||||
|
if grep -Fq 'config.ssh.insert_key' "$MOCK_VAGRANTFILE_CAPTURE"; then
|
||||||
|
printf 'prewarm Vagrantfile unexpectedly overrides Vagrant SSH key insertion\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fq 'virtualbox.memory = 1024' "$MOCK_VAGRANTFILE_CAPTURE"
|
||||||
|
grep -Fq 'virtualbox.cpus = 2' "$MOCK_VAGRANTFILE_CAPTURE"
|
||||||
|
grep -Fq 'config.vm.boot_timeout = 600' "$MOCK_VAGRANTFILE_CAPTURE"
|
||||||
|
mapping_file="$mock_master_root/bento_ubuntu-26.04-202606.01.0-amd64.uuid"
|
||||||
|
test -s "$mapping_file"
|
||||||
|
cmp -s "$mapping_file" "$mock_box_dir/master_id"
|
||||||
|
grep -Fq 'Created and recorded owned master' "$first_output"
|
||||||
|
grep -Fq 'modifyvm' "$MOCK_VBOX_LOG"
|
||||||
|
grep -Fq 'setextradata' "$MOCK_VBOX_LOG"
|
||||||
|
if grep -Fq "$unowned_uuid" "$MOCK_VBOX_LOG"; then
|
||||||
|
printf 'unowned cached master UUID was unexpectedly inspected or modified\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if grep -Eq 'unregistervm|closemedium' "$MOCK_VBOX_LOG"; then
|
||||||
|
printf 'master preparation invoked a destructive VirtualBox command\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
: > "$MOCK_VAGRANT_LOG"
|
||||||
|
"$script" > "$second_output"
|
||||||
|
grep -Fq 'Reusing owned master' "$second_output"
|
||||||
|
if grep -Fq 'up ' "$MOCK_VAGRANT_LOG"; then
|
||||||
|
printf 'valid owned master was unexpectedly rebuilt\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
stale_uuid="$(tr -d '[:space:]' < "$mapping_file")"
|
||||||
|
rm -f -- "$mock_state/vm-$stale_uuid"
|
||||||
|
: > "$MOCK_VAGRANT_LOG"
|
||||||
|
"$script" > "$third_output"
|
||||||
|
grep -Fq 'rebuilding without deleting any VM or disk' "$third_output"
|
||||||
|
grep -Fq 'up ' "$MOCK_VAGRANT_LOG"
|
||||||
|
if grep -Eq 'unregistervm|closemedium' "$MOCK_VBOX_LOG"; then
|
||||||
|
printf 'stale master recovery invoked a destructive VirtualBox command\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'Vagrant box master preparation fixture test passed\n'
|
||||||
Executable
+24
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
if (($# < 2)); then
|
||||||
|
printf 'Usage: vagrant-up-timed.sh WORKDIR MACHINE [MACHINE ...]\n' >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
workdir="$1"
|
||||||
|
shift
|
||||||
|
timing_log="${K3S_CI_CREATE_TIMING_LOG:-${RUNNER_TEMP:-/tmp}/k3s-ci-create-timing.log}"
|
||||||
|
mkdir -p -- "${timing_log%/*}"
|
||||||
|
|
||||||
|
printf '%s batch-start machines=%s\n' "$(date --iso-8601=ns)" "$*" | tee -a "$timing_log"
|
||||||
|
set +e
|
||||||
|
VAGRANT_CWD="$workdir" vagrant up "$@" --provider virtualbox --no-provision 2>&1 |
|
||||||
|
while IFS= read -r line; do
|
||||||
|
printf '%s %s\n' "$(date --iso-8601=ns)" "$line"
|
||||||
|
done | tee -a "$timing_log"
|
||||||
|
rc=${PIPESTATUS[0]}
|
||||||
|
set -e
|
||||||
|
printf '%s batch-end rc=%d machines=%s\n' "$(date --iso-8601=ns)" "$rc" "$*" | tee -a "$timing_log"
|
||||||
|
exit "$rc"
|
||||||
Executable
+98
@@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# The single-quoted expressions below are written into fake executables and
|
||||||
|
# intentionally expand only when those executables run.
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
repo_root=$(git rev-parse --show-toplevel)
|
||||||
|
test_root=$(mktemp -d)
|
||||||
|
fake_bin="$test_root/bin"
|
||||||
|
fake_log="$test_root/vagrant.log"
|
||||||
|
output="$test_root/output.txt"
|
||||||
|
mkdir -p "$fake_bin"
|
||||||
|
trap 'rm -rf "$test_root"' EXIT
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
'#!/usr/bin/env bash' \
|
||||||
|
'set -Eeuo pipefail' \
|
||||||
|
'printf "%s\n" bento/debian-13 bento/rockylinux-10.1 bento/ubuntu-26.04' \
|
||||||
|
>"$fake_bin/yq"
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
'#!/usr/bin/env bash' \
|
||||||
|
'set -Eeuo pipefail' \
|
||||||
|
'emit_box() {' \
|
||||||
|
' case "$1" in' \
|
||||||
|
' bento/debian-13) version=202510.26.0 ;;' \
|
||||||
|
' bento/rockylinux-10.1) version=202512.01.0 ;;' \
|
||||||
|
' bento/ubuntu-26.04) version=202606.01.0 ;;' \
|
||||||
|
' *) printf "Unexpected box: %s\n" "$1" >&2; exit 1 ;;' \
|
||||||
|
' esac' \
|
||||||
|
' printf "0,,box-name,%s\n" "$1"' \
|
||||||
|
' printf "0,,box-provider,virtualbox\n"' \
|
||||||
|
' printf "0,,box-version,%s\n" "$version"' \
|
||||||
|
' printf "0,,box-architecture,amd64\n"' \
|
||||||
|
'}' \
|
||||||
|
'if [[ "${1:-}" == box && "${2:-}" == list ]]; then' \
|
||||||
|
' emit_box bento/debian-13' \
|
||||||
|
' emit_box bento/rockylinux-10.1' \
|
||||||
|
' if [[ "${FAKE_PRESENT_MODE:-all}" == all ]]; then' \
|
||||||
|
' emit_box bento/ubuntu-26.04' \
|
||||||
|
' fi' \
|
||||||
|
'elif [[ "${1:-}" == box && "${2:-}" == add ]]; then' \
|
||||||
|
' printf "%s\n" "$*" >>"${FAKE_VAGRANT_LOG:?}"' \
|
||||||
|
'else' \
|
||||||
|
' printf "Unexpected vagrant arguments: %s\n" "$*" >&2' \
|
||||||
|
' exit 1' \
|
||||||
|
'fi' \
|
||||||
|
>"$fake_bin/vagrant"
|
||||||
|
chmod +x "$fake_bin/yq" "$fake_bin/vagrant"
|
||||||
|
|
||||||
|
PATH="$fake_bin:$PATH" \
|
||||||
|
FAKE_VAGRANT_LOG="$fake_log" \
|
||||||
|
"$repo_root/.github/download-boxes.sh" >"$output"
|
||||||
|
grep -Fq 'All pinned Vagrant boxes are already present.' "$output"
|
||||||
|
[[ ! -e "$fake_log" ]]
|
||||||
|
|
||||||
|
PATH="$fake_bin:$PATH" \
|
||||||
|
FAKE_PRESENT_MODE=partial \
|
||||||
|
FAKE_VAGRANT_LOG="$fake_log" \
|
||||||
|
"$repo_root/.github/download-boxes.sh" >"$output"
|
||||||
|
grep -Fxq \
|
||||||
|
'box add --provider virtualbox --box-version 202606.01.0 --architecture amd64 bento/ubuntu-26.04' \
|
||||||
|
"$fake_log"
|
||||||
|
|
||||||
|
incomplete_lock="$test_root/incomplete.lock"
|
||||||
|
printf '%s\n' \
|
||||||
|
'bento/debian-13 202510.26.0 amd64' \
|
||||||
|
'bento/rockylinux-10.1 202512.01.0 amd64' \
|
||||||
|
>"$incomplete_lock"
|
||||||
|
if PATH="$fake_bin:$PATH" \
|
||||||
|
VAGRANT_BOX_LOCK_FILE="$incomplete_lock" \
|
||||||
|
FAKE_VAGRANT_LOG="$fake_log" \
|
||||||
|
"$repo_root/.github/download-boxes.sh" >"$output" 2>&1; then
|
||||||
|
printf 'Download script accepted a lock missing a scenario box.\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fq 'Scenario boxes missing from the lock file:' "$output"
|
||||||
|
grep -Fq 'bento/ubuntu-26.04' "$output"
|
||||||
|
|
||||||
|
duplicate_lock="$test_root/duplicate.lock"
|
||||||
|
printf '%s\n' \
|
||||||
|
'bento/debian-13 202510.26.0 amd64' \
|
||||||
|
'bento/debian-13 202508.10.0 amd64' \
|
||||||
|
'bento/rockylinux-10.1 202512.01.0 amd64' \
|
||||||
|
'bento/ubuntu-26.04 202606.01.0 amd64' \
|
||||||
|
>"$duplicate_lock"
|
||||||
|
if PATH="$fake_bin:$PATH" \
|
||||||
|
VAGRANT_BOX_LOCK_FILE="$duplicate_lock" \
|
||||||
|
FAKE_VAGRANT_LOG="$fake_log" \
|
||||||
|
"$repo_root/.github/download-boxes.sh" >"$output" 2>&1; then
|
||||||
|
printf 'Download script accepted duplicate box lock entries.\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fq 'Duplicate Vagrant box lock entries:' "$output"
|
||||||
|
|
||||||
|
printf 'Vagrant box download tests passed.\n'
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# box version architecture
|
||||||
|
bento/debian-13 202510.26.0 amd64
|
||||||
|
bento/rockylinux-10.1 202512.01.0 amd64
|
||||||
|
bento/ubuntu-26.04 202606.01.0 amd64
|
||||||
+29
-14
@@ -5,38 +5,53 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
molecule:
|
molecule:
|
||||||
name: cache
|
name: cache
|
||||||
runs-on: self-hosted
|
runs-on: [self-hosted, linux, x64, k3s-ci, virtualbox, nested-virt]
|
||||||
env:
|
env:
|
||||||
PYTHON_VERSION: "3.11"
|
PYTHON_VERSION: "3.11"
|
||||||
|
VAGRANT_DEFAULT_PROVIDER: virtualbox
|
||||||
|
VAGRANT_HOME: ${{ github.workspace }}/.vagrant-home
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the codebase
|
- name: Check out the codebase
|
||||||
uses: actions/checkout@e2f20e631ae6d7dd3b768f56a5d2af784dd54791 # v3 2.5.0
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # 7.0.1
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
|
- name: Check nested VirtualBox platform
|
||||||
|
run: |
|
||||||
|
set -Eeuo pipefail
|
||||||
|
grep -Eq 'vmx|svm' /proc/cpuinfo
|
||||||
|
test -c /dev/kvm
|
||||||
|
test -c /dev/vboxdrv
|
||||||
|
VBoxManage --version
|
||||||
|
vagrant --version
|
||||||
|
test -r /etc/vbox/networks.conf
|
||||||
|
test "$(stat -c '%u' /etc/vbox/networks.conf)" -eq 0
|
||||||
|
free -h
|
||||||
|
df -Pk "${RUNNER_TEMP}"
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@75f3110429a8c05be0e1bf360334e4cced2b63fa # 2.3.3
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # 7.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
cache: 'pip' # caching pip dependencies
|
cache: 'pip' # caching pip dependencies
|
||||||
|
|
||||||
- name: Cache Vagrant boxes
|
- name: Cache Vagrant boxes
|
||||||
id: cache-vagrant
|
id: cache-vagrant
|
||||||
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # 4.0
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # 6.1.0
|
||||||
with:
|
with:
|
||||||
lookup-only: true #if it exists, we don't need to restore and can skip the next step
|
# This producer only needs to know whether the immutable cache exists.
|
||||||
|
# Molecule jobs restore it after this job completes.
|
||||||
|
lookup-only: true
|
||||||
path: |
|
path: |
|
||||||
~/.vagrant.d/boxes
|
.vagrant-home/boxes
|
||||||
key: vagrant-boxes-${{ hashFiles('**/molecule.yml') }}
|
key: vagrant-boxes-${{ runner.name }}-${{ runner.os }}-${{ runner.arch }}-virtualbox-7.2-vagrant-2.4-${{ hashFiles('.github/vagrant-boxes.lock') }} # yamllint disable-line rule:line-length
|
||||||
restore-keys: |
|
|
||||||
vagrant-boxes
|
|
||||||
|
|
||||||
- name: Download Vagrant boxes for all scenarios
|
- name: Download Vagrant boxes for all scenarios
|
||||||
# To save some cache space, all scenarios share the same cache key.
|
# An exact hit skips both cache restoration and upstream downloads.
|
||||||
# On the other hand, this means that the cache contents should be
|
# A lock change builds and saves one clean, version-pinned cache.
|
||||||
# the same across all scenarios. This step ensures that.
|
if: steps.cache-vagrant.outputs.cache-hit != 'true'
|
||||||
if: steps.cache-vagrant.outputs.cache-hit != 'true' # only run if false since this is just a cache step
|
|
||||||
run: |
|
run: |
|
||||||
./.github/download-boxes.sh
|
./.github/download-boxes.sh
|
||||||
|
./.github/scripts/prepare-vagrant-box-masters.sh
|
||||||
vagrant box list
|
vagrant box list
|
||||||
@@ -2,17 +2,39 @@
|
|||||||
name: "CI"
|
name: "CI"
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- synchronize
|
||||||
|
- reopened
|
||||||
|
- ready_for_review
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '**/README.md'
|
- '**/.gitignore'
|
||||||
|
- '**/FUNDING.yml'
|
||||||
|
- '**/host.ini'
|
||||||
|
- '**/*.md'
|
||||||
|
- '**/.editorconfig'
|
||||||
|
- '**/ansible.example.cfg'
|
||||||
|
- '**/deploy.sh'
|
||||||
|
- '**/LICENSE'
|
||||||
|
- '**/reboot.sh'
|
||||||
|
- '**/reset.sh'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ci-${{ github.event.pull_request.number || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre:
|
pre:
|
||||||
uses: ./.github/workflows/cache.yml
|
uses: ./.github/workflows/cache.yml
|
||||||
|
needs: [lint]
|
||||||
lint:
|
lint:
|
||||||
uses: ./.github/workflows/lint.yml
|
uses: ./.github/workflows/lint.yml
|
||||||
needs: [pre]
|
|
||||||
test:
|
test:
|
||||||
uses: ./.github/workflows/test.yml
|
uses: ./.github/workflows/test.yml
|
||||||
needs: [pre, lint]
|
needs: [pre, lint]
|
||||||
+31
-10
@@ -5,24 +5,25 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
pre-commit-ci:
|
pre-commit-ci:
|
||||||
name: Pre-Commit
|
name: Pre-Commit
|
||||||
runs-on: self-hosted
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
PYTHON_VERSION: "3.11"
|
PYTHON_VERSION: "3.12"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the codebase
|
- name: Check out the codebase
|
||||||
uses: actions/checkout@e2f20e631ae6d7dd3b768f56a5d2af784dd54791 # v3 2.5.0
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # 7.0.1
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@75f3110429a8c05be0e1bf360334e4cced2b63fa # 2.3.3
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # 7.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
cache: 'pip' # caching pip dependencies
|
cache: 'pip' # caching pip dependencies
|
||||||
|
|
||||||
- name: Restore Ansible cache
|
- name: Restore Ansible cache
|
||||||
uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # 4.0
|
id: cache-ansible
|
||||||
|
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # 6.1.0
|
||||||
with:
|
with:
|
||||||
path: ~/.ansible/collections
|
path: ~/.ansible/collections
|
||||||
key: ansible-${{ hashFiles('collections/requirements.yml') }}
|
key: ansible-${{ hashFiles('collections/requirements.yml') }}
|
||||||
@@ -37,17 +38,37 @@ jobs:
|
|||||||
python3 -m pip install -r requirements.txt
|
python3 -m pip install -r requirements.txt
|
||||||
echo "::endgroup::"
|
echo "::endgroup::"
|
||||||
|
|
||||||
|
- name: Install Ansible collections with retries
|
||||||
|
if: steps.cache-ansible.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
set -Eeuo pipefail
|
||||||
|
for attempt in 1 2 3 4 5; do
|
||||||
|
if ansible-galaxy collection install -r collections/requirements.yml; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "Ansible Galaxy attempt ${attempt} failed; retrying."
|
||||||
|
sleep $((attempt * 10))
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
- name: Save Ansible collection cache
|
||||||
|
if: steps.cache-ansible.outputs.cache-hit != 'true'
|
||||||
|
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # 6.1.0
|
||||||
|
with:
|
||||||
|
path: ~/.ansible/collections
|
||||||
|
key: ansible-${{ hashFiles('collections/requirements.yml') }}
|
||||||
|
|
||||||
- name: Run pre-commit
|
- name: Run pre-commit
|
||||||
uses: pre-commit/action@646c83fcd040023954eafda54b4db0192ce70507 # 3.0.0
|
uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # 3.0.1
|
||||||
|
|
||||||
ensure-pinned-actions:
|
ensure-pinned-actions:
|
||||||
name: Ensure SHA Pinned Actions
|
name: Ensure SHA Pinned Actions
|
||||||
runs-on: self-hosted
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@e2f20e631ae6d7dd3b768f56a5d2af784dd54791 # v3 2.5.0
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # 7.0.1
|
||||||
- name: Ensure SHA pinned actions
|
- name: Ensure SHA pinned actions
|
||||||
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@af2eb3226618e2494e3d9084f515ad6dcf16e229 # 2.0.1
|
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@3db98c0363e2fa5df3e1c4c471777a7c10b24cc9 # 5.0.5
|
||||||
with:
|
with:
|
||||||
allowlist: |
|
allowlist: |
|
||||||
aws-actions/
|
aws-actions/
|
||||||
|
|||||||
+65
-23
@@ -5,44 +5,64 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
molecule:
|
molecule:
|
||||||
name: Molecule
|
name: Molecule
|
||||||
runs-on: self-hosted
|
runs-on: [self-hosted, linux, x64, k3s-ci, virtualbox, nested-virt]
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
scenario:
|
scenario:
|
||||||
- default
|
- default
|
||||||
- ipv6
|
|
||||||
- single_node
|
- single_node
|
||||||
fail-fast: false
|
- calico
|
||||||
|
- cilium
|
||||||
|
- kube-vip
|
||||||
|
# - ipv6
|
||||||
|
fail-fast: true
|
||||||
|
max-parallel: 1
|
||||||
env:
|
env:
|
||||||
PYTHON_VERSION: "3.11"
|
PYTHON_VERSION: "3.11"
|
||||||
|
VAGRANT_DEFAULT_PROVIDER: virtualbox
|
||||||
|
VAGRANT_HOME: ${{ github.workspace }}/.vagrant-home
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the codebase
|
- name: Check out the codebase
|
||||||
uses: actions/checkout@e2f20e631ae6d7dd3b768f56a5d2af784dd54791 # v3 2.5.0
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # 7.0.1
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
- name: Configure VirtualBox
|
- name: Clean repository-owned resources before testing
|
||||||
run: |-
|
run: ./.github/scripts/cleanup-runner-resources.sh --apply
|
||||||
sudo mkdir -p /etc/vbox
|
|
||||||
cat <<EOF | sudo tee -a /etc/vbox/networks.conf > /dev/null
|
- name: Record host-only network baseline
|
||||||
* 192.168.30.0/24
|
run: ./.github/scripts/cleanup-runner-resources.sh --snapshot
|
||||||
* fdad:bad:ba55::/64
|
|
||||||
EOF
|
- name: Check nested VirtualBox platform
|
||||||
|
run: |
|
||||||
|
set -Eeuo pipefail
|
||||||
|
grep -Eq 'vmx|svm' /proc/cpuinfo
|
||||||
|
test -c /dev/kvm
|
||||||
|
test -c /dev/vboxdrv
|
||||||
|
VBoxManage --version
|
||||||
|
vagrant --version
|
||||||
|
test -r /etc/vbox/networks.conf
|
||||||
|
test "$(stat -c '%u' /etc/vbox/networks.conf)" -eq 0
|
||||||
|
free -h
|
||||||
|
df -Pk "${RUNNER_TEMP}"
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@75f3110429a8c05be0e1bf360334e4cced2b63fa # 2.3.3
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # 7.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
cache: 'pip' # caching pip dependencies
|
cache: 'pip' # caching pip dependencies
|
||||||
|
|
||||||
- name: Restore vagrant Boxes cache
|
- name: Restore vagrant Boxes cache
|
||||||
uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # 4.0
|
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # 6.1.0
|
||||||
with:
|
with:
|
||||||
path: ~/.vagrant.d/boxes
|
path: .vagrant-home/boxes
|
||||||
key: vagrant-boxes-${{ hashFiles('**/molecule.yml') }}
|
key: vagrant-boxes-${{ runner.name }}-${{ runner.os }}-${{ runner.arch }}-virtualbox-7.2-vagrant-2.4-${{ hashFiles('.github/vagrant-boxes.lock') }} # yamllint disable-line rule:line-length
|
||||||
fail-on-cache-miss: true
|
fail-on-cache-miss: true
|
||||||
|
|
||||||
|
- name: Prepare runner-owned Vagrant box masters
|
||||||
|
run: ./.github/scripts/prepare-vagrant-box-masters.sh
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
echo "::group::Upgrade pip"
|
echo "::group::Upgrade pip"
|
||||||
@@ -54,7 +74,20 @@ jobs:
|
|||||||
echo "::endgroup::"
|
echo "::endgroup::"
|
||||||
|
|
||||||
- name: Test with molecule
|
- name: Test with molecule
|
||||||
run: molecule test --scenario-name ${{ matrix.scenario }}
|
run: |
|
||||||
|
set -Eeuo pipefail
|
||||||
|
resource_dir="${RUNNER_TEMP}/logs/resources/${{ matrix.scenario }}"
|
||||||
|
timing_file="${RUNNER_TEMP}/logs/timing/${{ matrix.scenario }}.txt"
|
||||||
|
mkdir -p -- "${timing_file%/*}"
|
||||||
|
./.github/scripts/monitor-runner-resources.sh "$resource_dir" 10 &
|
||||||
|
monitor_pid=$!
|
||||||
|
stop_monitor() {
|
||||||
|
kill -TERM "$monitor_pid" 2>/dev/null || true
|
||||||
|
wait "$monitor_pid" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
trap stop_monitor EXIT
|
||||||
|
/usr/bin/time -v -o "$timing_file" \
|
||||||
|
molecule test --scenario-name ${{ matrix.scenario }}
|
||||||
timeout-minutes: 90
|
timeout-minutes: 90
|
||||||
env:
|
env:
|
||||||
ANSIBLE_K3S_LOG_DIR: ${{ runner.temp }}/logs/k3s-ansible/${{ matrix.scenario }}
|
ANSIBLE_K3S_LOG_DIR: ${{ runner.temp }}/logs/k3s-ansible/${{ matrix.scenario }}
|
||||||
@@ -62,15 +95,24 @@ jobs:
|
|||||||
ANSIBLE_TIMEOUT: 120
|
ANSIBLE_TIMEOUT: 120
|
||||||
PY_COLORS: 1
|
PY_COLORS: 1
|
||||||
ANSIBLE_FORCE_COLOR: 1
|
ANSIBLE_FORCE_COLOR: 1
|
||||||
|
K3S_CI_CREATE_TIMING_LOG: ${{ runner.temp }}/logs/timing/${{ matrix.scenario }}-create.log
|
||||||
|
|
||||||
|
- name: Collect runner diagnostics
|
||||||
|
if: always()
|
||||||
|
run: ./.github/scripts/collect-runner-diagnostics.sh "${RUNNER_TEMP}/logs/runner"
|
||||||
|
env:
|
||||||
|
K3S_CI_SCENARIO_NAME: ${{ matrix.scenario }}
|
||||||
|
|
||||||
|
- name: Clean repository-owned resources after testing
|
||||||
|
if: always()
|
||||||
|
run: ./.github/scripts/cleanup-runner-resources.sh --apply
|
||||||
|
|
||||||
- name: Upload log files
|
- name: Upload log files
|
||||||
if: always() # do this even if a step before has failed
|
if: always() # do this even if a step before has failed
|
||||||
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # 3.1.1
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # 7.0.1
|
||||||
with:
|
with:
|
||||||
name: logs
|
name: logs-${{ matrix.scenario }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
path: |
|
path: |
|
||||||
${{ runner.temp }}/logs
|
${{ runner.temp }}/logs
|
||||||
|
if-no-files-found: warn
|
||||||
- name: Delete old box versions
|
retention-days: 14
|
||||||
if: always() # do this even if a step before has failed
|
|
||||||
run: vagrant box prune --force
|
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
.env/
|
.env/
|
||||||
*.log
|
*.log
|
||||||
ansible.cfg
|
ansible.cfg
|
||||||
|
.ansible/
|
||||||
|
kubeconfig
|
||||||
|
zIgnore/
|
||||||
+35
-6
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: f71fa2c1f9cf5cb705f73dffe4b21f7c61470ba9 # frozen: v4.4.0
|
rev: v4.5.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: requirements-txt-fixer
|
- id: requirements-txt-fixer
|
||||||
- id: sort-simple-yaml
|
- id: sort-simple-yaml
|
||||||
@@ -12,24 +12,53 @@ repos:
|
|||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
args: [--markdown-linebreak-ext=md]
|
args: [--markdown-linebreak-ext=md]
|
||||||
- repo: https://github.com/adrienverge/yamllint.git
|
- repo: https://github.com/adrienverge/yamllint.git
|
||||||
rev: b05e028c5881819161d11cb543fd96a30c06cceb # frozen: v1.32.0
|
rev: v1.33.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: yamllint
|
- id: yamllint
|
||||||
args: [-c=.yamllint]
|
args: [-c=.yamllint]
|
||||||
- repo: https://github.com/ansible-community/ansible-lint.git
|
- repo: https://github.com/ansible-community/ansible-lint.git
|
||||||
rev: 3293b64b939c0de16ef8cb81dd49255e475bf89a # frozen: v6.17.2
|
rev: v6.22.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: ansible-lint
|
- id: ansible-lint
|
||||||
|
additional_dependencies: [ansible-core==2.18.0]
|
||||||
|
language_version: python3.12
|
||||||
|
args: [--offline]
|
||||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||||
rev: 375289a39f5708101b1f916eb729e8d6da96993f # frozen: v0.9.0.5
|
rev: v0.9.0.6
|
||||||
hooks:
|
hooks:
|
||||||
- id: shellcheck
|
- id: shellcheck
|
||||||
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
||||||
rev: 12885e376b93dc4536ad68d156065601e4433665 # frozen: v1.5.1
|
rev: v1.5.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: remove-crlf
|
- id: remove-crlf
|
||||||
- id: remove-tabs
|
- id: remove-tabs
|
||||||
- repo: https://github.com/sirosen/texthooks
|
- repo: https://github.com/sirosen/texthooks
|
||||||
rev: c4ffd3e31669dd4fa4d31a23436cc13839730084 # frozen: 0.5.0
|
rev: 0.6.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: fix-smartquotes
|
- id: fix-smartquotes
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: cleanup-runner-resources-test
|
||||||
|
name: cleanup runner resources test
|
||||||
|
entry: .github/scripts/test-cleanup-runner-resources.sh
|
||||||
|
language: system
|
||||||
|
pass_filenames: false
|
||||||
|
files: ^\.github/scripts/(cleanup-runner-resources|test-cleanup-runner-resources)\.sh$
|
||||||
|
- id: download-vagrant-boxes-test
|
||||||
|
name: Vagrant box download test
|
||||||
|
entry: .github/test-download-boxes.sh
|
||||||
|
language: system
|
||||||
|
pass_filenames: false
|
||||||
|
files: ^\.github/(download-boxes|test-download-boxes)\.sh$|^\.github/vagrant-boxes\.lock$
|
||||||
|
- id: prepare-vagrant-box-masters-test
|
||||||
|
name: Vagrant box master preparation test
|
||||||
|
entry: .github/scripts/test-prepare-vagrant-box-masters.sh
|
||||||
|
language: system
|
||||||
|
pass_filenames: false
|
||||||
|
files: ^\.github/scripts/(prepare-vagrant-box-masters|test-prepare-vagrant-box-masters)\.sh$
|
||||||
|
- id: k3s-server-bootstrap-test
|
||||||
|
name: K3s transient bootstrap test
|
||||||
|
entry: .github/scripts/test-k3s-server-bootstrap.sh
|
||||||
|
language: system
|
||||||
|
pass_filenames: false
|
||||||
|
files: ^roles/k3s_server/tasks/(main|join_master)\.yml$|^\.github/scripts/test-k3s-server-bootstrap\.sh$
|
||||||
@@ -2,10 +2,19 @@
|
|||||||
extends: default
|
extends: default
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
|
comments:
|
||||||
|
min-spaces-from-content: 1
|
||||||
|
comments-indentation: false
|
||||||
|
braces:
|
||||||
|
max-spaces-inside: 1
|
||||||
|
octal-values:
|
||||||
|
forbid-implicit-octal: true
|
||||||
|
forbid-explicit-octal: true
|
||||||
line-length:
|
line-length:
|
||||||
max: 120
|
max: 120
|
||||||
level: warning
|
level: warning
|
||||||
truthy:
|
truthy:
|
||||||
allowed-values: ['true', 'false']
|
allowed-values: ["true", "false"]
|
||||||
|
|
||||||
ignore:
|
ignore:
|
||||||
- galaxy.yml
|
- galaxy.yml
|
||||||
@@ -8,7 +8,7 @@ This is based on the work from [this fork](https://github.com/212850a/k3s-ansibl
|
|||||||
|
|
||||||
If you want more context on how this works, see:
|
If you want more context on how this works, see:
|
||||||
|
|
||||||
📄 [Documentation](https://technotim.live/posts/k3s-etcd-ansible/) (including example commands)
|
📄 [Documentation](https://technotim.com/posts/k3s-etcd-ansible/) (including example commands)
|
||||||
|
|
||||||
📺 [Watch the Video](https://www.youtube.com/watch?v=CbkEWcUZ7zM)
|
📺 [Watch the Video](https://www.youtube.com/watch?v=CbkEWcUZ7zM)
|
||||||
|
|
||||||
@@ -16,9 +16,9 @@ If you want more context on how this works, see:
|
|||||||
|
|
||||||
Build a Kubernetes cluster using Ansible with k3s. The goal is easily install a HA Kubernetes cluster on machines running:
|
Build a Kubernetes cluster using Ansible with k3s. The goal is easily install a HA Kubernetes cluster on machines running:
|
||||||
|
|
||||||
- [x] Debian (tested on version 11)
|
- [x] Debian (tested on version 13)
|
||||||
- [x] Ubuntu (tested on version 22.04)
|
- [x] Ubuntu (tested on version 26.04 LTS)
|
||||||
- [x] Rocky (tested on version 9)
|
- [x] Rocky (tested on version 10)
|
||||||
|
|
||||||
on processor architecture:
|
on processor architecture:
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ on processor architecture:
|
|||||||
|
|
||||||
## ✅ System requirements
|
## ✅ System requirements
|
||||||
|
|
||||||
- Control Node (the machine you are running `ansible` commands) must have Ansible 2.11+ If you need a quick primer on Ansible [you can check out my docs and setting up Ansible](https://technotim.live/posts/ansible-automation/).
|
- Control Node (the machine you are running `ansible` commands) must have Ansible 2.11+ If you need a quick primer on Ansible [you can check out my docs and setting up Ansible](https://technotim.com/posts/ansible-automation/).
|
||||||
|
|
||||||
- You will also need to install collections that this playbook uses by running `ansible-galaxy collection install -r ./collections/requirements.yml` (important❗)
|
- You will also need to install collections that this playbook uses by running `ansible-galaxy collection install -r ./collections/requirements.yml` (important❗)
|
||||||
|
|
||||||
@@ -96,16 +96,102 @@ ansible-playbook reset.yml -i inventory/my-cluster/hosts.ini
|
|||||||
To copy your `kube config` locally so that you can access your **Kubernetes** cluster run:
|
To copy your `kube config` locally so that you can access your **Kubernetes** cluster run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scp debian@master_ip:~/.kube/config ~/.kube/config
|
scp debian@master_ip:/etc/rancher/k3s/k3s.yaml ~/.kube/config
|
||||||
```
|
```
|
||||||
|
If you get file Permission denied, go into the node and temporarly run:
|
||||||
|
```bash
|
||||||
|
sudo chmod 777 /etc/rancher/k3s/k3s.yaml
|
||||||
|
```
|
||||||
|
Then copy with the scp command and reset the permissions back to:
|
||||||
|
```bash
|
||||||
|
sudo chmod 600 /etc/rancher/k3s/k3s.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll then want to modify the config to point to master IP by running:
|
||||||
|
```bash
|
||||||
|
sudo nano ~/.kube/config
|
||||||
|
```
|
||||||
|
Then change `server: https://127.0.0.1:6443` to match your master IP: `server: https://192.168.1.222:6443`
|
||||||
|
|
||||||
### 🔨 Testing your cluster
|
### 🔨 Testing your cluster
|
||||||
|
|
||||||
See the commands [here](https://technotim.live/posts/k3s-etcd-ansible/#testing-your-cluster).
|
See the commands [here](https://technotim.com/posts/k3s-etcd-ansible/#testing-your-cluster).
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Role(s) | Variable | Type | Default | Required | Description |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| `download` | `k3s_version` | string | ❌ | Required | K3s binaries version |
|
||||||
|
| `k3s_agent`, `k3s_server`, `k3s_server_post` | `apiserver_endpoint` | string | ❌ | Required | Virtual ip-address configured on each master |
|
||||||
|
| `k3s_agent` | `extra_agent_args` | string | `null` | Not required | Extra arguments for agents nodes |
|
||||||
|
| `k3s_agent`, `k3s_server` | `group_name_master` | string | `null` | Not required | Name othe master group |
|
||||||
|
| `k3s_agent` | `k3s_token` | string | `null` | Not required | Token used to communicate between masters |
|
||||||
|
| `k3s_agent`, `k3s_server` | `proxy_env` | dict | `null` | Not required | Internet proxy configurations |
|
||||||
|
| `k3s_agent`, `k3s_server` | `proxy_env.HTTP_PROXY` | string | ❌ | Required | HTTP internet proxy |
|
||||||
|
| `k3s_agent`, `k3s_server` | `proxy_env.HTTPS_PROXY` | string | ❌ | Required | HTTP internet proxy |
|
||||||
|
| `k3s_agent`, `k3s_server` | `proxy_env.NO_PROXY` | string | ❌ | Required | Addresses that will not use the proxies |
|
||||||
|
| `k3s_agent`, `k3s_server`, `reset` | `systemd_dir` | string | `/etc/systemd/system` | Not required | Path to systemd services |
|
||||||
|
| `k3s_custom_registries` | `custom_registries_yaml` | string | ❌ | Required | YAML block defining custom registries. The following is an example that pulls all images used in this playbook through your private registries. It also allows you to pull your own images from your private registry, without having to use imagePullSecrets in your deployments. If all you need is your own images and you don't care about caching the docker/quay/ghcr.io images, you can just remove those from the mirrors: section. |
|
||||||
|
| `k3s_server`, `k3s_server_post` | `cilium_bgp` | bool | `~` | Not required | Enable cilium BGP control plane for LB services and pod cidrs. Disables the use of MetalLB. |
|
||||||
|
| `k3s_server`, `k3s_server_post` | `cilium_iface` | string | ❌ | Not required | The network interface used for when Cilium is enabled |
|
||||||
|
| `k3s_server` | `extra_server_args` | string | `""` | Not required | Extra arguments for server nodes |
|
||||||
|
| `k3s_server` | `k3s_create_kubectl_symlink` | bool | `false` | Not required | Create the kubectl -> k3s symlink |
|
||||||
|
| `k3s_server` | `k3s_create_crictl_symlink` | bool | `true` | Not required | Create the crictl -> k3s symlink |
|
||||||
|
| `k3s_server` | `kube_vip_arp` | bool | `true` | Not required | Enables kube-vip ARP broadcasts |
|
||||||
|
| `k3s_server` | `kube_vip_bgp` | bool | `false` | Not required | Enables kube-vip BGP peering |
|
||||||
|
| `k3s_server` | `kube_vip_bgp_routerid` | string | `"127.0.0.1"` | Not required | Defines the router ID for the kube-vip BGP server |
|
||||||
|
| `k3s_server` | `kube_vip_bgp_as` | string | `"64513"` | Not required | Defines the AS for the kube-vip BGP server |
|
||||||
|
| `k3s_server` | `kube_vip_bgp_peeraddress` | string | `"192.168.30.1"` | Not required | Defines the address for the kube-vip BGP peer |
|
||||||
|
| `k3s_server` | `kube_vip_bgp_peeras` | string | `"64512"` | Not required | Defines the AS for the kube-vip BGP peer |
|
||||||
|
| `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_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_tag_version` | string | `v0.7.2` | Not required | Image tag for kube-vip |
|
||||||
|
| `k3s_server` | `kube_vip_cloud_provider_tag_version` | string | `main` | 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`, `k3s_server_post` | `metal_lb_controller_tag_version` | string | `v0.14.3` | Not required | Image tag for MetalLB |
|
||||||
|
| `k3s_server` | `metal_lb_speaker_tag_version` | string | `v0.14.3` | Not required | Image tag for MetalLB |
|
||||||
|
| `k3s_server` | `metal_lb_type` | string | `native` | Not required | Use FRR mode or native. Valid values are `frr` and `native` |
|
||||||
|
| `k3s_server` | `retry_count` | int | `20` | Not required | Amount of retries when verifying that nodes joined |
|
||||||
|
| `k3s_server` | `server_init_args` | string | ❌ | Not required | Arguments for server nodes |
|
||||||
|
| `k3s_server_post` | `bpf_lb_algorithm` | string | `maglev` | Not required | BPF lb algorithm |
|
||||||
|
| `k3s_server_post` | `bpf_lb_mode` | string | `hybrid` | Not required | BPF lb mode |
|
||||||
|
| `k3s_server_post` | `calico_blocksize` | int | `26` | Not required | IP pool block size |
|
||||||
|
| `k3s_server_post` | `calico_ebpf` | bool | `false` | Not required | Use eBPF dataplane instead of iptables |
|
||||||
|
| `k3s_server_post` | `calico_encapsulation` | string | `VXLANCrossSubnet` | Not required | IP pool encapsulation |
|
||||||
|
| `k3s_server_post` | `calico_natOutgoing` | string | `Enabled` | Not required | IP pool NAT outgoing |
|
||||||
|
| `k3s_server_post` | `calico_nodeSelector` | string | `all()` | Not required | IP pool node selector |
|
||||||
|
| `k3s_server_post` | `calico_iface` | string | `~` | Not required | The network interface used for when Calico is enabled |
|
||||||
|
| `k3s_server_post` | `calico_tag` | string | `v3.27.2` | Not required | Calico version tag |
|
||||||
|
| `k3s_server_post` | `cilium_bgp_my_asn` | int | `64513` | Not required | Local ASN for BGP peer |
|
||||||
|
| `k3s_server_post` | `cilium_bgp_peer_asn` | int | `64512` | Not required | BGP peer ASN |
|
||||||
|
| `k3s_server_post` | `cilium_bgp_peer_address` | string | `~` | Not required | BGP peer address |
|
||||||
|
| `k3s_server_post` | `cilium_bgp_neighbors` | list | `[]` | Not required | List of BGP peer ASN & address pairs |
|
||||||
|
| `k3s_server_post` | `cilium_bgp_neighbors_groups` | list | `['k3s_all']` | Not required | Inventory group in which to search for additional `cilium_bgp_neighbors` parameters to merge. |
|
||||||
|
| `k3s_server_post` | `cilium_bgp_lb_cidr` | string | `192.168.31.0/24` | Not required | BGP load balancer IP range |
|
||||||
|
| `k3s_server_post` | `cilium_exportPodCIDR` | bool | `true` | Not required | Export pod CIDR |
|
||||||
|
| `k3s_server_post` | `cilium_hubble` | bool | `true` | Not required | Enable Cilium Hubble |
|
||||||
|
| `k3s_server_post` | `cilium_hubble` | bool | `true` | Not required | Enable Cilium Hubble |
|
||||||
|
| `k3s_server_post` | `cilium_mode` | string | `native` | Not required | Inner-node communication mode (choices are `native` and `routed`) |
|
||||||
|
| `k3s_server_post` | `cluster_cidr` | string | `10.52.0.0/16` | Not required | Inner-cluster IP range |
|
||||||
|
| `k3s_server_post` | `enable_bpf_masquerade` | bool | `true` | Not required | Use IP masquerading |
|
||||||
|
| `k3s_server_post` | `kube_proxy_replacement` | bool | `true` | Not required | Replace the native kube-proxy with Cilium |
|
||||||
|
| `k3s_server_post` | `metal_lb_available_timeout` | string | `240s` | Not required | Wait for MetalLB resources |
|
||||||
|
| `k3s_server_post` | `metal_lb_ip_range` | string | `192.168.30.80-192.168.30.90` | Not required | MetalLB ip range for load balancer |
|
||||||
|
| `k3s_server_post` | `metal_lb_controller_tag_version` | string | `v0.14.3` | Not required | Image tag for MetalLB |
|
||||||
|
| `k3s_server_post` | `metal_lb_mode` | string | `layer2` | Not required | Metallb mode (choices are `bgp` and `layer2`) |
|
||||||
|
| `k3s_server_post` | `metal_lb_bgp_my_asn` | string | `~` | Not required | BGP 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 |
|
||||||
|
| `lxc` | `custom_reboot_command` | string | `~` | Not required | Command to run on reboot |
|
||||||
|
| `prereq` | `system_timezone` | string | `null` | Not required | Timezone to be set on all nodes |
|
||||||
|
| `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`) |
|
||||||
|
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
Be sure to see [this post](https://github.com/techno-tim/k3s-ansible/discussions/20) on how to troubleshoot common problems
|
Be sure to see [this post](https://github.com/timothystewart6/k3s-ansible/discussions/20) on how to troubleshoot common problems
|
||||||
|
|
||||||
### Testing the playbook using molecule
|
### Testing the playbook using molecule
|
||||||
|
|
||||||
@@ -132,7 +218,7 @@ collections:
|
|||||||
- name: community.general
|
- name: community.general
|
||||||
- name: ansible.posix
|
- name: ansible.posix
|
||||||
- name: kubernetes.core
|
- name: kubernetes.core
|
||||||
- name: https://github.com/techno-tim/k3s-ansible.git
|
- name: https://github.com/timothystewart6/k3s-ansible.git
|
||||||
type: git
|
type: git
|
||||||
version: master
|
version: master
|
||||||
```
|
```
|
||||||
|
|||||||
+3
-3
@@ -56,16 +56,16 @@ dependencies:
|
|||||||
kubernetes.core: '*'
|
kubernetes.core: '*'
|
||||||
|
|
||||||
# The URL of the originating SCM repository
|
# The URL of the originating SCM repository
|
||||||
repository: https://github.com/techno-tim/k3s-ansible
|
repository: https://github.com/timothystewart6/k3s-ansible
|
||||||
|
|
||||||
# The URL to any online docs
|
# The URL to any online docs
|
||||||
documentation: https://github.com/techno-tim/k3s-ansible
|
documentation: https://github.com/timothystewart6/k3s-ansible
|
||||||
|
|
||||||
# The URL to the homepage of the collection/project
|
# The URL to the homepage of the collection/project
|
||||||
homepage: https://www.youtube.com/watch?v=CbkEWcUZ7zM
|
homepage: https://www.youtube.com/watch?v=CbkEWcUZ7zM
|
||||||
|
|
||||||
# The URL to the collection issue tracker
|
# The URL to the collection issue tracker
|
||||||
issues: https://github.com/techno-tim/k3s-ansible/issues
|
issues: https://github.com/timothystewart6/k3s-ansible/issues
|
||||||
|
|
||||||
# A list of file glob-like patterns used to filter any files or directories that should not be included in the build
|
# A list of file glob-like patterns used to filter any files or directories that should not be included in the build
|
||||||
# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This
|
# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This
|
||||||
|
|||||||
@@ -1,53 +1,103 @@
|
|||||||
---
|
---
|
||||||
k3s_version: v1.25.16+k3s4
|
k3s_version: v1.30.2+k3s2
|
||||||
# this is the user that has ssh access to these machines
|
# this is the user that has ssh access to these machines
|
||||||
ansible_user: ansibleuser
|
ansible_user: ansibleuser
|
||||||
systemd_dir: /etc/systemd/system
|
systemd_dir: /etc/systemd/system
|
||||||
|
|
||||||
# Set your timezone
|
# Set your timezone
|
||||||
system_timezone: "Your/Timezone"
|
system_timezone: Your/Timezone
|
||||||
|
|
||||||
# interface which will be used for flannel
|
# interface which will be used for flannel
|
||||||
flannel_iface: "eth0"
|
flannel_iface: eth0
|
||||||
|
|
||||||
|
# uncomment calico_iface to use tigera operator/calico cni instead of flannel https://docs.tigera.io/calico/latest/about
|
||||||
|
# calico_iface: "eth0"
|
||||||
|
calico_ebpf: false # use eBPF dataplane instead of iptables
|
||||||
|
calico_tag: v3.28.0 # calico version tag
|
||||||
|
|
||||||
|
# uncomment cilium_iface to use cilium cni instead of flannel or calico
|
||||||
|
# ensure v4.19.57, v5.1.16, v5.2.0 or more recent kernel
|
||||||
|
# cilium_iface: "eth0"
|
||||||
|
cilium_mode: native # native when nodes on same subnet or using bgp, else set routed
|
||||||
|
cilium_tag: v1.16.0 # cilium version tag
|
||||||
|
cilium_hubble: true # enable hubble observability relay and ui
|
||||||
|
|
||||||
|
# if using calico or cilium, you may specify the cluster pod cidr pool
|
||||||
|
cluster_cidr: 10.52.0.0/16
|
||||||
|
|
||||||
|
# enable cilium bgp control plane for lb services and pod cidrs. disables metallb.
|
||||||
|
cilium_bgp: false
|
||||||
|
|
||||||
|
# bgp parameters for cilium cni. only active when cilium_iface is defined and cilium_bgp is true.
|
||||||
|
cilium_bgp_my_asn: "64513"
|
||||||
|
cilium_bgp_peer_asn: "64512"
|
||||||
|
cilium_bgp_peer_address: 192.168.30.1
|
||||||
|
cilium_bgp_lb_cidr: 192.168.31.0/24 # cidr for cilium loadbalancer ipam
|
||||||
|
|
||||||
|
# enable kube-vip ARP broadcasts
|
||||||
|
kube_vip_arp: true
|
||||||
|
|
||||||
|
# enable kube-vip BGP peering
|
||||||
|
kube_vip_bgp: false
|
||||||
|
|
||||||
|
# bgp parameters for kube-vip
|
||||||
|
kube_vip_bgp_routerid: "127.0.0.1" # Defines the router ID for the BGP server
|
||||||
|
kube_vip_bgp_as: "64513" # Defines the AS for the BGP server
|
||||||
|
kube_vip_bgp_peeraddress: "192.168.30.1" # Defines the address for the BGP peer
|
||||||
|
kube_vip_bgp_peeras: "64512" # Defines the AS for the BGP peer
|
||||||
|
|
||||||
# apiserver_endpoint is virtual ip-address which will be configured on each master
|
# apiserver_endpoint is virtual ip-address which will be configured on each master
|
||||||
apiserver_endpoint: "192.168.30.222"
|
apiserver_endpoint: 192.168.30.222
|
||||||
|
|
||||||
# k3s_token is required masters can talk together securely
|
# k3s_token is required masters can talk together securely
|
||||||
# this token should be alpha numeric only
|
# this token should be alpha numeric only
|
||||||
k3s_token: "some-SUPER-DEDEUPER-secret-password"
|
k3s_token: some-SUPER-DEDEUPER-secret-password
|
||||||
|
|
||||||
# The IP on which the node is reachable in the cluster.
|
# The IP on which the node is reachable in the cluster.
|
||||||
# Here, a sensible default is provided, you can still override
|
# Here, a sensible default is provided, you can still override
|
||||||
# it for each of your hosts, though.
|
# it for each of your hosts, though.
|
||||||
k3s_node_ip: '{{ ansible_facts[flannel_iface]["ipv4"]["address"] }}'
|
k3s_node_ip: "{{ ansible_facts[(cilium_iface | default(calico_iface | default(flannel_iface)))]['ipv4']['address'] }}"
|
||||||
|
|
||||||
# Disable the taint manually by setting: k3s_master_taint = false
|
# Disable the taint manually by setting: k3s_master_taint = false
|
||||||
k3s_master_taint: "{{ true if groups['node'] | default([]) | length >= 1 else false }}"
|
k3s_master_taint: "{{ true if groups['node'] | default([]) | length >= 1 else false }}"
|
||||||
|
|
||||||
# these arguments are recommended for servers as well as agents:
|
# these arguments are recommended for servers as well as agents:
|
||||||
extra_args: >-
|
extra_args: >-
|
||||||
--flannel-iface={{ flannel_iface }}
|
{{ '--flannel-iface=' + flannel_iface if calico_iface is not defined and cilium_iface is not defined else '' }}
|
||||||
--node-ip={{ k3s_node_ip }}
|
--node-ip={{ k3s_node_ip }}
|
||||||
|
|
||||||
# change these to your liking, the only required are: --disable servicelb, --tls-san {{ apiserver_endpoint }}
|
# change these to your liking, the only required are: --disable servicelb, --tls-san {{ apiserver_endpoint }}
|
||||||
|
# the contents of the if block is also required if using calico or cilium
|
||||||
extra_server_args: >-
|
extra_server_args: >-
|
||||||
{{ extra_args }}
|
{{ extra_args }}
|
||||||
{{ '--node-taint node-role.kubernetes.io/master=true:NoSchedule' if k3s_master_taint else '' }}
|
{{ '--node-taint node-role.kubernetes.io/master=true:NoSchedule' if k3s_master_taint else '' }}
|
||||||
|
{% if calico_iface is defined or cilium_iface is defined %}
|
||||||
|
--flannel-backend=none
|
||||||
|
--disable-network-policy
|
||||||
|
--cluster-cidr={{ cluster_cidr | default('10.52.0.0/16') }}
|
||||||
|
{% endif %}
|
||||||
--tls-san {{ apiserver_endpoint }}
|
--tls-san {{ apiserver_endpoint }}
|
||||||
--disable servicelb
|
--disable servicelb
|
||||||
--disable traefik
|
--disable traefik
|
||||||
|
|
||||||
extra_agent_args: >-
|
extra_agent_args: >-
|
||||||
{{ extra_args }}
|
{{ extra_args }}
|
||||||
|
|
||||||
# image tag for kube-vip
|
# image tag for kube-vip
|
||||||
kube_vip_tag_version: "v0.5.12"
|
kube_vip_tag_version: v0.8.2
|
||||||
|
|
||||||
|
# tag for kube-vip-cloud-provider manifest
|
||||||
|
# kube_vip_cloud_provider_tag_version: "main"
|
||||||
|
|
||||||
|
# kube-vip ip range for load balancer
|
||||||
|
# (uncomment to use kube-vip for services instead of MetalLB)
|
||||||
|
# kube_vip_lb_ip_range: "192.168.30.80-192.168.30.90"
|
||||||
|
|
||||||
# metallb type frr or native
|
# metallb type frr or native
|
||||||
metal_lb_type: "native"
|
metal_lb_type: native
|
||||||
|
|
||||||
# metallb mode layer2 or bgp
|
# metallb mode layer2 or bgp
|
||||||
metal_lb_mode: "layer2"
|
metal_lb_mode: layer2
|
||||||
|
|
||||||
# bgp options
|
# bgp options
|
||||||
# metal_lb_bgp_my_asn: "64513"
|
# metal_lb_bgp_my_asn: "64513"
|
||||||
@@ -55,11 +105,11 @@ metal_lb_mode: "layer2"
|
|||||||
# metal_lb_bgp_peer_address: "192.168.30.1"
|
# metal_lb_bgp_peer_address: "192.168.30.1"
|
||||||
|
|
||||||
# image tag for metal lb
|
# image tag for metal lb
|
||||||
metal_lb_speaker_tag_version: "v0.13.9"
|
metal_lb_speaker_tag_version: v0.14.8
|
||||||
metal_lb_controller_tag_version: "v0.13.9"
|
metal_lb_controller_tag_version: v0.14.8
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
# 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.
|
||||||
@@ -122,6 +172,10 @@ custom_registries_yaml: |
|
|||||||
username: yourusername
|
username: yourusername
|
||||||
password: yourpassword
|
password: yourpassword
|
||||||
|
|
||||||
|
# On some distros like Diet Pi, there is no dbus installed. dbus required by the default reboot command.
|
||||||
|
# Uncomment if you need a custom reboot command
|
||||||
|
# custom_reboot_command: /usr/sbin/shutdown -r now
|
||||||
|
|
||||||
# Only enable and configure these if you access the internet through a proxy
|
# Only enable and configure these if you access the internet through a proxy
|
||||||
# proxy_env:
|
# proxy_env:
|
||||||
# HTTP_PROXY: "http://proxy.domain.local:3128"
|
# HTTP_PROXY: "http://proxy.domain.local:3128"
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
---
|
---
|
||||||
ansible_user: '{{ proxmox_lxc_ssh_user }}'
|
ansible_user: "{{ proxmox_lxc_ssh_user }}"
|
||||||
@@ -13,6 +13,12 @@ We have these scenarios:
|
|||||||
To save a bit of test time, this cluster is _not_ highly available, it consists of only one control and one worker node.
|
To save a bit of test time, this cluster is _not_ highly available, it consists of only one control and one worker node.
|
||||||
- **single_node**:
|
- **single_node**:
|
||||||
Very similar to the default scenario, but uses only a single node for all cluster functionality.
|
Very similar to the default scenario, but uses only a single node for all cluster functionality.
|
||||||
|
- **calico**:
|
||||||
|
The same as single node, but uses calico cni instead of flannel.
|
||||||
|
- **cilium**:
|
||||||
|
The same as single node, but uses cilium cni instead of flannel.
|
||||||
|
- **kube-vip**
|
||||||
|
The same as single node, but uses kube-vip as service loadbalancer instead of MetalLB
|
||||||
|
|
||||||
## How to execute
|
## How to execute
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
dependency:
|
||||||
|
name: galaxy
|
||||||
|
driver:
|
||||||
|
name: vagrant
|
||||||
|
platforms:
|
||||||
|
- name: control1
|
||||||
|
box: bento/ubuntu-26.04
|
||||||
|
memory: 4096
|
||||||
|
cpus: 4
|
||||||
|
groups:
|
||||||
|
- k3s_cluster
|
||||||
|
- master
|
||||||
|
interfaces:
|
||||||
|
- network_name: private_network
|
||||||
|
ip: 192.168.30.62
|
||||||
|
provisioner:
|
||||||
|
name: ansible
|
||||||
|
env:
|
||||||
|
ANSIBLE_VERBOSITY: 1
|
||||||
|
playbooks:
|
||||||
|
converge: ../resources/converge.yml
|
||||||
|
side_effect: ../resources/reset.yml
|
||||||
|
verify: ../resources/verify.yml
|
||||||
|
inventory:
|
||||||
|
links:
|
||||||
|
group_vars: ../../inventory/sample/group_vars
|
||||||
|
scenario:
|
||||||
|
test_sequence:
|
||||||
|
- dependency
|
||||||
|
- cleanup
|
||||||
|
- destroy
|
||||||
|
- syntax
|
||||||
|
- create
|
||||||
|
- prepare
|
||||||
|
- converge
|
||||||
|
# idempotence is not possible with the playbook in its current form.
|
||||||
|
- verify
|
||||||
|
# We are repurposing side_effect here to test the reset playbook.
|
||||||
|
# This is why we do not run it before verify (which tests the cluster),
|
||||||
|
# but after the verify step.
|
||||||
|
- side_effect
|
||||||
|
- cleanup
|
||||||
|
- destroy
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
- name: Apply overrides
|
||||||
|
hosts: all
|
||||||
|
serial: 1
|
||||||
|
tasks:
|
||||||
|
- name: Override host variables
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
# See:
|
||||||
|
# https://github.com/flannel-io/flannel/blob/67d603aaf45ef80f5dd39f43714fc5e6f8a637eb/Documentation/troubleshooting.md#Vagrant
|
||||||
|
calico_iface: "{{ 'eth1' if 'eth1' in ansible_facts.interfaces else 'enp0s8' }}"
|
||||||
|
kube_vip_iface: "{{ 'eth1' if 'eth1' in ansible_facts.interfaces else 'enp0s8' }}"
|
||||||
|
|
||||||
|
# The test VMs might be a bit slow, so we give them more time to join the cluster:
|
||||||
|
retry_count: 45
|
||||||
|
|
||||||
|
# Make sure that our IP ranges do not collide with those of the other scenarios
|
||||||
|
apiserver_endpoint: 192.168.30.224
|
||||||
|
metal_lb_ip_range: 192.168.30.100-192.168.30.109
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
dependency:
|
||||||
|
name: galaxy
|
||||||
|
driver:
|
||||||
|
name: vagrant
|
||||||
|
platforms:
|
||||||
|
- name: control1
|
||||||
|
box: bento/ubuntu-26.04
|
||||||
|
memory: 4096
|
||||||
|
cpus: 4
|
||||||
|
groups:
|
||||||
|
- k3s_cluster
|
||||||
|
- master
|
||||||
|
interfaces:
|
||||||
|
- network_name: private_network
|
||||||
|
ip: 192.168.30.63
|
||||||
|
provisioner:
|
||||||
|
name: ansible
|
||||||
|
env:
|
||||||
|
ANSIBLE_VERBOSITY: 1
|
||||||
|
playbooks:
|
||||||
|
converge: ../resources/converge.yml
|
||||||
|
side_effect: ../resources/reset.yml
|
||||||
|
verify: ../resources/verify.yml
|
||||||
|
inventory:
|
||||||
|
links:
|
||||||
|
group_vars: ../../inventory/sample/group_vars
|
||||||
|
scenario:
|
||||||
|
test_sequence:
|
||||||
|
- dependency
|
||||||
|
- cleanup
|
||||||
|
- destroy
|
||||||
|
- syntax
|
||||||
|
- create
|
||||||
|
- prepare
|
||||||
|
- converge
|
||||||
|
# idempotence is not possible with the playbook in its current form.
|
||||||
|
- verify
|
||||||
|
# We are repurposing side_effect here to test the reset playbook.
|
||||||
|
# This is why we do not run it before verify (which tests the cluster),
|
||||||
|
# but after the verify step.
|
||||||
|
- side_effect
|
||||||
|
- cleanup
|
||||||
|
- destroy
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
- name: Apply overrides
|
||||||
|
hosts: all
|
||||||
|
serial: 1
|
||||||
|
tasks:
|
||||||
|
- name: Override host variables
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
# See:
|
||||||
|
# https://github.com/flannel-io/flannel/blob/67d603aaf45ef80f5dd39f43714fc5e6f8a637eb/Documentation/troubleshooting.md#Vagrant
|
||||||
|
cilium_iface: "{{ 'eth1' if 'eth1' in ansible_facts.interfaces else 'enp0s8' }}"
|
||||||
|
kube_vip_iface: "{{ 'eth1' if 'eth1' in ansible_facts.interfaces else 'enp0s8' }}"
|
||||||
|
|
||||||
|
# The test VMs might be a bit slow, so we give them more time to join the cluster:
|
||||||
|
retry_count: 45
|
||||||
|
|
||||||
|
# Make sure that our IP ranges do not collide with those of the other scenarios
|
||||||
|
apiserver_endpoint: 192.168.30.225
|
||||||
|
metal_lb_ip_range: 192.168.30.110-192.168.30.119
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
---
|
||||||
|
- name: Create
|
||||||
|
hosts: localhost
|
||||||
|
connection: local
|
||||||
|
gather_facts: false
|
||||||
|
no_log: "{{ molecule_no_log }}"
|
||||||
|
vars:
|
||||||
|
create_batches:
|
||||||
|
- [control1, control2]
|
||||||
|
- [control3, node1]
|
||||||
|
- [node2]
|
||||||
|
tasks:
|
||||||
|
- name: Verify that bounded batches cover the configured platforms exactly once
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- create_batches | flatten | sort == molecule_yml.platforms | map(attribute='name') | sort
|
||||||
|
- create_batches | flatten | length == create_batches | flatten | unique | length
|
||||||
|
- create_batches | map('length') | max <= 2
|
||||||
|
fail_msg: Bounded create batches do not match the configured default platforms.
|
||||||
|
|
||||||
|
- name: Generate the complete Vagrant configuration # noqa fqcn[action]
|
||||||
|
vagrant:
|
||||||
|
instances: "{{ molecule_yml.platforms }}"
|
||||||
|
default_box: "{{ molecule_yml.driver.default_box | default('generic/alpine316') }}"
|
||||||
|
provider_name: "{{ molecule_yml.driver.provider.name | default(omit, true) }}"
|
||||||
|
provision: "{{ molecule_yml.driver.provision | default(omit) }}"
|
||||||
|
cachier: "{{ molecule_yml.driver.cachier | default(omit) }}"
|
||||||
|
parallel: false
|
||||||
|
state: halt
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Start clean Vagrant guests in bounded batches
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: >-
|
||||||
|
{{
|
||||||
|
[playbook_dir + '/../../.github/scripts/vagrant-up-timed.sh',
|
||||||
|
molecule_ephemeral_directory] + item
|
||||||
|
}}
|
||||||
|
loop: "{{ create_batches }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item | join(', ') }}"
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Reconcile all instances and collect their connection configuration # noqa fqcn[action]
|
||||||
|
vagrant:
|
||||||
|
instances: "{{ molecule_yml.platforms }}"
|
||||||
|
default_box: "{{ molecule_yml.driver.default_box | default('generic/alpine316') }}"
|
||||||
|
provider_name: "{{ molecule_yml.driver.provider.name | default(omit, true) }}"
|
||||||
|
provision: "{{ molecule_yml.driver.provision | default(omit) }}"
|
||||||
|
cachier: "{{ molecule_yml.driver.cachier | default(omit) }}"
|
||||||
|
parallel: false
|
||||||
|
state: up
|
||||||
|
register: server
|
||||||
|
no_log: false
|
||||||
|
|
||||||
|
- name: Populate instance configuration dictionaries
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
instance_conf_dict:
|
||||||
|
instance: "{{ item.Host }}"
|
||||||
|
address: "{{ item.HostName }}"
|
||||||
|
user: "{{ item.User }}"
|
||||||
|
port: "{{ item.Port }}"
|
||||||
|
identity_file: "{{ item.IdentityFile }}"
|
||||||
|
loop: "{{ server.results }}"
|
||||||
|
register: instance_config_dict
|
||||||
|
|
||||||
|
- name: Convert instance configuration dictionaries to a list
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
instance_conf: >-
|
||||||
|
{{
|
||||||
|
instance_config_dict.results
|
||||||
|
| map(attribute='ansible_facts.instance_conf_dict')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Write Molecule instance configuration
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: "{{ instance_conf | to_json | from_json | to_yaml }}"
|
||||||
|
dest: "{{ molecule_instance_config }}"
|
||||||
|
mode: "0600"
|
||||||
@@ -3,69 +3,73 @@ dependency:
|
|||||||
name: galaxy
|
name: galaxy
|
||||||
driver:
|
driver:
|
||||||
name: vagrant
|
name: vagrant
|
||||||
|
# The Vagrant driver warns that parallel VirtualBox creation can cause
|
||||||
|
# platform issues. Keep this five-node, mixed-distribution scenario serial.
|
||||||
|
parallel: false
|
||||||
platforms:
|
platforms:
|
||||||
|
|
||||||
- name: control1
|
- name: control1
|
||||||
box: generic/ubuntu2204
|
box: bento/ubuntu-26.04
|
||||||
memory: 1024
|
memory: 1024
|
||||||
cpus: 2
|
cpus: 2
|
||||||
groups:
|
groups:
|
||||||
- k3s_cluster
|
- k3s_cluster
|
||||||
- master
|
- master
|
||||||
|
# Keep adapter 2 stable across linked-clone rebuilds so stale host-only
|
||||||
|
# neighbor state still identifies the current scenario guest.
|
||||||
|
provider_raw_config_args:
|
||||||
|
- "customize ['modifyvm', :id, '--mac-address2', '080027A13038']"
|
||||||
interfaces:
|
interfaces:
|
||||||
- network_name: private_network
|
- network_name: private_network
|
||||||
ip: 192.168.30.38
|
ip: 192.168.30.38
|
||||||
config_options:
|
|
||||||
# We currently can not use public-key based authentication on Ubuntu 22.04,
|
|
||||||
# see: https://github.com/chef/bento/issues/1405
|
|
||||||
ssh.username: "vagrant"
|
|
||||||
ssh.password: "vagrant"
|
|
||||||
|
|
||||||
- name: control2
|
- name: control2
|
||||||
box: generic/debian11
|
box: bento/debian-13
|
||||||
memory: 1024
|
memory: 1024
|
||||||
cpus: 2
|
cpus: 2
|
||||||
groups:
|
groups:
|
||||||
- k3s_cluster
|
- k3s_cluster
|
||||||
- master
|
- master
|
||||||
|
provider_raw_config_args:
|
||||||
|
- "customize ['modifyvm', :id, '--mac-address2', '080027A13039']"
|
||||||
interfaces:
|
interfaces:
|
||||||
- network_name: private_network
|
- network_name: private_network
|
||||||
ip: 192.168.30.39
|
ip: 192.168.30.39
|
||||||
|
|
||||||
- name: control3
|
- name: control3
|
||||||
box: generic/rocky9
|
box: bento/rockylinux-10.1
|
||||||
memory: 1024
|
memory: 1024
|
||||||
cpus: 2
|
cpus: 2
|
||||||
groups:
|
groups:
|
||||||
- k3s_cluster
|
- k3s_cluster
|
||||||
- master
|
- master
|
||||||
|
provider_raw_config_args:
|
||||||
|
- "customize ['modifyvm', :id, '--mac-address2', '080027A13040']"
|
||||||
interfaces:
|
interfaces:
|
||||||
- network_name: private_network
|
- network_name: private_network
|
||||||
ip: 192.168.30.40
|
ip: 192.168.30.40
|
||||||
|
|
||||||
- name: node1
|
- name: node1
|
||||||
box: generic/ubuntu2204
|
box: bento/ubuntu-26.04
|
||||||
memory: 1024
|
memory: 1024
|
||||||
cpus: 2
|
cpus: 2
|
||||||
groups:
|
groups:
|
||||||
- k3s_cluster
|
- k3s_cluster
|
||||||
- node
|
- node
|
||||||
|
provider_raw_config_args:
|
||||||
|
- "customize ['modifyvm', :id, '--mac-address2', '080027A13041']"
|
||||||
interfaces:
|
interfaces:
|
||||||
- network_name: private_network
|
- network_name: private_network
|
||||||
ip: 192.168.30.41
|
ip: 192.168.30.41
|
||||||
config_options:
|
|
||||||
# We currently can not use public-key based authentication on Ubuntu 22.04,
|
|
||||||
# see: https://github.com/chef/bento/issues/1405
|
|
||||||
ssh.username: "vagrant"
|
|
||||||
ssh.password: "vagrant"
|
|
||||||
|
|
||||||
- name: node2
|
- name: node2
|
||||||
box: generic/rocky9
|
box: bento/rockylinux-10.1
|
||||||
memory: 1024
|
memory: 1024
|
||||||
cpus: 2
|
cpus: 2
|
||||||
groups:
|
groups:
|
||||||
- k3s_cluster
|
- k3s_cluster
|
||||||
- node
|
- node
|
||||||
|
provider_raw_config_args:
|
||||||
|
- "customize ['modifyvm', :id, '--mac-address2', '080027A13042']"
|
||||||
interfaces:
|
interfaces:
|
||||||
- network_name: private_network
|
- network_name: private_network
|
||||||
ip: 192.168.30.42
|
ip: 192.168.30.42
|
||||||
@@ -75,6 +79,7 @@ provisioner:
|
|||||||
env:
|
env:
|
||||||
ANSIBLE_VERBOSITY: 1
|
ANSIBLE_VERBOSITY: 1
|
||||||
playbooks:
|
playbooks:
|
||||||
|
create: create.yml
|
||||||
converge: ../resources/converge.yml
|
converge: ../resources/converge.yml
|
||||||
side_effect: ../resources/reset.yml
|
side_effect: ../resources/reset.yml
|
||||||
verify: ../resources/verify.yml
|
verify: ../resources/verify.yml
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
---
|
---
|
||||||
- name: Apply overrides
|
- name: Apply overrides
|
||||||
hosts: all
|
hosts: all
|
||||||
|
serial: 1
|
||||||
tasks:
|
tasks:
|
||||||
- name: Override host variables
|
- name: Override host variables
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
# See:
|
# See:
|
||||||
# https://github.com/flannel-io/flannel/blob/67d603aaf45ef80f5dd39f43714fc5e6f8a637eb/Documentation/troubleshooting.md#Vagrant
|
# https://github.com/flannel-io/flannel/blob/67d603aaf45ef80f5dd39f43714fc5e6f8a637eb/Documentation/troubleshooting.md#Vagrant
|
||||||
flannel_iface: eth1
|
flannel_iface: "{{ 'eth1' if 'eth1' in ansible_facts.interfaces else 'enp0s8' }}"
|
||||||
|
# kube-vip cannot infer the cluster interface in these multi-NIC
|
||||||
|
# Vagrant guests because the default route is on eth0.
|
||||||
|
kube_vip_iface: "{{ 'eth1' if 'eth1' in ansible_facts.interfaces else 'enp0s8' }}"
|
||||||
|
|
||||||
# The test VMs might be a bit slow, so we give them more time to join the cluster:
|
# The test VMs might be a bit slow, so we give them more time to join the cluster:
|
||||||
retry_count: 45
|
retry_count: 45
|
||||||
@@ -5,18 +5,137 @@
|
|||||||
|
|
||||||
- name: Network setup
|
- name: Network setup
|
||||||
hosts: all
|
hosts: all
|
||||||
|
vars:
|
||||||
|
primary_master: "{{ groups[group_name_master | default('master')][0] }}"
|
||||||
|
primary_cluster_ip: >-
|
||||||
|
{{ hostvars[primary_master].k3s_node_ip | split(',') | first }}
|
||||||
|
cluster_interface: >-
|
||||||
|
{{ cilium_iface | default(calico_iface | default(flannel_iface)) }}
|
||||||
|
primary_cluster_interface: >-
|
||||||
|
{{ hostvars[primary_master].cilium_iface
|
||||||
|
| default(hostvars[primary_master].calico_iface
|
||||||
|
| default(hostvars[primary_master].flannel_iface)) }}
|
||||||
|
primary_cluster_mac: >-
|
||||||
|
{{ hostvars[primary_master].ansible_facts[primary_cluster_interface].macaddress }}
|
||||||
tasks:
|
tasks:
|
||||||
- name: Disable firewalld
|
- name: Gather service facts
|
||||||
when: ansible_distribution == "Rocky"
|
ansible.builtin.service_facts:
|
||||||
# Rocky Linux comes with firewalld enabled. It blocks some of the network
|
|
||||||
# connections needed for our k3s cluster. For our test setup, we just disable
|
- name: Disable guest firewall services
|
||||||
# it since the VM host's firewall is still active for connections to and from
|
# The disposable test guests use an isolated VirtualBox network. A distro
|
||||||
# the Internet.
|
# firewall can allow ICMP while silently blocking the inter-node Kubernetes
|
||||||
|
# API connection, so disable the known guest firewalls consistently.
|
||||||
# When building your own cluster, please DO NOT blindly copy this. Instead,
|
# When building your own cluster, please DO NOT blindly copy this. Instead,
|
||||||
# please create a custom firewall configuration that fits your network design
|
# please create a custom firewall configuration that fits your network design
|
||||||
# and security needs.
|
# and security needs.
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
name: firewalld
|
name: "{{ item }}"
|
||||||
enabled: false
|
enabled: false
|
||||||
state: stopped
|
state: stopped
|
||||||
become: true
|
become: true
|
||||||
|
loop:
|
||||||
|
- firewalld.service
|
||||||
|
- nftables.service
|
||||||
|
- ufw.service
|
||||||
|
when: item in ansible_facts.services
|
||||||
|
|
||||||
|
- name: Verify the private cluster interface
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- flannel_iface in ansible_facts
|
||||||
|
- ansible_facts[flannel_iface].ipv4 is defined
|
||||||
|
- ansible_facts[flannel_iface].ipv4.address is defined
|
||||||
|
fail_msg: >-
|
||||||
|
The Vagrant private interface {{ flannel_iface }} does not have an
|
||||||
|
IPv4 address on {{ inventory_hostname }}.
|
||||||
|
|
||||||
|
- name: Pin disposable cluster peer neighbor entries
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- ip
|
||||||
|
- neigh
|
||||||
|
- replace
|
||||||
|
- "{{ peer_cluster_ip }}"
|
||||||
|
- lladdr
|
||||||
|
- "{{ peer_cluster_mac }}"
|
||||||
|
- nud
|
||||||
|
- permanent
|
||||||
|
- dev
|
||||||
|
- "{{ cluster_interface }}"
|
||||||
|
become: true
|
||||||
|
changed_when: false
|
||||||
|
loop: "{{ groups['k3s_cluster'] }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ inventory_hostname }} -> {{ item }}"
|
||||||
|
vars:
|
||||||
|
peer_cluster_interface: >-
|
||||||
|
{{ hostvars[item].cilium_iface
|
||||||
|
| default(hostvars[item].calico_iface
|
||||||
|
| default(hostvars[item].flannel_iface)) }}
|
||||||
|
peer_cluster_ip: >-
|
||||||
|
{{ hostvars[item].k3s_node_ip | split(',') | first }}
|
||||||
|
peer_cluster_mac: >-
|
||||||
|
{{ hostvars[item].ansible_facts[peer_cluster_interface].macaddress }}
|
||||||
|
when: item != inventory_hostname
|
||||||
|
|
||||||
|
- name: Verify guest-to-guest cluster network reachability
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- ping
|
||||||
|
- -c
|
||||||
|
- "1"
|
||||||
|
- -W
|
||||||
|
- "1"
|
||||||
|
- "{{ primary_cluster_ip }}"
|
||||||
|
register: primary_cluster_ping
|
||||||
|
until: primary_cluster_ping.rc == 0
|
||||||
|
retries: 6
|
||||||
|
delay: 2
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Read the primary neighbor entry
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- ip
|
||||||
|
- neigh
|
||||||
|
- show
|
||||||
|
- to
|
||||||
|
- "{{ primary_cluster_ip }}"
|
||||||
|
- dev
|
||||||
|
- "{{ cluster_interface }}"
|
||||||
|
register: primary_cluster_neighbor
|
||||||
|
changed_when: false
|
||||||
|
when: inventory_hostname != primary_master
|
||||||
|
|
||||||
|
- name: Verify the primary neighbor identity
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- (primary_cluster_mac | lower) in (primary_cluster_neighbor.stdout | lower)
|
||||||
|
fail_msg: >-
|
||||||
|
{{ inventory_hostname }} resolved primary {{ primary_cluster_ip }} to
|
||||||
|
an unexpected MAC on {{ cluster_interface }}. Expected
|
||||||
|
{{ primary_cluster_mac }}, got: {{ primary_cluster_neighbor.stdout }}
|
||||||
|
when: inventory_hostname != primary_master
|
||||||
|
|
||||||
|
- name: Verify GitHub release host DNS
|
||||||
|
ansible.builtin.getent:
|
||||||
|
database: hosts
|
||||||
|
key: github.com
|
||||||
|
register: github_dns
|
||||||
|
retries: 6
|
||||||
|
delay: 5
|
||||||
|
until: github_dns is succeeded
|
||||||
|
|
||||||
|
- name: Verify k3s checksum URL is reachable
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: >-
|
||||||
|
https://github.com/k3s-io/k3s/releases/download/{{ k3s_version
|
||||||
|
}}/sha256sum-amd64.txt
|
||||||
|
method: HEAD
|
||||||
|
follow_redirects: safe
|
||||||
|
status_code: [200, 302]
|
||||||
|
timeout: 15
|
||||||
|
register: k3s_checksum_request
|
||||||
|
retries: 3
|
||||||
|
delay: 5
|
||||||
|
until: k3s_checksum_request.status in [200, 302]
|
||||||
@@ -5,7 +5,7 @@ driver:
|
|||||||
name: vagrant
|
name: vagrant
|
||||||
platforms:
|
platforms:
|
||||||
- name: control1
|
- name: control1
|
||||||
box: generic/ubuntu2204
|
box: bento/ubuntu-26.04
|
||||||
memory: 1024
|
memory: 1024
|
||||||
cpus: 2
|
cpus: 2
|
||||||
groups:
|
groups:
|
||||||
@@ -14,14 +14,9 @@ platforms:
|
|||||||
interfaces:
|
interfaces:
|
||||||
- network_name: private_network
|
- network_name: private_network
|
||||||
ip: fdad:bad:ba55::de:11
|
ip: fdad:bad:ba55::de:11
|
||||||
config_options:
|
|
||||||
# We currently can not use public-key based authentication on Ubuntu 22.04,
|
|
||||||
# see: https://github.com/chef/bento/issues/1405
|
|
||||||
ssh.username: "vagrant"
|
|
||||||
ssh.password: "vagrant"
|
|
||||||
|
|
||||||
- name: control2
|
- name: control2
|
||||||
box: generic/ubuntu2204
|
box: bento/ubuntu-26.04
|
||||||
memory: 1024
|
memory: 1024
|
||||||
cpus: 2
|
cpus: 2
|
||||||
groups:
|
groups:
|
||||||
@@ -30,14 +25,9 @@ platforms:
|
|||||||
interfaces:
|
interfaces:
|
||||||
- network_name: private_network
|
- network_name: private_network
|
||||||
ip: fdad:bad:ba55::de:12
|
ip: fdad:bad:ba55::de:12
|
||||||
config_options:
|
|
||||||
# We currently can not use public-key based authentication on Ubuntu 22.04,
|
|
||||||
# see: https://github.com/chef/bento/issues/1405
|
|
||||||
ssh.username: "vagrant"
|
|
||||||
ssh.password: "vagrant"
|
|
||||||
|
|
||||||
- name: node1
|
- name: node1
|
||||||
box: generic/ubuntu2204
|
box: bento/ubuntu-26.04
|
||||||
memory: 1024
|
memory: 1024
|
||||||
cpus: 2
|
cpus: 2
|
||||||
groups:
|
groups:
|
||||||
@@ -46,11 +36,6 @@ platforms:
|
|||||||
interfaces:
|
interfaces:
|
||||||
- network_name: private_network
|
- network_name: private_network
|
||||||
ip: fdad:bad:ba55::de:21
|
ip: fdad:bad:ba55::de:21
|
||||||
config_options:
|
|
||||||
# We currently can not use public-key based authentication on Ubuntu 22.04,
|
|
||||||
# see: https://github.com/chef/bento/issues/1405
|
|
||||||
ssh.username: "vagrant"
|
|
||||||
ssh.password: "vagrant"
|
|
||||||
provisioner:
|
provisioner:
|
||||||
name: ansible
|
name: ansible
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
---
|
---
|
||||||
- name: Apply overrides
|
- name: Apply overrides
|
||||||
hosts: all
|
hosts: all
|
||||||
|
serial: 1
|
||||||
tasks:
|
tasks:
|
||||||
- name: Override host variables (1/2)
|
- name: Override host variables (1/2)
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
# See:
|
# See:
|
||||||
# https://github.com/flannel-io/flannel/blob/67d603aaf45ef80f5dd39f43714fc5e6f8a637eb/Documentation/troubleshooting.md#Vagrant
|
# https://github.com/flannel-io/flannel/blob/67d603aaf45ef80f5dd39f43714fc5e6f8a637eb/Documentation/troubleshooting.md#Vagrant
|
||||||
flannel_iface: eth1
|
flannel_iface: "{{ 'eth1' if 'eth1' in ansible_facts.interfaces else 'enp0s8' }}"
|
||||||
|
|
||||||
# In this scenario, we have multiple interfaces that the VIP could be
|
# In this scenario, we have multiple interfaces that the VIP could be
|
||||||
# broadcasted on. Since we have assigned a dedicated private network
|
# broadcasted on. Since we have assigned a dedicated private network
|
||||||
# here, let's make sure that it is used.
|
# here, let's make sure that it is used.
|
||||||
kube_vip_iface: eth1
|
kube_vip_iface: "{{ 'eth1' if 'eth1' in ansible_facts.interfaces else 'enp0s8' }}"
|
||||||
|
|
||||||
# The test VMs might be a bit slow, so we give them more time to join the cluster:
|
# The test VMs might be a bit slow, so we give them more time to join the cluster:
|
||||||
retry_count: 45
|
retry_count: 45
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
dest: /etc/netplan/55-flannel-ipv4.yaml
|
dest: /etc/netplan/55-flannel-ipv4.yaml
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: 0644
|
mode: "0644"
|
||||||
register: netplan_template
|
register: netplan_template
|
||||||
|
|
||||||
- name: Apply netplan configuration
|
- name: Apply netplan configuration
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
dependency:
|
||||||
|
name: galaxy
|
||||||
|
driver:
|
||||||
|
name: vagrant
|
||||||
|
platforms:
|
||||||
|
- name: control1
|
||||||
|
box: bento/ubuntu-26.04
|
||||||
|
memory: 4096
|
||||||
|
cpus: 4
|
||||||
|
groups:
|
||||||
|
- k3s_cluster
|
||||||
|
- master
|
||||||
|
interfaces:
|
||||||
|
- network_name: private_network
|
||||||
|
ip: 192.168.30.62
|
||||||
|
provisioner:
|
||||||
|
name: ansible
|
||||||
|
env:
|
||||||
|
ANSIBLE_VERBOSITY: 1
|
||||||
|
playbooks:
|
||||||
|
converge: ../resources/converge.yml
|
||||||
|
side_effect: ../resources/reset.yml
|
||||||
|
verify: ../resources/verify.yml
|
||||||
|
inventory:
|
||||||
|
links:
|
||||||
|
group_vars: ../../inventory/sample/group_vars
|
||||||
|
scenario:
|
||||||
|
test_sequence:
|
||||||
|
- dependency
|
||||||
|
- cleanup
|
||||||
|
- destroy
|
||||||
|
- syntax
|
||||||
|
- create
|
||||||
|
- prepare
|
||||||
|
- converge
|
||||||
|
# idempotence is not possible with the playbook in its current form.
|
||||||
|
- verify
|
||||||
|
# We are repurposing side_effect here to test the reset playbook.
|
||||||
|
# This is why we do not run it before verify (which tests the cluster),
|
||||||
|
# but after the verify step.
|
||||||
|
- side_effect
|
||||||
|
- cleanup
|
||||||
|
- destroy
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
- name: Apply overrides
|
||||||
|
hosts: all
|
||||||
|
serial: 1
|
||||||
|
tasks:
|
||||||
|
- name: Override host variables
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
# See:
|
||||||
|
# https://github.com/flannel-io/flannel/blob/67d603aaf45ef80f5dd39f43714fc5e6f8a637eb/Documentation/troubleshooting.md#Vagrant
|
||||||
|
flannel_iface: "{{ 'eth1' if 'eth1' in ansible_facts.interfaces else 'enp0s8' }}"
|
||||||
|
kube_vip_iface: "{{ 'eth1' if 'eth1' in ansible_facts.interfaces else 'enp0s8' }}"
|
||||||
|
|
||||||
|
# The test VMs might be a bit slow, so we give them more time to join the cluster:
|
||||||
|
retry_count: 45
|
||||||
|
|
||||||
|
# Make sure that our IP ranges do not collide with those of the other scenarios
|
||||||
|
apiserver_endpoint: 192.168.30.225
|
||||||
|
# Use kube-vip instead of MetalLB
|
||||||
|
kube_vip_lb_ip_range: 192.168.30.110-192.168.30.119
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
name: nginx
|
name: nginx
|
||||||
namespace: "{{ testing_namespace }}"
|
namespace: "{{ testing_namespace }}"
|
||||||
kubeconfig: "{{ kubecfg_path }}"
|
kubeconfig: "{{ kubecfg_path }}"
|
||||||
vars: &load_balancer_metadata
|
vars:
|
||||||
metallb_ip: status.loadBalancer.ingress[0].ip
|
metallb_ip: status.loadBalancer.ingress[0].ip
|
||||||
metallb_port: spec.ports[0].port
|
metallb_port: spec.ports[0].port
|
||||||
register: nginx_services
|
register: nginx_services
|
||||||
@@ -43,10 +43,10 @@
|
|||||||
{{ nginx_services.resources[0].status.loadBalancer.ingress[0].ip }}
|
{{ nginx_services.resources[0].status.loadBalancer.ingress[0].ip }}
|
||||||
port_: >-
|
port_: >-
|
||||||
{{ nginx_services.resources[0].spec.ports[0].port }}
|
{{ nginx_services.resources[0].spec.ports[0].port }}
|
||||||
# Deactivated linter rules:
|
# Deactivated linter rules:
|
||||||
# - jinja[invalid]: As of version 6.6.0, ansible-lint complains that the input to ipwrap
|
# - jinja[invalid]: As of version 6.6.0, ansible-lint complains that the input to ipwrap
|
||||||
# would be undefined. This will not be the case during playbook execution.
|
# would be undefined. This will not be the case during playbook execution.
|
||||||
# noqa jinja[invalid]
|
# noqa jinja[invalid]
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: "Remove namespace: {{ testing_namespace }}"
|
- name: "Remove namespace: {{ testing_namespace }}"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that: found_nodes == expected_nodes
|
that: found_nodes == expected_nodes
|
||||||
success_msg: "Found nodes as expected: {{ found_nodes }}"
|
success_msg: "Found nodes as expected: {{ found_nodes }}"
|
||||||
fail_msg: "Expected nodes {{ expected_nodes }}, but found nodes {{ found_nodes }}"
|
fail_msg: Expected nodes {{ expected_nodes }}, but found nodes {{ found_nodes }}
|
||||||
vars:
|
vars:
|
||||||
found_nodes: >-
|
found_nodes: >-
|
||||||
{{ cluster_nodes | json_query('resources[*].metadata.name') | unique | sort }}
|
{{ cluster_nodes | json_query('resources[*].metadata.name') | unique | sort }}
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
| unique
|
| unique
|
||||||
| sort
|
| sort
|
||||||
}}
|
}}
|
||||||
# Deactivated linter rules:
|
# Deactivated linter rules:
|
||||||
# - jinja[invalid]: As of version 6.6.0, ansible-lint complains that the input to ipwrap
|
# - jinja[invalid]: As of version 6.6.0, ansible-lint complains that the input to ipwrap
|
||||||
# would be undefined. This will not be the case during playbook execution.
|
# would be undefined. This will not be the case during playbook execution.
|
||||||
# noqa jinja[invalid]
|
# noqa jinja[invalid]
|
||||||
@@ -5,14 +5,9 @@ driver:
|
|||||||
name: vagrant
|
name: vagrant
|
||||||
platforms:
|
platforms:
|
||||||
- name: control1
|
- name: control1
|
||||||
box: generic/ubuntu2204
|
box: bento/ubuntu-26.04
|
||||||
memory: 4096
|
memory: 4096
|
||||||
cpus: 4
|
cpus: 4
|
||||||
config_options:
|
|
||||||
# We currently can not use public-key based authentication on Ubuntu 22.04,
|
|
||||||
# see: https://github.com/chef/bento/issues/1405
|
|
||||||
ssh.username: "vagrant"
|
|
||||||
ssh.password: "vagrant"
|
|
||||||
groups:
|
groups:
|
||||||
- k3s_cluster
|
- k3s_cluster
|
||||||
- master
|
- master
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
---
|
---
|
||||||
- name: Apply overrides
|
- name: Apply overrides
|
||||||
hosts: all
|
hosts: all
|
||||||
|
serial: 1
|
||||||
tasks:
|
tasks:
|
||||||
- name: Override host variables
|
- name: Override host variables
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
# See:
|
# See:
|
||||||
# https://github.com/flannel-io/flannel/blob/67d603aaf45ef80f5dd39f43714fc5e6f8a637eb/Documentation/troubleshooting.md#Vagrant
|
# https://github.com/flannel-io/flannel/blob/67d603aaf45ef80f5dd39f43714fc5e6f8a637eb/Documentation/troubleshooting.md#Vagrant
|
||||||
flannel_iface: eth1
|
flannel_iface: "{{ 'eth1' if 'eth1' in ansible_facts.interfaces else 'enp0s8' }}"
|
||||||
|
kube_vip_iface: "{{ 'eth1' if 'eth1' in ansible_facts.interfaces else 'enp0s8' }}"
|
||||||
|
|
||||||
# The test VMs might be a bit slow, so we give them more time to join the cluster:
|
# The test VMs might be a bit slow, so we give them more time to join the cluster:
|
||||||
retry_count: 45
|
retry_count: 45
|
||||||
|
|
||||||
# Make sure that our IP ranges do not collide with those of the default scenario
|
# Make sure that our IP ranges do not collide with those of the default scenario
|
||||||
apiserver_endpoint: "192.168.30.223"
|
apiserver_endpoint: 192.168.30.223
|
||||||
metal_lb_ip_range: "192.168.30.91-192.168.30.99"
|
metal_lb_ip_range: 192.168.30.91-192.168.30.99
|
||||||
+2
-1
@@ -5,5 +5,6 @@
|
|||||||
tasks:
|
tasks:
|
||||||
- name: Reboot the nodes (and Wait upto 5 mins max)
|
- name: Reboot the nodes (and Wait upto 5 mins max)
|
||||||
become: true
|
become: true
|
||||||
reboot:
|
ansible.builtin.reboot:
|
||||||
|
reboot_command: "{{ custom_reboot_command | default(omit) }}"
|
||||||
reboot_timeout: 300
|
reboot_timeout: 300
|
||||||
+5
-5
@@ -6,7 +6,7 @@
|
|||||||
#
|
#
|
||||||
ansible-compat==4.1.11
|
ansible-compat==4.1.11
|
||||||
# via molecule
|
# via molecule
|
||||||
ansible-core==2.16.2
|
ansible-core==2.18.0
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# ansible-compat
|
# ansible-compat
|
||||||
@@ -77,7 +77,7 @@ molecule==6.0.3
|
|||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# molecule-plugins
|
# molecule-plugins
|
||||||
molecule-plugins[vagrant]==23.5.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
|
||||||
@@ -96,9 +96,9 @@ platformdirs==4.1.0
|
|||||||
# via virtualenv
|
# via virtualenv
|
||||||
pluggy==1.3.0
|
pluggy==1.3.0
|
||||||
# via molecule
|
# via molecule
|
||||||
pre-commit==3.6.0
|
pre-commit==3.8.0
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
pre-commit-hooks==4.5.0
|
pre-commit-hooks==4.6.0
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
pyasn1==0.5.1
|
pyasn1==0.5.1
|
||||||
# via
|
# via
|
||||||
@@ -114,7 +114,7 @@ python-dateutil==2.8.2
|
|||||||
# via kubernetes
|
# via kubernetes
|
||||||
python-vagrant==1.0.0
|
python-vagrant==1.0.0
|
||||||
# via molecule-plugins
|
# via molecule-plugins
|
||||||
pyyaml==6.0.1
|
pyyaml==6.0.2
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# ansible-compat
|
# ansible-compat
|
||||||
|
|||||||
@@ -7,11 +7,12 @@
|
|||||||
become: true
|
become: true
|
||||||
- role: raspberrypi
|
- role: raspberrypi
|
||||||
become: true
|
become: true
|
||||||
vars: {state: absent}
|
vars: { state: absent }
|
||||||
post_tasks:
|
post_tasks:
|
||||||
- name: Reboot and wait for node to come back up
|
- name: Reboot and wait for node to come back up
|
||||||
become: true
|
become: true
|
||||||
reboot:
|
ansible.builtin.reboot:
|
||||||
|
reboot_command: "{{ custom_reboot_command | default(omit) }}"
|
||||||
reboot_timeout: 3600
|
reboot_timeout: 3600
|
||||||
|
|
||||||
- name: Revert changes to Proxmox cluster
|
- name: Revert changes to Proxmox cluster
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Manage the downloading of K3S binaries
|
||||||
|
options:
|
||||||
|
k3s_version:
|
||||||
|
description: The desired version of K3S
|
||||||
|
required: true
|
||||||
@@ -1,36 +1,46 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
- name: Download k3s binary x64
|
- name: Download k3s binary x64
|
||||||
get_url:
|
ansible.builtin.get_url:
|
||||||
url: https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/k3s
|
url: https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/k3s
|
||||||
checksum: sha256:https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/sha256sum-amd64.txt
|
checksum: sha256:https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/sha256sum-amd64.txt
|
||||||
dest: /usr/local/bin/k3s
|
dest: /usr/local/bin/k3s
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: 0755
|
mode: "0755"
|
||||||
|
register: k3s_download_x64
|
||||||
|
retries: 5
|
||||||
|
delay: 10
|
||||||
|
until: k3s_download_x64 is succeeded
|
||||||
when: ansible_facts.architecture == "x86_64"
|
when: ansible_facts.architecture == "x86_64"
|
||||||
|
|
||||||
- name: Download k3s binary arm64
|
- name: Download k3s binary arm64
|
||||||
get_url:
|
ansible.builtin.get_url:
|
||||||
url: https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/k3s-arm64
|
url: https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/k3s-arm64
|
||||||
checksum: sha256:https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/sha256sum-arm64.txt
|
checksum: sha256:https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/sha256sum-arm64.txt
|
||||||
dest: /usr/local/bin/k3s
|
dest: /usr/local/bin/k3s
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: 0755
|
mode: "0755"
|
||||||
|
register: k3s_download_arm64
|
||||||
|
retries: 5
|
||||||
|
delay: 10
|
||||||
|
until: k3s_download_arm64 is succeeded
|
||||||
when:
|
when:
|
||||||
- ( ansible_facts.architecture is search("arm") and
|
- ( ansible_facts.architecture is search("arm") and ansible_facts.userspace_bits == "64" )
|
||||||
ansible_facts.userspace_bits == "64" ) or
|
or ansible_facts.architecture is search("aarch64")
|
||||||
ansible_facts.architecture is search("aarch64")
|
|
||||||
|
|
||||||
- name: Download k3s binary armhf
|
- name: Download k3s binary armhf
|
||||||
get_url:
|
ansible.builtin.get_url:
|
||||||
url: https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/k3s-armhf
|
url: https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/k3s-armhf
|
||||||
checksum: sha256:https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/sha256sum-arm.txt
|
checksum: sha256:https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/sha256sum-arm.txt
|
||||||
dest: /usr/local/bin/k3s
|
dest: /usr/local/bin/k3s
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: 0755
|
mode: "0755"
|
||||||
|
register: k3s_download_armhf
|
||||||
|
retries: 5
|
||||||
|
delay: 10
|
||||||
|
until: k3s_download_armhf is succeeded
|
||||||
when:
|
when:
|
||||||
- ansible_facts.architecture is search("arm")
|
- ansible_facts.architecture is search("arm")
|
||||||
- ansible_facts.userspace_bits == "32"
|
- ansible_facts.userspace_bits == "32"
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
extra_agent_args: ""
|
||||||
|
group_name_master: master
|
||||||
|
systemd_dir: /etc/systemd/system
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Setup k3s agents
|
||||||
|
options:
|
||||||
|
apiserver_endpoint:
|
||||||
|
description: Virtual ip-address configured on each master
|
||||||
|
required: true
|
||||||
|
|
||||||
|
extra_agent_args:
|
||||||
|
description: Extra arguments for agents nodes
|
||||||
|
|
||||||
|
group_name_master:
|
||||||
|
description: Name of the master group
|
||||||
|
default: master
|
||||||
|
|
||||||
|
k3s_token:
|
||||||
|
description: Token used to communicate between masters
|
||||||
|
|
||||||
|
proxy_env:
|
||||||
|
type: dict
|
||||||
|
description:
|
||||||
|
- Internet proxy configurations.
|
||||||
|
- See https://docs.k3s.io/advanced#configuring-an-http-proxy for details
|
||||||
|
default: ~
|
||||||
|
options:
|
||||||
|
HTTP_PROXY:
|
||||||
|
description: HTTP internet proxy
|
||||||
|
required: true
|
||||||
|
HTTPS_PROXY:
|
||||||
|
description: HTTPS internet proxy
|
||||||
|
required: true
|
||||||
|
NO_PROXY:
|
||||||
|
description: Addresses that will not use the proxies
|
||||||
|
required: true
|
||||||
|
|
||||||
|
systemd_dir:
|
||||||
|
description: Path to systemd services
|
||||||
|
default: /etc/systemd/system
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
- name: Create k3s-node.service.d directory
|
- name: Create k3s-node.service.d directory
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: '{{ systemd_dir }}/k3s-node.service.d'
|
path: "{{ systemd_dir }}/k3s-node.service.d"
|
||||||
state: directory
|
state: directory
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: '0755'
|
mode: "0755"
|
||||||
|
when: proxy_env is defined
|
||||||
|
|
||||||
- name: Copy K3s http_proxy conf file
|
- name: Copy K3s http_proxy conf file
|
||||||
template:
|
ansible.builtin.template:
|
||||||
src: "http_proxy.conf.j2"
|
src: http_proxy.conf.j2
|
||||||
dest: "{{ systemd_dir }}/k3s-node.service.d/http_proxy.conf"
|
dest: "{{ systemd_dir }}/k3s-node.service.d/http_proxy.conf"
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: '0755'
|
mode: "0755"
|
||||||
|
when: proxy_env is defined
|
||||||
@@ -1,19 +1,35 @@
|
|||||||
---
|
---
|
||||||
|
- name: Check for PXE-booted system
|
||||||
|
block:
|
||||||
|
- name: Check if system is PXE-booted
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: cat /proc/cmdline
|
||||||
|
register: boot_cmdline
|
||||||
|
changed_when: false
|
||||||
|
check_mode: false
|
||||||
|
|
||||||
|
- name: Set fact for PXE-booted system
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
is_pxe_booted: "{{ 'root=/dev/nfs' in boot_cmdline.stdout }}"
|
||||||
|
when: boot_cmdline.stdout is defined
|
||||||
|
|
||||||
|
- name: Include http_proxy configuration tasks
|
||||||
|
ansible.builtin.include_tasks: http_proxy.yml
|
||||||
|
|
||||||
- name: Deploy K3s http_proxy conf
|
- name: Deploy K3s http_proxy conf
|
||||||
include_tasks: http_proxy.yml
|
ansible.builtin.include_tasks: http_proxy.yml
|
||||||
when: proxy_env is defined
|
when: proxy_env is defined
|
||||||
|
|
||||||
- name: Copy K3s service file
|
- name: Configure the k3s service
|
||||||
template:
|
ansible.builtin.template:
|
||||||
src: "k3s.service.j2"
|
src: k3s.service.j2
|
||||||
dest: "{{ systemd_dir }}/k3s-node.service"
|
dest: "{{ systemd_dir }}/k3s-node.service"
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: 0755
|
mode: "0755"
|
||||||
|
|
||||||
- name: Enable and check K3s service
|
- name: Manage k3s service
|
||||||
systemd:
|
ansible.builtin.systemd:
|
||||||
name: k3s-node
|
name: k3s-node
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
state: restarted
|
state: restarted
|
||||||
|
|||||||
@@ -7,11 +7,14 @@ After=network-online.target
|
|||||||
Type=notify
|
Type=notify
|
||||||
ExecStartPre=-/sbin/modprobe br_netfilter
|
ExecStartPre=-/sbin/modprobe br_netfilter
|
||||||
ExecStartPre=-/sbin/modprobe overlay
|
ExecStartPre=-/sbin/modprobe overlay
|
||||||
ExecStart=/usr/local/bin/k3s agent --server https://{{ apiserver_endpoint | ansible.utils.ipwrap }}:6443 --token {{ hostvars[groups[group_name_master | default('master')][0]]['token'] | default(k3s_token) }} {{ extra_agent_args | default("") }}
|
# Conditional snapshotter based on PXE boot status
|
||||||
|
ExecStart=/usr/local/bin/k3s agent \
|
||||||
|
--server https://{{ apiserver_endpoint | ansible.utils.ipwrap }}:6443 \
|
||||||
|
{% if is_pxe_booted | default(false) %}--snapshotter native \
|
||||||
|
{% endif %}--token {{ hostvars[groups[group_name_master | default('master')][0]]['token'] | default(k3s_token) }} \
|
||||||
|
{{ extra_agent_args }}
|
||||||
KillMode=process
|
KillMode=process
|
||||||
Delegate=yes
|
Delegate=yes
|
||||||
# Having non-zero Limit*s causes performance problems due to accounting overhead
|
|
||||||
# in the kernel. We recommend using cgroups to do container-local accounting.
|
|
||||||
LimitNOFILE=1048576
|
LimitNOFILE=1048576
|
||||||
LimitNPROC=infinity
|
LimitNPROC=infinity
|
||||||
LimitCORE=infinity
|
LimitCORE=infinity
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
# Indicates whether custom registries for k3s should be configured
|
|
||||||
# Possible values:
|
|
||||||
# - present
|
|
||||||
# - absent
|
|
||||||
state: present
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Configure the use of a custom container registry
|
||||||
|
options:
|
||||||
|
custom_registries_yaml:
|
||||||
|
description:
|
||||||
|
- YAML block defining custom registries.
|
||||||
|
- >
|
||||||
|
The following is an example that pulls all images used in
|
||||||
|
this playbook through your private registries.
|
||||||
|
- >
|
||||||
|
It also allows you to pull your own images from your private
|
||||||
|
registry, without having to use imagePullSecrets in your
|
||||||
|
deployments.
|
||||||
|
- >
|
||||||
|
If all you need is your own images and you don't care about
|
||||||
|
caching the docker/quay/ghcr.io images, you can just remove
|
||||||
|
those from the mirrors: section.
|
||||||
|
required: true
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
- name: Create directory /etc/rancher/k3s
|
- name: Create directory /etc/rancher/k3s
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: "/etc/{{ item }}"
|
path: /etc/{{ item }}
|
||||||
state: directory
|
state: directory
|
||||||
mode: '0755'
|
mode: "0755"
|
||||||
loop:
|
loop:
|
||||||
- rancher
|
- rancher
|
||||||
- rancher/k3s
|
- rancher/k3s
|
||||||
|
|
||||||
- name: Insert registries into /etc/rancher/k3s/registries.yaml
|
- name: Insert registries into /etc/rancher/k3s/registries.yaml
|
||||||
blockinfile:
|
ansible.builtin.blockinfile:
|
||||||
path: /etc/rancher/k3s/registries.yaml
|
path: /etc/rancher/k3s/registries.yaml
|
||||||
block: "{{ custom_registries_yaml }}"
|
block: "{{ custom_registries_yaml }}"
|
||||||
mode: '0600'
|
mode: "0600"
|
||||||
create: true
|
create: true
|
||||||
@@ -1,12 +1,30 @@
|
|||||||
---
|
---
|
||||||
# If you want to explicitly define an interface that ALL control nodes
|
extra_server_args: ""
|
||||||
# should use to propagate the VIP, define it here. Otherwise, kube-vip
|
|
||||||
# will determine the right interface automatically at runtime.
|
k3s_kubectl_binary: k3s kubectl
|
||||||
kube_vip_iface: null
|
|
||||||
|
|
||||||
# Name of the master group
|
|
||||||
group_name_master: master
|
group_name_master: master
|
||||||
|
|
||||||
|
kube_vip_arp: true
|
||||||
|
kube_vip_iface:
|
||||||
|
kube_vip_cloud_provider_tag_version: main
|
||||||
|
kube_vip_tag_version: v0.7.2
|
||||||
|
|
||||||
|
kube_vip_bgp: false
|
||||||
|
kube_vip_bgp_routerid: 127.0.0.1
|
||||||
|
kube_vip_bgp_as: "64513"
|
||||||
|
kube_vip_bgp_peeraddress: 192.168.30.1
|
||||||
|
kube_vip_bgp_peeras: "64512"
|
||||||
|
|
||||||
|
kube_vip_bgp_peers: []
|
||||||
|
kube_vip_bgp_peers_groups: ['k3s_master']
|
||||||
|
|
||||||
|
metal_lb_controller_tag_version: v0.14.3
|
||||||
|
metal_lb_speaker_tag_version: v0.14.3
|
||||||
|
metal_lb_type: native
|
||||||
|
|
||||||
|
retry_count: 20
|
||||||
|
|
||||||
# yamllint disable rule:line-length
|
# yamllint disable rule:line-length
|
||||||
server_init_args: >-
|
server_init_args: >-
|
||||||
{% if groups[group_name_master | default('master')] | length > 1 %}
|
{% if groups[group_name_master | default('master')] | length > 1 %}
|
||||||
@@ -17,4 +35,6 @@ server_init_args: >-
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
--token {{ k3s_token }}
|
--token {{ k3s_token }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ extra_server_args | default('') }}
|
{{ extra_server_args }}
|
||||||
|
|
||||||
|
systemd_dir: /etc/systemd/system
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Setup k3s servers
|
||||||
|
options:
|
||||||
|
apiserver_endpoint:
|
||||||
|
description: Virtual ip-address configured on each master
|
||||||
|
required: true
|
||||||
|
|
||||||
|
cilium_bgp:
|
||||||
|
description:
|
||||||
|
- Enable cilium BGP control plane for LB services and pod cidrs.
|
||||||
|
- Disables the use of MetalLB.
|
||||||
|
type: bool
|
||||||
|
default: ~
|
||||||
|
|
||||||
|
cilium_iface:
|
||||||
|
description: The network interface used for when Cilium is enabled
|
||||||
|
default: ~
|
||||||
|
|
||||||
|
extra_server_args:
|
||||||
|
description: Extra arguments for server nodes
|
||||||
|
default: ""
|
||||||
|
|
||||||
|
group_name_master:
|
||||||
|
description: Name of the master group
|
||||||
|
default: master
|
||||||
|
|
||||||
|
k3s_create_kubectl_symlink:
|
||||||
|
description: Create the kubectl -> k3s symlink
|
||||||
|
default: false
|
||||||
|
type: bool
|
||||||
|
|
||||||
|
k3s_create_crictl_symlink:
|
||||||
|
description: Create the crictl -> k3s symlink
|
||||||
|
default: false
|
||||||
|
type: bool
|
||||||
|
|
||||||
|
kube_vip_arp:
|
||||||
|
description: Enables kube-vip ARP broadcasts
|
||||||
|
default: true
|
||||||
|
type: bool
|
||||||
|
|
||||||
|
kube_vip_bgp:
|
||||||
|
description: Enables kube-vip BGP peering
|
||||||
|
default: false
|
||||||
|
type: bool
|
||||||
|
|
||||||
|
kube_vip_bgp_routerid:
|
||||||
|
description: Defines the router ID for the kube-vip BGP server
|
||||||
|
default: "127.0.0.1"
|
||||||
|
|
||||||
|
kube_vip_bgp_as:
|
||||||
|
description: Defines the AS for the kube-vip BGP server
|
||||||
|
default: "64513"
|
||||||
|
|
||||||
|
kube_vip_bgp_peeraddress:
|
||||||
|
description: Defines the address for the kube-vip BGP peer
|
||||||
|
default: "192.168.30.1"
|
||||||
|
|
||||||
|
kube_vip_bgp_peeras:
|
||||||
|
description: Defines the AS for the kube-vip BGP peer
|
||||||
|
default: "64512"
|
||||||
|
|
||||||
|
kube_vip_bgp_peers:
|
||||||
|
description: List of BGP peer ASN & address pairs
|
||||||
|
default: []
|
||||||
|
|
||||||
|
kube_vip_bgp_peers_groups:
|
||||||
|
description: Inventory group in which to search for additional kube_vip_bgp_peers parameters to merge.
|
||||||
|
default: ['k3s_master']
|
||||||
|
|
||||||
|
kube_vip_iface:
|
||||||
|
description:
|
||||||
|
- 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.
|
||||||
|
default: ~
|
||||||
|
|
||||||
|
kube_vip_tag_version:
|
||||||
|
description: Image tag for kube-vip
|
||||||
|
default: v0.7.2
|
||||||
|
|
||||||
|
kube_vip_cloud_provider_tag_version:
|
||||||
|
description: Tag for kube-vip-cloud-provider manifest when enabled
|
||||||
|
default: main
|
||||||
|
|
||||||
|
kube_vip_lb_ip_range:
|
||||||
|
description: IP range for kube-vip load balancer
|
||||||
|
default: ~
|
||||||
|
|
||||||
|
metal_lb_controller_tag_version:
|
||||||
|
description: Image tag for MetalLB
|
||||||
|
default: v0.14.3
|
||||||
|
|
||||||
|
metal_lb_speaker_tag_version:
|
||||||
|
description: Image tag for MetalLB
|
||||||
|
default: v0.14.3
|
||||||
|
|
||||||
|
metal_lb_type:
|
||||||
|
choices:
|
||||||
|
- frr
|
||||||
|
- native
|
||||||
|
default: native
|
||||||
|
description: Use FRR mode or native. Valid values are `frr` and `native`
|
||||||
|
|
||||||
|
proxy_env:
|
||||||
|
type: dict
|
||||||
|
description:
|
||||||
|
- Internet proxy configurations.
|
||||||
|
- See https://docs.k3s.io/advanced#configuring-an-http-proxy for details
|
||||||
|
default: ~
|
||||||
|
options:
|
||||||
|
HTTP_PROXY:
|
||||||
|
description: HTTP internet proxy
|
||||||
|
required: true
|
||||||
|
HTTPS_PROXY:
|
||||||
|
description: HTTPS internet proxy
|
||||||
|
required: true
|
||||||
|
NO_PROXY:
|
||||||
|
description: Addresses that will not use the proxies
|
||||||
|
required: true
|
||||||
|
|
||||||
|
retry_count:
|
||||||
|
description: Amount of retries when verifying that nodes joined
|
||||||
|
type: int
|
||||||
|
default: 20
|
||||||
|
|
||||||
|
server_init_args:
|
||||||
|
description: Arguments for server nodes
|
||||||
|
|
||||||
|
systemd_dir:
|
||||||
|
description: Path to systemd services
|
||||||
|
default: /etc/systemd/system
|
||||||
@@ -4,10 +4,110 @@
|
|||||||
|
|
||||||
- name: Fetch k3s-init.service logs
|
- name: Fetch k3s-init.service logs
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
cmd: journalctl --all --unit=k3s-init.service
|
cmd: >-
|
||||||
|
timeout --signal=TERM --kill-after=5s 30s journalctl --no-pager
|
||||||
|
--unit=k3s-init.service --since=-30min --lines=5000
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
register: k3s_init_log
|
register: k3s_init_log
|
||||||
|
|
||||||
|
- name: Fetch k3s-init.service status
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: >-
|
||||||
|
timeout --signal=TERM --kill-after=5s 15s systemctl status
|
||||||
|
k3s-init.service --no-pager --full
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
register: k3s_init_status
|
||||||
|
|
||||||
|
- name: Fetch IP address state
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: ip -br address
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
register: k3s_ip_address_state
|
||||||
|
|
||||||
|
- name: Fetch IP link state
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: ip -br link
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
register: k3s_ip_link_state
|
||||||
|
|
||||||
|
- name: Fetch IP route state
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: ip route show
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
register: k3s_ip_route_state
|
||||||
|
|
||||||
|
- name: Fetch IP neighbor state
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: ip neigh show
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
register: k3s_ip_neighbor_state
|
||||||
|
|
||||||
|
- name: Fetch IP rule state
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: ip rule show
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
register: k3s_ip_rule_state
|
||||||
|
|
||||||
|
- name: Fetch listening TCP sockets
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: ss -ltn
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
register: k3s_tcp_listener_state
|
||||||
|
|
||||||
|
- name: Fetch guest firewall service state
|
||||||
|
ansible.builtin.service_facts:
|
||||||
|
no_log: true
|
||||||
|
register: k3s_service_facts
|
||||||
|
|
||||||
|
- name: Fetch input firewall rules
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: iptables -S INPUT
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
register: k3s_input_firewall_rules
|
||||||
|
|
||||||
|
- name: Ping the primary Kubernetes API address from {{ ansible_hostname }}
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- ping
|
||||||
|
- -c
|
||||||
|
- "1"
|
||||||
|
- -W
|
||||||
|
- "1"
|
||||||
|
- "{{ hostvars[groups[group_name_master | default('master')][0]].k3s_node_ip | split(',') | first }}"
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
register: k3s_primary_api_ping
|
||||||
|
|
||||||
|
- name: Probe primary Kubernetes API from {{ ansible_hostname }}
|
||||||
|
ansible.builtin.wait_for:
|
||||||
|
host: "{{ hostvars[groups[group_name_master | default('master')][0]].k3s_node_ip | split(',') | first }}"
|
||||||
|
port: 6443
|
||||||
|
connect_timeout: 1
|
||||||
|
timeout: 2
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
register: k3s_primary_api_probe
|
||||||
|
|
||||||
- name: Create {{ log_destination }}
|
- name: Create {{ log_destination }}
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
run_once: true
|
run_once: true
|
||||||
@@ -23,6 +123,58 @@
|
|||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: content.j2
|
src: content.j2
|
||||||
dest: "{{ log_destination }}/k3s-init@{{ ansible_hostname }}.log"
|
dest: "{{ log_destination }}/k3s-init@{{ ansible_hostname }}.log"
|
||||||
mode: 0644
|
mode: "0644"
|
||||||
vars:
|
vars:
|
||||||
content: "{{ k3s_init_log.stdout }}"
|
content: |
|
||||||
|
=== k3s-init.service status ===
|
||||||
|
{{ k3s_init_status.stdout | regex_replace('--token(?:=| +)[^ ]+', '--token ***') }}
|
||||||
|
|
||||||
|
=== k3s-init.service journal ===
|
||||||
|
{{ k3s_init_log.stdout | regex_replace('--token(?:=| +)[^ ]+', '--token ***') }}
|
||||||
|
|
||||||
|
- name: Store network diagnostics to {{ log_destination }}
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: content.j2
|
||||||
|
dest: "{{ log_destination }}/network@{{ ansible_hostname }}.log"
|
||||||
|
mode: "0644"
|
||||||
|
vars:
|
||||||
|
content: |
|
||||||
|
=== ip -br address ===
|
||||||
|
{{ k3s_ip_address_state.stdout }}
|
||||||
|
|
||||||
|
=== ip -br link ===
|
||||||
|
{{ k3s_ip_link_state.stdout }}
|
||||||
|
|
||||||
|
=== ip route show ===
|
||||||
|
{{ k3s_ip_route_state.stdout }}
|
||||||
|
|
||||||
|
=== ip neigh show ===
|
||||||
|
{{ k3s_ip_neighbor_state.stdout }}
|
||||||
|
|
||||||
|
=== ip rule show ===
|
||||||
|
{{ k3s_ip_rule_state.stdout }}
|
||||||
|
|
||||||
|
=== ss -ltn ===
|
||||||
|
{{ k3s_tcp_listener_state.stdout }}
|
||||||
|
|
||||||
|
=== guest firewall services ===
|
||||||
|
firewalld={{ k3s_service_facts.ansible_facts.services.get('firewalld.service', {}).get('state', 'not-found') }}
|
||||||
|
nftables={{ k3s_service_facts.ansible_facts.services.get('nftables.service', {}).get('state', 'not-found') }}
|
||||||
|
ufw={{ k3s_service_facts.ansible_facts.services.get('ufw.service', {}).get('state', 'not-found') }}
|
||||||
|
|
||||||
|
=== iptables -S INPUT ===
|
||||||
|
rc={{ k3s_input_firewall_rules.rc }}
|
||||||
|
{{ k3s_input_firewall_rules.stdout }}
|
||||||
|
{{ k3s_input_firewall_rules.stderr }}
|
||||||
|
|
||||||
|
=== primary API ping ===
|
||||||
|
rc={{ k3s_primary_api_ping.rc }}
|
||||||
|
{{ k3s_primary_api_ping.stdout }}
|
||||||
|
{{ k3s_primary_api_ping.stderr }}
|
||||||
|
|
||||||
|
=== primary API probe ===
|
||||||
|
failed={{ k3s_primary_api_probe.failed | default(false) }}
|
||||||
|
elapsed={{ k3s_primary_api_probe.elapsed | default('unknown') }}
|
||||||
|
msg={{ k3s_primary_api_probe.msg | default('connected') }}
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
- name: Create k3s.service.d directory
|
- name: Create k3s.service.d directory
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: '{{ systemd_dir }}/k3s.service.d'
|
path: "{{ systemd_dir }}/k3s.service.d"
|
||||||
state: directory
|
state: directory
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: '0755'
|
mode: "0755"
|
||||||
|
|
||||||
|
|
||||||
- name: Copy K3s http_proxy conf file
|
- name: Copy K3s http_proxy conf file
|
||||||
template:
|
ansible.builtin.template:
|
||||||
src: "http_proxy.conf.j2"
|
src: http_proxy.conf.j2
|
||||||
dest: "{{ systemd_dir }}/k3s.service.d/http_proxy.conf"
|
dest: "{{ systemd_dir }}/k3s.service.d/http_proxy.conf"
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: '0755'
|
mode: "0755"
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
- name: Verify primary Kubernetes API reachability from {{ joining_master }}
|
||||||
|
ansible.builtin.wait_for:
|
||||||
|
host: >-
|
||||||
|
{{ hostvars[groups[group_name_master | default('master')][0]].k3s_node_ip
|
||||||
|
| split(',') | first }}
|
||||||
|
port: 6443
|
||||||
|
connect_timeout: 2
|
||||||
|
timeout: 30
|
||||||
|
delegate_to: "{{ joining_master }}"
|
||||||
|
|
||||||
|
- name: Join transient k3s-init service for {{ joining_master }}
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: >-
|
||||||
|
systemd-run -p RestartSec=2 -p Restart=on-failure
|
||||||
|
-p Delegate=yes -p TasksMax=infinity -p KillMode=process
|
||||||
|
-p LimitNOFILE=1048576 -p LimitNPROC=infinity -p LimitCORE=infinity
|
||||||
|
--unit=k3s-init
|
||||||
|
k3s server {{ hostvars[joining_master].k3s_server_init_args }}
|
||||||
|
creates: "{{ systemd_dir }}/k3s-init.service"
|
||||||
|
delegate_to: "{{ joining_master }}"
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Wait for primary registration of {{ joining_master }}
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: >-
|
||||||
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} get node
|
||||||
|
{{ hostvars[joining_master].ansible_hostname }}
|
||||||
|
delegate_to: "{{ groups[group_name_master | default('master')][0] }}"
|
||||||
|
register: joined_master
|
||||||
|
until: joined_master.rc == 0
|
||||||
|
retries: "{{ retry_count | default(20) }}"
|
||||||
|
delay: 2
|
||||||
|
changed_when: false
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
- name: Create manifests directory on first master
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /var/lib/rancher/k3s/server/manifests
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
when: ansible_hostname == hostvars[groups[group_name_master | default('master')][0]]['ansible_hostname']
|
||||||
|
|
||||||
|
- name: Download vip cloud provider manifest to first master
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: https://raw.githubusercontent.com/kube-vip/kube-vip-cloud-provider/{{ kube_vip_cloud_provider_tag_version | default('main') }}/manifest/kube-vip-cloud-controller.yaml # noqa yaml[line-length]
|
||||||
|
dest: /var/lib/rancher/k3s/server/manifests/kube-vip-cloud-controller.yaml
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
when: ansible_hostname == hostvars[groups[group_name_master | default('master')][0]]['ansible_hostname']
|
||||||
|
|
||||||
|
- name: Copy kubevip configMap manifest to first master
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: kubevip.yaml.j2
|
||||||
|
dest: /var/lib/rancher/k3s/server/manifests/kubevip.yaml
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
when: ansible_hostname == hostvars[groups[group_name_master | default('master')][0]]['ansible_hostname']
|
||||||
+121
-53
@@ -1,166 +1,234 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
- name: Stop k3s-init
|
- name: Stop k3s-init
|
||||||
systemd:
|
ansible.builtin.systemd:
|
||||||
name: k3s-init
|
name: k3s-init
|
||||||
state: stopped
|
state: stopped
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
# k3s-init won't work if the port is already in use
|
# k3s-init won't work if the port is already in use
|
||||||
- name: Stop k3s
|
- name: Stop k3s
|
||||||
systemd:
|
ansible.builtin.systemd:
|
||||||
name: k3s
|
name: k3s
|
||||||
state: stopped
|
state: stopped
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
- name: Clean previous runs of k3s-init # noqa command-instead-of-module
|
- name: Clean previous runs of k3s-init # noqa command-instead-of-module
|
||||||
# The systemd module does not support "reset-failed", so we need to resort to command.
|
# The systemd module does not support "reset-failed", so we need to resort to command.
|
||||||
command: systemctl reset-failed k3s-init
|
ansible.builtin.command: systemctl reset-failed k3s-init
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Deploy K3s http_proxy conf
|
- name: Deploy K3s http_proxy conf
|
||||||
include_tasks: http_proxy.yml
|
ansible.builtin.include_tasks: http_proxy.yml
|
||||||
when: proxy_env is defined
|
when: proxy_env is defined
|
||||||
|
|
||||||
- name: Deploy vip manifest
|
- name: Deploy vip manifest
|
||||||
include_tasks: vip.yml
|
ansible.builtin.include_tasks: vip.yml
|
||||||
|
|
||||||
- name: Deploy metallb manifest
|
- name: Deploy metallb manifest
|
||||||
include_tasks: metallb.yml
|
ansible.builtin.include_tasks: metallb.yml
|
||||||
tags: metallb
|
tags: metallb
|
||||||
|
when: kube_vip_lb_ip_range is not defined and (not cilium_bgp or cilium_iface is not defined)
|
||||||
|
|
||||||
- name: Init cluster inside the transient k3s-init service
|
- name: Deploy kube-vip manifest
|
||||||
command:
|
ansible.builtin.include_tasks: kube-vip.yml
|
||||||
cmd: "systemd-run -p RestartSec=2 \
|
tags: kubevip
|
||||||
-p Restart=on-failure \
|
when: kube_vip_lb_ip_range is defined
|
||||||
--unit=k3s-init \
|
|
||||||
k3s server {{ server_init_args }}"
|
|
||||||
creates: "{{ systemd_dir }}/k3s-init.service"
|
|
||||||
|
|
||||||
- name: Verification
|
- name: Initialize and verify the K3s control plane
|
||||||
|
any_errors_fatal: true
|
||||||
when: not ansible_check_mode
|
when: not ansible_check_mode
|
||||||
block:
|
block:
|
||||||
- name: Verify that all nodes actually joined (check k3s-init.service if this fails)
|
- name: Materialize per-host server initialization arguments
|
||||||
command:
|
ansible.builtin.set_fact:
|
||||||
cmd: k3s kubectl get nodes -l "node-role.kubernetes.io/master=true" -o=jsonpath="{.items[*].metadata.name}"
|
k3s_server_init_args: "{{ server_init_args }}"
|
||||||
register: nodes
|
no_log: true
|
||||||
until: nodes.rc == 0 and (nodes.stdout.split() | length) == (groups[group_name_master | default('master')] | length) # yamllint disable-line rule:line-length
|
when: groups[group_name_master | default('master')] | length > 1
|
||||||
retries: "{{ retry_count | default(20) }}"
|
|
||||||
delay: 10
|
- name: Orchestrate control-plane initialization from the first master
|
||||||
changed_when: false
|
when: inventory_hostname == groups[group_name_master | default('master')][0]
|
||||||
|
block:
|
||||||
|
- name: Init the first master inside the transient k3s-init service
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: >-
|
||||||
|
systemd-run -p RestartSec=2 -p Restart=on-failure
|
||||||
|
-p Delegate=yes -p TasksMax=infinity -p KillMode=process
|
||||||
|
-p LimitNOFILE=1048576 -p LimitNPROC=infinity -p LimitCORE=infinity
|
||||||
|
--unit=k3s-init k3s server {{ server_init_args }}
|
||||||
|
creates: "{{ systemd_dir }}/k3s-init.service"
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Wait for the first master Kubernetes API
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "{{ k3s_kubectl_binary | default('k3s kubectl') }} get --raw=/readyz"
|
||||||
|
register: first_master_api
|
||||||
|
until: first_master_api.rc == 0
|
||||||
|
retries: "{{ retry_count | default(20) }}"
|
||||||
|
delay: 2
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Verify the first master API listener on its node address
|
||||||
|
ansible.builtin.wait_for:
|
||||||
|
host: >-
|
||||||
|
{{ hostvars[groups[group_name_master | default('master')][0]].k3s_node_ip
|
||||||
|
| split(',') | first }}
|
||||||
|
port: 6443
|
||||||
|
connect_timeout: 2
|
||||||
|
timeout: "{{ retry_count | default(20) * 2 }}"
|
||||||
|
|
||||||
|
- name: Join additional masters one at a time
|
||||||
|
ansible.builtin.include_tasks: join_master.yml
|
||||||
|
loop: "{{ groups[group_name_master | default('master')][1:] }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: joining_master
|
||||||
|
when: groups[group_name_master | default('master')] | length > 1
|
||||||
|
|
||||||
|
- name: Verify that all expected control-plane nodes registered
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: >-
|
||||||
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} get nodes
|
||||||
|
-o=jsonpath='{.items[*].metadata.name}'
|
||||||
|
register: nodes
|
||||||
|
until:
|
||||||
|
- nodes.rc == 0
|
||||||
|
- expected_control_plane_names | difference(nodes.stdout.split()) | length == 0
|
||||||
|
retries: "{{ retry_count | default(20) }}"
|
||||||
|
delay: 2
|
||||||
|
changed_when: false
|
||||||
|
vars:
|
||||||
|
expected_control_plane_names: >-
|
||||||
|
{{
|
||||||
|
groups[group_name_master | default('master')]
|
||||||
|
| map('extract', hostvars, 'ansible_hostname')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Wait for K3s bootstrap CRDs to become established
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: >-
|
||||||
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} wait
|
||||||
|
--for=condition=Established --timeout=5s
|
||||||
|
crd/addons.k3s.cattle.io
|
||||||
|
crd/helmcharts.helm.cattle.io
|
||||||
|
crd/helmchartconfigs.helm.cattle.io
|
||||||
|
register: bootstrap_crds
|
||||||
|
until: bootstrap_crds.rc == 0
|
||||||
|
retries: "{{ retry_count | default(20) }}"
|
||||||
|
delay: 2
|
||||||
|
changed_when: false
|
||||||
always:
|
always:
|
||||||
- name: Save logs of k3s-init.service
|
- name: Save logs of k3s-init.service
|
||||||
include_tasks: fetch_k3s_init_logs.yml
|
ansible.builtin.include_tasks: fetch_k3s_init_logs.yml
|
||||||
when: log_destination
|
when: log_destination
|
||||||
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) }}
|
||||||
- name: Kill the temporary service used for initialization
|
- name: Kill the temporary service used for initialization
|
||||||
systemd:
|
ansible.builtin.systemd:
|
||||||
name: k3s-init
|
name: k3s-init
|
||||||
state: stopped
|
state: stopped
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
- name: Copy K3s service file
|
- name: Copy K3s service file
|
||||||
register: k3s_service
|
register: k3s_service
|
||||||
template:
|
ansible.builtin.template:
|
||||||
src: "k3s.service.j2"
|
src: k3s.service.j2
|
||||||
dest: "{{ systemd_dir }}/k3s.service"
|
dest: "{{ systemd_dir }}/k3s.service"
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: 0644
|
mode: "0644"
|
||||||
|
|
||||||
- name: Enable and check K3s service
|
- name: Enable and check K3s service
|
||||||
systemd:
|
ansible.builtin.systemd:
|
||||||
name: k3s
|
name: k3s
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
state: restarted
|
state: restarted
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
- name: Wait for node-token
|
- name: Wait for node-token
|
||||||
wait_for:
|
ansible.builtin.wait_for:
|
||||||
path: /var/lib/rancher/k3s/server/node-token
|
path: /var/lib/rancher/k3s/server/node-token
|
||||||
|
|
||||||
- name: Register node-token file access mode
|
- name: Register node-token file access mode
|
||||||
stat:
|
ansible.builtin.stat:
|
||||||
path: /var/lib/rancher/k3s/server
|
path: /var/lib/rancher/k3s/server
|
||||||
register: p
|
register: p
|
||||||
|
|
||||||
- name: Change file access node-token
|
- name: Change file access node-token
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: /var/lib/rancher/k3s/server
|
path: /var/lib/rancher/k3s/server
|
||||||
mode: "g+rx,o+rx"
|
mode: g+rx,o+rx
|
||||||
|
|
||||||
- name: Read node-token from master
|
- name: Read node-token from master
|
||||||
slurp:
|
ansible.builtin.slurp:
|
||||||
src: /var/lib/rancher/k3s/server/node-token
|
src: /var/lib/rancher/k3s/server/node-token
|
||||||
register: node_token
|
register: node_token
|
||||||
|
|
||||||
- name: Store Master node-token
|
- name: Store Master node-token
|
||||||
set_fact:
|
ansible.builtin.set_fact:
|
||||||
token: "{{ node_token.content | b64decode | regex_replace('\n', '') }}"
|
token: "{{ node_token.content | b64decode | regex_replace('\n', '') }}"
|
||||||
|
|
||||||
- name: Restore node-token file access
|
- name: Restore node-token file access
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: /var/lib/rancher/k3s/server
|
path: /var/lib/rancher/k3s/server
|
||||||
mode: "{{ p.stat.mode }}"
|
mode: "{{ p.stat.mode }}"
|
||||||
|
|
||||||
- name: Create directory .kube
|
- name: Create directory .kube
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: "{{ ansible_user_dir }}/.kube"
|
path: "{{ ansible_user_dir }}/.kube"
|
||||||
state: directory
|
state: directory
|
||||||
owner: "{{ ansible_user_id }}"
|
owner: "{{ ansible_user_id }}"
|
||||||
mode: "u=rwx,g=rx,o="
|
mode: u=rwx,g=rx,o=
|
||||||
|
|
||||||
- name: Copy config file to user home directory
|
- name: Copy config file to user home directory
|
||||||
copy:
|
ansible.builtin.copy:
|
||||||
src: /etc/rancher/k3s/k3s.yaml
|
src: /etc/rancher/k3s/k3s.yaml
|
||||||
dest: "{{ ansible_user_dir }}/.kube/config"
|
dest: "{{ ansible_user_dir }}/.kube/config"
|
||||||
remote_src: true
|
remote_src: true
|
||||||
owner: "{{ ansible_user_id }}"
|
owner: "{{ ansible_user_id }}"
|
||||||
mode: "u=rw,g=,o="
|
mode: u=rw,g=,o=
|
||||||
|
|
||||||
- name: Configure kubectl cluster to {{ endpoint_url }}
|
- name: Configure kubectl cluster to {{ endpoint_url }}
|
||||||
command: >-
|
ansible.builtin.command: >-
|
||||||
k3s kubectl config set-cluster default
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} config set-cluster default
|
||||||
--server={{ endpoint_url }}
|
--server={{ endpoint_url }}
|
||||||
--kubeconfig {{ ansible_user_dir }}/.kube/config
|
--kubeconfig {{ ansible_user_dir }}/.kube/config
|
||||||
changed_when: true
|
changed_when: true
|
||||||
vars:
|
vars:
|
||||||
endpoint_url: >-
|
endpoint_url: >-
|
||||||
https://{{ apiserver_endpoint | ansible.utils.ipwrap }}:6443
|
https://{{ apiserver_endpoint | ansible.utils.ipwrap }}:6443
|
||||||
# Deactivated linter rules:
|
# Deactivated linter rules:
|
||||||
# - jinja[invalid]: As of version 6.6.0, ansible-lint complains that the input to ipwrap
|
# - jinja[invalid]: As of version 6.6.0, ansible-lint complains that the input to ipwrap
|
||||||
# would be undefined. This will not be the case during playbook execution.
|
# would be undefined. This will not be the case during playbook execution.
|
||||||
# noqa jinja[invalid]
|
# noqa jinja[invalid]
|
||||||
|
|
||||||
- name: Create kubectl symlink
|
- name: Create kubectl symlink
|
||||||
file:
|
ansible.builtin.file:
|
||||||
src: /usr/local/bin/k3s
|
src: /usr/local/bin/k3s
|
||||||
dest: /usr/local/bin/kubectl
|
dest: /usr/local/bin/kubectl
|
||||||
state: link
|
state: link
|
||||||
|
when: k3s_create_kubectl_symlink | default(true) | bool
|
||||||
|
|
||||||
- name: Create crictl symlink
|
- name: Create crictl symlink
|
||||||
file:
|
ansible.builtin.file:
|
||||||
src: /usr/local/bin/k3s
|
src: /usr/local/bin/k3s
|
||||||
dest: /usr/local/bin/crictl
|
dest: /usr/local/bin/crictl
|
||||||
state: link
|
state: link
|
||||||
|
when: k3s_create_crictl_symlink | default(true) | bool
|
||||||
|
|
||||||
- name: Get contents of manifests folder
|
- name: Get contents of manifests folder
|
||||||
find:
|
ansible.builtin.find:
|
||||||
paths: /var/lib/rancher/k3s/server/manifests
|
paths: /var/lib/rancher/k3s/server/manifests
|
||||||
file_type: file
|
file_type: file
|
||||||
register: k3s_server_manifests
|
register: k3s_server_manifests
|
||||||
|
|
||||||
- name: Get sub dirs of manifests folder
|
- name: Get sub dirs of manifests folder
|
||||||
find:
|
ansible.builtin.find:
|
||||||
paths: /var/lib/rancher/k3s/server/manifests
|
paths: /var/lib/rancher/k3s/server/manifests
|
||||||
file_type: directory
|
file_type: directory
|
||||||
register: k3s_server_manifests_directories
|
register: k3s_server_manifests_directories
|
||||||
|
|
||||||
- name: Remove manifests and folders that are only needed for bootstrapping cluster so k3s doesn't auto apply on start
|
- name: Remove manifests and folders that are only needed for bootstrapping cluster so k3s doesn't auto apply on start
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: "{{ item.path }}"
|
path: "{{ item.path }}"
|
||||||
state: absent
|
state: absent
|
||||||
with_items:
|
with_items:
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
---
|
---
|
||||||
- name: Create manifests directory on first master
|
- name: Create manifests directory on first master
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: /var/lib/rancher/k3s/server/manifests
|
path: /var/lib/rancher/k3s/server/manifests
|
||||||
state: directory
|
state: directory
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: 0644
|
mode: "0644"
|
||||||
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: "Download to first master: manifest for metallb-{{ metal_lb_type }}"
|
- name: "Download to first master: manifest for metallb-{{ metal_lb_type }}"
|
||||||
ansible.builtin.get_url:
|
ansible.builtin.get_url:
|
||||||
url: "https://raw.githubusercontent.com/metallb/metallb/{{ metal_lb_controller_tag_version }}/config/manifests/metallb-{{ metal_lb_type }}.yaml" # noqa yaml[line-length]
|
url: https://raw.githubusercontent.com/metallb/metallb/{{ metal_lb_controller_tag_version }}/config/manifests/metallb-{{ metal_lb_type }}.yaml # noqa yaml[line-length]
|
||||||
dest: "/var/lib/rancher/k3s/server/manifests/metallb-crds.yaml"
|
dest: /var/lib/rancher/k3s/server/manifests/metallb-crds.yaml
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: 0644
|
mode: "0644"
|
||||||
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 }}
|
||||||
ansible.builtin.replace:
|
ansible.builtin.replace:
|
||||||
path: "/var/lib/rancher/k3s/server/manifests/metallb-crds.yaml"
|
path: /var/lib/rancher/k3s/server/manifests/metallb-crds.yaml
|
||||||
regexp: "{{ item.change | ansible.builtin.regex_escape }}"
|
regexp: "{{ item.change | ansible.builtin.regex_escape }}"
|
||||||
replace: "{{ item.to }}"
|
replace: "{{ item.to }}"
|
||||||
with_items:
|
with_items:
|
||||||
- change: "metallb/speaker:{{ metal_lb_controller_tag_version }}"
|
- change: metallb/speaker:{{ metal_lb_controller_tag_version }}
|
||||||
to: "metallb/speaker:{{ metal_lb_speaker_tag_version }}"
|
to: metallb/speaker:{{ metal_lb_speaker_tag_version }}
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.change }} => {{ item.to }}"
|
label: "{{ item.change }} => {{ item.to }}"
|
||||||
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']
|
||||||
@@ -1,27 +1,31 @@
|
|||||||
---
|
---
|
||||||
|
- name: Set _kube_vip_bgp_peers fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
_kube_vip_bgp_peers: "{{ lookup('community.general.merge_variables', '^kube_vip_bgp_peers__.+$', initial_value=kube_vip_bgp_peers, groups=kube_vip_bgp_peers_groups) }}" # yamllint disable-line rule:line-length
|
||||||
|
|
||||||
- name: Create manifests directory on first master
|
- name: Create manifests directory on first master
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: /var/lib/rancher/k3s/server/manifests
|
path: /var/lib/rancher/k3s/server/manifests
|
||||||
state: directory
|
state: directory
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: 0644
|
mode: "0644"
|
||||||
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: Download vip rbac manifest to first master
|
- name: Download vip rbac manifest to first master
|
||||||
ansible.builtin.get_url:
|
ansible.builtin.get_url:
|
||||||
url: "https://raw.githubusercontent.com/kube-vip/kube-vip/{{ kube_vip_tag_version }}/docs/manifests/rbac.yaml"
|
url: https://kube-vip.io/manifests/rbac.yaml
|
||||||
dest: "/var/lib/rancher/k3s/server/manifests/vip-rbac.yaml"
|
dest: /var/lib/rancher/k3s/server/manifests/vip-rbac.yaml
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: 0644
|
mode: "0644"
|
||||||
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 vip manifest to first master
|
- name: Copy vip manifest to first master
|
||||||
template:
|
ansible.builtin.template:
|
||||||
src: "vip.yaml.j2"
|
src: vip.yaml.j2
|
||||||
dest: "/var/lib/rancher/k3s/server/manifests/vip.yaml"
|
dest: /var/lib/rancher/k3s/server/manifests/vip.yaml
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: 0644
|
mode: "0644"
|
||||||
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']
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: kubevip
|
||||||
|
namespace: kube-system
|
||||||
|
data:
|
||||||
|
{% if kube_vip_lb_ip_range is string %}
|
||||||
|
{# kube_vip_lb_ip_range was used in the legacy way: single string instead of a list #}
|
||||||
|
{# => transform to list with single element #}
|
||||||
|
{% set kube_vip_lb_ip_range = [kube_vip_lb_ip_range] %}
|
||||||
|
{% endif %}
|
||||||
|
range-global: {{ kube_vip_lb_ip_range | join(',') }}
|
||||||
@@ -27,7 +27,9 @@ spec:
|
|||||||
- manager
|
- manager
|
||||||
env:
|
env:
|
||||||
- name: vip_arp
|
- name: vip_arp
|
||||||
value: "true"
|
value: "{{ 'true' if kube_vip_arp | default(true) | bool else 'false' }}"
|
||||||
|
- name: bgp_enable
|
||||||
|
value: "{{ 'true' if kube_vip_bgp | default(false) | bool else 'false' }}"
|
||||||
- name: port
|
- name: port
|
||||||
value: "6443"
|
value: "6443"
|
||||||
{% if kube_vip_iface %}
|
{% if kube_vip_iface %}
|
||||||
@@ -43,7 +45,7 @@ spec:
|
|||||||
- name: vip_ddns
|
- name: vip_ddns
|
||||||
value: "false"
|
value: "false"
|
||||||
- name: svc_enable
|
- name: svc_enable
|
||||||
value: "false"
|
value: "{{ 'true' if kube_vip_lb_ip_range is defined else 'false' }}"
|
||||||
- name: vip_leaderelection
|
- name: vip_leaderelection
|
||||||
value: "true"
|
value: "true"
|
||||||
- name: vip_leaseduration
|
- name: vip_leaseduration
|
||||||
@@ -54,6 +56,29 @@ spec:
|
|||||||
value: "2"
|
value: "2"
|
||||||
- name: address
|
- name: address
|
||||||
value: {{ apiserver_endpoint }}
|
value: {{ apiserver_endpoint }}
|
||||||
|
{% if kube_vip_bgp | default(false) | bool %}
|
||||||
|
{% if kube_vip_bgp_routerid is defined %}
|
||||||
|
- name: bgp_routerid
|
||||||
|
value: "{{ kube_vip_bgp_routerid }}"
|
||||||
|
{% endif %}
|
||||||
|
{% if _kube_vip_bgp_peers | length > 0 %}
|
||||||
|
- name: bgppeers
|
||||||
|
value: "{{ _kube_vip_bgp_peers | map(attribute='peer_address') | zip(_kube_vip_bgp_peers| map(attribute='peer_asn')) | map('join', ',') | join(':') }}" # yamllint disable-line rule:line-length
|
||||||
|
{% else %}
|
||||||
|
{% if kube_vip_bgp_as is defined %}
|
||||||
|
- name: bgp_as
|
||||||
|
value: "{{ kube_vip_bgp_as }}"
|
||||||
|
{% endif %}
|
||||||
|
{% if kube_vip_bgp_peeraddress is defined %}
|
||||||
|
- name: bgp_peeraddress
|
||||||
|
value: "{{ kube_vip_bgp_peeraddress }}"
|
||||||
|
{% endif %}
|
||||||
|
{% if kube_vip_bgp_peeras is defined %}
|
||||||
|
- name: bgp_peeras
|
||||||
|
value: "{{ kube_vip_bgp_peeras }}"
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
image: ghcr.io/kube-vip/kube-vip:{{ kube_vip_tag_version }}
|
image: ghcr.io/kube-vip/kube-vip:{{ kube_vip_tag_version }}
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: kube-vip
|
name: kube-vip
|
||||||
|
|||||||
@@ -1,6 +1,32 @@
|
|||||||
---
|
---
|
||||||
# Timeout to wait for MetalLB services to come up
|
k3s_kubectl_binary: k3s kubectl
|
||||||
metal_lb_available_timeout: 240s
|
|
||||||
|
|
||||||
# Name of the master group
|
bpf_lb_algorithm: maglev
|
||||||
|
bpf_lb_mode: hybrid
|
||||||
|
|
||||||
|
calico_blockSize: 26 # noqa var-naming
|
||||||
|
calico_ebpf: false
|
||||||
|
calico_encapsulation: VXLANCrossSubnet
|
||||||
|
calico_natOutgoing: Enabled # noqa var-naming
|
||||||
|
calico_nodeSelector: all() # noqa var-naming
|
||||||
|
calico_tag: v3.27.2
|
||||||
|
|
||||||
|
cilium_bgp: false
|
||||||
|
cilium_exportPodCIDR: true # noqa var-naming
|
||||||
|
cilium_bgp_my_asn: 64513
|
||||||
|
cilium_bgp_peer_asn: 64512
|
||||||
|
cilium_bgp_neighbors: []
|
||||||
|
cilium_bgp_neighbors_groups: ['k3s_all']
|
||||||
|
cilium_bgp_lb_cidr: 192.168.31.0/24
|
||||||
|
cilium_hubble: true
|
||||||
|
cilium_mode: native
|
||||||
|
|
||||||
|
cluster_cidr: 10.52.0.0/16
|
||||||
|
enable_bpf_masquerade: true
|
||||||
|
kube_proxy_replacement: true
|
||||||
group_name_master: master
|
group_name_master: master
|
||||||
|
|
||||||
|
metal_lb_mode: layer2
|
||||||
|
metal_lb_available_timeout: 240s
|
||||||
|
metal_lb_controller_tag_version: v0.14.3
|
||||||
|
metal_lb_ip_range: 192.168.30.80-192.168.30.90
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Configure k3s cluster
|
||||||
|
options:
|
||||||
|
apiserver_endpoint:
|
||||||
|
description: Virtual ip-address configured on each master
|
||||||
|
required: true
|
||||||
|
|
||||||
|
bpf_lb_algorithm:
|
||||||
|
description: BPF lb algorithm
|
||||||
|
default: maglev
|
||||||
|
|
||||||
|
bpf_lb_mode:
|
||||||
|
description: BPF lb mode
|
||||||
|
default: hybrid
|
||||||
|
|
||||||
|
calico_blockSize:
|
||||||
|
description: IP pool block size
|
||||||
|
type: int
|
||||||
|
default: 26
|
||||||
|
|
||||||
|
calico_ebpf:
|
||||||
|
description: Use eBPF dataplane instead of iptables
|
||||||
|
type: bool
|
||||||
|
default: false
|
||||||
|
|
||||||
|
calico_encapsulation:
|
||||||
|
description: IP pool encapsulation
|
||||||
|
default: VXLANCrossSubnet
|
||||||
|
|
||||||
|
calico_natOutgoing:
|
||||||
|
description: IP pool NAT outgoing
|
||||||
|
default: Enabled
|
||||||
|
|
||||||
|
calico_nodeSelector:
|
||||||
|
description: IP pool node selector
|
||||||
|
default: all()
|
||||||
|
|
||||||
|
calico_iface:
|
||||||
|
description: The network interface used for when Calico is enabled
|
||||||
|
default: ~
|
||||||
|
|
||||||
|
calico_tag:
|
||||||
|
description: Calico version tag
|
||||||
|
default: v3.27.2
|
||||||
|
|
||||||
|
cilium_bgp:
|
||||||
|
description:
|
||||||
|
- Enable cilium BGP control plane for LB services and pod cidrs.
|
||||||
|
- Disables the use of MetalLB.
|
||||||
|
type: bool
|
||||||
|
default: false
|
||||||
|
|
||||||
|
cilium_bgp_my_asn:
|
||||||
|
description: Local ASN for BGP peer
|
||||||
|
type: int
|
||||||
|
default: 64513
|
||||||
|
|
||||||
|
cilium_bgp_peer_asn:
|
||||||
|
description: BGP peer ASN
|
||||||
|
type: int
|
||||||
|
default: 64512
|
||||||
|
|
||||||
|
cilium_bgp_peer_address:
|
||||||
|
description: BGP peer address
|
||||||
|
default: ~
|
||||||
|
|
||||||
|
cilium_bgp_neighbors:
|
||||||
|
description: List of BGP peer ASN & address pairs
|
||||||
|
default: []
|
||||||
|
|
||||||
|
cilium_bgp_neighbors_groups:
|
||||||
|
description: Inventory group in which to search for additional cilium_bgp_neighbors parameters to merge.
|
||||||
|
default: ['k3s_all']
|
||||||
|
|
||||||
|
cilium_bgp_lb_cidr:
|
||||||
|
description: BGP load balancer IP range
|
||||||
|
default: 192.168.31.0/24
|
||||||
|
|
||||||
|
cilium_exportPodCIDR:
|
||||||
|
description: Export pod CIDR
|
||||||
|
type: bool
|
||||||
|
default: true
|
||||||
|
|
||||||
|
cilium_hubble:
|
||||||
|
description: Enable Cilium Hubble
|
||||||
|
type: bool
|
||||||
|
default: true
|
||||||
|
|
||||||
|
cilium_iface:
|
||||||
|
description: The network interface used for when Cilium is enabled
|
||||||
|
default: ~
|
||||||
|
|
||||||
|
cilium_mode:
|
||||||
|
description: Inner-node communication mode
|
||||||
|
default: native
|
||||||
|
choices:
|
||||||
|
- native
|
||||||
|
- routed
|
||||||
|
|
||||||
|
cluster_cidr:
|
||||||
|
description: Inner-cluster IP range
|
||||||
|
default: 10.52.0.0/16
|
||||||
|
|
||||||
|
enable_bpf_masquerade:
|
||||||
|
description: Use IP masquerading
|
||||||
|
type: bool
|
||||||
|
default: true
|
||||||
|
|
||||||
|
group_name_master:
|
||||||
|
description: Name of the master group
|
||||||
|
default: master
|
||||||
|
|
||||||
|
kube_proxy_replacement:
|
||||||
|
description: Replace the native kube-proxy with Cilium
|
||||||
|
type: bool
|
||||||
|
default: true
|
||||||
|
|
||||||
|
kube_vip_lb_ip_range:
|
||||||
|
description: IP range for kube-vip load balancer
|
||||||
|
default: ~
|
||||||
|
|
||||||
|
metal_lb_available_timeout:
|
||||||
|
description: Wait for MetalLB resources
|
||||||
|
default: 240s
|
||||||
|
|
||||||
|
metal_lb_ip_range:
|
||||||
|
description: MetalLB ip range for load balancer
|
||||||
|
default: 192.168.30.80-192.168.30.90
|
||||||
|
|
||||||
|
metal_lb_controller_tag_version:
|
||||||
|
description: Image tag for MetalLB
|
||||||
|
default: v0.14.3
|
||||||
|
|
||||||
|
metal_lb_mode:
|
||||||
|
description: Metallb mode
|
||||||
|
default: layer2
|
||||||
|
choices:
|
||||||
|
- bgp
|
||||||
|
- layer2
|
||||||
|
|
||||||
|
metal_lb_bgp_my_asn:
|
||||||
|
description: BGP ASN configurations
|
||||||
|
default: ~
|
||||||
|
|
||||||
|
metal_lb_bgp_peer_asn:
|
||||||
|
description: BGP peer ASN configurations
|
||||||
|
default: ~
|
||||||
|
|
||||||
|
metal_lb_bgp_peer_address:
|
||||||
|
description: BGP peer address
|
||||||
|
default: ~
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
---
|
||||||
|
- name: Deploy Calico to cluster
|
||||||
|
when: ansible_hostname == hostvars[groups[group_name_master | default('master')][0]]['ansible_hostname']
|
||||||
|
run_once: true
|
||||||
|
block:
|
||||||
|
- name: Create manifests directory on first master
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /tmp/k3s
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: "Download to first master: manifest for Tigera Operator and Calico CRDs"
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: https://raw.githubusercontent.com/projectcalico/calico/{{ calico_tag }}/manifests/tigera-operator.yaml
|
||||||
|
dest: /tmp/k3s/tigera-operator.yaml
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Copy Calico custom resources manifest to first master
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: calico.crs.j2
|
||||||
|
dest: /tmp/k3s/custom-resources.yaml
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Deploy or replace Tigera Operator
|
||||||
|
block:
|
||||||
|
- name: Deploy Tigera Operator
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "{{ k3s_kubectl_binary | default('k3s kubectl') }} create -f /tmp/k3s/tigera-operator.yaml"
|
||||||
|
register: create_operator
|
||||||
|
changed_when: "'created' in create_operator.stdout"
|
||||||
|
failed_when: "'Error' in create_operator.stderr and 'already exists' not in create_operator.stderr"
|
||||||
|
rescue:
|
||||||
|
- name: Replace existing Tigera Operator
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "{{ k3s_kubectl_binary | default('k3s kubectl') }} replace -f /tmp/k3s/tigera-operator.yaml"
|
||||||
|
register: replace_operator
|
||||||
|
changed_when: "'replaced' in replace_operator.stdout"
|
||||||
|
failed_when: "'Error' in replace_operator.stderr"
|
||||||
|
|
||||||
|
- name: Wait for Tigera Operator resources
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} wait {{ item.type }}/{{ item.name }}
|
||||||
|
--namespace='tigera-operator'
|
||||||
|
--for=condition=Available=True
|
||||||
|
--timeout=30s
|
||||||
|
register: tigera_result
|
||||||
|
changed_when: false
|
||||||
|
until: tigera_result is succeeded
|
||||||
|
retries: 7
|
||||||
|
delay: 7
|
||||||
|
with_items:
|
||||||
|
- { name: tigera-operator, type: deployment }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.type }}/{{ item.name }}"
|
||||||
|
|
||||||
|
- name: Deploy Calico custom resources
|
||||||
|
block:
|
||||||
|
- name: Deploy custom resources for Calico
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "{{ k3s_kubectl_binary | default('k3s kubectl') }} create -f /tmp/k3s/custom-resources.yaml"
|
||||||
|
register: create_cr
|
||||||
|
changed_when: "'created' in create_cr.stdout"
|
||||||
|
failed_when: "'Error' in create_cr.stderr and 'already exists' not in create_cr.stderr"
|
||||||
|
rescue:
|
||||||
|
- name: Apply new Calico custom resource manifest
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "{{ k3s_kubectl_binary | default('k3s kubectl') }} apply -f /tmp/k3s/custom-resources.yaml"
|
||||||
|
register: apply_cr
|
||||||
|
changed_when: "'configured' in apply_cr.stdout or 'created' in apply_cr.stdout"
|
||||||
|
failed_when: "'Error' in apply_cr.stderr"
|
||||||
|
|
||||||
|
- name: Wait for Calico system resources to be available
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
{% if item.type == 'daemonset' %}
|
||||||
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} wait pods
|
||||||
|
--namespace='{{ item.namespace }}'
|
||||||
|
--selector={{ item.selector }}
|
||||||
|
--for=condition=Ready
|
||||||
|
{% else %}
|
||||||
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} wait {{ item.type }}/{{ item.name }}
|
||||||
|
--namespace='{{ item.namespace }}'
|
||||||
|
--for=condition=Available
|
||||||
|
{% endif %}
|
||||||
|
--timeout=30s
|
||||||
|
register: cr_result
|
||||||
|
changed_when: false
|
||||||
|
until: cr_result is succeeded
|
||||||
|
retries: 30
|
||||||
|
delay: 7
|
||||||
|
with_items:
|
||||||
|
- { name: calico-typha, type: deployment, namespace: calico-system }
|
||||||
|
- { name: calico-kube-controllers, type: deployment, namespace: calico-system }
|
||||||
|
- name: csi-node-driver
|
||||||
|
type: daemonset
|
||||||
|
selector: k8s-app=csi-node-driver
|
||||||
|
namespace: calico-system
|
||||||
|
- name: calico-node
|
||||||
|
type: daemonset
|
||||||
|
selector: k8s-app=calico-node
|
||||||
|
namespace: calico-system
|
||||||
|
- { name: calico-apiserver, type: deployment, namespace: calico-apiserver }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.type }}/{{ item.name }}"
|
||||||
|
|
||||||
|
- name: Patch Felix configuration for eBPF mode
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: >
|
||||||
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} patch felixconfiguration default
|
||||||
|
--type='merge'
|
||||||
|
--patch='{"spec": {"bpfKubeProxyIptablesCleanupEnabled": false}}'
|
||||||
|
register: patch_result
|
||||||
|
changed_when: "'felixconfiguration.projectcalico.org/default patched' in patch_result.stdout"
|
||||||
|
failed_when: "'Error' in patch_result.stderr"
|
||||||
|
when: calico_ebpf
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
---
|
||||||
|
- name: Prepare Cilium CLI on first master and deploy CNI
|
||||||
|
when: ansible_hostname == hostvars[groups[group_name_master | default('master')][0]]['ansible_hostname']
|
||||||
|
run_once: true
|
||||||
|
block:
|
||||||
|
- name: Create tmp directory on first master
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /tmp/k3s
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Check if Cilium CLI is installed
|
||||||
|
ansible.builtin.command: cilium version
|
||||||
|
register: cilium_cli_installed
|
||||||
|
failed_when: false
|
||||||
|
changed_when: false
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Check for Cilium CLI version in command output
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
installed_cli_version: >-
|
||||||
|
{{
|
||||||
|
cilium_cli_installed.stdout_lines
|
||||||
|
| join(' ')
|
||||||
|
| regex_findall('cilium-cli: (v\d+\.\d+\.\d+)')
|
||||||
|
| first
|
||||||
|
| default('unknown')
|
||||||
|
}}
|
||||||
|
when: cilium_cli_installed.rc == 0
|
||||||
|
|
||||||
|
- name: Get latest stable Cilium CLI version file
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt
|
||||||
|
dest: /tmp/k3s/cilium-cli-stable.txt
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Read Cilium CLI stable version from file
|
||||||
|
ansible.builtin.command: cat /tmp/k3s/cilium-cli-stable.txt
|
||||||
|
register: cli_ver
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Log installed Cilium CLI version
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "Installed Cilium CLI version: {{ installed_cli_version | default('Not installed') }}"
|
||||||
|
|
||||||
|
- name: Log latest stable Cilium CLI version
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "Latest Cilium CLI version: {{ cli_ver.stdout }}"
|
||||||
|
|
||||||
|
- name: Determine if Cilium CLI needs installation or update
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cilium_cli_needs_update: >-
|
||||||
|
{{
|
||||||
|
cilium_cli_installed.rc != 0 or
|
||||||
|
(cilium_cli_installed.rc == 0 and
|
||||||
|
installed_cli_version != cli_ver.stdout)
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Install or update Cilium CLI
|
||||||
|
when: cilium_cli_needs_update
|
||||||
|
block:
|
||||||
|
- name: Set architecture variable
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cli_arch: "{{ 'arm64' if ansible_architecture == 'aarch64' else 'amd64' }}"
|
||||||
|
|
||||||
|
- name: Download Cilium CLI and checksum
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "{{ cilium_base_url }}/cilium-linux-{{ cli_arch }}{{ item }}"
|
||||||
|
dest: /tmp/k3s/cilium-linux-{{ cli_arch }}{{ item }}
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
loop:
|
||||||
|
- .tar.gz
|
||||||
|
- .tar.gz.sha256sum
|
||||||
|
vars:
|
||||||
|
cilium_base_url: https://github.com/cilium/cilium-cli/releases/download/{{ cli_ver.stdout }}
|
||||||
|
|
||||||
|
- name: Verify the downloaded tarball
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
cd /tmp/k3s && sha256sum --check cilium-linux-{{ cli_arch }}.tar.gz.sha256sum
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Extract Cilium CLI to /usr/local/bin
|
||||||
|
ansible.builtin.unarchive:
|
||||||
|
src: /tmp/k3s/cilium-linux-{{ cli_arch }}.tar.gz
|
||||||
|
dest: /usr/local/bin
|
||||||
|
remote_src: true
|
||||||
|
|
||||||
|
- name: Remove downloaded tarball and checksum file
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: absent
|
||||||
|
loop:
|
||||||
|
- /tmp/k3s/cilium-linux-{{ cli_arch }}.tar.gz
|
||||||
|
- /tmp/k3s/cilium-linux-{{ cli_arch }}.tar.gz.sha256sum
|
||||||
|
|
||||||
|
- name: Wait for connectivity to kube VIP
|
||||||
|
ansible.builtin.command: ping -c 1 {{ apiserver_endpoint }}
|
||||||
|
register: ping_result
|
||||||
|
until: ping_result.rc == 0
|
||||||
|
retries: 21
|
||||||
|
delay: 1
|
||||||
|
ignore_errors: true
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Fail if kube VIP not reachable
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: API endpoint {{ apiserver_endpoint }} is not reachable
|
||||||
|
when: ping_result.rc != 0
|
||||||
|
|
||||||
|
- name: Test for existing Cilium install
|
||||||
|
ansible.builtin.command: |
|
||||||
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} -n kube-system get daemonsets cilium
|
||||||
|
register: cilium_installed
|
||||||
|
failed_when: false
|
||||||
|
changed_when: false
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Check existing Cilium install
|
||||||
|
when: cilium_installed.rc == 0
|
||||||
|
block:
|
||||||
|
- name: Check Cilium version
|
||||||
|
ansible.builtin.command: cilium version
|
||||||
|
register: cilium_version
|
||||||
|
failed_when: false
|
||||||
|
changed_when: false
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Parse installed Cilium version
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
installed_cilium_version: >-
|
||||||
|
{{
|
||||||
|
cilium_version.stdout_lines
|
||||||
|
| join(' ')
|
||||||
|
| regex_findall('cilium image.+(\d+\.\d+\.\d+)')
|
||||||
|
| first
|
||||||
|
| default('unknown')
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Determine if Cilium needs update
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cilium_needs_update: >-
|
||||||
|
{{ 'v' + installed_cilium_version != cilium_tag }}
|
||||||
|
|
||||||
|
- name: Log result
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >
|
||||||
|
Installed Cilium version: {{ installed_cilium_version }},
|
||||||
|
Target Cilium version: {{ cilium_tag }},
|
||||||
|
Update needed: {{ cilium_needs_update }}
|
||||||
|
|
||||||
|
- name: Install Cilium
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
{% if cilium_installed.rc != 0 %}
|
||||||
|
cilium install
|
||||||
|
{% else %}
|
||||||
|
cilium upgrade
|
||||||
|
{% endif %}
|
||||||
|
--version "{{ cilium_tag }}"
|
||||||
|
--helm-set operator.replicas="1"
|
||||||
|
{{ '--helm-set devices=' + cilium_iface if cilium_iface != 'auto' else '' }}
|
||||||
|
--helm-set ipam.operator.clusterPoolIPv4PodCIDRList={{ cluster_cidr }}
|
||||||
|
{% if cilium_mode == "native" or (cilium_bgp and cilium_exportPodCIDR != 'false') %}
|
||||||
|
--helm-set ipv4NativeRoutingCIDR={{ cluster_cidr }}
|
||||||
|
{% endif %}
|
||||||
|
--helm-set k8sServiceHost="127.0.0.1"
|
||||||
|
--helm-set k8sServicePort="6444"
|
||||||
|
--helm-set routingMode={{ cilium_mode }}
|
||||||
|
--helm-set autoDirectNodeRoutes={{ "true" if cilium_mode == "native" else "false" }}
|
||||||
|
--helm-set kubeProxyReplacement={{ kube_proxy_replacement }}
|
||||||
|
--helm-set bpf.masquerade={{ enable_bpf_masquerade }}
|
||||||
|
--helm-set bgpControlPlane.enabled={{ cilium_bgp | default("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.ui.enabled={{ "true" if cilium_hubble else "false" }}
|
||||||
|
{% if kube_proxy_replacement is not false %}
|
||||||
|
--helm-set bpf.loadBalancer.algorithm={{ bpf_lb_algorithm }}
|
||||||
|
--helm-set bpf.loadBalancer.mode={{ bpf_lb_mode }}
|
||||||
|
{% endif %}
|
||||||
|
environment:
|
||||||
|
KUBECONFIG: "{{ ansible_user_dir }}/.kube/config"
|
||||||
|
register: cilium_install_result
|
||||||
|
changed_when: cilium_install_result.rc == 0
|
||||||
|
when: cilium_installed.rc != 0 or cilium_needs_update
|
||||||
|
|
||||||
|
- name: Wait for Cilium resources
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
{% if item.type == 'daemonset' %}
|
||||||
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} wait pods
|
||||||
|
--namespace=kube-system
|
||||||
|
--selector='k8s-app=cilium'
|
||||||
|
--for=condition=Ready
|
||||||
|
{% else %}
|
||||||
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} wait {{ item.type }}/{{ item.name }}
|
||||||
|
--namespace=kube-system
|
||||||
|
--for=condition=Available
|
||||||
|
{% endif %}
|
||||||
|
--timeout=30s
|
||||||
|
register: cr_result
|
||||||
|
changed_when: false
|
||||||
|
until: cr_result is succeeded
|
||||||
|
retries: 30
|
||||||
|
delay: 7
|
||||||
|
with_items:
|
||||||
|
- { name: cilium-operator, type: deployment }
|
||||||
|
- { name: cilium, type: daemonset, selector: k8s-app=cilium }
|
||||||
|
- { name: hubble-relay, type: deployment, check_hubble: true }
|
||||||
|
- { name: hubble-ui, type: deployment, check_hubble: true }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.type }}/{{ item.name }}"
|
||||||
|
when: >-
|
||||||
|
not item.check_hubble | default(false) or (item.check_hubble | default(false) and cilium_hubble)
|
||||||
|
|
||||||
|
- name: Configure Cilium BGP
|
||||||
|
when: cilium_bgp
|
||||||
|
block:
|
||||||
|
- name: Set _cilium_bgp_neighbors fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
_cilium_bgp_neighbors: "{{ lookup('community.general.merge_variables', '^cilium_bgp_neighbors__.+$', initial_value=cilium_bgp_neighbors, groups=cilium_bgp_neighbors_groups) }}" # yamllint disable-line rule:line-length
|
||||||
|
|
||||||
|
- name: Copy BGP manifests to first master
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: cilium.crs.j2
|
||||||
|
dest: /tmp/k3s/cilium-bgp.yaml
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Apply BGP manifests
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "{{ k3s_kubectl_binary | default('k3s kubectl') }} apply -f /tmp/k3s/cilium-bgp.yaml"
|
||||||
|
register: apply_cr
|
||||||
|
changed_when: "'configured' in apply_cr.stdout or 'created' in apply_cr.stdout"
|
||||||
|
failed_when: "'is invalid' in apply_cr.stderr"
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Print error message if BGP manifests application fails
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ apply_cr.stderr }}"
|
||||||
|
when: "'is invalid' in apply_cr.stderr"
|
||||||
|
|
||||||
|
- name: Test for BGP config resources
|
||||||
|
ansible.builtin.command: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- "{{ k3s_kubectl_binary | default('k3s kubectl') }} get CiliumBGPPeeringPolicy.cilium.io"
|
||||||
|
- "{{ k3s_kubectl_binary | default('k3s kubectl') }} get CiliumLoadBalancerIPPool.cilium.io"
|
||||||
|
changed_when: false
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item }}"
|
||||||
@@ -1,9 +1,20 @@
|
|||||||
---
|
---
|
||||||
|
- name: Deploy calico
|
||||||
|
ansible.builtin.include_tasks: calico.yml
|
||||||
|
tags: calico
|
||||||
|
when: calico_iface is defined and cilium_iface is not defined
|
||||||
|
|
||||||
|
- name: Deploy cilium
|
||||||
|
ansible.builtin.include_tasks: cilium.yml
|
||||||
|
tags: cilium
|
||||||
|
when: cilium_iface is defined
|
||||||
|
|
||||||
- name: Deploy metallb pool
|
- name: Deploy metallb pool
|
||||||
include_tasks: metallb.yml
|
ansible.builtin.include_tasks: metallb.yml
|
||||||
tags: metallb
|
tags: metallb
|
||||||
|
when: kube_vip_lb_ip_range is not defined and (not cilium_bgp or cilium_iface is not defined)
|
||||||
|
|
||||||
- name: Remove tmp directory used for manifests
|
- name: Remove tmp directory used for manifests
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: /tmp/k3s
|
path: /tmp/k3s
|
||||||
state: absent
|
state: absent
|
||||||
@@ -1,32 +1,53 @@
|
|||||||
---
|
---
|
||||||
- name: Create manifests directory for temp configuration
|
- name: Create manifests directory for temp configuration
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: /tmp/k3s
|
path: /tmp/k3s
|
||||||
state: directory
|
state: directory
|
||||||
owner: "{{ ansible_user_id }}"
|
owner: "{{ ansible_user_id }}"
|
||||||
mode: 0755
|
mode: "0755"
|
||||||
with_items: "{{ groups[group_name_master | default('master')] }}"
|
with_items: "{{ groups[group_name_master | default('master')] }}"
|
||||||
run_once: true
|
run_once: true
|
||||||
|
|
||||||
|
- name: Delete outdated metallb replicas
|
||||||
|
ansible.builtin.shell: |-
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
REPLICAS=$({{ k3s_kubectl_binary | default('k3s kubectl') }} --namespace='metallb-system' get replicasets \
|
||||||
|
-l 'component=controller,app=metallb' \
|
||||||
|
-o jsonpath='{.items[0].spec.template.spec.containers[0].image}, {.items[0].metadata.name}' 2>/dev/null || true)
|
||||||
|
REPLICAS_SETS=$(echo ${REPLICAS} | grep -v '{{ metal_lb_controller_tag_version }}' | sed -e "s/^.*\s//g")
|
||||||
|
if [ -n "${REPLICAS_SETS}" ] ; then
|
||||||
|
for REPLICAS in "${REPLICAS_SETS}"
|
||||||
|
do
|
||||||
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} --namespace='metallb-system' \
|
||||||
|
delete rs "${REPLICAS}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
changed_when: false
|
||||||
|
run_once: true
|
||||||
|
with_items: "{{ groups[group_name_master | default('master')] }}"
|
||||||
|
|
||||||
- name: Copy metallb CRs manifest to first master
|
- name: Copy metallb CRs manifest to first master
|
||||||
template:
|
ansible.builtin.template:
|
||||||
src: "metallb.crs.j2"
|
src: metallb.crs.j2
|
||||||
dest: "/tmp/k3s/metallb-crs.yaml"
|
dest: /tmp/k3s/metallb-crs.yaml
|
||||||
owner: "{{ ansible_user_id }}"
|
owner: "{{ ansible_user_id }}"
|
||||||
mode: 0755
|
mode: "0755"
|
||||||
with_items: "{{ groups[group_name_master | default('master')] }}"
|
with_items: "{{ groups[group_name_master | default('master')] }}"
|
||||||
run_once: true
|
run_once: true
|
||||||
|
|
||||||
- name: Test metallb-system namespace
|
- name: Test metallb-system namespace
|
||||||
command: >-
|
ansible.builtin.command: >-
|
||||||
k3s kubectl -n metallb-system
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} -n metallb-system
|
||||||
changed_when: false
|
changed_when: false
|
||||||
with_items: "{{ groups[group_name_master | default('master')] }}"
|
with_items: "{{ groups[group_name_master | default('master')] }}"
|
||||||
run_once: true
|
run_once: true
|
||||||
|
|
||||||
- name: Wait for MetalLB resources
|
- name: Wait for MetalLB resources
|
||||||
command: >-
|
ansible.builtin.command: >-
|
||||||
k3s kubectl wait {{ item.resource }}
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} wait {{ item.resource }}
|
||||||
--namespace='metallb-system'
|
--namespace='metallb-system'
|
||||||
{% if item.name | default(False) -%}{{ item.name }}{%- endif %}
|
{% if item.name | default(False) -%}{{ item.name }}{%- endif %}
|
||||||
{% if item.selector | default(False) -%}--selector='{{ item.selector }}'{%- endif %}
|
{% if item.selector | default(False) -%}--selector='{{ item.selector }}'{%- endif %}
|
||||||
@@ -62,16 +83,30 @@
|
|||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.description }}"
|
label: "{{ item.description }}"
|
||||||
|
|
||||||
|
- name: Set metallb webhook service name
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
metallb_webhook_service_name: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(metal_lb_controller_tag_version | regex_replace('^v', ''))
|
||||||
|
is
|
||||||
|
version('0.14.4', '<', version_type='semver')
|
||||||
|
) | ternary(
|
||||||
|
'webhook-service',
|
||||||
|
'metallb-webhook-service'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
- name: Test metallb-system webhook-service endpoint
|
- name: Test metallb-system webhook-service endpoint
|
||||||
command: >-
|
ansible.builtin.command: >-
|
||||||
k3s kubectl -n metallb-system get endpoints webhook-service
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} -n metallb-system get endpoints {{ metallb_webhook_service_name }}
|
||||||
changed_when: false
|
changed_when: false
|
||||||
with_items: "{{ groups[group_name_master | default('master')] }}"
|
with_items: "{{ groups[group_name_master | default('master')] }}"
|
||||||
run_once: true
|
run_once: true
|
||||||
|
|
||||||
- name: Apply metallb CRs
|
- name: Apply metallb CRs
|
||||||
command: >-
|
ansible.builtin.command: >-
|
||||||
k3s kubectl apply -f /tmp/k3s/metallb-crs.yaml
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} apply -f /tmp/k3s/metallb-crs.yaml
|
||||||
--timeout='{{ metal_lb_available_timeout }}'
|
--timeout='{{ metal_lb_available_timeout }}'
|
||||||
register: this
|
register: this
|
||||||
changed_when: false
|
changed_when: false
|
||||||
@@ -80,8 +115,8 @@
|
|||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
- name: Test metallb-system resources for Layer 2 configuration
|
- name: Test metallb-system resources for Layer 2 configuration
|
||||||
command: >-
|
ansible.builtin.command: >-
|
||||||
k3s kubectl -n metallb-system get {{ item }}
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} -n metallb-system get {{ item }}
|
||||||
changed_when: false
|
changed_when: false
|
||||||
run_once: true
|
run_once: true
|
||||||
when: metal_lb_mode == "layer2"
|
when: metal_lb_mode == "layer2"
|
||||||
@@ -90,8 +125,8 @@
|
|||||||
- L2Advertisement
|
- L2Advertisement
|
||||||
|
|
||||||
- name: Test metallb-system resources for BGP configuration
|
- name: Test metallb-system resources for BGP configuration
|
||||||
command: >-
|
ansible.builtin.command: >-
|
||||||
k3s kubectl -n metallb-system get {{ item }}
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} -n metallb-system get {{ item }}
|
||||||
changed_when: false
|
changed_when: false
|
||||||
run_once: true
|
run_once: true
|
||||||
when: metal_lb_mode == "bgp"
|
when: metal_lb_mode == "bgp"
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# This section includes base Calico installation configuration.
|
||||||
|
# For more information, see: https://docs.tigera.io/calico/latest/reference/installation/api#operator.tigera.io/v1.Installation
|
||||||
|
apiVersion: operator.tigera.io/v1
|
||||||
|
kind: Installation
|
||||||
|
metadata:
|
||||||
|
name: default
|
||||||
|
spec:
|
||||||
|
# Configures Calico networking.
|
||||||
|
calicoNetwork:
|
||||||
|
# Note: The ipPools section cannot be modified post-install.
|
||||||
|
ipPools:
|
||||||
|
- blockSize: {{ calico_blockSize }}
|
||||||
|
cidr: {{ cluster_cidr }}
|
||||||
|
encapsulation: {{ calico_encapsulation }}
|
||||||
|
natOutgoing: {{ calico_natOutgoing }}
|
||||||
|
nodeSelector: {{ calico_nodeSelector }}
|
||||||
|
nodeAddressAutodetectionV4:
|
||||||
|
interface: {{ calico_iface }}
|
||||||
|
linuxDataplane: {{ 'BPF' if calico_ebpf else 'Iptables' }}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# This section configures the Calico API server.
|
||||||
|
# For more information, see: https://docs.tigera.io/calico/latest/reference/installation/api#operator.tigera.io/v1.APIServer
|
||||||
|
apiVersion: operator.tigera.io/v1
|
||||||
|
kind: APIServer
|
||||||
|
metadata:
|
||||||
|
name: default
|
||||||
|
spec: {}
|
||||||
|
|
||||||
|
{% if calico_ebpf %}
|
||||||
|
---
|
||||||
|
kind: ConfigMap
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: kubernetes-services-endpoint
|
||||||
|
namespace: tigera-operator
|
||||||
|
data:
|
||||||
|
KUBERNETES_SERVICE_HOST: '{{ apiserver_endpoint }}'
|
||||||
|
KUBERNETES_SERVICE_PORT: '6443'
|
||||||
|
{% endif %}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
apiVersion: "cilium.io/v2alpha1"
|
||||||
|
kind: CiliumBGPPeeringPolicy
|
||||||
|
metadata:
|
||||||
|
name: 01-bgp-peering-policy
|
||||||
|
spec: # CiliumBGPPeeringPolicySpec
|
||||||
|
virtualRouters: # []CiliumBGPVirtualRouter
|
||||||
|
- localASN: {{ cilium_bgp_my_asn }}
|
||||||
|
exportPodCIDR: {{ cilium_exportPodCIDR | default('true') }}
|
||||||
|
neighbors: # []CiliumBGPNeighbor
|
||||||
|
{% if _cilium_bgp_neighbors | length > 0 %}
|
||||||
|
{% for item in _cilium_bgp_neighbors %}
|
||||||
|
- peerAddress: '{{ item.peer_address + "/32"}}'
|
||||||
|
peerASN: {{ item.peer_asn }}
|
||||||
|
eBGPMultihopTTL: 10
|
||||||
|
connectRetryTimeSeconds: 120
|
||||||
|
holdTimeSeconds: 90
|
||||||
|
keepAliveTimeSeconds: 30
|
||||||
|
gracefulRestart:
|
||||||
|
enabled: true
|
||||||
|
restartTimeSeconds: 120
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
- peerAddress: '{{ cilium_bgp_peer_address + "/32"}}'
|
||||||
|
peerASN: {{ cilium_bgp_peer_asn }}
|
||||||
|
eBGPMultihopTTL: 10
|
||||||
|
connectRetryTimeSeconds: 120
|
||||||
|
holdTimeSeconds: 90
|
||||||
|
keepAliveTimeSeconds: 30
|
||||||
|
gracefulRestart:
|
||||||
|
enabled: true
|
||||||
|
restartTimeSeconds: 120
|
||||||
|
{% endif %}
|
||||||
|
serviceSelector:
|
||||||
|
matchExpressions:
|
||||||
|
- {key: somekey, operator: NotIn, values: ['never-used-value']}
|
||||||
|
---
|
||||||
|
apiVersion: "cilium.io/v2alpha1"
|
||||||
|
kind: CiliumLoadBalancerIPPool
|
||||||
|
metadata:
|
||||||
|
name: "01-lb-pool"
|
||||||
|
spec:
|
||||||
|
blocks:
|
||||||
|
{% if "/" in cilium_bgp_lb_cidr %}
|
||||||
|
- cidr: {{ cilium_bgp_lb_cidr }}
|
||||||
|
{% else %}
|
||||||
|
- start: {{ cilium_bgp_lb_cidr.split('-')[0] }}
|
||||||
|
stop: {{ cilium_bgp_lb_cidr.split('-')[1] }}
|
||||||
|
{% endif %}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
- name: Reboot server
|
- name: Reboot server
|
||||||
become: true
|
become: true
|
||||||
reboot:
|
ansible.builtin.reboot:
|
||||||
|
reboot_command: "{{ custom_reboot_command | default(omit) }}"
|
||||||
listen: reboot server
|
listen: reboot server
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Configure LXC
|
||||||
|
options:
|
||||||
|
custom_reboot_command:
|
||||||
|
default: ~
|
||||||
|
description: Command to run on reboot
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
---
|
---
|
||||||
- name: Check for rc.local file
|
- name: Check for rc.local file
|
||||||
stat:
|
ansible.builtin.stat:
|
||||||
path: /etc/rc.local
|
path: /etc/rc.local
|
||||||
register: rcfile
|
register: rcfile
|
||||||
|
|
||||||
- name: Create rc.local if needed
|
- name: Create rc.local if needed
|
||||||
lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: /etc/rc.local
|
path: /etc/rc.local
|
||||||
line: "#!/bin/sh -e"
|
line: "#!/bin/sh -e"
|
||||||
create: true
|
create: true
|
||||||
insertbefore: BOF
|
insertbefore: BOF
|
||||||
mode: "u=rwx,g=rx,o=rx"
|
mode: u=rwx,g=rx,o=rx
|
||||||
when: not rcfile.stat.exists
|
when: not rcfile.stat.exists
|
||||||
|
|
||||||
- name: Write rc.local file
|
- name: Write rc.local file
|
||||||
blockinfile:
|
ansible.builtin.blockinfile:
|
||||||
path: /etc/rc.local
|
path: /etc/rc.local
|
||||||
content: "{{ lookup('template', 'templates/rc.local.j2') }}"
|
content: "{{ lookup('template', 'templates/rc.local.j2') }}"
|
||||||
state: present
|
state: present
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
---
|
---
|
||||||
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
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Prerequisites
|
||||||
|
options:
|
||||||
|
system_timezone:
|
||||||
|
description: Timezone to be set on all nodes
|
||||||
@@ -34,10 +34,10 @@
|
|||||||
tags: sysctl
|
tags: sysctl
|
||||||
|
|
||||||
- name: Add br_netfilter to /etc/modules-load.d/
|
- name: Add br_netfilter to /etc/modules-load.d/
|
||||||
copy:
|
ansible.builtin.copy:
|
||||||
content: "br_netfilter"
|
content: br_netfilter
|
||||||
dest: /etc/modules-load.d/br_netfilter.conf
|
dest: /etc/modules-load.d/br_netfilter.conf
|
||||||
mode: "u=rw,g=,o="
|
mode: u=rw,g=,o=
|
||||||
when: ansible_os_family == "RedHat"
|
when: ansible_os_family == "RedHat"
|
||||||
|
|
||||||
- name: Load br_netfilter
|
- name: Load br_netfilter
|
||||||
@@ -59,11 +59,11 @@
|
|||||||
tags: sysctl
|
tags: sysctl
|
||||||
|
|
||||||
- name: Add /usr/local/bin to sudo secure_path
|
- name: Add /usr/local/bin to sudo secure_path
|
||||||
lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
line: 'Defaults secure_path = {{ secure_path[ansible_os_family] }}'
|
line: Defaults secure_path = {{ secure_path[ansible_os_family] }}
|
||||||
regexp: "Defaults(\\s)*secure_path(\\s)*="
|
regexp: Defaults(\s)*secure_path(\s)*=
|
||||||
state: present
|
state: present
|
||||||
insertafter: EOF
|
insertafter: EOF
|
||||||
path: /etc/sudoers
|
path: /etc/sudoers
|
||||||
validate: 'visudo -cf %s'
|
validate: visudo -cf %s
|
||||||
when: ansible_os_family in [ "RedHat", "Suse" ]
|
when: ansible_os_family in [ "RedHat", "Suse" ]
|
||||||
@@ -2,12 +2,12 @@
|
|||||||
- name: Reboot containers
|
- name: Reboot containers
|
||||||
block:
|
block:
|
||||||
- name: Get container ids from filtered files
|
- name: Get container ids from filtered files
|
||||||
set_fact:
|
ansible.builtin.set_fact:
|
||||||
proxmox_lxc_filtered_ids: >-
|
proxmox_lxc_filtered_ids: >-
|
||||||
{{ proxmox_lxc_filtered_files | map("split", "/") | map("last") | map("split", ".") | map("first") }}
|
{{ proxmox_lxc_filtered_files | map("split", "/") | map("last") | map("split", ".") | map("first") }}
|
||||||
listen: reboot containers
|
listen: reboot containers
|
||||||
- name: Reboot container
|
- name: Reboot container
|
||||||
command: "pct reboot {{ item }}"
|
ansible.builtin.command: pct reboot {{ item }}
|
||||||
loop: "{{ proxmox_lxc_filtered_ids }}"
|
loop: "{{ proxmox_lxc_filtered_ids }}"
|
||||||
changed_when: true
|
changed_when: true
|
||||||
listen: reboot containers
|
listen: reboot containers
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Proxmox LXC settings
|
||||||
|
options:
|
||||||
|
proxmox_lxc_ct_ids:
|
||||||
|
description: Proxmox container ID list
|
||||||
|
type: list
|
||||||
|
required: true
|
||||||
@@ -1,44 +1,43 @@
|
|||||||
---
|
---
|
||||||
- name: Check for container files that exist on this host
|
- name: Check for container files that exist on this host
|
||||||
stat:
|
ansible.builtin.stat:
|
||||||
path: "/etc/pve/lxc/{{ item }}.conf"
|
path: /etc/pve/lxc/{{ item }}.conf
|
||||||
loop: "{{ proxmox_lxc_ct_ids }}"
|
loop: "{{ proxmox_lxc_ct_ids }}"
|
||||||
register: stat_results
|
register: stat_results
|
||||||
|
|
||||||
- name: Filter out files that do not exist
|
- name: Filter out files that do not exist
|
||||||
set_fact:
|
ansible.builtin.set_fact:
|
||||||
proxmox_lxc_filtered_files:
|
proxmox_lxc_filtered_files: '{{ stat_results.results | rejectattr("stat.exists", "false") | map(attribute="stat.path") }}' # noqa yaml[line-length]
|
||||||
'{{ stat_results.results | rejectattr("stat.exists", "false") | map(attribute="stat.path") }}'
|
|
||||||
|
|
||||||
# https://gist.github.com/triangletodd/02f595cd4c0dc9aac5f7763ca2264185
|
# https://gist.github.com/triangletodd/02f595cd4c0dc9aac5f7763ca2264185
|
||||||
- name: Ensure lxc config has the right apparmor profile
|
- name: Ensure lxc config has the right apparmor profile
|
||||||
lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
dest: "{{ item }}"
|
dest: "{{ item }}"
|
||||||
regexp: "^lxc.apparmor.profile"
|
regexp: ^lxc.apparmor.profile
|
||||||
line: "lxc.apparmor.profile: unconfined"
|
line: "lxc.apparmor.profile: unconfined"
|
||||||
loop: "{{ proxmox_lxc_filtered_files }}"
|
loop: "{{ proxmox_lxc_filtered_files }}"
|
||||||
notify: reboot containers
|
notify: reboot containers
|
||||||
|
|
||||||
- name: Ensure lxc config has the right cgroup
|
- name: Ensure lxc config has the right cgroup
|
||||||
lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
dest: "{{ item }}"
|
dest: "{{ item }}"
|
||||||
regexp: "^lxc.cgroup.devices.allow"
|
regexp: ^lxc.cgroup.devices.allow
|
||||||
line: "lxc.cgroup.devices.allow: a"
|
line: "lxc.cgroup.devices.allow: a"
|
||||||
loop: "{{ proxmox_lxc_filtered_files }}"
|
loop: "{{ proxmox_lxc_filtered_files }}"
|
||||||
notify: reboot containers
|
notify: reboot containers
|
||||||
|
|
||||||
- name: Ensure lxc config has the right cap drop
|
- name: Ensure lxc config has the right cap drop
|
||||||
lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
dest: "{{ item }}"
|
dest: "{{ item }}"
|
||||||
regexp: "^lxc.cap.drop"
|
regexp: ^lxc.cap.drop
|
||||||
line: "lxc.cap.drop: "
|
line: "lxc.cap.drop: "
|
||||||
loop: "{{ proxmox_lxc_filtered_files }}"
|
loop: "{{ proxmox_lxc_filtered_files }}"
|
||||||
notify: reboot containers
|
notify: reboot containers
|
||||||
|
|
||||||
- name: Ensure lxc config has the right mounts
|
- name: Ensure lxc config has the right mounts
|
||||||
lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
dest: "{{ item }}"
|
dest: "{{ item }}"
|
||||||
regexp: "^lxc.mount.auto"
|
regexp: ^lxc.mount.auto
|
||||||
line: 'lxc.mount.auto: "proc:rw sys:rw"'
|
line: 'lxc.mount.auto: "proc:rw sys:rw"'
|
||||||
loop: "{{ proxmox_lxc_filtered_files }}"
|
loop: "{{ proxmox_lxc_filtered_files }}"
|
||||||
notify: reboot containers
|
notify: reboot containers
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Reboot
|
- name: Reboot
|
||||||
reboot:
|
ansible.builtin.reboot:
|
||||||
|
reboot_command: "{{ custom_reboot_command | default(omit) }}"
|
||||||
listen: reboot
|
listen: reboot
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Adjust some Raspberry Pi specific requisites
|
||||||
|
options:
|
||||||
|
state:
|
||||||
|
default: present
|
||||||
|
description:
|
||||||
|
- Indicates whether the k3s prerequisites for Raspberry Pi should be
|
||||||
|
- set up (possible values are `present` and `absent`)
|
||||||
@@ -1,44 +1,51 @@
|
|||||||
---
|
---
|
||||||
- name: Test for raspberry pi /proc/cpuinfo
|
- name: Test for raspberry pi /proc/cpuinfo
|
||||||
command: grep -E "Raspberry Pi|BCM2708|BCM2709|BCM2835|BCM2836" /proc/cpuinfo
|
ansible.builtin.command: grep -E "Raspberry Pi|BCM2708|BCM2709|BCM2835|BCM2836" /proc/cpuinfo
|
||||||
register: grep_cpuinfo_raspberrypi
|
register: grep_cpuinfo_raspberrypi
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Test for raspberry pi /proc/device-tree/model
|
- name: Test for raspberry pi /proc/device-tree/model
|
||||||
command: grep -E "Raspberry Pi" /proc/device-tree/model
|
ansible.builtin.command: grep -E "Raspberry Pi" /proc/device-tree/model
|
||||||
register: grep_device_tree_model_raspberrypi
|
register: grep_device_tree_model_raspberrypi
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Set raspberry_pi fact to true
|
- name: Set raspberry_pi fact to true
|
||||||
set_fact:
|
ansible.builtin.set_fact:
|
||||||
raspberry_pi: true
|
raspberry_pi: true
|
||||||
when:
|
when: grep_cpuinfo_raspberrypi.rc == 0 or grep_device_tree_model_raspberrypi.rc == 0
|
||||||
grep_cpuinfo_raspberrypi.rc == 0 or grep_device_tree_model_raspberrypi.rc == 0
|
|
||||||
|
|
||||||
- name: Set detected_distribution to Raspbian (ARM64 on Raspbian, Debian Buster/Bullseye/Bookworm)
|
- name: Set detected_distribution to Raspbian (ARM64 on Raspbian, Debian Buster/Bullseye/Bookworm)
|
||||||
set_fact:
|
ansible.builtin.set_fact:
|
||||||
detected_distribution: Raspbian
|
detected_distribution: Raspbian
|
||||||
vars:
|
vars:
|
||||||
allowed_descriptions:
|
allowed_descriptions:
|
||||||
- "[Rr]aspbian.*"
|
- "[Rr]aspbian.*"
|
||||||
- "Debian.*buster"
|
- Debian.*buster
|
||||||
- "Debian.*bullseye"
|
- Debian.*bullseye
|
||||||
- "Debian.*bookworm"
|
- Debian.*bookworm
|
||||||
when:
|
when:
|
||||||
- ansible_facts.architecture is search("aarch64")
|
- ansible_facts.architecture is search("aarch64")
|
||||||
- raspberry_pi|default(false)
|
- raspberry_pi|default(false)
|
||||||
- ansible_facts.lsb.description|default("") is match(allowed_descriptions | join('|'))
|
- ansible_facts.lsb.description|default("") is match(allowed_descriptions | join('|'))
|
||||||
|
|
||||||
|
- name: Set detected_distribution to Raspbian (ARM64 on Debian Bookworm)
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
detected_distribution: Raspbian
|
||||||
|
when:
|
||||||
|
- ansible_facts.architecture is search("aarch64")
|
||||||
|
- raspberry_pi|default(false)
|
||||||
|
- ansible_facts.lsb.description|default("") is match("Debian.*bookworm")
|
||||||
|
|
||||||
- name: Set detected_distribution_major_version
|
- name: Set detected_distribution_major_version
|
||||||
set_fact:
|
ansible.builtin.set_fact:
|
||||||
detected_distribution_major_version: "{{ ansible_facts.lsb.major_release }}"
|
detected_distribution_major_version: "{{ ansible_facts.lsb.major_release }}"
|
||||||
when:
|
when:
|
||||||
- detected_distribution | default("") == "Raspbian"
|
- detected_distribution | default("") == "Raspbian"
|
||||||
|
|
||||||
- name: Execute OS related tasks on the Raspberry Pi - {{ action_ }}
|
- name: Execute OS related tasks on the Raspberry Pi - {{ action_ }}
|
||||||
include_tasks: "{{ item }}"
|
ansible.builtin.include_tasks: "{{ item }}"
|
||||||
with_first_found:
|
with_first_found:
|
||||||
- "{{ action_ }}/{{ detected_distribution }}-{{ detected_distribution_major_version }}.yml"
|
- "{{ action_ }}/{{ detected_distribution }}-{{ detected_distribution_major_version }}.yml"
|
||||||
- "{{ action_ }}/{{ detected_distribution }}.yml"
|
- "{{ action_ }}/{{ detected_distribution }}.yml"
|
||||||
|
|||||||
@@ -1,19 +1,39 @@
|
|||||||
---
|
---
|
||||||
|
- name: Test for cmdline path
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /boot/firmware/cmdline.txt
|
||||||
|
register: boot_cmdline_path
|
||||||
|
failed_when: false
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set cmdline path based on Debian version and command result
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cmdline_path: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
boot_cmdline_path.stat.exists and
|
||||||
|
ansible_facts.lsb.description | default('') is match('Debian.*(?!(bookworm|sid))')
|
||||||
|
) | ternary(
|
||||||
|
'/boot/firmware/cmdline.txt',
|
||||||
|
'/boot/cmdline.txt'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
- name: Activating cgroup support
|
- name: Activating cgroup support
|
||||||
lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: /boot/cmdline.txt
|
path: "{{ cmdline_path }}"
|
||||||
regexp: '^((?!.*\bcgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory\b).*)$'
|
regexp: ^((?!.*\bcgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory\b).*)$
|
||||||
line: '\1 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory'
|
line: \1 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory
|
||||||
backrefs: true
|
backrefs: true
|
||||||
notify: reboot
|
notify: reboot
|
||||||
|
|
||||||
- name: Install iptables
|
- name: Install iptables
|
||||||
apt:
|
ansible.builtin.apt:
|
||||||
name: iptables
|
name: iptables
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
- name: Flush iptables before changing to iptables-legacy
|
- name: Flush iptables before changing to iptables-legacy
|
||||||
iptables:
|
ansible.builtin.iptables:
|
||||||
flush: true
|
flush: true
|
||||||
|
|
||||||
- name: Changing to iptables-legacy
|
- name: Changing to iptables-legacy
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
- name: Enable cgroup via boot commandline if not already enabled for Rocky
|
- name: Enable cgroup via boot commandline if not already enabled for Rocky
|
||||||
lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: /boot/cmdline.txt
|
path: /boot/cmdline.txt
|
||||||
backrefs: true
|
backrefs: true
|
||||||
regexp: '^((?!.*\bcgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory\b).*)$'
|
regexp: ^((?!.*\bcgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory\b).*)$
|
||||||
line: '\1 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory'
|
line: \1 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory
|
||||||
notify: reboot
|
notify: reboot
|
||||||
when: not ansible_check_mode
|
when: not ansible_check_mode
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
---
|
---
|
||||||
- name: Enable cgroup via boot commandline if not already enabled for Ubuntu on a Raspberry Pi
|
- name: Enable cgroup via boot commandline if not already enabled for Ubuntu on a Raspberry Pi
|
||||||
lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: /boot/firmware/cmdline.txt
|
path: /boot/firmware/cmdline.txt
|
||||||
backrefs: true
|
backrefs: true
|
||||||
regexp: '^((?!.*\bcgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory\b).*)$'
|
regexp: ^((?!.*\bcgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory\b).*)$
|
||||||
line: '\1 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory'
|
line: \1 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory
|
||||||
notify: reboot
|
notify: reboot
|
||||||
|
|
||||||
- name: Install linux-modules-extra-raspi
|
- name: Install linux-modules-extra-raspi
|
||||||
apt:
|
ansible.builtin.apt:
|
||||||
name: linux-modules-extra-raspi
|
name: linux-modules-extra-raspi
|
||||||
state: present
|
state: present
|
||||||
|
when: ansible_distribution_version is version('24.04', '<')
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
- name: Remove linux-modules-extra-raspi
|
- name: Remove linux-modules-extra-raspi
|
||||||
apt:
|
ansible.builtin.apt:
|
||||||
name: linux-modules-extra-raspi
|
name: linux-modules-extra-raspi
|
||||||
state: absent
|
state: absent
|
||||||
|
when: ansible_distribution_version is version('24.04', '<')
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
systemd_dir: /etc/systemd/system
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Reset all nodes
|
||||||
|
options:
|
||||||
|
systemd_dir:
|
||||||
|
description: Path to systemd services
|
||||||
|
default: /etc/systemd/system
|
||||||
+13
-12
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
- name: Disable services
|
- name: Disable services
|
||||||
systemd:
|
ansible.builtin.systemd:
|
||||||
name: "{{ item }}"
|
name: "{{ item }}"
|
||||||
state: stopped
|
state: stopped
|
||||||
enabled: false
|
enabled: false
|
||||||
@@ -12,12 +12,12 @@
|
|||||||
|
|
||||||
- name: RUN pkill -9 -f "k3s/data/[^/]+/bin/containerd-shim-runc"
|
- name: RUN pkill -9 -f "k3s/data/[^/]+/bin/containerd-shim-runc"
|
||||||
register: pkill_containerd_shim_runc
|
register: pkill_containerd_shim_runc
|
||||||
command: pkill -9 -f "k3s/data/[^/]+/bin/containerd-shim-runc"
|
ansible.builtin.command: pkill -9 -f "k3s/data/[^/]+/bin/containerd-shim-runc"
|
||||||
changed_when: "pkill_containerd_shim_runc.rc == 0"
|
changed_when: pkill_containerd_shim_runc.rc == 0
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
- name: Umount k3s filesystems
|
- name: Umount k3s filesystems
|
||||||
include_tasks: umount_with_children.yml
|
ansible.builtin.include_tasks: umount_with_children.yml
|
||||||
with_items:
|
with_items:
|
||||||
- /run/k3s
|
- /run/k3s
|
||||||
- /var/lib/kubelet
|
- /var/lib/kubelet
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
loop_var: mounted_fs
|
loop_var: mounted_fs
|
||||||
|
|
||||||
- name: Remove service files, binaries and data
|
- name: Remove service files, binaries and data
|
||||||
file:
|
ansible.builtin.file:
|
||||||
name: "{{ item }}"
|
name: "{{ item }}"
|
||||||
state: absent
|
state: absent
|
||||||
with_items:
|
with_items:
|
||||||
@@ -45,9 +45,10 @@
|
|||||||
- /var/lib/rancher/k3s
|
- /var/lib/rancher/k3s
|
||||||
- /var/lib/rancher/
|
- /var/lib/rancher/
|
||||||
- /var/lib/cni/
|
- /var/lib/cni/
|
||||||
|
- /etc/cni/net.d
|
||||||
|
|
||||||
- name: Remove K3s http_proxy files
|
- name: Remove K3s http_proxy files
|
||||||
file:
|
ansible.builtin.file:
|
||||||
name: "{{ item }}"
|
name: "{{ item }}"
|
||||||
state: absent
|
state: absent
|
||||||
with_items:
|
with_items:
|
||||||
@@ -58,22 +59,22 @@
|
|||||||
when: proxy_env is defined
|
when: proxy_env is defined
|
||||||
|
|
||||||
- name: Reload daemon_reload
|
- name: Reload daemon_reload
|
||||||
systemd:
|
ansible.builtin.systemd:
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
- name: Remove tmp directory used for manifests
|
- name: Remove tmp directory used for manifests
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: /tmp/k3s
|
path: /tmp/k3s
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
- name: Check if rc.local exists
|
- name: Check if rc.local exists
|
||||||
stat:
|
ansible.builtin.stat:
|
||||||
path: /etc/rc.local
|
path: /etc/rc.local
|
||||||
register: rcfile
|
register: rcfile
|
||||||
|
|
||||||
- name: Remove rc.local modifications for proxmox lxc containers
|
- name: Remove rc.local modifications for proxmox lxc containers
|
||||||
become: true
|
become: true
|
||||||
blockinfile:
|
ansible.builtin.blockinfile:
|
||||||
path: /etc/rc.local
|
path: /etc/rc.local
|
||||||
content: "{{ lookup('template', 'templates/rc.local.j2') }}"
|
content: "{{ lookup('template', 'templates/rc.local.j2') }}"
|
||||||
create: false
|
create: false
|
||||||
@@ -82,14 +83,14 @@
|
|||||||
|
|
||||||
- name: Check rc.local for cleanup
|
- name: Check rc.local for cleanup
|
||||||
become: true
|
become: true
|
||||||
slurp:
|
ansible.builtin.slurp:
|
||||||
src: /etc/rc.local
|
src: /etc/rc.local
|
||||||
register: rcslurp
|
register: rcslurp
|
||||||
when: proxmox_lxc_configure and rcfile.stat.exists
|
when: proxmox_lxc_configure and rcfile.stat.exists
|
||||||
|
|
||||||
- name: Cleanup rc.local if we only have a Shebang line
|
- name: Cleanup rc.local if we only have a Shebang line
|
||||||
become: true
|
become: true
|
||||||
file:
|
ansible.builtin.file:
|
||||||
path: /etc/rc.local
|
path: /etc/rc.local
|
||||||
state: absent
|
state: absent
|
||||||
when: proxmox_lxc_configure and rcfile.stat.exists and ((rcslurp.content | b64decode).splitlines() | length) <= 1
|
when: proxmox_lxc_configure and rcfile.stat.exists and ((rcslurp.content | b64decode).splitlines() | length) <= 1
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
- name: Get the list of mounted filesystems
|
- name: Get the list of mounted filesystems
|
||||||
shell: set -o pipefail && cat /proc/mounts | awk '{ print $2}' | grep -E "^{{ mounted_fs }}"
|
ansible.builtin.shell: set -o pipefail && cat /proc/mounts | awk '{ print $2}' | grep -E "^{{ mounted_fs }}"
|
||||||
register: get_mounted_filesystems
|
register: get_mounted_filesystems
|
||||||
args:
|
args:
|
||||||
executable: /bin/bash
|
executable: /bin/bash
|
||||||
@@ -12,5 +12,4 @@
|
|||||||
ansible.posix.mount:
|
ansible.posix.mount:
|
||||||
path: "{{ item }}"
|
path: "{{ item }}"
|
||||||
state: unmounted
|
state: unmounted
|
||||||
with_items:
|
with_items: "{{ get_mounted_filesystems.stdout_lines | reverse | list }}"
|
||||||
"{{ get_mounted_filesystems.stdout_lines | reverse | list }}"
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Proxmox LXC settings
|
||||||
|
options:
|
||||||
|
proxmox_lxc_ct_ids:
|
||||||
|
description: Proxmox container ID list
|
||||||
|
type: list
|
||||||
|
required: true
|
||||||
Loaded 100 of 102 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user