feat: Initial commit, empty from template.
This commit is contained in:
30
.taskfiles/bootstrap/Taskfile.yaml
Normal file
30
.taskfiles/bootstrap/Taskfile.yaml
Normal 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}}
|
||||
65
.taskfiles/talos/Taskfile.yaml
Normal file
65
.taskfiles/talos/Taskfile.yaml
Normal 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
|
||||
170
.taskfiles/template/Taskfile.yaml
Normal file
170
.taskfiles/template/Taskfile.yaml
Normal 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
|
||||
31
.taskfiles/template/resources/cluster.schema.cue
Normal file
31
.taskfiles/template/resources/cluster.schema.cue
Normal 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
|
||||
50
.taskfiles/template/resources/kubeconform.sh
Executable file
50
.taskfiles/template/resources/kubeconform.sh
Executable 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
|
||||
30
.taskfiles/template/resources/nodes.schema.cue
Normal file
30
.taskfiles/template/resources/nodes.schema.cue
Normal 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
|
||||
Reference in New Issue
Block a user