feat: Initial commit, empty from template.

This commit is contained in:
2026-01-27 23:27:14 +01:00
commit f77a808d74
133 changed files with 4479 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
---
version: '3'
tasks:
talos:
desc: Bootstrap the Talos cluster
dir: '{{.TALOS_DIR}}'
cmds:
- '[ -f talsecret.sops.yaml ] || talhelper gensecret | sops --filename-override talos/talsecret.sops.yaml --encrypt /dev/stdin > talsecret.sops.yaml'
- talhelper genconfig
- talhelper gencommand apply --extra-flags="--insecure" | bash
- until talhelper gencommand bootstrap | bash; do sleep 10; done
- until talhelper gencommand kubeconfig --extra-flags="{{.ROOT_DIR}} --force" | bash; do sleep 10; done
preconditions:
- test -f {{.ROOT_DIR}}/.sops.yaml
- test -f {{.SOPS_AGE_KEY_FILE}}
- test -f {{.TALOS_DIR}}/talconfig.yaml
- which talhelper talosctl sops
apps:
desc: Bootstrap apps into the Talos cluster
cmd: bash {{.SCRIPTS_DIR}}/bootstrap-apps.sh
preconditions:
- msg: Unsupported bash version, run `brew install bash` to upgrade
sh: '{{if eq OS "darwin"}}test -f /opt/homebrew/bin/bash || test -f /usr/local/bin/bash{{end}}'
- test -f {{.KUBECONFIG}}
- test -f {{.ROOT_DIR}}/.sops.yaml
- test -f {{.SCRIPTS_DIR}}/bootstrap-apps.sh
- test -f {{.SOPS_AGE_KEY_FILE}}

View File

@@ -0,0 +1,65 @@
---
version: '3'
tasks:
generate-config:
desc: Generate Talos configuration
dir: '{{.TALOS_DIR}}'
cmd: talhelper genconfig
preconditions:
- test -f {{.TALOS_DIR}}/talconfig.yaml
- test -f {{.ROOT_DIR}}/.sops.yaml
- test -f {{.SOPS_AGE_KEY_FILE}}
- which talhelper
apply-node:
desc: Apply Talos config to a node [IP=required]
dir: '{{.TALOS_DIR}}'
cmd: talhelper gencommand apply --node {{.IP}} --extra-flags '--mode={{.MODE}}' | bash
vars:
MODE: '{{.MODE | default "auto"}}'
requires:
vars: [IP]
preconditions:
- talosctl --nodes {{.IP}} get machineconfig
- talosctl config info
- test -f {{.TALOSCONFIG}}
- which talhelper talosctl yq
upgrade-node:
desc: Upgrade Talos on a single node [IP=required]
dir: '{{.TALOS_DIR}}'
cmd: talhelper gencommand upgrade --node {{.IP}} --extra-flags "--image='{{.TALOS_IMAGE}}:{{.TALOS_VERSION}}' --timeout=10m" | bash
vars:
TALOS_IMAGE:
sh: yq '.nodes[] | select(.ipAddress == "{{.IP}}") | .talosImageURL' {{.TALOS_DIR}}/talconfig.yaml
TALOS_VERSION:
sh: yq '.talosVersion' {{.TALOS_DIR}}/talenv.yaml
requires:
vars: [IP]
preconditions:
- talosctl --nodes {{.IP}} get machineconfig
- talosctl config info
- test -f {{.TALOSCONFIG}}
- which kubectl talhelper talosctl yq
upgrade-k8s:
desc: Upgrade Kubernetes
dir: '{{.TALOS_DIR}}'
cmd: talhelper gencommand upgrade-k8s --extra-flags "--to '{{.KUBERNETES_VERSION}}'" | bash
vars:
KUBERNETES_VERSION:
sh: yq '.kubernetesVersion' {{.TALOS_DIR}}/talenv.yaml
preconditions:
- talosctl config info
- test -f {{.TALOSCONFIG}}
- which talhelper talosctl yq
reset:
desc: Resets nodes back to maintenance mode
dir: '{{.TALOS_DIR}}'
prompt: This will destroy your cluster and reset the nodes back to maintenance mode... continue?
cmd: talhelper gencommand reset --extra-flags="--reboot {{- if eq .CLI_FORCE false }} --system-labels-to-wipe STATE --system-labels-to-wipe EPHEMERAL{{ end }} --graceful=false --wait=false" | bash
preconditions:
- which talhelper

