Initial commit
Label Sync / Label Sync (push) Failing after 1m7s
E2E / reject-invalid (bad-bgp-asn) (push) Skipped
E2E / reject-invalid (bad-mac-address) (push) Skipped
E2E / reject-invalid (bad-repo-url) (push) Skipped
E2E / reject-invalid (bad-vlan-tag) (push) Skipped
E2E / reject-invalid (duplicate-gateway-addrs) (push) Skipped
E2E / reject-invalid (duplicate-node-names) (push) Skipped
E2E / reject-invalid (gateway-node-collision) (push) Skipped
E2E / reject-invalid (missing-dns-token) (push) Skipped
E2E / reject-invalid (nested-cidr-overlap) (push) Skipped
E2E / reject-invalid (node-addr-outside-cidr) (push) Skipped
E2E / reject-invalid (tunnel-without-dns) (push) Skipped
E2E / accept-valid (selfhosted) (push) Skipped
E2E / reject-invalid (missing-known-hosts) (push) Skipped
E2E / reject-invalid (missing-schematic) (push) Skipped
E2E / reject-invalid (partial-bgp) (push) Skipped
E2E / accept-valid (internal) (push) Skipped
E2E / reject-invalid (missing-external-gateway) (push) Skipped
E2E / reject-invalid (node-uses-gateway-addr) (push) Skipped
E2E / reject-invalid (non-canonical-cidr) (push) Skipped
E2E / accept-valid (private) (push) Skipped
E2E / accept-valid (single-node) (push) Skipped
E2E / reject-invalid (overlapping-cidrs) (push) Skipped
E2E / reject-invalid (reserved-node-name) (push) Skipped
E2E / reject-invalid (tiny-svc-cidr) (push) Skipped
E2E / validator-tests (push) Skipped
E2E / accept-valid (direct) (push) Skipped
E2E / accept-valid (multi-controller) (push) Skipped
E2E / accept-valid (no-webhook) (push) Skipped
E2E / accept-valid (public) (push) Skipped
Label Sync / Label Sync (push) Failing after 1m7s
E2E / reject-invalid (bad-bgp-asn) (push) Skipped
E2E / reject-invalid (bad-mac-address) (push) Skipped
E2E / reject-invalid (bad-repo-url) (push) Skipped
E2E / reject-invalid (bad-vlan-tag) (push) Skipped
E2E / reject-invalid (duplicate-gateway-addrs) (push) Skipped
E2E / reject-invalid (duplicate-node-names) (push) Skipped
E2E / reject-invalid (gateway-node-collision) (push) Skipped
E2E / reject-invalid (missing-dns-token) (push) Skipped
E2E / reject-invalid (nested-cidr-overlap) (push) Skipped
E2E / reject-invalid (node-addr-outside-cidr) (push) Skipped
E2E / reject-invalid (tunnel-without-dns) (push) Skipped
E2E / accept-valid (selfhosted) (push) Skipped
E2E / reject-invalid (missing-known-hosts) (push) Skipped
E2E / reject-invalid (missing-schematic) (push) Skipped
E2E / reject-invalid (partial-bgp) (push) Skipped
E2E / accept-valid (internal) (push) Skipped
E2E / reject-invalid (missing-external-gateway) (push) Skipped
E2E / reject-invalid (node-uses-gateway-addr) (push) Skipped
E2E / reject-invalid (non-canonical-cidr) (push) Skipped
E2E / accept-valid (private) (push) Skipped
E2E / accept-valid (single-node) (push) Skipped
E2E / reject-invalid (overlapping-cidrs) (push) Skipped
E2E / reject-invalid (reserved-node-name) (push) Skipped
E2E / reject-invalid (tiny-svc-cidr) (push) Skipped
E2E / validator-tests (push) Skipped
E2E / accept-valid (direct) (push) Skipped
E2E / accept-valid (multi-controller) (push) Skipped
E2E / accept-valid (no-webhook) (push) Skipped
E2E / accept-valid (public) (push) Skipped
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
; https://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.cue]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.sh]
|
||||
indent_size = 4
|
||||
@@ -0,0 +1,10 @@
|
||||
* text=auto eol=lf
|
||||
*.env linguist-detectable linguist-language=SHELL
|
||||
*.json linguist-detectable linguist-language=JSON
|
||||
*.json5 linguist-detectable linguist-language=JSON5
|
||||
*.md linguist-detectable linguist-language=MARKDOWN
|
||||
*.sh linguist-detectable linguist-language=SHELL
|
||||
*.toml linguist-detectable linguist-language=TOML
|
||||
*.yml linguist-detectable linguist-language=YAML
|
||||
*.yaml linguist-detectable linguist-language=YAML
|
||||
*.yaml.j2 linguist-detectable linguist-language=YAML
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
- name: type/digest
|
||||
color: ffeC19
|
||||
- name: type/patch
|
||||
color: ffeC19
|
||||
- name: type/minor
|
||||
color: ff9800
|
||||
- name: type/major
|
||||
color: f6412d
|
||||
@@ -0,0 +1,5 @@
|
||||
changelog:
|
||||
exclude:
|
||||
authors:
|
||||
- github-actions
|
||||
- renovate
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: e2e-deny-server
|
||||
namespace: default
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
app: e2e-network-server
|
||||
ingress:
|
||||
- {}
|
||||
Executable
+291
@@ -0,0 +1,291 @@
|
||||
#!/usr/bin/env bash
|
||||
# Full-fidelity bootstrap e2e: takes maintenance-mode Talos VMs, discovers
|
||||
# their hardware the same way the README instructs users to, writes a
|
||||
# cluster.toml from the discovered facts, and runs the template's real
|
||||
# bootstrap flow against them.
|
||||
#
|
||||
# Two provisioning paths share this test body:
|
||||
# - CI: talosctl-cluster-action boots the nodes (talos-cluster.yaml) and
|
||||
# passes E2E_CONTROLPLANE_IPS / E2E_WORKER_IPS / E2E_CIDR; the action's
|
||||
# post step destroys them.
|
||||
# - Local: run with no env set; the script boots and destroys the cluster
|
||||
# itself. Requires Docker, /dev/kvm, passwordless sudo, qemu-system-x86,
|
||||
# and the repo's mise toolchain on PATH.
|
||||
#
|
||||
# Renders into the working tree like any configure run.
|
||||
set -euo pipefail
|
||||
|
||||
NAME="${E2E_NAME:-template-e2e}"
|
||||
MODE="${1:-all}"
|
||||
E2E_DIR=".github/template-tests/e2e"
|
||||
CIDR="${E2E_CIDR:-10.9.0.0/24}"
|
||||
PREFIX="${CIDR%/*}"
|
||||
PREFIX="${PREFIX%.*}"
|
||||
TALOSCTL="$(command -v talosctl)"
|
||||
# The Image Factory vanilla schematic, matching the ISO the nodes boot from.
|
||||
SCHEMATIC="376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
|
||||
if [ -n "${E2E_CONTROLPLANE_IPS:-}" ]; then
|
||||
PROVISIONED=true
|
||||
IFS=',' read -r -a CONTROLPLANES <<< "$E2E_CONTROLPLANE_IPS"
|
||||
IFS=',' read -r -a WORKERS <<< "${E2E_WORKER_IPS:-}"
|
||||
else
|
||||
PROVISIONED=false
|
||||
CONTROLPLANES=("$PREFIX.2")
|
||||
WORKERS=("$PREFIX.3")
|
||||
fi
|
||||
NODES=("${CONTROLPLANES[@]}" "${WORKERS[@]}")
|
||||
# The VMs reach the host at the gateway address; the rendered workspace is
|
||||
# served from there over git smart HTTP so Flux can sync it.
|
||||
GIT_HOST="${E2E_GATEWAY:-$PREFIX.1}"
|
||||
GIT_PORT=8418
|
||||
GIT_SERVER_CONTAINER=""
|
||||
|
||||
# The provisioner runs under sudo and writes state relative to its cwd and
|
||||
# TALOSCONFIG, so both are pointed at a scratch dir to keep root-owned files
|
||||
# out of the repo.
|
||||
if [ -n "${E2E_STATE:-}" ]; then
|
||||
STATE="$E2E_STATE"
|
||||
STATE_OWNED=false
|
||||
else
|
||||
STATE="$(mktemp -d)"
|
||||
STATE_OWNED=true
|
||||
fi
|
||||
mkdir -p "$STATE"
|
||||
GIT_PUSH_URL="http://127.0.0.1:$GIT_PORT/repo.git"
|
||||
|
||||
cleanup() {
|
||||
rc=$?
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
echo "==> e2e failed (rc=$rc), collecting diagnostics"
|
||||
kubectl get pods --all-namespaces 2>/dev/null || true
|
||||
kubectl get gitrepositories,kustomizations,helmreleases --all-namespaces 2>/dev/null || true
|
||||
kubectl get events --all-namespaces --sort-by=.lastTimestamp 2>/dev/null | tail -30 || true
|
||||
[ -n "$GIT_SERVER_CONTAINER" ] && docker logs --tail 5 "$GIT_SERVER_CONTAINER" 2>/dev/null || true
|
||||
for ip in "${NODES[@]}"; do
|
||||
talosctl -n "$ip" dmesg 2>/dev/null | tail -20 || true
|
||||
done
|
||||
fi
|
||||
if [ "$MODE" = all ]; then
|
||||
[ -n "$GIT_SERVER_CONTAINER" ] && docker stop "$GIT_SERVER_CONTAINER" >/dev/null 2>&1 || true
|
||||
fi
|
||||
if [ "$MODE" = all ] && [ "$PROVISIONED" = false ]; then
|
||||
(cd "$STATE" && sudo -E env TALOSCONFIG="$STATE/talosconfig" \
|
||||
"$TALOSCTL" cluster destroy --name "$NAME" --provisioner qemu >/dev/null 2>&1) || true
|
||||
fi
|
||||
if [ "$MODE" = all ] && [ "$STATE_OWNED" = true ]; then
|
||||
sudo rm -rf "$STATE" || true
|
||||
fi
|
||||
exit "$rc"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
start_local_git_server() {
|
||||
GIT_SERVER_CONTAINER="$NAME-git"
|
||||
docker run --detach --rm --name "$GIT_SERVER_CONTAINER" \
|
||||
--publish "$GIT_PORT:23232" \
|
||||
--env SOFT_SERVE_GIT_ENABLED=false \
|
||||
--env SOFT_SERVE_LFS_ENABLED=false \
|
||||
--env SOFT_SERVE_SSH_LISTEN_ADDR=127.0.0.1:23231 \
|
||||
--env SOFT_SERVE_STATS_ENABLED=false \
|
||||
--entrypoint /bin/sh \
|
||||
ghcr.io/charmbracelet/soft-serve:v0.11.6 \
|
||||
-c 'set -eu; ssh-keygen -q -t ed25519 -N "" -f /tmp/admin; export SOFT_SERVE_INITIAL_ADMIN_KEYS="$(cat /tmp/admin.pub)"; /usr/local/bin/soft serve & pid=$!; until ssh -q -i /tmp/admin -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -p 23231 localhost settings anon-access read-write; do sleep 1; done; ssh -q -i /tmp/admin -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -p 23231 localhost repo create repo; wait "$pid"' \
|
||||
>/dev/null
|
||||
deadline=$((SECONDS + 60))
|
||||
until git ls-remote "$GIT_PUSH_URL" >/dev/null 2>&1; do
|
||||
if (( SECONDS >= deadline )); then
|
||||
just log fatal "Soft Serve is not reachable"
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
prepare() {
|
||||
# In CI the action itself waits for every node's maintenance API before
|
||||
# returning, so the poll here covers only the local path, where cluster
|
||||
# create returns as soon as the VMs launch.
|
||||
if [ "$PROVISIONED" = false ]; then
|
||||
echo "==> booting maintenance-mode nodes"
|
||||
(cd "$STATE" && sudo -E env TALOSCONFIG="$STATE/talosconfig" \
|
||||
"$TALOSCTL" cluster create qemu --name "$NAME" --presets iso,maintenance \
|
||||
--controlplanes 1 --workers 1 --cidr "$CIDR" \
|
||||
--memory-controlplanes 4GiB --memory-workers 3GiB)
|
||||
|
||||
echo "==> waiting for the maintenance API"
|
||||
for ip in "${NODES[@]}"; do
|
||||
until talosctl -n "$ip" get links --insecure >/dev/null 2>&1; do sleep 5; done
|
||||
done
|
||||
fi
|
||||
|
||||
echo "==> discovering node hardware"
|
||||
declare -A MACS DISKS
|
||||
for ip in "${NODES[@]}"; do
|
||||
MACS[$ip]="$(talosctl -n "$ip" get links --insecure -o json \
|
||||
| jq -r 'select(.spec.type == "ether" and .spec.operationalState == "up" and (.metadata.id | startswith("bond") | not)) | .spec.hardwareAddr' | head -1)"
|
||||
DISKS[$ip]="/dev/$(talosctl -n "$ip" get disks --insecure -o json \
|
||||
| jq -r 'select(.spec.readonly == false and (.metadata.id | startswith("loop") | not)) | .metadata.id' | head -1)"
|
||||
echo " $ip mac=${MACS[$ip]} disk=${DISKS[$ip]}"
|
||||
done
|
||||
|
||||
echo "==> generating cluster.toml"
|
||||
export E2E_CIDR="$CIDR"
|
||||
export E2E_GATEWAY="${E2E_GATEWAY:-$PREFIX.1}"
|
||||
export E2E_GIT_HOST="$GIT_HOST"
|
||||
export E2E_GIT_PORT="$GIT_PORT"
|
||||
export E2E_PREFIX="$PREFIX"
|
||||
export E2E_SCHEMATIC="$SCHEMATIC"
|
||||
envsubst '${E2E_CIDR} ${E2E_GATEWAY} ${E2E_GIT_HOST} ${E2E_GIT_PORT} ${E2E_PREFIX} ${E2E_SCHEMATIC}' \
|
||||
< "$E2E_DIR/cluster.toml.tmpl" > cluster.toml
|
||||
index=0
|
||||
for ip in "${NODES[@]}"; do
|
||||
controller=false
|
||||
for cp in "${CONTROLPLANES[@]}"; do [ "$ip" = "$cp" ] && controller=true; done
|
||||
export E2E_NODE_NAME="e2e-$index"
|
||||
export E2E_NODE_ADDRESS="$ip"
|
||||
export E2E_NODE_CONTROLLER="$controller"
|
||||
export E2E_NODE_DISK="${DISKS[$ip]}"
|
||||
export E2E_NODE_MAC="${MACS[$ip]}"
|
||||
envsubst '${E2E_NODE_ADDRESS} ${E2E_NODE_CONTROLLER} ${E2E_NODE_DISK} ${E2E_NODE_MAC} ${E2E_NODE_NAME}' \
|
||||
< "$E2E_DIR/node.toml.tmpl" >> cluster.toml
|
||||
index=$((index + 1))
|
||||
done
|
||||
|
||||
echo "==> configure"
|
||||
just init
|
||||
just configure
|
||||
|
||||
# Flux's FluxInstance only reports Ready once its Git sync succeeds, so the
|
||||
# rendered kubernetes/ tree is committed to a bare repo and served to the
|
||||
# cluster — the same push-then-bootstrap flow the README walks users through.
|
||||
echo "==> publishing rendered repo"
|
||||
mkdir -p "$STATE/gitwork"
|
||||
cp -r kubernetes "$STATE/gitwork/"
|
||||
git -C "$STATE/gitwork" init --quiet --initial-branch main
|
||||
git -C "$STATE/gitwork" add --all
|
||||
git -C "$STATE/gitwork" -c user.name=e2e -c user.email=e2e@cluster.local \
|
||||
commit --quiet --message "rendered workspace"
|
||||
git -C "$STATE/gitwork" push --quiet "$GIT_PUSH_URL" main
|
||||
}
|
||||
|
||||
assert_cluster_health() {
|
||||
echo "==> asserting cluster health"
|
||||
kubectl wait nodes --all --for=condition=Ready --timeout=10m
|
||||
for ns in kube-system cert-manager flux-system; do
|
||||
kubectl wait pods --namespace "$ns" --all --for=condition=Ready --timeout=10m
|
||||
done
|
||||
|
||||
echo "==> asserting flux reconciliation"
|
||||
kubectl wait fluxinstance/flux --namespace flux-system --for=condition=Ready --timeout=10m
|
||||
kubectl wait gitrepositories --all --all-namespaces --for=condition=Ready --timeout=5m
|
||||
kubectl wait kustomizations --all --all-namespaces --for=condition=Ready --timeout=10m
|
||||
kubectl wait helmreleases --all --all-namespaces --for=condition=Ready --timeout=10m
|
||||
}
|
||||
|
||||
foundation() {
|
||||
deadline=$((SECONDS + 60))
|
||||
until git ls-remote "http://$GIT_HOST:$GIT_PORT/repo.git" >/dev/null 2>&1; do
|
||||
if (( SECONDS >= deadline )); then
|
||||
just log fatal "Rendered repository server is not reachable"
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo "==> bootstrap talos"
|
||||
just bootstrap talos
|
||||
|
||||
echo "==> bootstrap apps"
|
||||
just bootstrap apps
|
||||
assert_cluster_health
|
||||
|
||||
echo "==> asserting bootstrap idempotency"
|
||||
just configure
|
||||
just bootstrap talos
|
||||
just bootstrap apps
|
||||
assert_cluster_health
|
||||
}
|
||||
|
||||
flux_sops() {
|
||||
echo "==> asserting Flux SOPS decryption"
|
||||
SOPS_SECRET="$STATE/gitwork/kubernetes/apps/default/e2e-sops.sops.yaml"
|
||||
export E2E_SOPS_VALUE=flux-decrypted
|
||||
envsubst '${E2E_SOPS_VALUE}' < "$E2E_DIR/sops-secret.yaml.tmpl" > "$SOPS_SECRET"
|
||||
sops encrypt --filename-override kubernetes/apps/default/e2e-sops.sops.yaml \
|
||||
--in-place "$SOPS_SECRET"
|
||||
yq --inplace '.resources += ["./e2e-sops.sops.yaml"]' \
|
||||
"$STATE/gitwork/kubernetes/apps/default/kustomization.yaml"
|
||||
git -C "$STATE/gitwork" add --all
|
||||
git -C "$STATE/gitwork" -c user.name=e2e -c user.email=e2e@cluster.local \
|
||||
commit --quiet --message "test Flux SOPS decryption"
|
||||
git -C "$STATE/gitwork" push --quiet "$GIT_PUSH_URL" main
|
||||
flux reconcile kustomization cluster-apps --with-source --timeout=10m
|
||||
test "$(kubectl get secret e2e-sops --namespace default \
|
||||
--output jsonpath='{.data.value}' | base64 --decode)" = "flux-decrypted"
|
||||
}
|
||||
|
||||
networking() {
|
||||
echo "==> asserting pod networking and DNS"
|
||||
export E2E_CONTROLPLANE_NODE="$(kubectl get nodes \
|
||||
--selector=node-role.kubernetes.io/control-plane \
|
||||
--output jsonpath='{.items[0].metadata.name}')"
|
||||
export E2E_WORKER_NODE="$(kubectl get nodes \
|
||||
--selector='!node-role.kubernetes.io/control-plane' \
|
||||
--output jsonpath='{.items[0].metadata.name}')"
|
||||
NETWORK_CONFIG="$STATE/network.yaml"
|
||||
envsubst '${E2E_CONTROLPLANE_NODE} ${E2E_WORKER_NODE}' \
|
||||
< "$E2E_DIR/network.yaml.tmpl" > "$NETWORK_CONFIG"
|
||||
kubectl apply --filename "$NETWORK_CONFIG"
|
||||
kubectl wait pods/e2e-network-server pods/e2e-network-client \
|
||||
--namespace default --for=condition=Ready --timeout=5m
|
||||
SERVER_IP="$(kubectl get pod e2e-network-server --namespace default \
|
||||
--output jsonpath='{.status.podIP}')"
|
||||
kubectl exec --namespace default e2e-network-client -- \
|
||||
/agnhost connect --timeout=10s "$SERVER_IP:8080"
|
||||
kubectl exec --namespace default e2e-network-client -- \
|
||||
/agnhost connect --timeout=10s e2e-network-server.default.svc.cluster.local:8080
|
||||
kubectl exec --namespace default e2e-network-client -- \
|
||||
/agnhost connect --timeout=10s github.com:443
|
||||
kubectl apply --filename "$E2E_DIR/cilium-network-policy.yaml"
|
||||
deadline=$((SECONDS + 60))
|
||||
while kubectl exec --namespace default e2e-network-client -- \
|
||||
/agnhost connect --timeout=2s "$SERVER_IP:8080" &>/dev/null; do
|
||||
if (( SECONDS >= deadline )); then
|
||||
just log fatal "CiliumNetworkPolicy did not block pod traffic"
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
kubectl delete ciliumnetworkpolicy e2e-deny-server --namespace default
|
||||
deadline=$((SECONDS + 60))
|
||||
until kubectl exec --namespace default e2e-network-client -- \
|
||||
/agnhost connect --timeout=2s "$SERVER_IP:8080" &>/dev/null; do
|
||||
if (( SECONDS >= deadline )); then
|
||||
just log fatal "Pod traffic did not recover after removing CiliumNetworkPolicy"
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
}
|
||||
|
||||
summary() {
|
||||
kubectl get nodes --output wide
|
||||
kubectl get kustomizations,helmreleases --all-namespaces
|
||||
echo "==> e2e bootstrap succeeded"
|
||||
}
|
||||
|
||||
case "$MODE" in
|
||||
prepare) prepare ;;
|
||||
foundation) foundation ;;
|
||||
flux-sops) flux_sops ;;
|
||||
networking) networking ;;
|
||||
summary) summary ;;
|
||||
all)
|
||||
start_local_git_server
|
||||
prepare
|
||||
foundation
|
||||
flux_sops
|
||||
networking
|
||||
summary
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 {prepare|foundation|flux-sops|networking|summary|all}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,22 @@
|
||||
[network]
|
||||
node_cidr = "${E2E_CIDR}"
|
||||
default_gateway = "${E2E_GATEWAY}"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "${E2E_PREFIX}.100"
|
||||
|
||||
[gateways]
|
||||
internal = "${E2E_PREFIX}.101"
|
||||
dns = "${E2E_PREFIX}.102"
|
||||
|
||||
[domain]
|
||||
name = "e2e.example.com"
|
||||
|
||||
[dns]
|
||||
provider = "none"
|
||||
|
||||
[repository]
|
||||
url = "http://${E2E_GIT_HOST}:${E2E_GIT_PORT}/repo.git"
|
||||
|
||||
[talos]
|
||||
schematic_id = "${E2E_SCHEMATIC}"
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: e2e-network-server
|
||||
namespace: default
|
||||
labels:
|
||||
app: e2e-network-server
|
||||
spec:
|
||||
nodeName: "${E2E_WORKER_NODE}"
|
||||
containers:
|
||||
- name: server
|
||||
image: registry.k8s.io/e2e-test-images/agnhost:2.66.0
|
||||
args: ["netexec", "--http-port=8080"]
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: e2e-network-server
|
||||
namespace: default
|
||||
spec:
|
||||
selector:
|
||||
app: e2e-network-server
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: http
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: e2e-network-client
|
||||
namespace: default
|
||||
spec:
|
||||
nodeName: "${E2E_CONTROLPLANE_NODE}"
|
||||
containers:
|
||||
- name: client
|
||||
image: registry.k8s.io/e2e-test-images/agnhost:2.66.0
|
||||
args: ["pause"]
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
[[nodes]]
|
||||
name = "${E2E_NODE_NAME}"
|
||||
address = "${E2E_NODE_ADDRESS}"
|
||||
controller = ${E2E_NODE_CONTROLLER}
|
||||
disk = "${E2E_NODE_DISK}"
|
||||
mac_addr = "${E2E_NODE_MAC}"
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: e2e-sops
|
||||
namespace: default
|
||||
stringData:
|
||||
value: "${E2E_SOPS_VALUE}"
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/home-operations/talosctl-cluster-action/main/schema/talos-cluster.json
|
||||
# Maintenance-mode nodes for the bootstrap e2e: the action boots and destroys
|
||||
# them, and cluster.sh exercises the template's real bootstrap flow against
|
||||
# the unconfigured nodes.
|
||||
apiVersion: v1alpha1
|
||||
kind: TalosCluster
|
||||
metadata:
|
||||
name: template-e2e
|
||||
spec:
|
||||
controlplanes:
|
||||
count: 1
|
||||
memory: 4GiB
|
||||
workers:
|
||||
count: 1
|
||||
memory: 3GiB
|
||||
network:
|
||||
cidr: 10.9.0.0/24
|
||||
qemu:
|
||||
presets: [iso, maintenance]
|
||||
disks: [virtio:10GiB]
|
||||
@@ -0,0 +1,34 @@
|
||||
# Negative fixture: BGP router ASN above the 32-bit ASN range.
|
||||
# Expected to be rejected by _router_asn_in_range.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[cilium.bgp]
|
||||
router_addr = "10.10.1.1"
|
||||
router_asn = "4294967296"
|
||||
node_asn = "64514"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,29 @@
|
||||
# Negative fixture: malformed MAC address (uppercase + missing colons).
|
||||
# Expected to be rejected by the #Node.mac_addr regex constraint.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "AABBCCDDEEFF"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,30 @@
|
||||
# Negative fixture: scp-style git URL ("git@host:owner/repo.git") instead of
|
||||
# the canonical https:// or ssh://git@ form.
|
||||
# Expected to be rejected by the repository.url pattern.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "git@github.com:onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,30 @@
|
||||
# Negative fixture: vlan_tag outside the valid 1-4094 range.
|
||||
# Expected to be rejected by _vlan_tag_in_range.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
vlan_tag = "5000"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,29 @@
|
||||
# Negative fixture: gateways.internal == gateways.dns.
|
||||
# Expected to be rejected by `_addrs_check` (list.UniqueItems).
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.252"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,37 @@
|
||||
# Negative fixture: two nodes with the same name.
|
||||
# Expected to be rejected by `_node_name_check` (list.UniqueItems).
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.101"
|
||||
controller = false
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:01"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,29 @@
|
||||
# Negative fixture: a node reuses the internal gateway VIP.
|
||||
# Expected to be rejected by _addr_uniqueness_check.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.252"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,29 @@
|
||||
# Negative fixture: dns.provider "cloudflare" (the default) without a token.
|
||||
# Expected to be rejected by the Dns validator.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
provider = "cloudflare"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,28 @@
|
||||
# Negative fixture: cloudflare-tunnel ingress without gateways.external.
|
||||
# Expected to be rejected by the Config cross-check.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,30 @@
|
||||
# Negative fixture: ssh:// URL to a host without bundled SSH host keys
|
||||
# (github.com/gitlab.com/codeberg.org) and no repository.known_hosts set.
|
||||
# Expected to be rejected by the conditional known_hosts constraint.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "ssh://git@git.example.com/k8s/home-ops.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,28 @@
|
||||
# Negative fixture: a node without schematic_id and no [talos] default.
|
||||
# Expected to be rejected by the schematic resolution in Config.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
@@ -0,0 +1,30 @@
|
||||
# Negative fixture: node_cidr is nested inside the default pod_cidr
|
||||
# (10.42.0.0/16) without being string-equal to it.
|
||||
# Expected to be rejected by _cidr_overlap_check.
|
||||
[network]
|
||||
node_cidr = "10.42.128.0/17"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.42.128.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.42.128.252"
|
||||
dns = "10.42.128.253"
|
||||
external = "10.42.128.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.42.128.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,29 @@
|
||||
# Negative fixture: node address is not inside network.node_cidr.
|
||||
# Expected to be rejected by _node_addrs_in_node_cidr.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "192.168.1.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,30 @@
|
||||
# Negative fixture: a node claims the default gateway address (defaults to
|
||||
# the first IP in node_cidr).
|
||||
# Expected to be rejected by _addr_uniqueness_check.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.1"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,30 @@
|
||||
# Negative fixture: node_cidr written with host bits set instead of the
|
||||
# network address.
|
||||
# Expected to be rejected by _cidr_canonical_check.
|
||||
[network]
|
||||
node_cidr = "10.10.10.5/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,29 @@
|
||||
# Negative fixture: network.node_cidr overlaps the default kubernetes.pod_cidr (10.42.0.0/16).
|
||||
# Expected to be rejected by `_cidrs_check` (list.UniqueItems).
|
||||
[network]
|
||||
node_cidr = "10.42.0.0/16"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.42.0.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.42.0.252"
|
||||
dns = "10.42.0.253"
|
||||
external = "10.42.0.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.42.0.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,34 @@
|
||||
# Negative fixture: two of the three BGP fields set; previously this
|
||||
# silently left BGP disabled.
|
||||
# Expected to be rejected by the Bgp all-or-nothing check.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[cilium.bgp]
|
||||
router_addr = "10.10.1.1"
|
||||
router_asn = "64513"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,29 @@
|
||||
# Negative fixture: a node name uses the reserved word "controller".
|
||||
# Expected to be rejected by the #Node.name regex constraint.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "controller"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,33 @@
|
||||
# Negative fixture: svc_cidr too small to contain the derived CoreDNS
|
||||
# address (10th IP).
|
||||
# Expected to be rejected by _coredns_addr_in_svc_cidr.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes]
|
||||
svc_cidr = "10.43.0.0/29"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,32 @@
|
||||
# Negative fixture: cloudflare-tunnel ingress with dns.provider "none".
|
||||
# Expected to be rejected by the Config cross-check.
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
provider = "none"
|
||||
|
||||
[ingress]
|
||||
mode = "cloudflare-tunnel"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,38 @@
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[ingress]
|
||||
mode = "direct"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-1"
|
||||
address = "10.10.10.101"
|
||||
controller = false
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:01"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,34 @@
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
provider = "none"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-1"
|
||||
address = "10.10.10.101"
|
||||
controller = false
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:01"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,42 @@
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
|
||||
[repository]
|
||||
url = "ssh://git@github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
provider = "none"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-1"
|
||||
address = "10.10.10.101"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:01"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-2"
|
||||
address = "10.10.10.102"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:02"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,36 @@
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
webhook_provider = "none"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-1"
|
||||
address = "10.10.10.101"
|
||||
controller = false
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:01"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,39 @@
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "ssh://git@github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-1"
|
||||
address = "10.10.10.101"
|
||||
controller = false
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:01"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
mtu = 1500
|
||||
secureboot = true
|
||||
encrypt_disk = true
|
||||
kernel_modules = ["nvidia", "nvidia_uvm"]
|
||||
@@ -0,0 +1,59 @@
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
default_gateway = "10.10.10.1"
|
||||
vlan_tag = "100"
|
||||
dns_servers = ["1.1.1.1"]
|
||||
ntp_servers = ["162.159.200.123"]
|
||||
|
||||
[kubernetes]
|
||||
pod_cidr = "10.42.0.0/16"
|
||||
svc_cidr = "10.43.0.0/16"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
tls_sans = ["example.com"]
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
branch = "main"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[cilium]
|
||||
loadbalancer_mode = "dsr"
|
||||
|
||||
[cilium.bgp]
|
||||
router_addr = "10.10.1.1"
|
||||
router_asn = "64513"
|
||||
node_asn = "64514"
|
||||
|
||||
[talos]
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-1"
|
||||
address = "10.10.10.101"
|
||||
controller = false
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:01"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
mtu = 1500
|
||||
secureboot = true
|
||||
encrypt_disk = true
|
||||
kernel_modules = ["nvidia", "nvidia_uvm"]
|
||||
@@ -0,0 +1,39 @@
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
external = "10.10.10.251"
|
||||
|
||||
[repository]
|
||||
url = "ssh://git@git.example.com/k8s/home-ops.git"
|
||||
webhook_provider = "generic-hmac"
|
||||
known_hosts = """
|
||||
git.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
|
||||
"""
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
token = "fake"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-1"
|
||||
address = "10.10.10.101"
|
||||
controller = false
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:01"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,26 @@
|
||||
[network]
|
||||
node_cidr = "10.10.10.0/24"
|
||||
|
||||
[kubernetes.api]
|
||||
addr = "10.10.10.254"
|
||||
|
||||
[gateways]
|
||||
internal = "10.10.10.252"
|
||||
dns = "10.10.10.253"
|
||||
|
||||
[domain]
|
||||
name = "example.com"
|
||||
|
||||
[dns]
|
||||
provider = "none"
|
||||
|
||||
[repository]
|
||||
url = "https://github.com/onedr0p/cluster-template.git"
|
||||
|
||||
[[nodes]]
|
||||
name = "k8s-0"
|
||||
address = "10.10.10.100"
|
||||
controller = true
|
||||
disk = "/dev/sdfake"
|
||||
mac_addr = "00:00:00:00:00:00"
|
||||
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
||||
name: "Flate"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
filter:
|
||||
name: Flate - Filter
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
changed-files: ${{ steps.changed-files.outputs.changed_files }}
|
||||
steps:
|
||||
- name: Get Changed Files
|
||||
id: changed-files
|
||||
uses: bjw-s-labs/action-changed-files@a9a36fb08ce06db9b02fbd8026cc2c0945eb9841 # v0.6.0
|
||||
with:
|
||||
patterns: kubernetes/**/*
|
||||
|
||||
flate:
|
||||
if: ${{ needs.filter.outputs.changed-files != '[]' }}
|
||||
needs: filter
|
||||
name: Flate
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Flate
|
||||
uses: home-operations/flate/action@631b76b69c4e58c6f4d1cb01e23616fa61aebafa # v0.6.5
|
||||
|
||||
- name: Run Flate
|
||||
id: flate
|
||||
run: flate test all -p ./kubernetes/flux/cluster
|
||||
|
||||
success:
|
||||
if: ${{ !cancelled() }}
|
||||
needs: flate
|
||||
name: Flate - Success
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Any jobs failed?
|
||||
if: ${{ contains(needs.*.result, 'failure') }}
|
||||
run: exit 1
|
||||
|
||||
- name: All jobs passed or skipped?
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
||||
name: "Label Sync"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/labels.yaml
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
label-sync:
|
||||
name: Label Sync
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Sync Labels
|
||||
uses: EndBug/label-sync@52074158190acb45f3077f9099fea818aa43f97a # v2.3.3
|
||||
with:
|
||||
config-file: .github/labels.yaml
|
||||
delete-other-labels: true
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
||||
name: "E2E Cluster"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
schedule:
|
||||
- cron: "30 5 * * *"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
bootstrap:
|
||||
if: ${{ github.repository == 'onedr0p/cluster-template' }}
|
||||
name: bootstrap (qemu)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
services:
|
||||
git:
|
||||
image: ghcr.io/charmbracelet/soft-serve:v0.12.2
|
||||
env:
|
||||
SOFT_SERVE_GIT_ENABLED: "false"
|
||||
SOFT_SERVE_LFS_ENABLED: "false"
|
||||
SOFT_SERVE_SSH_LISTEN_ADDR: "127.0.0.1:23231"
|
||||
SOFT_SERVE_STATS_ENABLED: "false"
|
||||
ports:
|
||||
- 8418:23232
|
||||
entrypoint: /bin/sh
|
||||
command: >-
|
||||
-c "set -eu; ssh-keygen -q -t ed25519 -N '' -f /tmp/admin;
|
||||
export SOFT_SERVE_INITIAL_ADMIN_KEYS=$(cat /tmp/admin.pub);
|
||||
/usr/local/bin/soft serve & pid=$!;
|
||||
until ssh -q -i /tmp/admin -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -p 23231 localhost settings anon-access read-write; do sleep 1; done;
|
||||
ssh -q -i /tmp/admin -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -p 23231 localhost repo create repo;
|
||||
wait $pid"
|
||||
options: >-
|
||||
--health-cmd "git ls-remote http://localhost:23232/repo.git"
|
||||
--health-interval 2s
|
||||
--health-timeout 2s
|
||||
--health-retries 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install QEMU
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes --no-install-recommends gettext-base qemu-system-x86 qemu-utils ovmf
|
||||
|
||||
- name: Setup mise
|
||||
uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
- name: Boot maintenance-mode nodes
|
||||
id: cluster
|
||||
uses: home-operations/talosctl-cluster-action@fb6a31bf5de43218acc80d2e23958a16eee7380c # v0.2.2
|
||||
with:
|
||||
config: ./.github/template-tests/e2e/talos-cluster.yaml
|
||||
cache: true
|
||||
|
||||
- name: Export cluster environment
|
||||
env:
|
||||
CONTROLPLANE_IPS: "${{ steps.cluster.outputs.controlplane-ips }}"
|
||||
GATEWAY: "${{ steps.cluster.outputs.gateway }}"
|
||||
WORKER_IPS: "${{ steps.cluster.outputs.worker-ips }}"
|
||||
run: |
|
||||
echo "E2E_CONTROLPLANE_IPS=$CONTROLPLANE_IPS" >> "$GITHUB_ENV"
|
||||
echo "E2E_WORKER_IPS=$WORKER_IPS" >> "$GITHUB_ENV"
|
||||
echo "E2E_GATEWAY=$GATEWAY" >> "$GITHUB_ENV"
|
||||
echo "E2E_CIDR=10.9.0.0/24" >> "$GITHUB_ENV"
|
||||
echo "E2E_STATE=$RUNNER_TEMP/template-e2e" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Prepare cluster
|
||||
run: bash ./.github/template-tests/e2e/cluster.sh prepare
|
||||
|
||||
- name: Build healthy cluster foundation
|
||||
run: bash ./.github/template-tests/e2e/cluster.sh foundation
|
||||
|
||||
- name: Test Flux SOPS
|
||||
id: flux-sops
|
||||
run: bash ./.github/template-tests/e2e/cluster.sh flux-sops
|
||||
background: true
|
||||
|
||||
- name: Test networking
|
||||
id: networking
|
||||
run: bash ./.github/template-tests/e2e/cluster.sh networking
|
||||
background: true
|
||||
|
||||
- name: Wait for E2E tests
|
||||
wait: [flux-sops, networking]
|
||||
|
||||
- name: Summarize cluster
|
||||
run: bash ./.github/template-tests/e2e/cluster.sh summary
|
||||
@@ -0,0 +1,176 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
||||
name: "E2E"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
validate-invalid:
|
||||
if: ${{ github.repository == 'onedr0p/cluster-template' }}
|
||||
name: reject-invalid (${{ matrix.fixture }})
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
fixture:
|
||||
- overlapping-cidrs
|
||||
- nested-cidr-overlap
|
||||
- non-canonical-cidr
|
||||
- tiny-svc-cidr
|
||||
- duplicate-gateway-addrs
|
||||
- duplicate-node-names
|
||||
- reserved-node-name
|
||||
- bad-mac-address
|
||||
- bad-repo-url
|
||||
- missing-known-hosts
|
||||
- node-addr-outside-cidr
|
||||
- node-uses-gateway-addr
|
||||
- gateway-node-collision
|
||||
- bad-vlan-tag
|
||||
- bad-bgp-asn
|
||||
- missing-dns-token
|
||||
- tunnel-without-dns
|
||||
- missing-external-gateway
|
||||
- missing-schematic
|
||||
- partial-bgp
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup mise
|
||||
uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
- name: Assert schema rejects ${{ matrix.fixture }}.toml
|
||||
run: |
|
||||
fixture=./.github/template-tests/invalid/${{ matrix.fixture }}.toml
|
||||
if uv run --quiet --locked --no-dev ./template/scripts/validate.py "$fixture" >/dev/null 2>&1; then
|
||||
echo "::error::schema accepted invalid fixture ${{ matrix.fixture }} (expected rejection)"
|
||||
exit 1
|
||||
fi
|
||||
echo "schema correctly rejected ${{ matrix.fixture }}"
|
||||
# Also surface the actual error message in the log for debuggability.
|
||||
uv run --quiet --locked --no-dev ./template/scripts/validate.py "$fixture" || true
|
||||
|
||||
validator-tests:
|
||||
if: ${{ github.repository == 'onedr0p/cluster-template' }}
|
||||
name: validator-tests
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup mise
|
||||
uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
- name: Run validator tests
|
||||
run: uv run --quiet --locked pytest ./template/scripts/test_validate.py
|
||||
|
||||
validate-valid:
|
||||
if: ${{ github.repository == 'onedr0p/cluster-template' }}
|
||||
name: accept-valid (${{ matrix.fixture }})
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
fixture:
|
||||
- public
|
||||
- private
|
||||
- selfhosted
|
||||
- no-webhook
|
||||
- internal
|
||||
- direct
|
||||
- single-node
|
||||
- multi-controller
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup mise
|
||||
uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0
|
||||
with:
|
||||
experimental: true
|
||||
install_args: --locked
|
||||
|
||||
- name: Run init recipe
|
||||
run: just init
|
||||
|
||||
- name: Prepare files
|
||||
run: |
|
||||
cp ./.github/template-tests/valid/${{ matrix.fixture }}.toml cluster.toml
|
||||
echo '{"AccountTag":"fake","TunnelSecret":"fake","TunnelID":"fake"}' > cloudflare-tunnel.json
|
||||
touch kubeconfig
|
||||
|
||||
- name: Assert cluster.toml passes the JSON Schema
|
||||
run: taplo check --schema "file://$PWD/cluster.schema.json" ./cluster.toml
|
||||
|
||||
- name: Run configure recipe
|
||||
run: just configure
|
||||
|
||||
# Rendered output must already match the format-yaml pre-commit hook,
|
||||
# otherwise every `just configure` shows up as formatting churn.
|
||||
- name: Assert rendered output is formatted
|
||||
run: oxfmt --check ./.sops.yaml ./bootstrap ./kubernetes ./talos
|
||||
|
||||
- name: Install flate
|
||||
uses: home-operations/flate/action@631b76b69c4e58c6f4d1cb01e23616fa61aebafa # v0.6.5
|
||||
with:
|
||||
base: ""
|
||||
|
||||
- name: Run flate test
|
||||
run: flate test all -p ./kubernetes/flux/cluster
|
||||
|
||||
- name: Render bootstrap helmfile charts
|
||||
run: just template test-helmfile
|
||||
|
||||
- name: Dry run bootstrap talos recipe
|
||||
run: just --dry-run bootstrap talos
|
||||
|
||||
- name: Create talos secret
|
||||
run: just bootstrap talos-secret
|
||||
|
||||
- name: Render talos configs
|
||||
run: just talos render
|
||||
|
||||
- name: Validate talos configs
|
||||
run: |
|
||||
for config in ./talos/rendered/*.yaml; do
|
||||
talosctl validate --config "$config" --mode metal
|
||||
done
|
||||
|
||||
- name: Dry run bootstrap apps recipe
|
||||
run: just --dry-run bootstrap apps
|
||||
|
||||
- name: Run reset recipe
|
||||
run: yes | just template reset
|
||||
|
||||
- name: Run tidy recipe
|
||||
run: yes | just template tidy
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
||||
name: "Release"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 0 0 1 * *
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Get Previous Release Tag and Determine Next Tag
|
||||
id: determine-next-tag
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
result-encoding: string
|
||||
script: |
|
||||
const { data: releases } = await github.rest.repos.listReleases({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
per_page: 1,
|
||||
});
|
||||
|
||||
let previousTag = "0.0.0"; // Default if no previous release exists
|
||||
if (releases.length > 0) {
|
||||
previousTag = releases[0].tag_name;
|
||||
}
|
||||
|
||||
const [previousMajor, previousMinor, previousPatch] = previousTag.split('.').map(Number);
|
||||
const currentYear = new Date().getFullYear();
|
||||
const currentMonth = new Date().getMonth() + 1; // Months are 0-indexed in JavaScript
|
||||
|
||||
const nextMajorMinor = `${currentYear}.${currentMonth}`;
|
||||
let nextPatch;
|
||||
|
||||
if (`${previousMajor}.${previousMinor}` === nextMajorMinor) {
|
||||
console.log("Month release already exists for the year. Incrementing patch number by 1.");
|
||||
nextPatch = previousPatch + 1;
|
||||
} else {
|
||||
console.log("Month release does not exist for the year. Starting with patch number 0.");
|
||||
nextPatch = 0;
|
||||
}
|
||||
|
||||
return `${nextMajorMinor}.${nextPatch}`;
|
||||
|
||||
- name: Create Release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ steps.determine-next-tag.outputs.result }}
|
||||
run: gh release create "$TAG" --repo "$GITHUB_REPOSITORY" --generate-notes
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# Secrets
|
||||
*.pub
|
||||
*.key
|
||||
*.decrypted~*.yaml
|
||||
/age.key
|
||||
/cloudflare-tunnel.json
|
||||
/deploy.key
|
||||
/deploy.key.pub
|
||||
/flux-webhook-token.txt
|
||||
# Template config files
|
||||
/cluster.toml
|
||||
# Kubernetes
|
||||
kubeconfig
|
||||
talosconfig
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
# Misc.
|
||||
.claude/
|
||||
.private/
|
||||
.venv/
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
/talos/output/
|
||||
/talos/rendered/
|
||||
@@ -0,0 +1,36 @@
|
||||
[pre-commit]
|
||||
parallel = true
|
||||
skip = ["merge", "rebase"]
|
||||
|
||||
[pre-commit.commands.format-just]
|
||||
glob = ["justfile", "*.just", ".justfile"]
|
||||
run = 'for f in {staged_files}; do just --justfile "$f" --fmt; done'
|
||||
stage_fixed = true
|
||||
|
||||
[pre-commit.commands.format-mise]
|
||||
glob = [".mise.toml", ".mise/config.toml", ".mise/conf.d/*.toml"]
|
||||
run = "mise fmt"
|
||||
stage_fixed = true
|
||||
|
||||
[pre-commit.commands.format-json]
|
||||
glob = ["*.json", "*.json5", "*.jsonc"]
|
||||
run = "oxfmt {staged_files}"
|
||||
stage_fixed = true
|
||||
|
||||
[pre-commit.commands.format-markdown]
|
||||
glob = ["*.md", "*.markdown", "*.mdx"]
|
||||
run = "oxfmt {staged_files}"
|
||||
stage_fixed = true
|
||||
|
||||
[pre-commit.commands.format-yaml]
|
||||
glob = ["*.yaml", "*.yml"]
|
||||
run = "oxfmt {staged_files}"
|
||||
stage_fixed = true
|
||||
|
||||
[pre-commit.commands.mise-lock]
|
||||
glob = [".mise.toml", ".mise/config.toml", ".mise/conf.d/*.toml"]
|
||||
run = "mise lock && git add .mise/mise.lock"
|
||||
|
||||
[pre-commit.commands.zizmor]
|
||||
glob = [".github/workflows/*.yaml", ".github/actions/**/action.yaml"]
|
||||
run = "zizmor --offline {staged_files}"
|
||||
@@ -0,0 +1,5 @@
|
||||
# Tools only the template step needs; `just template tidy` archives this file.
|
||||
[tools]
|
||||
uv = "0.12.13"
|
||||
sd = "1.1.0"
|
||||
taplo = "0.10.0"
|
||||
@@ -0,0 +1,35 @@
|
||||
min_version = "2026.7.0"
|
||||
|
||||
[env]
|
||||
KUBECONFIG = "{{config_root}}/kubeconfig"
|
||||
SOPS_CONFIG = "{{config_root}}/.sops.yaml"
|
||||
SOPS_AGE_KEY_FILE = "{{config_root}}/age.key"
|
||||
TALOSCONFIG = "{{config_root}}/talos/talosconfig"
|
||||
|
||||
[tools]
|
||||
age = "1.3.2"
|
||||
cloudflared = "2026.9.1"
|
||||
flux2 = "2.9.5"
|
||||
gh = "2.100.0"
|
||||
gum = "2.0.1"
|
||||
helm = "4.3.0"
|
||||
helmfile = "1.7.4"
|
||||
jq = "1.8.2"
|
||||
just = "1.58.0"
|
||||
kubeconform = "0.8.0"
|
||||
kubectl = "1.37.0"
|
||||
kustomize = "5.8.1"
|
||||
lefthook = "2.1.12"
|
||||
node = "24.21.0"
|
||||
oxfmt = "0.67.0"
|
||||
sops = "3.13.3"
|
||||
talosctl = "1.14.0"
|
||||
"github:postfinance/topf" = "v0.6.0"
|
||||
yq = "4.53.6"
|
||||
zizmor = "1.30.1"
|
||||
|
||||
[settings]
|
||||
lockfile_platforms = ["linux-x64", "linux-arm64", "macos-arm64", "macos-x64"]
|
||||
|
||||
[hooks]
|
||||
postinstall = "lefthook install"
|
||||
+577
@@ -0,0 +1,577 @@
|
||||
# @generated - this file is auto-generated by `mise lock` https://mise.en.dev/dev-tools/mise-lock.html
|
||||
|
||||
[[tools.age]]
|
||||
version = "1.3.2"
|
||||
backend = "aqua:FiloSottile/age"
|
||||
|
||||
[tools.age."platforms.linux-arm64"]
|
||||
checksum = "sha256:6b8dc4333c53a5a57c9e5834e3a48f92605d7154014cd07269ff3327db5d37f4"
|
||||
url = "https://github.com/FiloSottile/age/releases/download/v1.3.2/age-v1.3.2-linux-arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/FiloSottile/age/releases/assets/535541932"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.age."platforms.linux-x64"]
|
||||
checksum = "sha256:cbe24006683f8eb669266162894b9a522a1af52f2665fbc63a4bb032ed26ac10"
|
||||
url = "https://github.com/FiloSottile/age/releases/download/v1.3.2/age-v1.3.2-linux-amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/FiloSottile/age/releases/assets/535541903"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.age."platforms.macos-arm64"]
|
||||
checksum = "sha256:e2020b073c44f692685a24d6abc378817eb81ffaaf49fd0531ef8565f767f2f5"
|
||||
url = "https://github.com/FiloSottile/age/releases/download/v1.3.2/age-v1.3.2-darwin-arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/FiloSottile/age/releases/assets/535541897"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.age."platforms.macos-x64"]
|
||||
checksum = "sha256:1d1e4bc66e1427edad7739ae7616157de0e79db8b6d2a1497d7d9925fb06a539"
|
||||
url = "https://github.com/FiloSottile/age/releases/download/v1.3.2/age-v1.3.2-darwin-amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/FiloSottile/age/releases/assets/535541887"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[[tools.cloudflared]]
|
||||
version = "2026.9.1"
|
||||
backend = "aqua:cloudflare/cloudflared"
|
||||
|
||||
[tools.cloudflared."platforms.linux-arm64"]
|
||||
checksum = "sha256:3d97437c71848bd8df68041e12436b484a661d95073ea1937f01a845ce88faa3"
|
||||
url = "https://github.com/cloudflare/cloudflared/releases/download/2026.9.1/cloudflared-linux-arm64"
|
||||
url_api = "https://api.github.com/repos/cloudflare/cloudflared/releases/assets/557330419"
|
||||
|
||||
[tools.cloudflared."platforms.linux-x64"]
|
||||
checksum = "sha256:03f1f25d1cc93b9ad6c60569d44060bc4f17ed97075760ed8cfca4b12dcd68cc"
|
||||
url = "https://github.com/cloudflare/cloudflared/releases/download/2026.9.1/cloudflared-linux-amd64"
|
||||
url_api = "https://api.github.com/repos/cloudflare/cloudflared/releases/assets/557330171"
|
||||
|
||||
[tools.cloudflared."platforms.macos-arm64"]
|
||||
checksum = "sha256:c27ab8fd0aa489449e3d201eb02f957ef460a13b613662928b1b23394bf1bcfe"
|
||||
url = "https://github.com/cloudflare/cloudflared/releases/download/2026.9.1/cloudflared-darwin-arm64.tgz"
|
||||
url_api = "https://api.github.com/repos/cloudflare/cloudflared/releases/assets/557329375"
|
||||
|
||||
[tools.cloudflared."platforms.macos-x64"]
|
||||
checksum = "sha256:ff0d3b51d5ff70eceef89d6b32145fee985018a2174596a5dbe405e2766e2ac4"
|
||||
url = "https://github.com/cloudflare/cloudflared/releases/download/2026.9.1/cloudflared-darwin-amd64.tgz"
|
||||
url_api = "https://api.github.com/repos/cloudflare/cloudflared/releases/assets/557330132"
|
||||
|
||||
[[tools.flux2]]
|
||||
version = "2.9.5"
|
||||
backend = "aqua:fluxcd/flux2"
|
||||
|
||||
[tools.flux2."platforms.linux-arm64"]
|
||||
checksum = "sha256:f3e159af616ec0b9bd0a405c2185cf09d06b74652c1de3c7f377e8166826651a"
|
||||
url = "https://github.com/fluxcd/flux2/releases/download/v2.9.5/flux_2.9.5_linux_arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/fluxcd/flux2/releases/assets/538282890"
|
||||
|
||||
[tools.flux2."platforms.linux-arm64".provenance.slsa]
|
||||
url = "https://github.com/fluxcd/flux2/releases/download/v2.9.5/provenance.intoto.jsonl"
|
||||
|
||||
[tools.flux2."platforms.linux-x64"]
|
||||
checksum = "sha256:b853df82adfd7736f580692f9f734473d571606307139f8fd20c2a80dd1ff473"
|
||||
url = "https://github.com/fluxcd/flux2/releases/download/v2.9.5/flux_2.9.5_linux_amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/fluxcd/flux2/releases/assets/538282891"
|
||||
|
||||
[tools.flux2."platforms.linux-x64".provenance.slsa]
|
||||
url = "https://github.com/fluxcd/flux2/releases/download/v2.9.5/provenance.intoto.jsonl"
|
||||
|
||||
[tools.flux2."platforms.macos-arm64"]
|
||||
checksum = "sha256:2869ef7151a6f1b27e6b5d2a6804f3ef23c7bdaa06a74e00d3fe5bfc646547fd"
|
||||
url = "https://github.com/fluxcd/flux2/releases/download/v2.9.5/flux_2.9.5_darwin_arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/fluxcd/flux2/releases/assets/538282876"
|
||||
|
||||
[tools.flux2."platforms.macos-arm64".provenance.slsa]
|
||||
url = "https://github.com/fluxcd/flux2/releases/download/v2.9.5/provenance.intoto.jsonl"
|
||||
|
||||
[tools.flux2."platforms.macos-x64"]
|
||||
checksum = "sha256:5748583cf5da035ca2d751190d2c15f5f656d305c166ec28a312a2f3b6799e31"
|
||||
url = "https://github.com/fluxcd/flux2/releases/download/v2.9.5/flux_2.9.5_darwin_amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/fluxcd/flux2/releases/assets/538282892"
|
||||
|
||||
[tools.flux2."platforms.macos-x64".provenance.slsa]
|
||||
url = "https://github.com/fluxcd/flux2/releases/download/v2.9.5/provenance.intoto.jsonl"
|
||||
|
||||
[[tools.gh]]
|
||||
version = "2.100.0"
|
||||
backend = "aqua:cli/cli"
|
||||
|
||||
[tools.gh."platforms.linux-arm64"]
|
||||
checksum = "sha256:ea4e7a581a32ccad6cc7923cb1576ac5859ba4b9a16ab22eb8f8a96e78e2e961"
|
||||
url = "https://github.com/cli/cli/releases/download/v2.100.0/gh_2.100.0_linux_arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/cli/cli/releases/assets/542974279"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.gh."platforms.linux-x64"]
|
||||
checksum = "sha256:e4d4bb4498e8d007abe545b6568926793ace1b6447da598294a610018cb164be"
|
||||
url = "https://github.com/cli/cli/releases/download/v2.100.0/gh_2.100.0_linux_amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/cli/cli/releases/assets/542974264"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.gh."platforms.macos-arm64"]
|
||||
checksum = "sha256:45f9a62da2f6e641a7fad57e2ce39656dfd7ef331372d80a2a2aed65abb01642"
|
||||
url = "https://github.com/cli/cli/releases/download/v2.100.0/gh_2.100.0_macOS_arm64.zip"
|
||||
url_api = "https://api.github.com/repos/cli/cli/releases/assets/542974295"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.gh."platforms.macos-x64"]
|
||||
checksum = "sha256:fcd7799e85eb575f3c7d2b1679bfbfedaefa1269d4bc7d096b51e10939b4812b"
|
||||
url = "https://github.com/cli/cli/releases/download/v2.100.0/gh_2.100.0_macOS_amd64.zip"
|
||||
url_api = "https://api.github.com/repos/cli/cli/releases/assets/542974293"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[[tools."github:postfinance/topf"]]
|
||||
version = "0.6.0"
|
||||
backend = "github:postfinance/topf"
|
||||
|
||||
[tools."github:postfinance/topf"."platforms.linux-arm64"]
|
||||
checksum = "sha256:7e5d4bf21f07ba91b83c4653eb65dd3cf5ee67aa8fa4131ca403b38791476367"
|
||||
url = "https://github.com/postfinance/topf/releases/download/v0.6.0/topf_linux_arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/postfinance/topf/releases/assets/542955869"
|
||||
|
||||
[tools."github:postfinance/topf"."platforms.linux-x64"]
|
||||
checksum = "sha256:458df4b25f4181a31ed361c0592194f7eb9e6e7b13e7096f8745453afdeaadfb"
|
||||
url = "https://github.com/postfinance/topf/releases/download/v0.6.0/topf_linux_amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/postfinance/topf/releases/assets/542955866"
|
||||
|
||||
[tools."github:postfinance/topf"."platforms.macos-arm64"]
|
||||
checksum = "sha256:48b22175d61eadba0c287411c34a90f73dbaa716fd792b7b28625d3edbf870e2"
|
||||
url = "https://github.com/postfinance/topf/releases/download/v0.6.0/topf_darwin_arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/postfinance/topf/releases/assets/542955870"
|
||||
|
||||
[tools."github:postfinance/topf"."platforms.macos-x64"]
|
||||
checksum = "sha256:b4c76fb5985c2e0c73d6a365a5ae7a45f97f9d61cb502a01b1ee82e63da0b1cd"
|
||||
url = "https://github.com/postfinance/topf/releases/download/v0.6.0/topf_darwin_amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/postfinance/topf/releases/assets/542955865"
|
||||
|
||||
[[tools.gum]]
|
||||
version = "2.0.1"
|
||||
backend = "aqua:charmbracelet/gum"
|
||||
|
||||
[tools.gum."platforms.linux-arm64"]
|
||||
checksum = "sha256:6998202a8fea27bb2007f69e44ec5dcb4cff5268c62d995de857eee0e2cd52cb"
|
||||
url = "https://github.com/charmbracelet/gum/releases/download/v2.0.1/gum_2.0.1_Linux_arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/charmbracelet/gum/releases/assets/556562997"
|
||||
provenance = "cosign"
|
||||
|
||||
[tools.gum."platforms.linux-x64"]
|
||||
checksum = "sha256:4dfe4547f960813864c803b3617aa64427fa32ca566707fde949e08975297c48"
|
||||
url = "https://github.com/charmbracelet/gum/releases/download/v2.0.1/gum_2.0.1_Linux_x86_64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/charmbracelet/gum/releases/assets/556563001"
|
||||
provenance = "cosign"
|
||||
|
||||
[tools.gum."platforms.macos-arm64"]
|
||||
checksum = "sha256:994662daab6fcfe9dcfc57d87ca42bcae2948d86fa864f9010a3848fa9bb7d6e"
|
||||
url = "https://github.com/charmbracelet/gum/releases/download/v2.0.1/gum_2.0.1_Darwin_arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/charmbracelet/gum/releases/assets/556563007"
|
||||
provenance = "cosign"
|
||||
|
||||
[tools.gum."platforms.macos-x64"]
|
||||
checksum = "sha256:4d125b60fbaa28ef1674bb16f3859ed4e813acb027a79d24603b598ea4a0b71f"
|
||||
url = "https://github.com/charmbracelet/gum/releases/download/v2.0.1/gum_2.0.1_Darwin_x86_64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/charmbracelet/gum/releases/assets/556562991"
|
||||
provenance = "cosign"
|
||||
|
||||
[[tools.helm]]
|
||||
version = "4.3.0"
|
||||
backend = "aqua:helm/helm"
|
||||
|
||||
[tools.helm."platforms.linux-arm64"]
|
||||
checksum = "sha256:31c5794dd55c66a51e6b7d2e2ac7a114ae8b1de41ff1d9ba51748ac973b06a08"
|
||||
url = "https://get.helm.sh/helm-v4.3.0-linux-arm64.tar.gz"
|
||||
|
||||
[tools.helm."platforms.linux-x64"]
|
||||
checksum = "sha256:86584a54def73570558f66f5111cc53dfed56689637ae32c1201205d494f54fb"
|
||||
url = "https://get.helm.sh/helm-v4.3.0-linux-amd64.tar.gz"
|
||||
|
||||
[tools.helm."platforms.macos-arm64"]
|
||||
checksum = "sha256:d3870437e1e95b67f8edbde964156c84a26503f560821d40c542441658934fba"
|
||||
url = "https://get.helm.sh/helm-v4.3.0-darwin-arm64.tar.gz"
|
||||
|
||||
[tools.helm."platforms.macos-x64"]
|
||||
checksum = "sha256:347a784877e0e20eac865e8d1c36a80f6bb0861d6f29abd34defb6570ef95d92"
|
||||
url = "https://get.helm.sh/helm-v4.3.0-darwin-amd64.tar.gz"
|
||||
|
||||
[[tools.helmfile]]
|
||||
version = "1.7.4"
|
||||
backend = "aqua:helmfile/helmfile"
|
||||
|
||||
[tools.helmfile."platforms.linux-arm64"]
|
||||
checksum = "sha256:0292f57a4638a21e775b0ce8bde37ee3fdd65dbbdbe0e5b9a06718f1dd04c7fa"
|
||||
url = "https://github.com/helmfile/helmfile/releases/download/v1.7.4/helmfile_1.7.4_linux_arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/helmfile/helmfile/releases/assets/516813268"
|
||||
|
||||
[tools.helmfile."platforms.linux-x64"]
|
||||
checksum = "sha256:f96ef0a015df06b29d7f38bf0ca08821018ae25eb96bf2c7abd3affa1b84e112"
|
||||
url = "https://github.com/helmfile/helmfile/releases/download/v1.7.4/helmfile_1.7.4_linux_amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/helmfile/helmfile/releases/assets/516813296"
|
||||
|
||||
[tools.helmfile."platforms.macos-arm64"]
|
||||
checksum = "sha256:e1490d371fecc1f2d9aae914ef34058f3cb2363c9ee63350b37a85c3994d71d9"
|
||||
url = "https://github.com/helmfile/helmfile/releases/download/v1.7.4/helmfile_1.7.4_darwin_arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/helmfile/helmfile/releases/assets/516813297"
|
||||
|
||||
[tools.helmfile."platforms.macos-x64"]
|
||||
checksum = "sha256:7a0951fcc5bb991d7ea3a0c80e35754eb3981dcadba19795edbec7cba2518ae2"
|
||||
url = "https://github.com/helmfile/helmfile/releases/download/v1.7.4/helmfile_1.7.4_darwin_amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/helmfile/helmfile/releases/assets/516813301"
|
||||
|
||||
[[tools.jq]]
|
||||
version = "1.8.2"
|
||||
backend = "aqua:jqlang/jq"
|
||||
|
||||
[tools.jq."platforms.linux-arm64"]
|
||||
checksum = "sha256:8b85c817833814ddca00a144c33705546355afccf0cf39b188f3cdb48b852309"
|
||||
url = "https://github.com/jqlang/jq/releases/download/jq-1.8.2/jq-linux-arm64"
|
||||
url_api = "https://api.github.com/repos/jqlang/jq/releases/assets/453012756"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.jq."platforms.linux-x64"]
|
||||
checksum = "sha256:b1c22172dd303f3be49e935aa56aa48a8b7a46e0bc838b4997d3bb451495870f"
|
||||
url = "https://github.com/jqlang/jq/releases/download/jq-1.8.2/jq-linux-amd64"
|
||||
url_api = "https://api.github.com/repos/jqlang/jq/releases/assets/453012752"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.jq."platforms.macos-arm64"]
|
||||
checksum = "sha256:2d75340ba57a4b4b4c8708a21c2dc8e958a48aaa8bba13b27f77f6e4c0eca07e"
|
||||
url = "https://github.com/jqlang/jq/releases/download/jq-1.8.2/jq-macos-arm64"
|
||||
url_api = "https://api.github.com/repos/jqlang/jq/releases/assets/453012783"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.jq."platforms.macos-x64"]
|
||||
checksum = "sha256:e94b266e3c26690550006abe63152b782280f4e14374accdf04cbde844f00bc0"
|
||||
url = "https://github.com/jqlang/jq/releases/download/jq-1.8.2/jq-macos-amd64"
|
||||
url_api = "https://api.github.com/repos/jqlang/jq/releases/assets/453012782"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[[tools.just]]
|
||||
version = "1.58.0"
|
||||
backend = "aqua:casey/just"
|
||||
|
||||
[tools.just."platforms.linux-arm64"]
|
||||
checksum = "sha256:748237128c4c40cbdabc65e841d05ceba13cc23a91eaba395495894c1d9764df"
|
||||
url = "https://github.com/casey/just/releases/download/1.58.0/just-1.58.0-aarch64-unknown-linux-musl.tar.gz"
|
||||
url_api = "https://api.github.com/repos/casey/just/releases/assets/500510099"
|
||||
|
||||
[tools.just."platforms.linux-x64"]
|
||||
checksum = "sha256:4a5cc2f53e6f0f8c59092a6cc38291eb729d46a7dd95d3ae582008881b84931d"
|
||||
url = "https://github.com/casey/just/releases/download/1.58.0/just-1.58.0-x86_64-unknown-linux-musl.tar.gz"
|
||||
url_api = "https://api.github.com/repos/casey/just/releases/assets/500509978"
|
||||
|
||||
[tools.just."platforms.macos-arm64"]
|
||||
checksum = "sha256:50ae3e996c974a0bf32ea7d10f495070df33f1b43e0616b2769e3d4821ed8f48"
|
||||
url = "https://github.com/casey/just/releases/download/1.58.0/just-1.58.0-aarch64-apple-darwin.tar.gz"
|
||||
url_api = "https://api.github.com/repos/casey/just/releases/assets/500509965"
|
||||
|
||||
[tools.just."platforms.macos-x64"]
|
||||
checksum = "sha256:9a09cfef66aaa79da58203970103a0684307716caaabd3e9844cacc4dc0f4023"
|
||||
url = "https://github.com/casey/just/releases/download/1.58.0/just-1.58.0-x86_64-apple-darwin.tar.gz"
|
||||
url_api = "https://api.github.com/repos/casey/just/releases/assets/500510084"
|
||||
|
||||
[[tools.kubeconform]]
|
||||
version = "0.8.0"
|
||||
backend = "aqua:yannh/kubeconform"
|
||||
|
||||
[tools.kubeconform."platforms.linux-arm64"]
|
||||
checksum = "sha256:1f53fc8e81258197a35e8603054162a5af1de8c5af13746c71ab680d9534ed87"
|
||||
url = "https://github.com/yannh/kubeconform/releases/download/v0.8.0/kubeconform-linux-arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/yannh/kubeconform/releases/assets/438612685"
|
||||
|
||||
[tools.kubeconform."platforms.linux-x64"]
|
||||
checksum = "sha256:9bc2bffbf71f261128533edaf912153948b7ff238f9a531ae6d34466ec287883"
|
||||
url = "https://github.com/yannh/kubeconform/releases/download/v0.8.0/kubeconform-linux-amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/yannh/kubeconform/releases/assets/438612666"
|
||||
|
||||
[tools.kubeconform."platforms.macos-arm64"]
|
||||
checksum = "sha256:f84f4dfbebf4a6b0b230385fa065a39ea35e02608c2b50d025dcf64775a69d67"
|
||||
url = "https://github.com/yannh/kubeconform/releases/download/v0.8.0/kubeconform-darwin-arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/yannh/kubeconform/releases/assets/438612680"
|
||||
|
||||
[tools.kubeconform."platforms.macos-x64"]
|
||||
checksum = "sha256:71dbc87ac9f24099a62b93570e65aa06312ba6ac8aea63b7f86e9d999edf5a92"
|
||||
url = "https://github.com/yannh/kubeconform/releases/download/v0.8.0/kubeconform-darwin-amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/yannh/kubeconform/releases/assets/438612679"
|
||||
|
||||
[[tools.kubectl]]
|
||||
version = "1.37.0"
|
||||
backend = "aqua:kubernetes/kubernetes/kubectl"
|
||||
|
||||
[tools.kubectl."platforms.linux-arm64"]
|
||||
checksum = "sha256:922df28df248cc00a9e025f947704f1d1482de64ece54cfe57e61f19eaf1eef3"
|
||||
url = "https://dl.k8s.io/v1.37.0/bin/linux/arm64/kubectl"
|
||||
|
||||
[tools.kubectl."platforms.linux-x64"]
|
||||
checksum = "sha256:6129359f4e1f3848a5572ccb0b26cf28b8ca08cef38c95a765b2f64a2c961a2f"
|
||||
url = "https://dl.k8s.io/v1.37.0/bin/linux/amd64/kubectl"
|
||||
|
||||
[tools.kubectl."platforms.macos-arm64"]
|
||||
checksum = "sha256:583beedaebe422e71d3f1a96acef8b1fef86ea2f09a45ad01aa6c9ce287c1380"
|
||||
url = "https://dl.k8s.io/v1.37.0/bin/darwin/arm64/kubectl"
|
||||
|
||||
[tools.kubectl."platforms.macos-x64"]
|
||||
checksum = "sha256:d5276c0f4fde77fc446070290f345944a7f1fda153df6b960e5fde93b7a9bccd"
|
||||
url = "https://dl.k8s.io/v1.37.0/bin/darwin/amd64/kubectl"
|
||||
|
||||
[[tools.kustomize]]
|
||||
version = "5.8.1"
|
||||
backend = "aqua:kubernetes-sigs/kustomize"
|
||||
|
||||
[tools.kustomize."platforms.linux-arm64"]
|
||||
checksum = "sha256:0953ea3e476f66d6ddfcd911d750f5167b9365aa9491b2326398e289fef2c142"
|
||||
url = "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v5.8.1/kustomize_v5.8.1_linux_arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/kubernetes-sigs/kustomize/releases/assets/353160971"
|
||||
|
||||
[tools.kustomize."platforms.linux-x64"]
|
||||
checksum = "sha256:029a7f0f4e1932c52a0476cf02a0fd855c0bb85694b82c338fc648dcb53a819d"
|
||||
url = "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v5.8.1/kustomize_v5.8.1_linux_amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/kubernetes-sigs/kustomize/releases/assets/353160972"
|
||||
|
||||
[tools.kustomize."platforms.macos-arm64"]
|
||||
checksum = "sha256:8886f8a78474e608cc81234f729fda188a9767da23e28925802f00ece2bab288"
|
||||
url = "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v5.8.1/kustomize_v5.8.1_darwin_arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/kubernetes-sigs/kustomize/releases/assets/353160975"
|
||||
|
||||
[tools.kustomize."platforms.macos-x64"]
|
||||
checksum = "sha256:ee7cf0c1e3592aa7bb66ba82b359933a95e7f2e0b36e5f53ed0a4535b017f2f8"
|
||||
url = "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v5.8.1/kustomize_v5.8.1_darwin_amd64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/kubernetes-sigs/kustomize/releases/assets/353160974"
|
||||
|
||||
[[tools.lefthook]]
|
||||
version = "2.1.12"
|
||||
backend = "aqua:evilmartians/lefthook"
|
||||
|
||||
[tools.lefthook."platforms.linux-arm64"]
|
||||
checksum = "sha256:d96ac16753f6d3d69b92098621c2a2427522a9c2c3e78cd7c9b4a9790b0b8f17"
|
||||
url = "https://github.com/evilmartians/lefthook/releases/download/v2.1.12/lefthook_2.1.12_Linux_aarch64.gz"
|
||||
url_api = "https://api.github.com/repos/evilmartians/lefthook/releases/assets/533627819"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.lefthook."platforms.linux-x64"]
|
||||
checksum = "sha256:dad908593c859d139b886c14913a44401d95288aa7642d9bdf6f3bd36bd788ff"
|
||||
url = "https://github.com/evilmartians/lefthook/releases/download/v2.1.12/lefthook_2.1.12_Linux_x86_64.gz"
|
||||
url_api = "https://api.github.com/repos/evilmartians/lefthook/releases/assets/533627780"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.lefthook."platforms.macos-arm64"]
|
||||
checksum = "sha256:f23eac328ca50c7b775dc24dc275cdeacb65e16cf8bd1745d18e3c563f704f94"
|
||||
url = "https://github.com/evilmartians/lefthook/releases/download/v2.1.12/lefthook_2.1.12_MacOS_arm64.gz"
|
||||
url_api = "https://api.github.com/repos/evilmartians/lefthook/releases/assets/533627758"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.lefthook."platforms.macos-x64"]
|
||||
checksum = "sha256:83c8591d338b1b789944480d9effe2807adc903cf27e4a16ab013e39d7723c31"
|
||||
url = "https://github.com/evilmartians/lefthook/releases/download/v2.1.12/lefthook_2.1.12_MacOS_x86_64.gz"
|
||||
url_api = "https://api.github.com/repos/evilmartians/lefthook/releases/assets/533627775"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[[tools.node]]
|
||||
version = "24.21.0"
|
||||
backend = "core:node"
|
||||
|
||||
[tools.node."platforms.linux-arm64"]
|
||||
checksum = "sha256:724282c3b43aec998aa9527380465b45d229e021b58035f5f4f63095eabfe5d5"
|
||||
url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-linux-arm64.tar.gz"
|
||||
|
||||
[tools.node."platforms.linux-x64"]
|
||||
checksum = "sha256:6e1db87ef58b8819e5d5402eff1536491b18edd8eb7bee5ef7897876e88dc5ff"
|
||||
url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-linux-x64.tar.gz"
|
||||
|
||||
[tools.node."platforms.macos-arm64"]
|
||||
checksum = "sha256:bed7eea5325e1108f32ce5228ddd6a5f0f08a499ee42aa7442aea583702f6057"
|
||||
url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-darwin-arm64.tar.gz"
|
||||
|
||||
[tools.node."platforms.macos-x64"]
|
||||
checksum = "sha256:1462cb3b3046b815cf8ea436d3da450ec1a9f11dac7e5a46b0ada5305d7e8097"
|
||||
url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-darwin-x64.tar.gz"
|
||||
|
||||
[[tools.oxfmt]]
|
||||
version = "0.67.0"
|
||||
backend = "npm:oxfmt"
|
||||
|
||||
[[tools.sd]]
|
||||
version = "1.1.0"
|
||||
backend = "aqua:chmln/sd"
|
||||
|
||||
[tools.sd."platforms.linux-arm64"]
|
||||
checksum = "sha256:ec8c93c0533ff21f4851d11566808d4082544baf063d9b96ea77c27e98b7cd99"
|
||||
url = "https://github.com/chmln/sd/releases/download/v1.1.0/sd-v1.1.0-aarch64-unknown-linux-musl.tar.gz"
|
||||
url_api = "https://api.github.com/repos/chmln/sd/releases/assets/362325535"
|
||||
|
||||
[tools.sd."platforms.linux-x64"]
|
||||
checksum = "sha256:3613eca74cd686739bb5a6d68319aa56c747e7315274d02323a2ca2b1c5d82d2"
|
||||
url = "https://github.com/chmln/sd/releases/download/v1.1.0/sd-v1.1.0-x86_64-unknown-linux-gnu.tar.gz"
|
||||
url_api = "https://api.github.com/repos/chmln/sd/releases/assets/362325296"
|
||||
|
||||
[tools.sd."platforms.macos-arm64"]
|
||||
checksum = "sha256:4bd3c09226376ca0a1d69589c91e86276fae36c5fbaaee669afce583f6682030"
|
||||
url = "https://github.com/chmln/sd/releases/download/v1.1.0/sd-v1.1.0-aarch64-apple-darwin.tar.gz"
|
||||
url_api = "https://api.github.com/repos/chmln/sd/releases/assets/362325451"
|
||||
|
||||
[tools.sd."platforms.macos-x64"]
|
||||
checksum = "sha256:1fca1e9c91813a8aac6821063c923107ba0f66a83309e095edcd3b202f67f97e"
|
||||
url = "https://github.com/chmln/sd/releases/download/v1.1.0/sd-v1.1.0-x86_64-apple-darwin.tar.gz"
|
||||
url_api = "https://api.github.com/repos/chmln/sd/releases/assets/362325430"
|
||||
|
||||
[[tools.sops]]
|
||||
version = "3.13.3"
|
||||
backend = "aqua:getsops/sops"
|
||||
|
||||
[tools.sops."platforms.linux-arm64"]
|
||||
checksum = "sha256:53b0abacd38ef1b12a66d6c100956691b9cefce018d91f81e73ddf7438b94d77"
|
||||
url = "https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.linux.arm64"
|
||||
url_api = "https://api.github.com/repos/getsops/sops/releases/assets/486808229"
|
||||
|
||||
[tools.sops."platforms.linux-arm64".provenance.slsa]
|
||||
url = "https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.intoto.jsonl"
|
||||
|
||||
[tools.sops."platforms.linux-x64"]
|
||||
checksum = "sha256:e5bec3346a873ae91d871550f3e698c1aad962aff462a080e40f25fde17fef6b"
|
||||
url = "https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.linux.amd64"
|
||||
url_api = "https://api.github.com/repos/getsops/sops/releases/assets/486808201"
|
||||
|
||||
[tools.sops."platforms.linux-x64".provenance.slsa]
|
||||
url = "https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.intoto.jsonl"
|
||||
|
||||
[tools.sops."platforms.macos-arm64"]
|
||||
checksum = "sha256:b97c0d434aab577dc40310e8d22ff9e45eef4c80638ab978daae9b4681c59286"
|
||||
url = "https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.darwin.arm64"
|
||||
url_api = "https://api.github.com/repos/getsops/sops/releases/assets/486808198"
|
||||
|
||||
[tools.sops."platforms.macos-arm64".provenance.slsa]
|
||||
url = "https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.intoto.jsonl"
|
||||
|
||||
[tools.sops."platforms.macos-x64"]
|
||||
checksum = "sha256:42162d5cef10b74fcf80a045a70e658d7ce6e63d6ea1be6f347e44015714468d"
|
||||
url = "https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.darwin.amd64"
|
||||
url_api = "https://api.github.com/repos/getsops/sops/releases/assets/486808228"
|
||||
|
||||
[tools.sops."platforms.macos-x64".provenance.slsa]
|
||||
url = "https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.intoto.jsonl"
|
||||
|
||||
[[tools.talosctl]]
|
||||
version = "1.14.0"
|
||||
backend = "aqua:siderolabs/talos"
|
||||
|
||||
[tools.talosctl."platforms.linux-arm64"]
|
||||
checksum = "sha256:19615e1d0eb222de86ec2f1487e7d6e74f5171a9038e73aeacde8cc647e3d9e0"
|
||||
url = "https://github.com/siderolabs/talos/releases/download/v1.14.0/talosctl-linux-arm64"
|
||||
url_api = "https://api.github.com/repos/siderolabs/talos/releases/assets/542359013"
|
||||
provenance = "cosign"
|
||||
|
||||
[tools.talosctl."platforms.linux-x64"]
|
||||
checksum = "sha256:2c147c4a99d124c95bd5c190fe054e0b3c93495f2243fd652ebd423adb8377c7"
|
||||
url = "https://github.com/siderolabs/talos/releases/download/v1.14.0/talosctl-linux-amd64"
|
||||
url_api = "https://api.github.com/repos/siderolabs/talos/releases/assets/542359015"
|
||||
provenance = "cosign"
|
||||
|
||||
[tools.talosctl."platforms.macos-arm64"]
|
||||
checksum = "sha256:f0c65a0e970b6f23cf0160e432e7496ba93957a49f369780d4e53888ed66ef46"
|
||||
url = "https://github.com/siderolabs/talos/releases/download/v1.14.0/talosctl-darwin-arm64"
|
||||
url_api = "https://api.github.com/repos/siderolabs/talos/releases/assets/542359001"
|
||||
provenance = "cosign"
|
||||
|
||||
[tools.talosctl."platforms.macos-x64"]
|
||||
checksum = "sha256:6563baa43774ef5c351e0d9f2fd720941c482da01fe3aed53ee9644b552453c5"
|
||||
url = "https://github.com/siderolabs/talos/releases/download/v1.14.0/talosctl-darwin-amd64"
|
||||
url_api = "https://api.github.com/repos/siderolabs/talos/releases/assets/542359004"
|
||||
provenance = "cosign"
|
||||
|
||||
[[tools.taplo]]
|
||||
version = "0.10.0"
|
||||
backend = "aqua:tamasfe/taplo"
|
||||
|
||||
[tools.taplo."platforms.linux-arm64"]
|
||||
url = "https://github.com/tamasfe/taplo/releases/download/0.10.0/taplo-linux-aarch64.gz"
|
||||
url_api = "https://api.github.com/repos/tamasfe/taplo/releases/assets/257322597"
|
||||
|
||||
[tools.taplo."platforms.linux-x64"]
|
||||
url = "https://github.com/tamasfe/taplo/releases/download/0.10.0/taplo-linux-x86_64.gz"
|
||||
url_api = "https://api.github.com/repos/tamasfe/taplo/releases/assets/257322600"
|
||||
|
||||
[tools.taplo."platforms.macos-arm64"]
|
||||
url = "https://github.com/tamasfe/taplo/releases/download/0.10.0/taplo-darwin-aarch64.gz"
|
||||
url_api = "https://api.github.com/repos/tamasfe/taplo/releases/assets/257323110"
|
||||
|
||||
[tools.taplo."platforms.macos-x64"]
|
||||
url = "https://github.com/tamasfe/taplo/releases/download/0.10.0/taplo-darwin-x86_64.gz"
|
||||
url_api = "https://api.github.com/repos/tamasfe/taplo/releases/assets/257323116"
|
||||
|
||||
[[tools.uv]]
|
||||
version = "0.12.13"
|
||||
backend = "aqua:astral-sh/uv"
|
||||
|
||||
[tools.uv."platforms.linux-arm64"]
|
||||
checksum = "sha256:2eaa5d94f5db7b3a1a092156b9420459e42ab0217d917fe74a876309cef9b5e9"
|
||||
url = "https://github.com/astral-sh/uv/releases/download/0.12.13/uv-aarch64-unknown-linux-gnu.tar.gz"
|
||||
url_api = "https://api.github.com/repos/astral-sh/uv/releases/assets/555670186"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.uv."platforms.linux-x64"]
|
||||
checksum = "sha256:745765a3b6e360ad76743599ae5c42e9278c7edf8bbff9fc76d05bf2623a04dd"
|
||||
url = "https://github.com/astral-sh/uv/releases/download/0.12.13/uv-x86_64-unknown-linux-gnu.tar.gz"
|
||||
url_api = "https://api.github.com/repos/astral-sh/uv/releases/assets/555670375"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.uv."platforms.macos-arm64"]
|
||||
checksum = "sha256:7e6ddb9316acc00f2296c82ff4d99977870ee34b2f0ddcae9444d714db9364ed"
|
||||
url = "https://github.com/astral-sh/uv/releases/download/0.12.13/uv-aarch64-apple-darwin.tar.gz"
|
||||
url_api = "https://api.github.com/repos/astral-sh/uv/releases/assets/555670160"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.uv."platforms.macos-x64"]
|
||||
checksum = "sha256:5e287ef61cb6a9b61b3a83fef124fd143e400468a7dac794230147a810e17119"
|
||||
url = "https://github.com/astral-sh/uv/releases/download/0.12.13/uv-x86_64-apple-darwin.tar.gz"
|
||||
url_api = "https://api.github.com/repos/astral-sh/uv/releases/assets/555670348"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[[tools.yq]]
|
||||
version = "4.53.6"
|
||||
backend = "aqua:mikefarah/yq"
|
||||
|
||||
[tools.yq."platforms.linux-arm64"]
|
||||
checksum = "sha256:88a1016bc1d657375a35864e4f44b6f333df8ff97b559f51bba0adcb2169df09"
|
||||
url = "https://github.com/mikefarah/yq/releases/download/v4.53.6/yq_linux_arm64"
|
||||
url_api = "https://api.github.com/repos/mikefarah/yq/releases/assets/522028007"
|
||||
provenance = "cosign"
|
||||
|
||||
[tools.yq."platforms.linux-x64"]
|
||||
checksum = "sha256:c5f056448f973ae7d39b5401949648a78f2dc1947d6a8eb65be60d5c504b9385"
|
||||
url = "https://github.com/mikefarah/yq/releases/download/v4.53.6/yq_linux_amd64"
|
||||
url_api = "https://api.github.com/repos/mikefarah/yq/releases/assets/522028022"
|
||||
provenance = "cosign"
|
||||
|
||||
[tools.yq."platforms.macos-arm64"]
|
||||
checksum = "sha256:cceb0b8d71ea5294334121f8429f33f92b920e7217d904a2f9f35443968ac424"
|
||||
url = "https://github.com/mikefarah/yq/releases/download/v4.53.6/yq_darwin_arm64"
|
||||
url_api = "https://api.github.com/repos/mikefarah/yq/releases/assets/522028033"
|
||||
provenance = "cosign"
|
||||
|
||||
[tools.yq."platforms.macos-x64"]
|
||||
checksum = "sha256:caa513cb04f3804b34d4752f0e0d7904fecb9e7cf1d34081289f83259319a7f6"
|
||||
url = "https://github.com/mikefarah/yq/releases/download/v4.53.6/yq_darwin_amd64"
|
||||
url_api = "https://api.github.com/repos/mikefarah/yq/releases/assets/522028031"
|
||||
provenance = "cosign"
|
||||
|
||||
[[tools.zizmor]]
|
||||
version = "1.30.1"
|
||||
backend = "aqua:zizmorcore/zizmor"
|
||||
|
||||
[tools.zizmor."platforms.linux-arm64"]
|
||||
checksum = "sha256:7ff1dce33bdd18fd2a4affe63bdd47efcccca97b2cec1c1863ec26e9e2647540"
|
||||
url = "https://github.com/zizmorcore/zizmor/releases/download/v1.30.1/zizmor-aarch64-unknown-linux-gnu.tar.gz"
|
||||
url_api = "https://api.github.com/repos/zizmorcore/zizmor/releases/assets/552067643"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.zizmor."platforms.linux-x64"]
|
||||
checksum = "sha256:e65324f4430c2717591937edcec90ccbefaf14c174f8ec9415e03ca875b46e1a"
|
||||
url = "https://github.com/zizmorcore/zizmor/releases/download/v1.30.1/zizmor-x86_64-unknown-linux-gnu.tar.gz"
|
||||
url_api = "https://api.github.com/repos/zizmorcore/zizmor/releases/assets/552067642"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.zizmor."platforms.macos-arm64"]
|
||||
checksum = "sha256:e28d22b087f9ebb8d99da6e740d348c930f559961c7c3f12badda54f882195a2"
|
||||
url = "https://github.com/zizmorcore/zizmor/releases/download/v1.30.1/zizmor-aarch64-apple-darwin.tar.gz"
|
||||
url_api = "https://api.github.com/repos/zizmorcore/zizmor/releases/assets/552067640"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.zizmor."platforms.macos-x64"]
|
||||
checksum = "sha256:10e6b18b11ea07e515a16f0f0518c7b07527bc9977c1fd5698181ce7f3554202"
|
||||
url = "https://github.com/zizmorcore/zizmor/releases/download/v1.30.1/zizmor-x86_64-apple-darwin.tar.gz"
|
||||
url_api = "https://api.github.com/repos/zizmorcore/zizmor/releases/assets/552067641"
|
||||
provenance = "github-attestations"
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"printWidth": 100
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
{
|
||||
$schema: "https://docs.renovatebot.com/renovate-schema.json",
|
||||
extends: ["github>home-operations/renovate-presets#8.1.0"],
|
||||
schedule: ["every weekend"],
|
||||
lockFileMaintenance: {
|
||||
enabled: true,
|
||||
schedule: ["before 3am on the first day of the month"],
|
||||
commitMessageExtra: "({{manager}})",
|
||||
additionalBranchPrefix: "{{manager}}-",
|
||||
},
|
||||
mise: {
|
||||
managerFilePatterns: ["/^\\.mise/conf\\.d/[^/]+\\.toml$/"],
|
||||
},
|
||||
packageRules: [
|
||||
{
|
||||
description: "GitHub Actions Group",
|
||||
matchManagers: ["github-actions"],
|
||||
groupName: "github-actions",
|
||||
minimumReleaseAge: "3 days",
|
||||
},
|
||||
{
|
||||
description: "Mise Tools Group",
|
||||
matchManagers: ["mise"],
|
||||
groupName: "mise tools",
|
||||
minimumReleaseAge: "3 days",
|
||||
},
|
||||
{
|
||||
description: "Talos Group",
|
||||
groupName: "talos",
|
||||
matchPackageNames: ["siderolabs/talos"],
|
||||
group: {
|
||||
commitMessageTopic: "{{{groupName}}} group",
|
||||
},
|
||||
minimumGroupSize: 2,
|
||||
},
|
||||
{
|
||||
description: "Flux Operator Group",
|
||||
groupName: "flux-operator",
|
||||
matchDatasources: ["docker"],
|
||||
matchPackageNames: ["/flux-operator/", "/flux-instance/"],
|
||||
group: {
|
||||
commitMessageTopic: "{{{groupName}}} group",
|
||||
},
|
||||
minimumGroupSize: 2,
|
||||
},
|
||||
{
|
||||
matchUpdateTypes: ["major"],
|
||||
addLabels: ["type/major"],
|
||||
},
|
||||
{
|
||||
matchUpdateTypes: ["minor"],
|
||||
addLabels: ["type/minor"],
|
||||
},
|
||||
{
|
||||
matchUpdateTypes: ["patch"],
|
||||
addLabels: ["type/patch"],
|
||||
},
|
||||
{
|
||||
matchUpdateTypes: ["digest"],
|
||||
addLabels: ["type/digest"],
|
||||
},
|
||||
{
|
||||
matchDatasources: ["docker"],
|
||||
addLabels: ["renovate/container"],
|
||||
},
|
||||
{
|
||||
matchDatasources: ["helm"],
|
||||
addLabels: ["renovate/helm"],
|
||||
},
|
||||
{
|
||||
matchManagers: ["github-actions"],
|
||||
addLabels: ["renovate/github-action"],
|
||||
},
|
||||
{
|
||||
matchDatasources: ["github-releases"],
|
||||
addLabels: ["renovate/github-release"],
|
||||
},
|
||||
],
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"blueglassblock.better-json5",
|
||||
"irongeek.vscode-env",
|
||||
"redhat.vscode-yaml",
|
||||
"signageos.signageos-vscode-sops",
|
||||
"hverlin.mise-vscode"
|
||||
]
|
||||
}
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"editor.bracketPairColorization.enabled": true,
|
||||
"files.associations": {
|
||||
"**/*.json5": "json5"
|
||||
},
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"sops.defaults.ageKeyFile": "age.key",
|
||||
"vs-kubernetes": {
|
||||
"vs-kubernetes.kubeconfig": "./kubeconfig",
|
||||
"vs-kubernetes.knownKubeconfigs": [
|
||||
"./kubeconfig"
|
||||
]
|
||||
},
|
||||
"yaml.schemaStore.enable": true,
|
||||
"yaml.schemas": {
|
||||
"kubernetes": "./kubernetes/**/*.yaml"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 onedr0p
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,474 @@
|
||||
# ⛵ Cluster Template
|
||||
|
||||
Welcome to my template designed for deploying a single Kubernetes cluster. Whether you're setting up a cluster at home on bare-metal or virtual machines (VMs), this project aims to simplify the process and make Kubernetes more accessible. This template is inspired by my personal [home-ops](https://github.com/onedr0p/home-ops) repository, providing a practical starting point for anyone interested in managing their own Kubernetes environment.
|
||||
|
||||
At its core, this project leverages [makejinja](https://github.com/mirkolenz/makejinja), a powerful tool for rendering templates. By reading the [cluster.toml](./cluster.sample.toml) configuration file—validated and defaulted by [pydantic](https://docs.pydantic.dev/)—Makejinja generates the necessary configurations to deploy a Kubernetes cluster with the following features:
|
||||
|
||||
- Easy configuration through a single TOML file.
|
||||
- Compatibility with home setups, whether on physical hardware or VMs.
|
||||
- A modular and extensible approach to cluster deployment and management.
|
||||
|
||||
With this approach, you'll gain a solid foundation to build and manage your Kubernetes cluster efficiently.
|
||||
|
||||
## ✨ Features
|
||||
|
||||
A Kubernetes cluster deployed with [Talos Linux](https://github.com/siderolabs/talos) and an opinionated implementation of [Flux](https://github.com/fluxcd/flux2) syncing from the Git provider of your choice (GitHub, GitLab, Gitea, Forgejo, Codeberg or self-hosted), [sops](https://github.com/getsops/sops) to manage secrets and [cloudflared](https://github.com/cloudflare/cloudflared) to access applications external to your local network.
|
||||
|
||||
- **Required:** Some knowledge of [Containers](https://opencontainers.org/), [YAML](https://noyaml.com/), [Git](https://git-scm.com/), and a **domain**. Exposing apps to the public internet requires a **Cloudflare account**; internal-only clusters don't.
|
||||
- **Included components:** [flux](https://github.com/fluxcd/flux2), [cilium](https://github.com/cilium/cilium), [cert-manager](https://github.com/cert-manager/cert-manager), [spegel](https://github.com/spegel-org/spegel), [reloader](https://github.com/stakater/Reloader), [envoy-gateway](https://github.com/envoyproxy/gateway), [external-dns](https://github.com/kubernetes-sigs/external-dns) and [cloudflared](https://github.com/cloudflare/cloudflared).
|
||||
|
||||
**Other features include:**
|
||||
|
||||
- Dev env managed w/ [mise](https://mise.jdx.dev/)
|
||||
- Workflow automation w/ [GitHub Actions](https://github.com/features/actions)
|
||||
- Dependency automation w/ [Renovate](https://www.mend.io/renovate)
|
||||
- Flux `HelmRelease` and `Kustomization` diffs w/ [flate](https://github.com/home-operations/flate)
|
||||
|
||||
Does this sound cool to you? If so, continue to read on! 👇
|
||||
|
||||
## 🚀 Let's Go!
|
||||
|
||||
There are **6 stages** outlined below for completing this project, make sure you follow the stages in order.
|
||||
|
||||
### Stage 1: Hardware Configuration
|
||||
|
||||
For a **stable** and **high-availability** production Kubernetes cluster, hardware selection is critical. NVMe/SSDs are strongly preferred over HDDs, and **Bare Metal is strongly recommended** over virtualized platforms like Proxmox.
|
||||
|
||||
Using **enterprise NVMe or SATA SSDs on Bare Metal** (even used drives) provides the most reliable performance and rock-solid stability. Consumer **NVMe or SATA SSDs**, on the other hand, carry risks such as latency spikes, corruption, and fsync delays, particularly in multi-node setups.
|
||||
|
||||
**Proxmox with enterprise drives can work** for testing or carefully tuned production clusters, but it introduces additional layers of potential I/O contention — especially if consumer drives are used. Any **replicated storage** (e.g., Rook-Ceph, Longhorn) should always use **dedicated disks separate from control plane and etcd nodes** to ensure reliability. Worker nodes are more flexible, but risky configurations should still be avoided for stateful workloads to maintain cluster stability.
|
||||
|
||||
These guidelines provide a strong baseline, but there are always exceptions and nuances. The best way to ensure your hardware configuration works is to **test it thoroughly and benchmark performance** under realistic workloads.
|
||||
|
||||
### Stage 2: Machine Preparation
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If you have **3 or more nodes** it is recommended to make 3 of them controller nodes for a highly available control plane. This project configures **all nodes** to be able to run workloads. **Worker nodes** are therefore **optional**.
|
||||
>
|
||||
> **Minimum system requirements**
|
||||
>
|
||||
> | Role | Cores | Memory | System Disk |
|
||||
> | -------------- | ----- | ------ | -------------- |
|
||||
> | Control/Worker | 4 | 16GB | 256GB SSD/NVMe |
|
||||
|
||||
1. Head over to the [Talos Linux Image Factory](https://factory.talos.dev) and follow the instructions. Be sure to only choose the **bare-minimum system extensions** as some might require additional configuration and prevent Talos from booting without it. Depending on your CPU start with the Intel/AMD system extensions (`i915`, `intel-ucode` & `mei` **or** `amdgpu` & `amd-ucode`), you can always add system extensions after Talos is installed and working.
|
||||
|
||||
2. This will eventually lead you to download a Talos Linux ISO (or for SBCs a RAW) image. Make sure to note the **schematic ID** you will need this later on.
|
||||
|
||||
3. Flash the Talos ISO or RAW image to a USB drive and boot from it on your nodes.
|
||||
|
||||
4. Verify with `nmap` that your nodes are available on the network. (Replace `192.168.1.0/24` with the network your nodes are on.)
|
||||
|
||||
```sh
|
||||
nmap -Pn -n -p 50000 192.168.1.0/24 -vv | grep 'Discovered'
|
||||
```
|
||||
|
||||
### Stage 3: Local Workstation
|
||||
|
||||
> [!TIP]
|
||||
> It is recommended to set the visibility of your repository to `Public` so you can easily request help if you get stuck.
|
||||
|
||||
1. Create a new repository by clicking the green `Use this template` button at the top of this page, then clone the new repo you just created and `cd` into it. Alternatively you can use the [GitHub CLI](https://cli.github.com/) ...
|
||||
|
||||
```sh
|
||||
export REPONAME="home-ops"
|
||||
gh repo create $REPONAME --template onedr0p/cluster-template --public --clone
|
||||
cd $REPONAME
|
||||
```
|
||||
|
||||
📍 _**Not using GitHub?** Any Git provider works (GitLab, Gitea, Forgejo, Codeberg or self-hosted). Create an empty repository on your provider, download this template with `git clone --depth 1 https://github.com/onedr0p/cluster-template`, re-initialize it with `git init` and push it to your repository._
|
||||
|
||||
2. **Install** the [Mise CLI](https://mise.jdx.dev/getting-started.html#installing-mise-cli) on your local workstation.
|
||||
|
||||
3. **Activate** Mise in your shell by following the [activation guide](https://mise.jdx.dev/getting-started.html#activate-mise).
|
||||
|
||||
4. Use `mise` to install the **required** CLI tools:
|
||||
|
||||
```sh
|
||||
mise trust
|
||||
mise install
|
||||
```
|
||||
|
||||
📍 _**Having trouble installing the tools?** Try unsetting the `GITHUB_TOKEN` env var and then run these commands again_
|
||||
|
||||
📍 _**Platforms:** `.mise/mise.lock` pins tool downloads for the platforms listed under `lockfile_platforms` in `.mise/config.toml`: Linux and macOS on amd64 and arm64 (`linux-x64`, `linux-arm64`, `macos-x64`, `macos-arm64`). If you also need musl (e.g. Alpine) or Windows, add the platform to that list (`linux-x64-musl`, `linux-arm64-musl`, `windows-x64`), run `mise lock`, and commit both files. Your own platform is always locked, even when it is not in the list._
|
||||
|
||||
5. Logout of the GitHub Container Registry as this may cause authorization problems in future steps when using the public registry:
|
||||
|
||||
```sh
|
||||
docker logout ghcr.io
|
||||
helm registry logout ghcr.io
|
||||
```
|
||||
|
||||
### Stage 4: Cloudflare configuration
|
||||
|
||||
> [!TIP]
|
||||
> **Internal-only cluster?** Set `provider = "none"` under `[dns]` in `cluster.toml` and skip this stage entirely: no Cloudflare account, API token, or `cloudflare-tunnel.json` is needed. Nothing is exposed to the internet, apps are reachable on your LAN via the internal gateway, and the wildcard certificate is issued by an in-cluster self-signed CA instead of Let's Encrypt.
|
||||
|
||||
> [!WARNING]
|
||||
> If any of the commands fail with `command not found` or `unknown command` it means `mise` is either not installed, activated or it could be configured incorrectly.
|
||||
|
||||
1. Create a Cloudflare API token for use with cloudflared and external-dns by reviewing the official [documentation](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) and following the instructions below.
|
||||
|
||||
- Click the blue `Use template` button for the `Edit zone DNS` template.
|
||||
- Name your token `kubernetes`
|
||||
- Under `Permissions`, click `+ Add More` and add permissions `Zone - DNS - Edit` and `Account - Cloudflare Tunnel - Read`
|
||||
- Limit the permissions to a specific account and/or zone resources and then click `Continue to Summary` and then `Create Token`.
|
||||
- **Save this token somewhere safe**, you will need it later on.
|
||||
|
||||
2. Create the Cloudflare Tunnel:
|
||||
|
||||
```sh
|
||||
cloudflared tunnel login
|
||||
cloudflared tunnel create --credentials-file cloudflare-tunnel.json kubernetes
|
||||
```
|
||||
|
||||
📍 _**Prefer port-forwarding over a tunnel?** Set `mode = "direct"` under `[ingress]` in `cluster.toml` and skip this step: no `cloudflare-tunnel.json` is needed. Instead, forward TCP 443 (and optionally 80) on your router to the `gateways.external` IP, and create an `external.<domain>` DNS record yourself pointing at your WAN address (an A record, or a CNAME to a DDNS hostname). Per-app records are still published automatically._
|
||||
|
||||
### Stage 5: Cluster configuration
|
||||
|
||||
1. Generate the config files from the sample files:
|
||||
|
||||
```sh
|
||||
just init
|
||||
```
|
||||
|
||||
2. Fill out the `cluster.toml` configuration file using the comments in it as a guide. Editors with TOML schema support (VS Code's Even Better TOML, taplo in Neovim) pick up the `#:schema` directive at the top of the file and provide completion and inline validation.
|
||||
|
||||
3. Template out the kubernetes and talos configuration files, if any issues come up be sure to read the error and adjust your config files accordingly.
|
||||
|
||||
```sh
|
||||
just configure
|
||||
```
|
||||
|
||||
4. Push your changes to git:
|
||||
|
||||
📍 _**Verify** all the `./bootstrap/**/*.sops.*`, `./kubernetes/**/*.sops.*` and `./talos/secrets.sops.yaml` files are **encrypted** with SOPS_
|
||||
|
||||
```sh
|
||||
git add -A
|
||||
git commit -m "chore: initial commit :rocket:"
|
||||
git push
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> Using a **private repository** (an `ssh://` URL in `cluster.toml`)? Make sure to paste the public key from `deploy.key.pub` into the deploy keys section of your repository settings (GitHub: `Settings/Deploy keys`, GitLab: `Settings/Repository/Deploy keys`, Gitea/Forgejo: `Settings/Deploy keys`). This will make sure Flux has read/write access to your repository.
|
||||
|
||||
### Stage 6: Bootstrap Talos, Kubernetes, and Flux
|
||||
|
||||
> [!WARNING]
|
||||
> It might take a while for the cluster to be setup (10+ minutes is normal). During which time you will see a variety of error messages like: "couldn't get current server API group list," "error: no matching resources found", etc. 'Ready' will remain "False" as no CNI is deployed yet. **This is normal.** If this step gets interrupted, e.g. by pressing <kbd>Ctrl</kbd> + <kbd>C</kbd>, you likely will need to [reset the cluster](#-reset) before trying again
|
||||
|
||||
1. Install Talos:
|
||||
|
||||
```sh
|
||||
just bootstrap talos
|
||||
```
|
||||
|
||||
2. Install cilium, coredns, spegel, flux and sync the cluster to the repository state:
|
||||
|
||||
```sh
|
||||
just bootstrap apps
|
||||
```
|
||||
|
||||
3. Watch the rollout of your cluster happen:
|
||||
|
||||
```sh
|
||||
kubectl get pods --all-namespaces --watch
|
||||
```
|
||||
|
||||
## 📣 Post installation
|
||||
|
||||
### ✅ Verifications
|
||||
|
||||
1. Check the status of Cilium:
|
||||
|
||||
```sh
|
||||
kubectl -n kube-system exec ds/cilium --container cilium-agent -- cilium status
|
||||
```
|
||||
|
||||
2. Check the status of Flux and if the Flux resources are up-to-date and in a ready state:
|
||||
|
||||
📍 _Run `just kube reconcile` to force Flux to sync your Git repository state_
|
||||
|
||||
```sh
|
||||
flux check
|
||||
flux get sources git flux-system
|
||||
flux get ks -A
|
||||
flux get hr -A
|
||||
```
|
||||
|
||||
3. Check TCP connectivity to both the internal and external gateways:
|
||||
|
||||
📍 _The variables are only placeholders, replace them with your actual values_
|
||||
|
||||
```sh
|
||||
nmap -Pn -n -p 443 ${gateways_internal} ${gateways_external} -vv
|
||||
```
|
||||
|
||||
4. Check you can resolve DNS for `echo`, this should resolve to `${gateways_external}`:
|
||||
|
||||
📍 _The variables are only placeholders, replace them with your actual values_
|
||||
|
||||
```sh
|
||||
dig @${gateways_dns} echo.${cloudflare_domain}
|
||||
```
|
||||
|
||||
5. Check the status of your wildcard `Certificate`:
|
||||
|
||||
```sh
|
||||
kubectl -n network describe certificates
|
||||
```
|
||||
|
||||
### 🌐 Public DNS
|
||||
|
||||
> [!TIP]
|
||||
> Use the `envoy-external` gateway on `HTTPRoutes` to make applications public to the internet. These are also accessible on your private network once you set up split DNS.
|
||||
|
||||
The `external-dns` application created in the `network` namespace will handle creating public DNS records. By default, `echo` and the `flux-webhook` are the only subdomains reachable from the public internet. In order to make additional applications public you must **set the correct gateway** like in the HelmRelease for `echo`.
|
||||
|
||||
### 🏠 Home DNS
|
||||
|
||||
> [!TIP]
|
||||
> Use the `envoy-internal` gateway on `HTTPRoutes` to make applications private to your network. If you're having trouble with internal DNS resolution check out [this](https://github.com/onedr0p/cluster-template/discussions/719) GitHub discussion.
|
||||
|
||||
`k8s_gateway` will provide DNS resolution to external Kubernetes resources (i.e. points of entry to the cluster) from any device that uses your home DNS server. For this to work, your home DNS server must be configured to forward DNS queries for `${cloudflare_domain}` to `${gateways_dns}` instead of the upstream DNS server(s) it normally uses. This is a form of **split DNS** (aka split-horizon DNS / conditional forwarding).
|
||||
|
||||
_... Nothing working? That is expected, this is DNS after all!_
|
||||
|
||||
### 🪝 Git Webhook
|
||||
|
||||
By default Flux will periodically check your git repository for changes. In-order to have Flux reconcile on `git push` you must configure your Git provider to send `push` events to Flux.
|
||||
|
||||
📍 _Don't want a webhook, or your Git provider can't reach the cluster? Set `webhook_provider = "none"` in `cluster.toml` and skip this section; Flux will keep polling on an interval._
|
||||
|
||||
1. Obtain the webhook path:
|
||||
|
||||
📍 _Hook id and path should look like `/hook/12ebd1e363c641dc3c2e430ecf3cee2b3c7a5ac9e1234506f6f5f3ce1230e123`_
|
||||
|
||||
```sh
|
||||
kubectl -n flux-system get receiver flux-webhook --output=jsonpath='{.status.webhookPath}'
|
||||
```
|
||||
|
||||
2. Piece together the full URL with the webhook path appended:
|
||||
|
||||
```text
|
||||
https://flux-webhook.${cloudflare_domain}/hook/12ebd1e363c641dc3c2e430ecf3cee2b3c7a5ac9e1234506f6f5f3ce1230e123
|
||||
```
|
||||
|
||||
3. Navigate to your repository settings and add a webhook with that URL and the secret token from `flux-webhook-token.txt`:
|
||||
|
||||
- **GitHub**: under "Settings/Webhooks" press the "Add webhook" button. Fill in the webhook URL, paste the token as the secret, Content type: `application/json`, Events: Choose Just the push event, and save.
|
||||
- **GitLab**: under "Settings/Webhooks" fill in the webhook URL, paste the token as the secret token, check the push events trigger, and save. Also set `webhook_provider = "gitlab"` in `cluster.toml`.
|
||||
- **Gitea/Forgejo**: under "Settings/Webhooks" add a **Gitea/Forgejo** webhook with the webhook URL, method `POST`, content type `application/json`, paste the token as the secret, trigger on push events, and save. Keep the default `webhook_provider = "github"` since these providers emulate GitHub webhooks.
|
||||
|
||||
## 💥 Reset
|
||||
|
||||
> [!CAUTION]
|
||||
> **Resetting** the cluster **multiple times in a short period of time** could lead to being **rate limited by DockerHub or Let's Encrypt**.
|
||||
|
||||
There might be a situation where you want to destroy your Kubernetes cluster. The following command will reset your nodes back to maintenance mode.
|
||||
|
||||
```sh
|
||||
just talos reset
|
||||
```
|
||||
|
||||
## 🛠️ Talos and Kubernetes Maintenance
|
||||
|
||||
### ⚙️ Updating Talos node configuration
|
||||
|
||||
> [!TIP]
|
||||
> Ensure you have updated `topf.yaml` and any patches with your updated configuration. In some cases you **not only need to apply the configuration but also upgrade talos** to apply new configuration.
|
||||
|
||||
```sh
|
||||
# Preview the rendered machine configs (optional)
|
||||
just talos render
|
||||
# Apply the config to the node
|
||||
just talos apply-node <node>
|
||||
# e.g. just talos apply-node k8s-0
|
||||
```
|
||||
|
||||
### ⬆️ Updating Talos and Kubernetes versions
|
||||
|
||||
> [!TIP]
|
||||
> Ensure the `talosVersion` and `kubernetesVersion` in `topf.yaml` are up-to-date with the version you wish to upgrade to.
|
||||
|
||||
```sh
|
||||
# Upgrade talos on a node
|
||||
just talos upgrade-node <node>
|
||||
# e.g. just talos upgrade-node k8s-0
|
||||
```
|
||||
|
||||
```sh
|
||||
# Upgrade cluster to a newer Kubernetes version
|
||||
just talos upgrade-k8s
|
||||
```
|
||||
|
||||
### ➕ Adding a node to your cluster
|
||||
|
||||
At some point you might want to expand your cluster to run more workloads and/or improve the reliability of your cluster. Keep in mind it is recommended to have an **odd number** of control plane nodes for quorum reasons.
|
||||
|
||||
You don't need to re-bootstrap the cluster to add new nodes. Follow these steps:
|
||||
|
||||
1. **Prepare the new node**: Review the [Stage 2: Machine Preparation](#stage-2-machine-preparation) section and boot your new node into maintenance mode.
|
||||
|
||||
2. **Get the node information**: While the node is in maintenance mode, retrieve the disk and MAC address information needed for configuration:
|
||||
|
||||
```sh
|
||||
talosctl get disks -n <ip> --insecure
|
||||
talosctl get links -n <ip> --insecure
|
||||
```
|
||||
|
||||
3. **Update the configuration**: Read the documentation for [topf](https://postfinance.github.io/topf/) and extend `topf.yaml` (and any `node/<hostname>/` patches) manually with the new node information (including the disk and MAC address from step 2).
|
||||
|
||||
4. **Apply the configuration**:
|
||||
|
||||
```sh
|
||||
# Preview the rendered machine configs (optional)
|
||||
just talos render
|
||||
|
||||
# Apply the configuration to the node
|
||||
just talos apply-node <node>
|
||||
# e.g. just talos apply-node k8s-3
|
||||
```
|
||||
|
||||
The node should join the cluster automatically and workloads will be scheduled once they report as ready.
|
||||
|
||||
## 🤖 Renovate
|
||||
|
||||
[Renovate](https://www.mend.io/renovate) is a tool that automates dependency management. It is designed to scan your repository around the clock and open PRs for out-of-date dependencies it finds. Common dependencies it can discover are Helm charts, container images, GitHub Actions and more! In most cases merging a PR will cause Flux to apply the update to your cluster.
|
||||
|
||||
To enable Renovate on GitHub, click the 'Configure' button over at their [Github app page](https://github.com/apps/renovate) and select your repository. On other Git providers you can [self-host Renovate](https://docs.renovatebot.com/getting-started/running/#self-hosting-renovate); note that fetching the shared preset in `.renovaterc.json5` requires a `GITHUB_COM_TOKEN`. Renovate creates a "Dependency Dashboard" as an issue in your repository, giving an overview of the status of all updates. The dashboard has interactive checkboxes that let you do things like advance scheduling or reattempt update PRs you closed without merging.
|
||||
|
||||
The base Renovate configuration in your repository can be viewed at [.renovaterc.json5](.renovaterc.json5). By default it is scheduled to be active with PRs every weekend, but you can [change the schedule to anything you want](https://docs.renovatebot.com/presets-schedule), or remove it if you want Renovate to open PRs immediately.
|
||||
|
||||
## 🐛 Debugging
|
||||
|
||||
Below is a general guide on trying to debug an issue with an resource or application. For example, if a workload/resource is not showing up or a pod has started but in a `CrashLoopBackOff` or `Pending` state. These steps do not include a way to fix the problem as the problem could be one of many different things.
|
||||
|
||||
1. Check if the Flux resources are up-to-date and in a ready state:
|
||||
|
||||
📍 _Run `just kube reconcile` to force Flux to sync your Git repository state_
|
||||
|
||||
```sh
|
||||
flux get sources git -A
|
||||
flux get ks -A
|
||||
flux get hr -A
|
||||
```
|
||||
|
||||
2. Do you see the pod of the workload you are debugging:
|
||||
|
||||
```sh
|
||||
kubectl -n <namespace> get pods -o wide
|
||||
```
|
||||
|
||||
3. Check the logs of the pod if it's there:
|
||||
|
||||
```sh
|
||||
kubectl -n <namespace> logs <pod-name> -f
|
||||
```
|
||||
|
||||
4. If a resource exists, try to describe it to see what problems it might have:
|
||||
|
||||
```sh
|
||||
kubectl -n <namespace> describe <resource> <name>
|
||||
```
|
||||
|
||||
5. Check the namespace events:
|
||||
|
||||
```sh
|
||||
kubectl -n <namespace> get events --sort-by='.metadata.creationTimestamp'
|
||||
```
|
||||
|
||||
Resolving problems that you have could take some tweaking of your YAML manifests in order to get things working, other times it could be a external factor like permissions on a NFS server. If you are unable to figure out your problem see the support sections below.
|
||||
|
||||
## 🧹 Tidy up
|
||||
|
||||
Once your cluster is fully configured and you no longer need to run `just configure`, it's a good idea to clean up the repository by removing the [template](./template) directory and any files related to the templating process. This will help eliminate unnecessary clutter from the upstream template repository and resolve any "duplicate registry" warnings from Renovate.
|
||||
|
||||
1. Tidy up your repository:
|
||||
|
||||
```sh
|
||||
just template tidy
|
||||
```
|
||||
|
||||
2. Push your changes to git:
|
||||
|
||||
```sh
|
||||
git add -A
|
||||
git commit -m "chore: tidy up :broom:"
|
||||
git push
|
||||
```
|
||||
|
||||
## ❔ What's next
|
||||
|
||||
There's a lot to absorb here, especially if you're new to these tools. Take some time to familiarize yourself with the tooling and understand how all the components interconnect. Dive into the documentation of the various tools included — they are a valuable resource. This shouldn't be a production environment yet, so embrace the freedom to experiment. Move fast, break things intentionally, and challenge yourself to fix them.
|
||||
|
||||
Below are some optional considerations you may want to explore.
|
||||
|
||||
### DNS
|
||||
|
||||
The template uses [k8s_gateway](https://github.com/k8s-gateway/k8s_gateway) to provide DNS for your applications, consider exploring [external-dns](https://github.com/kubernetes-sigs/external-dns) as an alternative.
|
||||
|
||||
External-DNS offers broad support for various DNS providers, including but not limited to:
|
||||
|
||||
- [Pi-hole](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/pihole.md)
|
||||
- [UniFi](https://github.com/kashalls/external-dns-unifi-webhook)
|
||||
- [Adguard Home](https://github.com/muhlba91/external-dns-provider-adguard)
|
||||
- [Bind](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/rfc2136.md)
|
||||
|
||||
This flexibility allows you to integrate seamlessly with a range of DNS solutions to suit your environment and offload DNS from your cluster to your router, or external device.
|
||||
|
||||
### Secrets
|
||||
|
||||
SOPS is an excellent tool for managing secrets in a GitOps workflow. However, it can become cumbersome when rotating secrets or maintaining a single source of truth for secret items.
|
||||
|
||||
For a more streamlined approach to those issues, consider [External Secrets](https://external-secrets.io/latest/). This tool allows you to move away from SOPs and leverage an external provider for managing your secrets. External Secrets supports a wide range of providers, from cloud-based solutions to self-hosted options.
|
||||
|
||||
### Storage
|
||||
|
||||
If your workloads require persistent storage with features like replication or connectivity to NFS, SMB, or iSCSI servers, there are several projects worth exploring:
|
||||
|
||||
- [rook-ceph](https://github.com/rook/rook) / [longhorn](https://github.com/longhorn/longhorn) / [openebs](https://github.com/openebs/openebs)
|
||||
- [democratic-csi](https://github.com/democratic-csi/democratic-csi)
|
||||
- [csi-driver-nfs](https://github.com/kubernetes-csi/csi-driver-nfs) / [csi-driver-smb](https://github.com/kubernetes-csi/csi-driver-smb)
|
||||
- [synology-csi](https://github.com/SynologyOpenSource/synology-csi)
|
||||
- [truenas-csi](https://github.com/truenas/truenas-csi) / [tns-csi](https://github.com/fenio/tns-csi)
|
||||
|
||||
These tools offer a variety of solutions to meet your persistent storage needs, whether you’re using cloud-native or self-hosted infrastructures.
|
||||
|
||||
### Community Repositories
|
||||
|
||||
Community member [@whazor](https://github.com/whazor) created [Kubesearch](https://kubesearch.dev) to allow searching Flux HelmReleases across Github and Gitlab repositories with the `kubesearch` topic.
|
||||
|
||||
## 🙋 Support
|
||||
|
||||
### Community
|
||||
|
||||
- Make a post in this repository's GitHub [Discussions](https://github.com/onedr0p/cluster-template/discussions).
|
||||
- Start a thread in the `#support` or `#cluster-template` channels in the [Home Operations](https://discord.gg/home-operations) Discord server.
|
||||
|
||||
## 📺 Media
|
||||
|
||||
Check out these videos below. If you find them helpful, a like and subscribe goes a long way!
|
||||
|
||||
<a href="https://youtube.com/watch?v=aeUKOpeoiUs">
|
||||
<img src="https://github.com/user-attachments/assets/2dab1c6f-7b27-4b94-a7ad-a6d9c5b17c78" alt="Youtube Video" width="300">
|
||||
</a>
|
||||
|
||||
<a href="https://youtube.com/watch?v=hoi2GzvJUXM">
|
||||
<img src="https://github.com/user-attachments/assets/5b939b90-0019-4515-b90c-321ffe7448cf" alt="Youtube Video" width="300">
|
||||
</a>
|
||||
|
||||
## 🙌 Related Projects
|
||||
|
||||
If this repo is too hot to handle or too cold to hold check out these following projects.
|
||||
|
||||
- [ajaykumar4/cluster-template](https://github.com/ajaykumar4/cluster-template) - _A template for deploying a Talos Kubernetes cluster including Argo for GitOps_
|
||||
- [mitchross/k3s-argocd-starter](https://github.com/mitchross/k3s-argocd-starter) - starter kit for k3s, argocd
|
||||
- [ricsanfre/pi-cluster](https://github.com/ricsanfre/pi-cluster) - _Pi Kubernetes Cluster. Homelab kubernetes cluster automated with Ansible and FluxCD_
|
||||
- [techno-tim/k3s-ansible](https://github.com/techno-tim/k3s-ansible) - _The easiest way to bootstrap a self-hosted High Availability Kubernetes cluster. A fully automated HA k3s etcd install with kube-vip, MetalLB, and more. Build. Destroy. Repeat._
|
||||
|
||||
## 🤝 Thanks
|
||||
|
||||
Big shout out to all the contributors, sponsors and everyone else who has helped on this project.
|
||||
@@ -0,0 +1,292 @@
|
||||
#:schema ./cluster.schema.json
|
||||
# =============================================================================
|
||||
# Physical LAN that your Talos nodes live on. Defines the address space
|
||||
# used for node IPs, the gateway/DNS/NTP servers nodes will use, and an
|
||||
# optional VLAN tag for switch ports that aren't natively tagged.
|
||||
# =============================================================================
|
||||
[network]
|
||||
|
||||
# The CIDR block your nodes' IPs come from. Every node's `address`, the Kube
|
||||
# API VIP, and the gateway VIPs (internal/dns/external) must all sit inside
|
||||
# this range.
|
||||
# REQUIRED. Example: "192.168.1.0/24"
|
||||
node_cidr = ""
|
||||
|
||||
# Upstream DNS servers Talos nodes use for name resolution. Defaults to
|
||||
# Cloudflare (1.1.1.1 / 1.0.0.1). Override if you run an internal resolver
|
||||
# (Pi-hole, Unbound, AdGuard) or want a different public provider.
|
||||
# OPTIONAL. Default: ["1.1.1.1", "1.0.0.1"]
|
||||
|
||||
# dns_servers = ["1.1.1.1", "1.0.0.1"]
|
||||
|
||||
# Upstream NTP servers. Defaults to Cloudflare's anycast NTP. Most homelabs
|
||||
# don't need to change this.
|
||||
# OPTIONAL. Default: ["162.159.200.1", "162.159.200.123"]
|
||||
|
||||
# ntp_servers = ["162.159.200.1", "162.159.200.123"]
|
||||
|
||||
# Default gateway IP that nodes use to reach the rest of your LAN/WAN.
|
||||
# Defaults to the first usable host in node_cidr (e.g. 192.168.1.1 for
|
||||
# 192.168.1.0/24), which is correct for most home routers. Override if your
|
||||
# router lives at a non-standard address inside the subnet.
|
||||
# OPTIONAL. Default: first IP in node_cidr
|
||||
|
||||
# default_gateway = ""
|
||||
|
||||
# 802.1Q VLAN tag to apply to the Talos node interface. Only set this if
|
||||
# your switch ports are configured as trunks (passing tagged traffic to the
|
||||
# nodes); access ports already untag VLAN traffic. Must be 1-4094.
|
||||
# REF: https://www.talos.dev/latest/advanced/advanced-networking/#vlans
|
||||
# OPTIONAL.
|
||||
|
||||
# vlan_tag = ""
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Cluster-internal control plane and overlay networks. The pod and service
|
||||
# CIDRs are in-cluster only — they don't have to be routable on your LAN
|
||||
# and never appear on the wire outside the nodes.
|
||||
# =============================================================================
|
||||
[kubernetes]
|
||||
|
||||
# CIDR Cilium hands out to pods. /16 gives ~64K pod IPs, which is well beyond
|
||||
# what a homelab needs but matches the upstream default. Must NOT overlap
|
||||
# with node_cidr or svc_cidr.
|
||||
# OPTIONAL. Default: "10.42.0.0/16"
|
||||
|
||||
# pod_cidr = "10.42.0.0/16"
|
||||
|
||||
# CIDR for ClusterIP services (the virtual IPs `kubectl get svc` shows).
|
||||
# Same /16 reasoning as pod_cidr. Must NOT overlap with node_cidr or
|
||||
# pod_cidr.
|
||||
# OPTIONAL. Default: "10.43.0.0/16"
|
||||
|
||||
# svc_cidr = "10.43.0.0/16"
|
||||
|
||||
# ClusterIP for the CoreDNS Service. Must be inside svc_cidr.
|
||||
# OPTIONAL. Default: the 10th IP in svc_cidr
|
||||
|
||||
# coredns_addr = ""
|
||||
|
||||
[kubernetes.api]
|
||||
|
||||
# Virtual IP for the Kubernetes API server. kubectl, flux, and every other
|
||||
# client connect here on port 6443. Must be an unused IP inside
|
||||
# network.node_cidr — kube-vip floats it across controller nodes.
|
||||
# REQUIRED.
|
||||
addr = ""
|
||||
|
||||
# Additional Subject Alternative Names to put on the Kube API cert. Useful
|
||||
# if you want to call the API by hostname (e.g. via a CNAME or local
|
||||
# /etc/hosts entry) instead of the raw IP.
|
||||
# OPTIONAL. Example: ["mycluster.example.com"]
|
||||
|
||||
# tls_sans = ["mycluster.example.com"]
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# LoadBalancer IPs that Cilium hands out to the cluster's edge gateways.
|
||||
# Each must be an unused address inside network.node_cidr, and all four
|
||||
# (these three plus kubernetes.api.addr) must be distinct.
|
||||
# =============================================================================
|
||||
[gateways]
|
||||
|
||||
# IP for the `envoy-internal` gateway — used by HTTPRoutes intended for
|
||||
# private/in-network access only. Most apps use this gateway by default.
|
||||
# REQUIRED.
|
||||
internal = ""
|
||||
|
||||
# IP for `k8s_gateway`, which serves DNS for cluster-managed hostnames.
|
||||
# Point your home DNS server's conditional forwarder for domain.name
|
||||
# at this IP to enable split-DNS resolution from your LAN.
|
||||
# REQUIRED.
|
||||
dns = ""
|
||||
|
||||
# IP for the `envoy-external` gateway — sits behind the ingress path (e.g.
|
||||
# the cloudflared tunnel) and handles traffic exposed to the public
|
||||
# internet. HTTPRoutes that reference this gateway become reachable via
|
||||
# your public domain.
|
||||
# REQUIRED unless ingress.mode is "none" (internal-only cluster).
|
||||
external = ""
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# The Git repo Flux will sync from. This is the single source of truth
|
||||
# for everything in your cluster — once bootstrapped, changes are made by
|
||||
# pushing to this repo. Any Git host works: GitHub, GitLab, Gitea,
|
||||
# Forgejo, Codeberg or self-hosted.
|
||||
# =============================================================================
|
||||
[repository]
|
||||
|
||||
# Full clone URL of the repository this cluster will pull from.
|
||||
# Must be the repo you cloned this template into.
|
||||
# Use `https://` if the repo is publicly readable (or `http://` for a
|
||||
# LAN-local git server). Use `ssh://git@` if it is
|
||||
# private: the template then wires up a deploy key (`deploy.key.pub`) so
|
||||
# Flux can clone over SSH; see the README for the extra setup step.
|
||||
# REQUIRED. Examples:
|
||||
# "https://github.com/onedr0p/home-ops.git"
|
||||
# "ssh://git@gitlab.com/onedr0p/home-ops.git"
|
||||
# "ssh://git@git.example.com/k8s/home-ops.git"
|
||||
url = ""
|
||||
|
||||
# Branch Flux watches. Changes pushed to this branch get reconciled into the
|
||||
# cluster.
|
||||
# OPTIONAL. Default: "main"
|
||||
|
||||
# branch = "main"
|
||||
|
||||
# Webhook payload format the Flux webhook Receiver verifies, so pushes are
|
||||
# reconciled instantly. Gitea and Forgejo emulate GitHub webhooks, so keep
|
||||
# "github" for them. Use "generic-hmac" for anything else that signs with
|
||||
# HMAC; see https://fluxcd.io/flux/components/notification/receivers/
|
||||
# Use "none" to skip the webhook entirely (e.g. your Git host cannot reach
|
||||
# the cluster); Flux then only polls on an interval.
|
||||
# OPTIONAL. Default: "github".
|
||||
# Allowed: "github" | "gitlab" | "generic-hmac" | "none"
|
||||
|
||||
# webhook_provider = "github"
|
||||
|
||||
# SSH host keys for your Git host (`ssh-keyscan -t ed25519,ecdsa,rsa <host>`
|
||||
# output). Only used with `ssh://` URLs. Keys for github.com, gitlab.com and
|
||||
# codeberg.org are bundled; REQUIRED for any other host.
|
||||
# OPTIONAL. Example:
|
||||
# known_hosts = """
|
||||
# git.example.com ssh-ed25519 AAAA...
|
||||
# """
|
||||
|
||||
# known_hosts = ""
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# The domain your cluster's hostnames live under. Used for every rendered
|
||||
# hostname (echo, flux-webhook, internal split DNS) and the wildcard
|
||||
# certificate, regardless of DNS provider.
|
||||
# =============================================================================
|
||||
[domain]
|
||||
|
||||
# REQUIRED. Example: "example.com"
|
||||
name = ""
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Public DNS authority and certificate issuance. With "cloudflare",
|
||||
# external-dns publishes records automatically and cert-manager issues a
|
||||
# Let's Encrypt wildcard via ACME DNS-01. With "none", nothing is
|
||||
# published and the wildcard certificate is issued by an in-cluster
|
||||
# self-signed CA instead (internal-only cluster).
|
||||
# =============================================================================
|
||||
[dns]
|
||||
|
||||
# OPTIONAL. Default: "cloudflare". Allowed: "cloudflare" | "none"
|
||||
|
||||
# provider = "cloudflare"
|
||||
|
||||
# Cloudflare API token (NOT the global API key) with `Zone - DNS - Edit` and
|
||||
# `Account - Cloudflare Tunnel - Read` permissions, scoped to the zone
|
||||
# above. See the README for token creation steps.
|
||||
# REQUIRED when provider is "cloudflare"; must be empty otherwise.
|
||||
token = ""
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# How the public internet reaches the cluster's external gateway. With
|
||||
# "cloudflare-tunnel", cloudflared connects outbound so no ports are
|
||||
# forwarded (requires dns.provider = "cloudflare" and
|
||||
# cloudflare-tunnel.json). With "direct", you forward TCP 443 (and
|
||||
# optionally 80) on your router to gateways.external and point an
|
||||
# `external.<domain>` DNS record at your WAN address (A record or DDNS
|
||||
# CNAME) yourself; per-app records are still published automatically.
|
||||
# With "none", nothing is exposed and apps are only reachable on your
|
||||
# LAN via the internal gateway.
|
||||
# =============================================================================
|
||||
[ingress]
|
||||
|
||||
# OPTIONAL. Default: "cloudflare-tunnel" when dns.provider is "cloudflare",
|
||||
# otherwise "none". Allowed: "cloudflare-tunnel" | "direct" | "none"
|
||||
|
||||
# mode = "cloudflare-tunnel"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# CNI configuration. Defaults are sane for most homelab setups; touch
|
||||
# this section only if you need BGP peering or a different LB mode.
|
||||
# =============================================================================
|
||||
[cilium]
|
||||
|
||||
# How Cilium's load balancer handles return traffic. `dsr` (Direct Server
|
||||
# Return) preserves the client IP and is faster, but requires a switch
|
||||
# fabric that won't drop asymmetric flows. `snat` masquerades the client
|
||||
# and is the safe default for unknown topologies.
|
||||
# REF: https://docs.cilium.io/en/stable/network/kubernetes/kubeproxy-free/
|
||||
# OPTIONAL. Default: "dsr". Allowed: "dsr" | "snat"
|
||||
|
||||
# loadbalancer_mode = "dsr"
|
||||
|
||||
# Cilium BGP peering — advertises Service IPs to your upstream router so
|
||||
# LoadBalancer addresses become reachable from anywhere on your LAN
|
||||
# (rather than only via L2 ARP). Set ALL THREE fields below to enable;
|
||||
# leaving any blank disables BGP entirely.
|
||||
# REF: https://docs.cilium.io/en/latest/network/bgp-control-plane/bgp-control-plane/
|
||||
[cilium.bgp]
|
||||
|
||||
# IP of your BGP-speaking router. The cluster peers with it from each node.
|
||||
# OPTIONAL. Example: "192.168.1.1"
|
||||
|
||||
# router_addr = ""
|
||||
|
||||
# ASN your router uses for BGP. Anything in the private range (64512-65534)
|
||||
# is fine if you're not peering with the public internet.
|
||||
# OPTIONAL. Example: "64513"
|
||||
|
||||
# router_asn = ""
|
||||
|
||||
# ASN the cluster's nodes use for BGP. Pick a different value than
|
||||
# router_asn so peering is eBGP rather than iBGP.
|
||||
# OPTIONAL. Example: "64514"
|
||||
|
||||
# node_asn = ""
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Talos Image Factory settings shared by all nodes.
|
||||
# =============================================================================
|
||||
[talos]
|
||||
|
||||
# Default schematic for every node that doesn't set its own schematic_id.
|
||||
# The 64-character hex string from your build at https://factory.talos.dev/
|
||||
# OPTIONAL if every node sets schematic_id itself.
|
||||
schematic_id = ""
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# One [[nodes]] table per physical machine or VM in the cluster. At least
|
||||
# one controller (controller=true) is required; worker nodes are optional.
|
||||
# For HA, use 3 controllers.
|
||||
#
|
||||
# Discover hardware details from a node already booted into Talos
|
||||
# maintenance mode:
|
||||
# talosctl get disks -n <node-ip> --insecure
|
||||
# talosctl get links -n <node-ip> --insecure
|
||||
# Schematic ID is the 64-character hex string from your build at:
|
||||
# https://factory.talos.dev/
|
||||
#
|
||||
# The block below is a template — copy it once per node, uncomment, and
|
||||
# fill in the values.
|
||||
# =============================================================================
|
||||
|
||||
# [[nodes]]
|
||||
# name = "k8s-0" # Hostname; must match [a-z0-9-]+ (not "global"/"controller"/"worker").
|
||||
# address = "192.168.1.10" # Static IP; must be inside network.node_cidr.
|
||||
# controller = true # true = control-plane (etcd + API server), false = worker.
|
||||
# disk = "/dev/nvme0n1" # Block device or /dev/disk/by-id/... symlink to install Talos onto.
|
||||
# mac_addr = "aa:bb:cc:dd:ee:ff" # Primary NIC MAC.
|
||||
#
|
||||
# # Optional when [talos] sets a cluster-wide default:
|
||||
# schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba" # 64-hex from factory.talos.dev.
|
||||
#
|
||||
# # Optional advanced fields (each independently uncommentable):
|
||||
# mtu = 1500 # Set only for jumbo frames / non-1500 MTUs (1450-9000).
|
||||
# secureboot = false # UEFI SecureBoot — requires a SecureBoot-enabled schematic.
|
||||
# encrypt_disk = false # TPM-bound full-disk encryption.
|
||||
# kernel_modules = ["nvidia", "nvidia_uvm"] # Only for schematics shipping matching extensions.
|
||||
@@ -0,0 +1,433 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$defs": {
|
||||
"Api": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"addr": {
|
||||
"format": "ipv4",
|
||||
"title": "Addr",
|
||||
"type": "string"
|
||||
},
|
||||
"tls_sans": {
|
||||
"anyOf": [
|
||||
{
|
||||
"items": {
|
||||
"$ref": "#/$defs/Fqdn"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"title": "Tls Sans"
|
||||
}
|
||||
},
|
||||
"required": ["addr"],
|
||||
"title": "Api",
|
||||
"type": "object"
|
||||
},
|
||||
"Asn": {
|
||||
"type": "string"
|
||||
},
|
||||
"Bgp": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"router_addr": {
|
||||
"anyOf": [
|
||||
{
|
||||
"format": "ipv4",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"const": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"default": "",
|
||||
"title": "Router Addr"
|
||||
},
|
||||
"router_asn": {
|
||||
"$ref": "#/$defs/Asn",
|
||||
"default": ""
|
||||
},
|
||||
"node_asn": {
|
||||
"$ref": "#/$defs/Asn",
|
||||
"default": ""
|
||||
}
|
||||
},
|
||||
"title": "Bgp",
|
||||
"type": "object"
|
||||
},
|
||||
"Cidr": {
|
||||
"format": "ipv4network",
|
||||
"type": "string"
|
||||
},
|
||||
"Cilium": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"loadbalancer_mode": {
|
||||
"default": "dsr",
|
||||
"enum": ["dsr", "snat"],
|
||||
"title": "Loadbalancer Mode",
|
||||
"type": "string"
|
||||
},
|
||||
"bgp": {
|
||||
"$ref": "#/$defs/Bgp",
|
||||
"default": {
|
||||
"router_addr": "",
|
||||
"router_asn": "",
|
||||
"node_asn": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "Cilium",
|
||||
"type": "object"
|
||||
},
|
||||
"Dns": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"provider": {
|
||||
"default": "cloudflare",
|
||||
"enum": ["cloudflare", "none"],
|
||||
"title": "Provider",
|
||||
"type": "string"
|
||||
},
|
||||
"token": {
|
||||
"default": "",
|
||||
"title": "Token",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "Dns",
|
||||
"type": "object"
|
||||
},
|
||||
"Domain": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "#/$defs/Fqdn"
|
||||
}
|
||||
},
|
||||
"required": ["name"],
|
||||
"title": "Domain",
|
||||
"type": "object"
|
||||
},
|
||||
"Fqdn": {
|
||||
"pattern": "^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,}$",
|
||||
"type": "string"
|
||||
},
|
||||
"Gateways": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"internal": {
|
||||
"format": "ipv4",
|
||||
"title": "Internal",
|
||||
"type": "string"
|
||||
},
|
||||
"dns": {
|
||||
"format": "ipv4",
|
||||
"title": "Dns",
|
||||
"type": "string"
|
||||
},
|
||||
"external": {
|
||||
"anyOf": [
|
||||
{
|
||||
"format": "ipv4",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"title": "External"
|
||||
}
|
||||
},
|
||||
"required": ["internal", "dns"],
|
||||
"title": "Gateways",
|
||||
"type": "object"
|
||||
},
|
||||
"Ingress": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"mode": {
|
||||
"default": "cloudflare-tunnel",
|
||||
"enum": ["cloudflare-tunnel", "direct", "none"],
|
||||
"title": "Mode",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "Ingress",
|
||||
"type": "object"
|
||||
},
|
||||
"Kubernetes": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"pod_cidr": {
|
||||
"$ref": "#/$defs/Cidr",
|
||||
"default": "10.42.0.0/16"
|
||||
},
|
||||
"svc_cidr": {
|
||||
"$ref": "#/$defs/Cidr",
|
||||
"default": "10.43.0.0/16"
|
||||
},
|
||||
"coredns_addr": {
|
||||
"format": "ipv4",
|
||||
"title": "Coredns Addr",
|
||||
"type": "string"
|
||||
},
|
||||
"api": {
|
||||
"$ref": "#/$defs/Api"
|
||||
}
|
||||
},
|
||||
"required": ["api"],
|
||||
"title": "Kubernetes",
|
||||
"type": "object"
|
||||
},
|
||||
"Network": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"node_cidr": {
|
||||
"$ref": "#/$defs/Cidr"
|
||||
},
|
||||
"dns_servers": {
|
||||
"default": ["1.1.1.1", "1.0.0.1"],
|
||||
"items": {
|
||||
"format": "ipv4",
|
||||
"type": "string"
|
||||
},
|
||||
"title": "Dns Servers",
|
||||
"type": "array"
|
||||
},
|
||||
"ntp_servers": {
|
||||
"default": ["162.159.200.1", "162.159.200.123"],
|
||||
"items": {
|
||||
"format": "ipv4",
|
||||
"type": "string"
|
||||
},
|
||||
"title": "Ntp Servers",
|
||||
"type": "array"
|
||||
},
|
||||
"default_gateway": {
|
||||
"format": "ipv4",
|
||||
"title": "Default Gateway",
|
||||
"type": "string"
|
||||
},
|
||||
"vlan_tag": {
|
||||
"anyOf": [
|
||||
{
|
||||
"pattern": "^[0-9]+$",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"title": "Vlan Tag"
|
||||
}
|
||||
},
|
||||
"required": ["node_cidr"],
|
||||
"title": "Network",
|
||||
"type": "object"
|
||||
},
|
||||
"Node": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"pattern": "^[a-z0-9][a-z0-9\\-]{0,61}[a-z0-9]$|^[a-z0-9]$",
|
||||
"title": "Name",
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"format": "ipv4",
|
||||
"title": "Address",
|
||||
"type": "string"
|
||||
},
|
||||
"controller": {
|
||||
"title": "Controller",
|
||||
"type": "boolean"
|
||||
},
|
||||
"disk": {
|
||||
"title": "Disk",
|
||||
"type": "string"
|
||||
},
|
||||
"mac_addr": {
|
||||
"pattern": "^([0-9a-f]{2}:){5}[0-9a-f]{2}$",
|
||||
"title": "Mac Addr",
|
||||
"type": "string"
|
||||
},
|
||||
"schematic_id": {
|
||||
"anyOf": [
|
||||
{
|
||||
"pattern": "^[a-z0-9]{64}$",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"title": "Schematic Id"
|
||||
},
|
||||
"mtu": {
|
||||
"default": 1500,
|
||||
"maximum": 9000,
|
||||
"minimum": 1450,
|
||||
"title": "Mtu",
|
||||
"type": "integer"
|
||||
},
|
||||
"secureboot": {
|
||||
"default": false,
|
||||
"title": "Secureboot",
|
||||
"type": "boolean"
|
||||
},
|
||||
"encrypt_disk": {
|
||||
"default": false,
|
||||
"title": "Encrypt Disk",
|
||||
"type": "boolean"
|
||||
},
|
||||
"kernel_modules": {
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": "Kernel Modules",
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": ["name", "address", "controller", "disk", "mac_addr"],
|
||||
"title": "Node",
|
||||
"type": "object"
|
||||
},
|
||||
"Repository": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"url": {
|
||||
"pattern": "^(https?://|ssh://git@)[^/]+/.+$",
|
||||
"title": "Url",
|
||||
"type": "string"
|
||||
},
|
||||
"branch": {
|
||||
"default": "main",
|
||||
"minLength": 1,
|
||||
"title": "Branch",
|
||||
"type": "string"
|
||||
},
|
||||
"webhook_provider": {
|
||||
"default": "github",
|
||||
"enum": ["github", "gitlab", "generic-hmac", "none"],
|
||||
"title": "Webhook Provider",
|
||||
"type": "string"
|
||||
},
|
||||
"known_hosts": {
|
||||
"default": "",
|
||||
"title": "Known Hosts",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["url"],
|
||||
"title": "Repository",
|
||||
"type": "object"
|
||||
},
|
||||
"Spegel": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"title": "Enabled"
|
||||
}
|
||||
},
|
||||
"title": "Spegel",
|
||||
"type": "object"
|
||||
},
|
||||
"Talos": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"schematic_id": {
|
||||
"anyOf": [
|
||||
{
|
||||
"pattern": "^[a-z0-9]{64}$",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"title": "Schematic Id"
|
||||
}
|
||||
},
|
||||
"title": "Talos",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"network": {
|
||||
"$ref": "#/$defs/Network"
|
||||
},
|
||||
"kubernetes": {
|
||||
"$ref": "#/$defs/Kubernetes"
|
||||
},
|
||||
"gateways": {
|
||||
"$ref": "#/$defs/Gateways"
|
||||
},
|
||||
"repository": {
|
||||
"$ref": "#/$defs/Repository"
|
||||
},
|
||||
"domain": {
|
||||
"$ref": "#/$defs/Domain"
|
||||
},
|
||||
"dns": {
|
||||
"$ref": "#/$defs/Dns"
|
||||
},
|
||||
"ingress": {
|
||||
"$ref": "#/$defs/Ingress"
|
||||
},
|
||||
"cilium": {
|
||||
"$ref": "#/$defs/Cilium",
|
||||
"default": {
|
||||
"loadbalancer_mode": "dsr",
|
||||
"bgp": {
|
||||
"node_asn": "",
|
||||
"router_addr": "",
|
||||
"router_asn": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"talos": {
|
||||
"$ref": "#/$defs/Talos",
|
||||
"default": {
|
||||
"schematic_id": null
|
||||
}
|
||||
},
|
||||
"spegel": {
|
||||
"$ref": "#/$defs/Spegel",
|
||||
"default": {
|
||||
"enabled": null
|
||||
}
|
||||
},
|
||||
"nodes": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/Node"
|
||||
},
|
||||
"title": "Nodes",
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": ["network", "kubernetes", "gateways", "repository", "domain", "dns", "nodes"],
|
||||
"title": "cluster.toml",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
set quiet
|
||||
set minimum-version := '1.55.1'
|
||||
set default-list
|
||||
set default-script
|
||||
set shell := ['bash', '-euo', 'pipefail', '-c']
|
||||
set script-interpreter := ['bash', '-euo', 'pipefail']
|
||||
|
||||
[group('bootstrap')]
|
||||
mod? bootstrap 'bootstrap'
|
||||
|
||||
[group('kubernetes')]
|
||||
mod? kube 'kubernetes'
|
||||
|
||||
[group('talos')]
|
||||
mod? talos 'talos'
|
||||
|
||||
[private]
|
||||
log lvl msg *args:
|
||||
gum log -t rfc3339 -s -l "{{ lvl }}" "{{ msg }}" {{ args }}
|
||||
|
||||
# === template ===
|
||||
|
||||
[group('template')]
|
||||
mod template 'template'
|
||||
|
||||
[doc('Render and validate configuration files')]
|
||||
[group('template')]
|
||||
configure:
|
||||
just template configure
|
||||
|
||||
[doc('Initialize configuration files (cluster.toml, age key, deploy key, webhook token)')]
|
||||
[group('template')]
|
||||
init:
|
||||
just template init
|
||||
@@ -0,0 +1,19 @@
|
||||
[makejinja]
|
||||
inputs = ["./template/overrides","./template/config"]
|
||||
output = "./"
|
||||
exclude_patterns = ["*.partial.yaml.j2"]
|
||||
data = ["./cluster.toml"]
|
||||
import_paths = ["./template/scripts"]
|
||||
loaders = ["plugin:Plugin"]
|
||||
jinja_suffix = ".j2"
|
||||
copy_metadata = true
|
||||
force = true
|
||||
undefined = "strict"
|
||||
|
||||
[makejinja.delimiter]
|
||||
block_start = "#%"
|
||||
block_end = "%#"
|
||||
comment_start = "#|"
|
||||
comment_end = "#|"
|
||||
variable_start = "#{"
|
||||
variable_end = "}#"
|
||||
@@ -0,0 +1,16 @@
|
||||
[project]
|
||||
name = "cluster-template"
|
||||
version = "0.0.0"
|
||||
requires-python = ">=3.14"
|
||||
dependencies = [
|
||||
"makejinja==2.8.3",
|
||||
"pydantic==2.13.5",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pytest>=8",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
package = false
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
creation_rules:
|
||||
- path_regex: talos/.*\.sops\.ya?ml
|
||||
mac_only_encrypted: true
|
||||
age: "#{ age_key('public') }#"
|
||||
- path_regex: (bootstrap|kubernetes)/.*\.sops\.ya?ml
|
||||
encrypted_regex: "^(data|stringData)$"
|
||||
mac_only_encrypted: true
|
||||
age: "#{ age_key('public') }#"
|
||||
stores:
|
||||
yaml:
|
||||
indent: 2
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: deploy-key
|
||||
namespace: flux-system
|
||||
stringData:
|
||||
identity: |
|
||||
#% filter indent(width=4, first=False) %#
|
||||
#{ deploy_key() }#
|
||||
#% endfilter %#
|
||||
known_hosts: |
|
||||
#% filter indent(width=4, first=False) %#
|
||||
#{ repository.known_hosts }#
|
||||
#% endfilter %#
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/helmfile
|
||||
|
||||
# Bootstraps core applications that provide a minimal runtime base for the
|
||||
# cluster. These releases are installed first so the cluster has the resources
|
||||
# Flux needs before its own reconciliation begins.
|
||||
#
|
||||
# After this bootstrap phase, Flux is ready to take over management of the
|
||||
# application stack and continue reconciling downstream state.
|
||||
|
||||
helmDefaults:
|
||||
cleanupOnFail: true
|
||||
forceConflicts: true
|
||||
wait: true
|
||||
waitForJobs: true
|
||||
|
||||
bases:
|
||||
- default.yaml
|
||||
|
||||
releases:
|
||||
- name: cilium
|
||||
namespace: kube-system
|
||||
inherit:
|
||||
- template: default
|
||||
|
||||
- name: coredns
|
||||
namespace: kube-system
|
||||
inherit:
|
||||
- template: default
|
||||
needs: ["kube-system/cilium"]
|
||||
#% if spegel.enabled %#
|
||||
|
||||
- name: spegel
|
||||
namespace: kube-system
|
||||
inherit:
|
||||
- template: default
|
||||
needs: ["kube-system/coredns"]
|
||||
#% endif %#
|
||||
|
||||
- name: cert-manager
|
||||
namespace: cert-manager
|
||||
inherit:
|
||||
- template: default
|
||||
#% if spegel.enabled %#
|
||||
needs: ["kube-system/spegel"]
|
||||
#% else %#
|
||||
needs: ["kube-system/coredns"]
|
||||
#% endif %#
|
||||
|
||||
- name: flux-operator
|
||||
namespace: flux-system
|
||||
inherit:
|
||||
- template: default
|
||||
needs: ["cert-manager/cert-manager"]
|
||||
|
||||
- name: flux-instance
|
||||
namespace: flux-system
|
||||
inherit:
|
||||
- template: default
|
||||
needs: ["flux-system/flux-operator"]
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/helmfile
|
||||
|
||||
# Bootstraps cluster-wide Custom Resource Definitions (CRDs) by extracting them
|
||||
# from upstream Helm charts and applying them directly with kubectl. The releases
|
||||
# below are never reconciled with helmfile apply or helmfile sync — only their
|
||||
# CRDs are rendered (via --include-crds) and piped to the cluster.
|
||||
#
|
||||
# Installing CRDs out-of-band ensures they exist before Flux begins reconciling
|
||||
# workloads that reference them, avoiding the need for dependsOn chains on nearly
|
||||
# every Kustomization that consumes a CRD-backed resource.
|
||||
|
||||
helmDefaults:
|
||||
args:
|
||||
- --include-crds
|
||||
- --no-hooks
|
||||
|
||||
bases:
|
||||
- default.yaml
|
||||
|
||||
releases:
|
||||
#% if dns.provider == 'cloudflare' and ingress.mode != 'none' %#
|
||||
- name: cloudflare-dns
|
||||
namespace: network
|
||||
inherit:
|
||||
- template: default
|
||||
|
||||
#% endif %#
|
||||
- name: envoy-gateway
|
||||
namespace: network
|
||||
inherit:
|
||||
- template: default
|
||||
|
||||
- name: prometheus-operator-crds
|
||||
namespace: observability
|
||||
chart: oci://ghcr.io/prometheus-community/charts/prometheus-operator-crds
|
||||
version: 29.0.0
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/helmfile
|
||||
templates:
|
||||
default:
|
||||
chart: '{{ (fromYaml (tpl (readFile "./templates/release.yaml.gotmpl") .)).chart }}'
|
||||
version: '{{ (fromYaml (tpl (readFile "./templates/release.yaml.gotmpl") .)).version }}'
|
||||
values:
|
||||
- ./templates/values.yaml.gotmpl
|
||||
@@ -0,0 +1,3 @@
|
||||
{{- $oci := fromYaml (readFile (printf "../../kubernetes/apps/%s/%s/app/ocirepository.yaml" .Release.Namespace .Release.Name)) -}}
|
||||
chart: {{ $oci.spec.url }}
|
||||
version: {{ $oci.spec.ref.tag }}
|
||||
@@ -0,0 +1 @@
|
||||
{{ (fromYaml (readFile (printf "../../../kubernetes/apps/%s/%s/app/helmrelease.yaml" .Release.Namespace .Release.Name))).spec.values | toYaml }}
|
||||
@@ -0,0 +1,105 @@
|
||||
set no-exit-message
|
||||
set quiet
|
||||
set shell := ['bash', '-euo', 'pipefail', '-c']
|
||||
set script-interpreter := ['bash', '-euo', 'pipefail']
|
||||
set default-list
|
||||
set default-script
|
||||
|
||||
kubernetes_dir := justfile_dir() + '/kubernetes'
|
||||
|
||||
[doc('Bootstrap the Talos cluster')]
|
||||
[group('bootstrap')]
|
||||
talos: talos-secret talos-apply talos-talosconfig talos-kubeconfig
|
||||
|
||||
[doc('Bootstrap apps into the Talos cluster')]
|
||||
[group('bootstrap')]
|
||||
apps: apps-ready apps-namespaces apps-secrets apps-crds apps-helm
|
||||
just log info "Cluster is bootstrapped — Flux will start syncing the Git repository"
|
||||
|
||||
# No sops call or existence guard is needed: the bundle is stored already
|
||||
# encrypted with the repo's age recipient, and existing bundles are left
|
||||
# untouched.
|
||||
[private]
|
||||
[working-directory('../talos')]
|
||||
talos-secret:
|
||||
just log info "Generating secrets" stage "{{ recipe_name() }}"
|
||||
topf secrets --confirm=false > /dev/null
|
||||
|
||||
[private]
|
||||
[working-directory('../talos')]
|
||||
talos-apply:
|
||||
just log info "Applying talos config and bootstrapping" stage "{{ recipe_name() }}"
|
||||
topf apply --auto-bootstrap --confirm=false
|
||||
|
||||
[private]
|
||||
[working-directory('../talos')]
|
||||
talos-talosconfig:
|
||||
just log info "Generating talosconfig" stage "{{ recipe_name() }}"
|
||||
topf talosconfig > talosconfig
|
||||
|
||||
# topf issues short-lived admin certs by default; 8760h keeps the
|
||||
# kubeconfig usable long-term.
|
||||
[private]
|
||||
[working-directory('../talos')]
|
||||
talos-kubeconfig:
|
||||
just log info "Fetching kubeconfig" stage "{{ recipe_name() }}"
|
||||
topf kubeconfig --validity 8760h > "{{ justfile_dir() }}/kubeconfig"
|
||||
|
||||
[private]
|
||||
apps-crds:
|
||||
just log info "Applying CRDs" stage "{{ recipe_name() }}"
|
||||
if ! helmfile --file "{{ source_directory() }}/helmfile/crds.yaml" template --quiet | yq eval-all --exit-status 'select(.kind == "CustomResourceDefinition")' | kubectl apply --server-side --force-conflicts --filename -; then
|
||||
just log fatal "Failed to apply crds"
|
||||
fi
|
||||
|
||||
[private]
|
||||
apps-helm:
|
||||
just log info "Syncing helmfile" stage "{{ recipe_name() }}"
|
||||
if ! helmfile --file "{{ source_directory() }}/helmfile/apps.yaml" sync --hide-notes; then
|
||||
just log fatal "Failed to sync helmfile"
|
||||
fi
|
||||
|
||||
[private]
|
||||
apps-namespaces:
|
||||
just log info "Applying namespaces for apps" stage "{{ recipe_name() }}"
|
||||
for app in "{{ kubernetes_dir }}/apps"/*/; do
|
||||
ns="$(basename "$app")"
|
||||
if kubectl create namespace "$ns" --dry-run=client -o yaml \
|
||||
| kubectl apply --server-side --filename - &>/dev/null; then
|
||||
just log info "Namespace applied" namespace "$ns"
|
||||
else
|
||||
just log fatal "Failed to apply namespace" namespace "$ns"
|
||||
fi
|
||||
done
|
||||
|
||||
[private]
|
||||
apps-secrets:
|
||||
just log info "Applying secrets for apps" stage "{{ recipe_name() }}"
|
||||
for secret in \
|
||||
"{{ source_directory() }}/deploy-key.sops.yaml" \
|
||||
"{{ source_directory() }}/sops-age.sops.yaml" \
|
||||
"{{ kubernetes_dir }}/components/sops/cluster-secrets.sops.yaml"
|
||||
do
|
||||
name="$(basename "$secret" .sops.yaml)"
|
||||
if sops decrypt "$secret" \
|
||||
| kubectl --namespace flux-system apply --server-side --filename - &>/dev/null; then
|
||||
just log info "Secret applied" resource "$name"
|
||||
else
|
||||
just log fatal "Failed to apply secret" resource "$name"
|
||||
fi
|
||||
done
|
||||
|
||||
# Wait until nodes register as Ready=False. They only become Ready=True once the CNI is healthy.
|
||||
[private]
|
||||
apps-ready:
|
||||
just log info "Waiting for nodes to register as Ready=False" stage "{{ recipe_name() }}"
|
||||
if ! kubectl wait nodes --for=condition=Ready=True --all --timeout=10s &>/dev/null; then
|
||||
deadline=$((SECONDS + 600))
|
||||
until kubectl wait nodes --for=condition=Ready=False --all --timeout=10s &>/dev/null; do
|
||||
if (( SECONDS >= deadline )); then
|
||||
just log fatal "Timed out waiting for nodes to register"
|
||||
fi
|
||||
just log info "Nodes not available, waiting for nodes to be available. Retrying in 5 seconds..."
|
||||
sleep 5
|
||||
done
|
||||
fi
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: sops-age
|
||||
namespace: flux-system
|
||||
stringData:
|
||||
age.agekey: "#{ age_key('private') }#"
|
||||
@@ -0,0 +1,52 @@
|
||||
#% if dns.provider == 'cloudflare' %#
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: letsencrypt-production
|
||||
spec:
|
||||
acme:
|
||||
privateKeySecretRef:
|
||||
name: letsencrypt-production
|
||||
profile: shortlived
|
||||
server: https://acme-v02.api.letsencrypt.org/directory
|
||||
solvers:
|
||||
- dns01:
|
||||
cloudflare:
|
||||
apiTokenSecretRef:
|
||||
name: cert-manager-secret
|
||||
key: api-token
|
||||
selector:
|
||||
dnsZones: ["${SECRET_DOMAIN}"]
|
||||
#% else %#
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: selfsigned
|
||||
spec:
|
||||
selfSigned: {}
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: internal-ca
|
||||
spec:
|
||||
isCA: true
|
||||
commonName: internal-ca
|
||||
secretName: internal-ca
|
||||
privateKey:
|
||||
algorithm: ECDSA
|
||||
size: 256
|
||||
issuerRef:
|
||||
name: selfsigned
|
||||
kind: ClusterIssuer
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: internal-ca
|
||||
spec:
|
||||
ca:
|
||||
secretName: internal-ca
|
||||
#% endif %#
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: cert-manager
|
||||
spec:
|
||||
chartRef:
|
||||
kind: OCIRepository
|
||||
name: cert-manager
|
||||
interval: 1h
|
||||
values:
|
||||
crds:
|
||||
enabled: true
|
||||
replicaCount: #{ 2 if nodes | length > 1 else 1 }#
|
||||
dns01RecursiveNameservers: https://1.1.1.1:443/dns-query,https://1.0.0.1:443/dns-query
|
||||
dns01RecursiveNameserversOnly: true
|
||||
prometheus:
|
||||
enabled: true
|
||||
servicemonitor:
|
||||
enabled: true
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ./clusterissuer.yaml
|
||||
- ./helmrelease.yaml
|
||||
- ./ocirepository.yaml
|
||||
#% if dns.provider == 'cloudflare' %#
|
||||
- ./secret.sops.yaml
|
||||
#% endif %#
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: OCIRepository
|
||||
metadata:
|
||||
name: cert-manager
|
||||
spec:
|
||||
interval: 15m
|
||||
layerSelector:
|
||||
mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
|
||||
operation: copy
|
||||
ref:
|
||||
tag: v1.21.2
|
||||
url: oci://quay.io/jetstack/charts/cert-manager
|
||||
@@ -0,0 +1,9 @@
|
||||
#% if dns.provider == 'cloudflare' %#
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: cert-manager-secret
|
||||
stringData:
|
||||
api-token: "#{ dns.token }#"
|
||||
#% endif %#
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: cert-manager
|
||||
spec:
|
||||
healthChecks:
|
||||
- apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
name: cert-manager
|
||||
namespace: cert-manager
|
||||
- apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
name: #{ cluster_issuer }#
|
||||
healthCheckExprs:
|
||||
- apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
current: status.conditions.exists(e, e.type == 'Ready' && e.status == 'True')
|
||||
interval: 1h
|
||||
path: ./kubernetes/apps/cert-manager/cert-manager/app
|
||||
postBuild:
|
||||
substituteFrom:
|
||||
- name: cluster-secrets
|
||||
kind: Secret
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
targetNamespace: cert-manager
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: cert-manager
|
||||
|
||||
components:
|
||||
- ../../components/sops
|
||||
|
||||
resources:
|
||||
- ./namespace.yaml
|
||||
- ./cert-manager/ks.yaml
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: cert-manager
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/prune: disabled
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: echo
|
||||
spec:
|
||||
chartRef:
|
||||
kind: OCIRepository
|
||||
name: echo
|
||||
interval: 1h
|
||||
values:
|
||||
replicaCount: #{ 2 if nodes | length > 1 else 1 }#
|
||||
config:
|
||||
kubernetes: true
|
||||
trustedProxies:
|
||||
- "#{ kubernetes.pod_cidr }#"
|
||||
httpRoute:
|
||||
enabled: true
|
||||
hostnames:
|
||||
- "{{ .Release.Name }}.${SECRET_DOMAIN}"
|
||||
parentRefs:
|
||||
- name: envoy-#{ 'external' if ingress.mode != 'none' else 'internal' }#
|
||||
namespace: network
|
||||
monitoring:
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
limits:
|
||||
memory: 64Mi
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ./helmrelease.yaml
|
||||
- ./ocirepository.yaml
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: OCIRepository
|
||||
metadata:
|
||||
name: echo
|
||||
spec:
|
||||
interval: 15m
|
||||
layerSelector:
|
||||
mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
|
||||
operation: copy
|
||||
ref:
|
||||
tag: 0.2.5
|
||||
url: oci://ghcr.io/home-operations/charts/echo
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: echo
|
||||
spec:
|
||||
interval: 1h
|
||||
path: ./kubernetes/apps/default/echo/app
|
||||
postBuild:
|
||||
substituteFrom:
|
||||
- name: cluster-secrets
|
||||
kind: Secret
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
targetNamespace: default
|
||||
wait: false
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: default
|
||||
|
||||
components:
|
||||
- ../../components/sops
|
||||
|
||||
resources:
|
||||
- ./namespace.yaml
|
||||
- ./echo/ks.yaml
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: default
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/prune: disabled
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: flux-instance
|
||||
spec:
|
||||
chartRef:
|
||||
kind: OCIRepository
|
||||
name: flux-instance
|
||||
interval: 1h
|
||||
values:
|
||||
commonAnnotations:
|
||||
fluxcd.controlplane.io/reconcileArtifactEvery: 1h
|
||||
instance:
|
||||
cluster:
|
||||
networkPolicy: false
|
||||
components:
|
||||
- source-controller
|
||||
- kustomize-controller
|
||||
- helm-controller
|
||||
- notification-controller
|
||||
sync:
|
||||
kind: GitRepository
|
||||
url: "#{ repository.url }#"
|
||||
#% if repository.url.startswith('ssh://') %#
|
||||
pullSecret: deploy-key
|
||||
#% endif %#
|
||||
ref: "refs/heads/#{ repository.branch }#"
|
||||
path: kubernetes/flux/cluster
|
||||
commonMetadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: flux
|
||||
kustomize:
|
||||
patches:
|
||||
- # Increase the number of workers
|
||||
patch: |
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --concurrent=10
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --requeue-dependency=5s
|
||||
target:
|
||||
kind: Deployment
|
||||
name: (kustomize-controller|helm-controller|source-controller)
|
||||
- # Increase the memory limits
|
||||
patch: |
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: all
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: manager
|
||||
resources:
|
||||
limits:
|
||||
memory: 1Gi
|
||||
target:
|
||||
kind: Deployment
|
||||
name: (kustomize-controller|helm-controller|source-controller)
|
||||
- # Enable in-memory kustomize builds
|
||||
patch: |
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --concurrent=20
|
||||
- op: replace
|
||||
path: /spec/template/spec/volumes/0
|
||||
value:
|
||||
name: temp
|
||||
emptyDir:
|
||||
medium: Memory
|
||||
target:
|
||||
kind: Deployment
|
||||
name: kustomize-controller
|
||||
- # Enable Helm repositories caching
|
||||
patch: |
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --helm-cache-max-size=10
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --helm-cache-ttl=60m
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --helm-cache-purge-interval=5m
|
||||
target:
|
||||
kind: Deployment
|
||||
name: source-controller
|
||||
- # Flux near OOM detection for Helm
|
||||
patch: |
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --feature-gates=OOMWatch=true
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --oom-watch-memory-threshold=95
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --oom-watch-interval=500ms
|
||||
target:
|
||||
kind: Deployment
|
||||
name: helm-controller
|
||||
- # Disable chart digest tracking
|
||||
patch: |
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --feature-gates=DisableChartDigestTracking=true
|
||||
target:
|
||||
kind: Deployment
|
||||
name: helm-controller
|
||||
- # Controller-level SOPS decryption
|
||||
patch: |
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --sops-age-secret=sops-age
|
||||
target:
|
||||
kind: Deployment
|
||||
name: kustomize-controller
|
||||
- # Watch configmaps and secrets attached to HelmReleases and Kustomizations
|
||||
patch: |-
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --watch-configs-label-selector=owner!=helm
|
||||
target:
|
||||
kind: Deployment
|
||||
name: (helm-controller|kustomize-controller)
|
||||
- # Cancel health checks on new Kustomizations revisions
|
||||
patch: |-
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --feature-gates=CancelHealthCheckOnNewRevision=true
|
||||
target:
|
||||
kind: Deployment
|
||||
name: kustomize-controller
|
||||
@@ -0,0 +1,22 @@
|
||||
#% if repository.webhook_provider != 'none' %#
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: flux-webhook
|
||||
spec:
|
||||
hostnames: ["flux-webhook.${SECRET_DOMAIN}"]
|
||||
parentRefs:
|
||||
- name: envoy-#{ 'external' if ingress.mode != 'none' else 'internal' }#
|
||||
namespace: network
|
||||
sectionName: https
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: webhook-receiver
|
||||
namespace: flux-system
|
||||
port: 80
|
||||
matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /hook/
|
||||
#% endif %#
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ./helmrelease.yaml
|
||||
- ./ocirepository.yaml
|
||||
#% if repository.webhook_provider != 'none' %#
|
||||
- ./secret.sops.yaml
|
||||
- ./httproute.yaml
|
||||
- ./receiver.yaml
|
||||
#% endif %#
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: OCIRepository
|
||||
metadata:
|
||||
name: flux-instance
|
||||
spec:
|
||||
interval: 15m
|
||||
layerSelector:
|
||||
mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
|
||||
operation: copy
|
||||
ref:
|
||||
tag: 0.60.0
|
||||
url: oci://ghcr.io/controlplaneio-fluxcd/charts/flux-instance
|
||||
@@ -0,0 +1,21 @@
|
||||
#% if repository.webhook_provider != 'none' %#
|
||||
---
|
||||
apiVersion: notification.toolkit.fluxcd.io/v1
|
||||
kind: Receiver
|
||||
metadata:
|
||||
name: flux-webhook
|
||||
spec:
|
||||
type: #{ repository.webhook_provider }#
|
||||
events: ["ping", "push"]
|
||||
secretRef:
|
||||
name: flux-webhook-token
|
||||
resources:
|
||||
- apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
- apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
#% endif %#
|
||||
@@ -0,0 +1,9 @@
|
||||
#% if repository.webhook_provider != 'none' %#
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: flux-webhook-token
|
||||
stringData:
|
||||
token: "#{ webhook_token() }#"
|
||||
#% endif %#
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: flux-instance
|
||||
spec:
|
||||
dependsOn:
|
||||
- name: flux-operator
|
||||
interval: 1h
|
||||
path: ./kubernetes/apps/flux-system/flux-instance/app
|
||||
postBuild:
|
||||
substituteFrom:
|
||||
- name: cluster-secrets
|
||||
kind: Secret
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
targetNamespace: flux-system
|
||||
wait: false
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: flux-operator
|
||||
spec:
|
||||
chartRef:
|
||||
kind: OCIRepository
|
||||
name: flux-operator
|
||||
interval: 1h
|
||||
values:
|
||||
serviceMonitor:
|
||||
create: true
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ./helmrelease.yaml
|
||||
- ./ocirepository.yaml
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: OCIRepository
|
||||
metadata:
|
||||
name: flux-operator
|
||||
spec:
|
||||
interval: 15m
|
||||
layerSelector:
|
||||
mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
|
||||
operation: copy
|
||||
ref:
|
||||
tag: 0.60.0
|
||||
url: oci://ghcr.io/controlplaneio-fluxcd/charts/flux-operator
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: flux-operator
|
||||
spec:
|
||||
interval: 1h
|
||||
path: ./kubernetes/apps/flux-system/flux-operator/app
|
||||
postBuild:
|
||||
substituteFrom:
|
||||
- name: cluster-secrets
|
||||
kind: Secret
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
targetNamespace: flux-system
|
||||
wait: true
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: flux-system
|
||||
|
||||
components:
|
||||
- ../../components/sops
|
||||
|
||||
resources:
|
||||
- ./namespace.yaml
|
||||
- ./flux-instance/ks.yaml
|
||||
- ./flux-operator/ks.yaml
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/prune: disabled
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: cilium
|
||||
spec:
|
||||
chartRef:
|
||||
kind: OCIRepository
|
||||
name: cilium
|
||||
interval: 1h
|
||||
values:
|
||||
autoDirectNodeRoutes: true
|
||||
bpf:
|
||||
masquerade: true
|
||||
# Ref: https://github.com/siderolabs/talos/issues/10002
|
||||
hostLegacyRouting: true
|
||||
#% if cilium_bgp_enabled %#
|
||||
bgpControlPlane:
|
||||
enabled: true
|
||||
#% endif %#
|
||||
cni:
|
||||
# Required for pairing with Multus CNI
|
||||
exclusive: false
|
||||
cgroup:
|
||||
automount:
|
||||
enabled: false
|
||||
hostRoot: /sys/fs/cgroup
|
||||
# The stable bond name is defined in talos/all/20-network-links.yaml.tpl
|
||||
devices: bond0+
|
||||
dashboards:
|
||||
enabled: true
|
||||
endpointRoutes:
|
||||
enabled: true
|
||||
envoy:
|
||||
enabled: false
|
||||
gatewayAPI:
|
||||
enabled: false
|
||||
hubble:
|
||||
enabled: false
|
||||
ipam:
|
||||
mode: kubernetes
|
||||
ipv4NativeRoutingCIDR: "#{ kubernetes.pod_cidr }#"
|
||||
k8sServiceHost: 127.0.0.1
|
||||
k8sServicePort: 7445
|
||||
kubeProxyReplacement: true
|
||||
kubeProxyReplacementHealthzBindAddr: 0.0.0.0:10256
|
||||
l2announcements:
|
||||
enabled: true
|
||||
loadBalancer:
|
||||
algorithm: maglev
|
||||
mode: "#{ cilium.loadbalancer_mode }#"
|
||||
localRedirectPolicies:
|
||||
enabled: true
|
||||
operator:
|
||||
dashboards:
|
||||
enabled: true
|
||||
prometheus:
|
||||
enabled: true
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
replicas: #{ 2 if nodes | length > 1 else 1 }#
|
||||
rollOutPods: true
|
||||
prometheus:
|
||||
enabled: true
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
trustCRDsExist: true
|
||||
rollOutCiliumPods: true
|
||||
routingMode: native
|
||||
securityContext:
|
||||
capabilities:
|
||||
ciliumAgent:
|
||||
- CHOWN
|
||||
- KILL
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
- IPC_LOCK
|
||||
- SYS_ADMIN
|
||||
- SYS_RESOURCE
|
||||
- PERFMON
|
||||
- BPF
|
||||
- DAC_OVERRIDE
|
||||
- FOWNER
|
||||
- SETGID
|
||||
- SETUID
|
||||
cleanCiliumState:
|
||||
- NET_ADMIN
|
||||
- SYS_ADMIN
|
||||
- SYS_RESOURCE
|
||||
socketLB:
|
||||
enabled: true
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ./helmrelease.yaml
|
||||
- ./ocirepository.yaml
|
||||
- ./networks.yaml
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
apiVersion: cilium.io/v2alpha1
|
||||
kind: CiliumLoadBalancerIPPool
|
||||
metadata:
|
||||
name: pool
|
||||
spec:
|
||||
allowFirstLastIPs: "No"
|
||||
blocks:
|
||||
- cidr: "#{ network.node_cidr }#"
|
||||
---
|
||||
apiVersion: cilium.io/v2alpha1
|
||||
kind: CiliumL2AnnouncementPolicy
|
||||
metadata:
|
||||
name: l2-policy
|
||||
spec:
|
||||
loadBalancerIPs: true
|
||||
# NOTE: interfaces might need to be set if you have more than one active NIC on your hosts
|
||||
# interfaces:
|
||||
# - ^eno[0-9]+
|
||||
# - ^eth[0-9]+
|
||||
nodeSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/os: linux
|
||||
#% if cilium_bgp_enabled %#
|
||||
---
|
||||
apiVersion: cilium.io/v2alpha1
|
||||
kind: CiliumBGPAdvertisement
|
||||
metadata:
|
||||
name: bgp-advertisement-config
|
||||
labels:
|
||||
advertise: bgp
|
||||
spec:
|
||||
advertisements:
|
||||
- advertisementType: Service
|
||||
service:
|
||||
addresses:
|
||||
- LoadBalancerIP
|
||||
selector:
|
||||
matchExpressions:
|
||||
- { key: somekey, operator: NotIn, values: ["never-used-value"] }
|
||||
---
|
||||
apiVersion: cilium.io/v2alpha1
|
||||
kind: CiliumBGPPeerConfig
|
||||
metadata:
|
||||
name: bgp-peer-config-v4
|
||||
spec:
|
||||
families:
|
||||
- afi: ipv4
|
||||
safi: unicast
|
||||
advertisements:
|
||||
matchLabels:
|
||||
advertise: bgp
|
||||
---
|
||||
apiVersion: cilium.io/v2alpha1
|
||||
kind: CiliumBGPClusterConfig
|
||||
metadata:
|
||||
name: bgp-cluster-config
|
||||
spec:
|
||||
nodeSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/os: linux
|
||||
bgpInstances:
|
||||
- name: instance-#{ cilium.bgp.node_asn }#
|
||||
localASN: #{ cilium.bgp.node_asn }#
|
||||
peers:
|
||||
- name: peer-#{ cilium.bgp.router_asn }#-v4
|
||||
peerASN: #{ cilium.bgp.router_asn }#
|
||||
peerAddress: #{ cilium.bgp.router_addr }#
|
||||
peerConfigRef:
|
||||
name: bgp-peer-config-v4
|
||||
#% endif %#
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: OCIRepository
|
||||
metadata:
|
||||
name: cilium
|
||||
spec:
|
||||
interval: 15m
|
||||
layerSelector:
|
||||
mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
|
||||
operation: copy
|
||||
ref:
|
||||
tag: 1.20.1
|
||||
url: oci://quay.io/cilium/charts/cilium
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user