View File

@@ -0,0 +1,170 @@
---
version: '3'
vars:
MAKEJINJA_CONFIG_FILE: '{{.ROOT_DIR}}/makejinja.toml'
TEMPLATE_DIR: '{{.ROOT_DIR}}/templates'
TEMPLATE_RESOURCES_DIR: '{{.ROOT_DIR}}/.taskfiles/template/resources'
TEMPLATE_CONFIG_FILE: '{{.ROOT_DIR}}/cluster.yaml'
TEMPLATE_NODE_CONFIG_FILE: '{{.ROOT_DIR}}/nodes.yaml'
tasks:
:init:
desc: Initialize configuration files
cmds:
- task: generate-template-config
- task: generate-age-key
- task: generate-deploy-key
- task: generate-push-token
generate-template-config:
internal: true
cmds:
- mv {{.TEMPLATE_CONFIG_FILE | replace ".yaml" ".sample.yaml"}} {{.TEMPLATE_CONFIG_FILE}}
- mv {{.TEMPLATE_NODE_CONFIG_FILE | replace ".yaml" ".sample.yaml"}} {{.TEMPLATE_NODE_CONFIG_FILE}}
status:
- test -f {{.TEMPLATE_CONFIG_FILE}}
- test -f {{.TEMPLATE_NODE_CONFIG_FILE}}
generate-age-key:
internal: true
cmd: age-keygen --output {{.SOPS_AGE_KEY_FILE}}
status:
- test -f {{.SOPS_AGE_KEY_FILE}}
preconditions:
- which age-keygen
generate-deploy-key:
internal: true
cmd: ssh-keygen -t ed25519 -C "deploy-key" -f {{.ROOT_DIR}}/github-deploy.key -q -P ""
status:
- test -f {{.ROOT_DIR}}/github-deploy.key
preconditions:
- which ssh-keygen
generate-push-token:
internal: true
cmd: python -c "import secrets; print(secrets.token_hex(16))" > {{.ROOT_DIR}}/github-push-token.txt
status:
- test -f {{.ROOT_DIR}}/github-push-token.txt
:configure:
desc: Render and validate configuration files
prompt: Any conflicting files in the kubernetes directory will be overwritten... continue?
cmds:
- task: validate-schemas
- task: render-configs
- task: encrypt-secrets
- task: validate-kubernetes-config
- task: validate-talos-config
preconditions:
- msg: An existing Age key interferes with the age key in this repository, rename or delete ~/.config/sops/age/keys.txt
sh: '! test -f ~/.config/sops/age/keys.txt'
- msg: File cluster.yaml not found, did you run `task init`?
sh: test -f {{.TEMPLATE_CONFIG_FILE}}
- msg: File nodes.yaml not found, did you run `task init`?
sh: test -f {{.TEMPLATE_NODE_CONFIG_FILE}}
- msg: File cloudflare-tunnel.json not found, see the README for information on creating it.
sh: test -f {{.ROOT_DIR}}/cloudflare-tunnel.json
validate-schemas:
internal: true
cmds:
- cue vet {{.TEMPLATE_CONFIG_FILE}} {{.TEMPLATE_RESOURCES_DIR}}/cluster.schema.cue
- cue vet {{.TEMPLATE_NODE_CONFIG_FILE}} {{.TEMPLATE_RESOURCES_DIR}}/nodes.schema.cue
preconditions:
- test -f {{.TEMPLATE_RESOURCES_DIR}}/cluster.schema.cue
- test -f {{.TEMPLATE_RESOURCES_DIR}}/nodes.schema.cue
- which cue
render-configs:
internal: true
cmd: makejinja
env:
PYTHONDONTWRITEBYTECODE: '1'
preconditions:
- test -f {{.TEMPLATE_DIR}}/scripts/plugin.py
- test -f {{.MAKEJINJA_CONFIG_FILE}}
- which makejinja
encrypt-secrets:
internal: true
cmds:
- for: { var: SECRET_FILES }
cmd: |
if [ $(sops filestatus "{{.ITEM}}" | jq ".encrypted") == "false" ]; then
sops --encrypt --in-place "{{.ITEM}}"
fi
vars:
SECRET_FILES:
sh: find "{{.BOOTSTRAP_DIR}}" "{{.KUBERNETES_DIR}}" "{{.TALOS_DIR}}" -type f -name "*.sops.*" -print
preconditions:
- test -f {{.SOPS_AGE_KEY_FILE}}
- test -f {{.ROOT_DIR}}/.sops.yaml
- which jq sops
validate-kubernetes-config:
internal: true
cmd: bash {{.TEMPLATE_RESOURCES_DIR}}/kubeconform.sh {{.KUBERNETES_DIR}}
preconditions:
- test -f {{.TEMPLATE_RESOURCES_DIR}}/kubeconform.sh
- which kubeconform
validate-talos-config:
internal: true
dir: '{{.TALOS_DIR}}'
cmd: talhelper validate talconfig {{.TALOS_DIR}}/talconfig.yaml
preconditions:
- test -f {{.TALOS_DIR}}/talconfig.yaml
- which talhelper
debug:
desc: Gather common resources in your cluster
cmds:
- for:
matrix:
RESOURCE: [certificates, certificaterequests, gitrepositories, helmrepositories, helmreleases, httproutes, kustomizations, nodes, pods]
cmd: kubectl get --all-namespaces {{.ITEM.RESOURCE}}
preconditions:
- test -f {{.KUBECONFIG}}
- which kubectl
tidy:
desc: Archive template related files and directories
prompt: All files and directories related to the templating process will be archived... continue?
cmds:
- mkdir -p {{.TIDY_FOLDER}}
- rm -rf {{.ROOT_DIR}}/.github/tests
- rm -rf {{.ROOT_DIR}}/.github/workflows/e2e.yaml
- rm -rf {{.ROOT_DIR}}/.github/workflows/mise.yaml
- rm -rf {{.ROOT_DIR}}/.github/workflows/release.yaml
- |
{{.SED}} -i 's/(..\.j2)\?//g' {{.ROOT_DIR}}/.renovaterc.json5
- mv {{.TEMPLATE_DIR}} {{.TIDY_FOLDER}}/templates
- mv {{.MAKEJINJA_CONFIG_FILE}} {{.TIDY_FOLDER}}/makejinja.toml
- mv {{.TEMPLATE_CONFIG_FILE}} {{.TIDY_FOLDER}}/cluster.yaml
- mv {{.TEMPLATE_NODE_CONFIG_FILE}} {{.TIDY_FOLDER}}/nodes.yaml
- |
{{.SED}} -i '/template:/d' {{.ROOT_DIR}}/Taskfile.yaml
- mv {{.ROOT_DIR}}/.taskfiles/template {{.TIDY_FOLDER}}/.taskfiles/
vars:
TIDY_FOLDER: '{{.PRIVATE_DIR}}/{{now | unixEpoch}}'
SED:
sh: which gsed || which sed
preconditions:
- msg: Unsupported sed version, run `brew install gsed` to upgrade
sh: '{{if eq OS "darwin"}}test -f /opt/homebrew/bin/gsed || test -f /usr/local/bin/gsed{{end}}'
- test -d {{.ROOT_DIR}}/.taskfiles/template
- test -d {{.TEMPLATE_DIR}}
- test -f {{.MAKEJINJA_CONFIG_FILE}}
- test -f {{.ROOT_DIR}}/.renovaterc.json5
reset:
desc: Remove templated files and directories
prompt: Remove all templated files and directories... continue?
cmds:
- rm -rf {{.BOOTSTRAP_DIR}}
- rm -rf {{.KUBERNETES_DIR}}
- rm -rf {{.TALOS_DIR}}
- rm -rf {{.ROOT_DIR}}/.sops.yaml

View File

@@ -0,0 +1,31 @@
package config
import (
"net"
)
#Config: {
node_cidr: net.IPCIDR & !=cluster_pod_cidr & !=cluster_svc_cidr
node_dns_servers?: [...net.IPv4]
node_ntp_servers?: [...net.IPv4]
node_default_gateway?: net.IPv4 & !=""
node_vlan_tag?: string & !=""
cluster_pod_cidr: *"10.42.0.0/16" | net.IPCIDR & !=node_cidr & !=cluster_svc_cidr
cluster_svc_cidr: *"10.43.0.0/16" | net.IPCIDR & !=node_cidr & !=cluster_pod_cidr
cluster_api_addr: net.IPv4
cluster_api_tls_sans?: [...net.FQDN]
cluster_gateway_addr: net.IPv4 & !=cluster_api_addr & !=cluster_dns_gateway_addr & !=cloudflare_gateway_addr
cluster_dns_gateway_addr: net.IPv4 & !=cluster_api_addr & !=cluster_gateway_addr & !=cloudflare_gateway_addr
repository_name: string
repository_branch?: string & !=""
repository_visibility?: *"public" | "private"
cloudflare_domain: net.FQDN
cloudflare_token: string
cloudflare_gateway_addr: net.IPv4 & !=cluster_api_addr & !=cluster_gateway_addr & !=cluster_dns_gateway_addr
cilium_bgp_router_addr?: net.IPv4 & !=""
cilium_bgp_router_asn?: string & !=""
cilium_bgp_node_asn?: string & !=""
cilium_loadbalancer_mode?: *"dsr" | "snat"
}
#Config

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env bash
set -euo pipefail
KUBERNETES_DIR=$1
[[ -z "${KUBERNETES_DIR}" ]] && echo "Kubernetes location not specified" && exit 1
kustomize_args=("--load-restrictor=LoadRestrictionsNone")
kustomize_config="kustomization.yaml"
kubeconform_args=(
"-strict"
"-ignore-missing-schemas"
"-skip"
"Gateway,HTTPRoute,Secret"
"-schema-location"
"default"
"-schema-location"
"https://kubernetes-schemas.pages.dev/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json"
"-verbose"
)
echo "=== Validating standalone manifests in ${KUBERNETES_DIR}/flux ==="
find "${KUBERNETES_DIR}/flux" -maxdepth 1 -type f -name '*.yaml' -print0 | while IFS= read -r -d $'\0' file;
do
kubeconform "${kubeconform_args[@]}" "${file}"
if [[ ${PIPESTATUS[0]} != 0 ]]; then
exit 1
fi
done
echo "=== Validating kustomizations in ${KUBERNETES_DIR}/flux ==="
find "${KUBERNETES_DIR}/flux" -type f -name $kustomize_config -print0 | while IFS= read -r -d $'\0' file;
do
echo "=== Validating kustomizations in ${file/%$kustomize_config} ==="
kustomize build "${file/%$kustomize_config}" "${kustomize_args[@]}" | kubeconform "${kubeconform_args[@]}"
if [[ ${PIPESTATUS[0]} != 0 ]]; then
exit 1
fi
done
echo "=== Validating kustomizations in ${KUBERNETES_DIR}/apps ==="
find "${KUBERNETES_DIR}/apps" -type f -name $kustomize_config -print0 | while IFS= read -r -d $'\0' file;
do
echo "=== Validating kustomizations in ${file/%$kustomize_config} ==="
kustomize build "${file/%$kustomize_config}" "${kustomize_args[@]}" | kubeconform "${kubeconform_args[@]}"
if [[ ${PIPESTATUS[0]} != 0 ]]; then
exit 1
fi
done

View File

@@ -0,0 +1,30 @@
package config
import (
"net"
"list"
)
#Config: {
nodes: [...#Node]
_nodes_check: {
name: list.UniqueItems() & [for item in nodes {item.name}]
address: list.UniqueItems() & [for item in nodes {item.address}]
mac_addr: list.UniqueItems() & [for item in nodes {item.mac_addr}]
}
}
#Node: {
name: =~"^[a-z0-9][a-z0-9\\-]{0,61}[a-z0-9]$|^[a-z0-9]$" & !="global" & !="controller" & !="worker"
address: net.IPv4
controller: bool
disk: string
mac_addr: =~"^([0-9a-f]{2}[:]){5}([0-9a-f]{2})$"
schematic_id: =~"^[a-z0-9]{64}$"
mtu?: >=1450 & <=9000
secureboot?: bool
encrypt_disk?: bool
kernel_modules?: [...string]
}
#Config