Skip to content

Commit 32a4a51

Browse files
author
Gunther Klessinger
committed
Initial: cluster creation and proxy recreation
Proxy Style, i.e. with only one pub ip Incl. DNS
1 parent 2cd281a commit 32a4a51

21 files changed

+1742
-0
lines changed

.gitignore

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Python-generated files
2+
__pycache__/
3+
*.py[oc]
4+
build/
5+
dist/
6+
wheels/
7+
*.egg-info
8+
9+
# Virtual environments
10+
.venv
11+
README.md
12+
mysecrets.py

environ

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# vim: ft=sh
2+
# shellcheck disable=all
3+
4+
NAME="company"
5+
6+
DNS_API_TOKEN=""
7+
DNS_PROVIDER="digitalocean"
8+
DOMAIN="py:keyval"
9+
EMAIL="py:keyval"
10+
FN_SSH_KEY="$HOME/.ssh/hetzner-cluster-lois"
11+
12+
HK_HOST_NETWORK=8
13+
HK_VER="2.2.3"
14+
HK_LOCATION="fsn1"
15+
16+
# flux
17+
GITOPS_BRANCH="main"
18+
GITOPS_HOST="py:keyval"
19+
GITOPS_OWNER="company"
20+
GITOPS_PATH="clusters/staging"
21+
GITOPS_REPO="k8s"
22+
GITOPS_TOKEN=""
23+
24+
DNS_API_TOKEN="pass:DO/pat"
25+
GITOPS_TOKEN="pass:AX/gitlabflux"
26+
HCLOUD_TOKEN="pass:HCloudLoisRO"
27+
HCLOUD_TOKEN_WRITE="pass:HCloudLois"

justfile

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# https://cheatography.com/linux-china/cheat-sheets/justfile/
2+
set dotenv-filename := 'environ'
3+
set dotenv-load := true
4+
set dotenv-required := true
5+
set export
6+
set unstable
7+
set script-interpreter := ['uv', 'run']
8+
alias c := create-cluster
9+
alias cfg := render-config
10+
alias t := test
11+
alias p := pyhk3
12+
alias pf := port-forward
13+
alias elk := download-kubectl
14+
15+
default:
16+
@just --list
17+
18+
pyhk3 *ARGS:
19+
uv run pyhk3 {{ARGS}}
20+
21+
do *ARGS:
22+
just p do {{ARGS}}
23+
24+
ssh *ARGS:
25+
just p do ssh {{ARGS}}
26+
27+
render-config:
28+
just p hk3s render_config
29+
30+
port-forward:
31+
just p do port_forward
32+
33+
[confirm('Sure to destroy all servers of the cluster?')]
34+
rm:
35+
just p do remove all
36+
37+
38+
39+
[confirm('Sure to destroy proxy (if existing) and recreate it?')]
40+
recover-proxy:
41+
just p do delete proxy
42+
just p create proxy proxylb dns
43+
just p recover hk3sconfig
44+
just p recover kubeconfig
45+
46+
create-cluster:
47+
just p create proxy k3s proxylb dns
48+
49+
download-kubectl:
50+
just p do download_kubectl
51+
52+
test:
53+
uv run pytest ./tests/test_setup.py

keyval.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
Custom calc-ed values for <key>='py:<this module path>' constructs
3+
❗ module path w/o '.py'
4+
5+
Evaluated only once, not per key.
6+
"""
7+
8+
try:
9+
from mysecrets import sec # whatever
10+
except ImportError:
11+
sec = {}
12+
13+
14+
DOMAIN = sec.get('DOMAIN', 'cluster.company.net')
15+
EMAIL = sec.get('EMAIL', '[email protected]')
16+
GITOPS_HOST = sec.get('GITOPS_HOST', 'gitlab.company.com')

pyproject.toml

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
[project]
2+
name = "pyhk3"
3+
version = "0.1.0"
4+
description = "Add your description here"
5+
readme = "README.md"
6+
requires-python = ">=3.10"
7+
authors = [{ name = "Gunther Klessinger", email = "[email protected]" }]
8+
dependencies = [
9+
"absl-py>=2.1.0",
10+
"pyyaml>=6.0.2",
11+
"requests>=2.32.3",
12+
"rich>=13.9.4",
13+
"sh>=2.2.1",
14+
"structlog>=25.1.0",
15+
]
16+
17+
18+
[build-system]
19+
requires = ["hatchling"]
20+
build-backend = "hatchling.build"
21+
22+
23+
[tool.hatch.build.targets.sdist]
24+
include = ["src/pyhk3"]
25+
26+
27+
[dependency-groups]
28+
dev = ["pytest>=8.3.4"]
29+
30+
31+
[project.scripts]
32+
pyhk3 = "pyhk3.cli:main"
33+
34+
35+
[tool.pyright]
36+
# https://github.com/microsoft/pyright/blob/main/docs/configuration.md
37+
# ruff is enough
38+
reportSelfClsParameterName = false
39+
pythonPlatform = "Linux"
40+
typeCheckingMode = "off"
41+
reportMissingImports = true
42+
reportMissingTypeStubs = false
43+
reportUndefinedVariable = true
44+
reportGeneralTypeIssues = false
45+
reportUnusedExpression = false
46+
root = "src"
47+
include = ["src", "tests"]
48+
# pythonVersion = "3.8"
49+
executionEnvironments = [{ root = "src" }, { root = "tests" }]
50+
venvPath = "./"
51+
venv = ".venv"
52+
53+
[tool.ruff]
54+
# https://docs.astral.sh/ruff/rules/
55+
line-length = 90
56+
extend-select = ["Q"]
57+
select = ["E", "F", "B"] # Enable flake8-bugbear (`B`) rules.
58+
ignore = [
59+
"E501", # Never enforce `E501` (line length violations).
60+
"E741", # short var names
61+
"E731", # no lambda
62+
"B006", # mutables in signature
63+
]
64+
65+
[tool.ruff.lint]
66+
fixable = ["ALL"]
67+
unfixable = [
68+
"B", # Avoid trying to fix flake8-bugbear (`B`) violations.
69+
"F401", # Unused Import
70+
"F841", # variable assigned but not used
71+
]
72+
73+
74+
[tool.ruff.flake8-quotes]
75+
docstring-quotes = "double"
76+
inline-quotes = "single"
77+
78+
[tool.ruff.format]
79+
# Prefer single quotes over double quotes
80+
quote-style = "single"

src/pyhk3/__init__.py

Whitespace-only changes.

src/pyhk3/add.py

Whitespace-only changes.

src/pyhk3/assets/create_templ.py

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
T_UNIT_FWD = (
2+
lambda net: f"""
3+
[Unit]
4+
Description=Bastion IP Forwarder
5+
6+
[Service]
7+
Type=oneshot
8+
ExecStart=/bin/bash -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'
9+
ExecStart=/bin/bash -c 'iptables -t nat -A POSTROUTING -s "10.{net}.0.0/16" -o eth0 -j MASQUERADE'
10+
11+
[Install]
12+
WantedBy=multi-user.target
13+
"""
14+
)
15+
16+
17+
T_SSHD = """
18+
grep -q "HCLOUD_TOKEN" /etc/sshd/sshd_config || {
19+
echo 'AcceptEnv HCLOUD_TOKEN' >> /etc/ssh/sshd_config
20+
echo 'PasswordAuthentication no' >> /etc/ssh/sshd_config
21+
echo 'ClientAliveInterval 60' >> /etc/ssh/sshd_config
22+
echo 'ClientAliveCountMax 4' >> /etc/ssh/sshd_config
23+
systemctl daemon-reload
24+
systemctl reload sshd || systemctl restart ssh
25+
}
26+
type binenv 2>/dev/null || sed -i '1iexport PATH="$HOME/.binenv:$PATH"' ~/.bashrc
27+
test -e "/root/.ssh/id_ed25519" || ssh-keygen -q -t ecdsa -N '' -f "/root/.ssh/id_ed25519" >/dev/null
28+
touch /etc/postinstalled
29+
"""
30+
31+
32+
T_INST_TOOLS_PROXY = """
33+
function have_ { type "$1" 2>/dev/null ; }
34+
function install_binenv {
35+
echo "Installing $binenv"
36+
wget -q "https://raw.githubusercontent.com/axgkl/binaries/refs/heads/master/binenv-install.sh" -O - | bash
37+
have_ "binenv" || { echo "binenv install failed" && exit 1; }
38+
}
39+
export URL_BINENV_DISTRIS="%(URL_BINENV_DISTRIS|)s"
40+
export BINENV_TOOLS="%(BINENV_TOOLS_PROXY|)s %(BINENV_ADD_TOOLS_PROXY|)s"
41+
have_ "binenv" || install_binenv
42+
eval "binenv install $BINENV_TOOLS"
43+
"""
44+
45+
46+
T_CADDY = (
47+
lambda cfg: f"""
48+
useradd -m -d /home/caddy caddy || true
49+
mkdir -p /opt/caddy
50+
test -f /root/caddy && mv /root/caddy /opt/caddy/caddy
51+
chmod +x /opt/caddy/caddy
52+
echo -e '{cfg}' > /opt/caddy/config.json
53+
chown -R caddy:caddy /opt/caddy
54+
echo -e '
55+
[Unit]
56+
Description=Caddy Web Server
57+
Documentation=https://caddyserver.com/docs/
58+
After=network.target
59+
60+
[Service]
61+
User=caddy
62+
ExecStartPre=/usr/bin/mkdir -p /home/caddy/.config
63+
ExecStart=/opt/caddy/caddy run --config /opt/caddy/config.json
64+
TimeoutStopSec=5s
65+
LimitNOFILE=1048576
66+
LimitNPROC=512
67+
PrivateTmp=true
68+
ProtectSystem=full
69+
AmbientCapabilities=CAP_NET_BIND_SERVICE
70+
71+
[Install]
72+
WantedBy=multi-user.target
73+
' > /etc/systemd/system/caddy.service
74+
systemctl daemon-reload
75+
for a in enable restart; do systemctl $a caddy; done
76+
systemctl status caddy
77+
"""
78+
)

src/pyhk3/assets/hk3s.yml

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
cluster_name: "%(NAME)s"
3+
kubeconfig_path: "/root/.kube/config"
4+
k3s_version: "%(HK_VER_K3S)s"
5+
networking:
6+
ssh:
7+
port: 22
8+
use_agent: false # set to true if your key has a passphrase
9+
public_key_path: "~/.ssh/id_ed25519.pub"
10+
private_key_path: "~/.ssh/id_ed25519"
11+
allowed_networks:
12+
ssh:
13+
- 0.0.0.0/0
14+
api:
15+
- 0.0.0.0/0
16+
public_network:
17+
ipv4: false
18+
ipv6: true
19+
private_network:
20+
enabled: true
21+
subnet: 10.%(HK_HOST_NETWORK)s.0.0/16
22+
existing_network_name: ten-%(HK_HOST_NETWORK)s
23+
cni:
24+
enabled: true
25+
encryption: false
26+
mode: T(HK_CNI)s
27+
cluster_cidr: "%(HK_CIDR_CLUSTER)s"
28+
service_cidr: "%(HK_CIDR_SERVICE)s"
29+
cluster_dns: "%(HK_DNS_CLUSTER)s"
30+
datastore:
31+
mode: etcd # etcd (default) or external
32+
external_datastore_endpoint: postgres://....
33+
schedule_workloads_on_masters: T(HK_MASTERS_ARE_WORKERS)s
34+
create_load_balancer_for_the_kubernetes_api: false
35+
protect_against_deletion: false
36+
37+
masters_pool:
38+
instance_type: "%(HK_MASTERS_TYPE)s"
39+
instance_count: T(HK_MASTERS_COUNT)s
40+
location: "%(HK_LOCATION)s"
41+
image: "%(HK_MASTERS_IMG)s"
42+
43+
worker_node_pools:
44+
- name: "%(NAME)s-small-static"
45+
instance_type: "%(HK_WORKERS_TYPE)s"
46+
instance_count: T(HK_WORKERS_COUNT)s
47+
location: "%(HK_LOCATION)s"
48+
image: "%(HK_WORKERS_IMG)s"
49+
# labels:
50+
# - key: purpose
51+
# value: blah
52+
# taints:
53+
# - key: something
54+
# value: value1:NoSchedule
55+
56+
- name: "%(NAME)s-medium-autoscaled"
57+
instance_type: "%(HK_AUTOSCL_TYPE)s"
58+
location: "%(HK_LOCATION)s"
59+
image: "%(HK_AUTOSCL_IMG)s"
60+
autoscaling:
61+
enabled: true
62+
min_instances: 0
63+
max_instances: T(HK_AUTOSCL_COUNT)s
64+
65+
embedded_registry_mirror:
66+
enabled: T(HK_REGISTRY_MIRROR)s
67+
68+
additional_packages:
69+
- ifupdown
70+
71+
api_server_hostname: "first_master"

src/pyhk3/assets/hk3s_post_create.yml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
post_create_commands:
2+
- echo "Started" > /.status
3+
- timedatectl set-timezone Europe/Berlin
4+
- echo '%(key_proxy)s' >> /root/.ssh/authorized_keys
5+
- echo '%(key_local)s' >> /root/.ssh/authorized_keys
6+
- echo "root:$(head -c 50 /dev/urandom | base64)" | chpasswd
7+
- mkdir -p /etc/network/interfaces.d
8+
- iface="$(ip -o -4 addr list | grep " 10.%(HK_HOST_NETWORK)s." | cut -d " " -f 2)"
9+
- |
10+
cat > /etc/network/interfaces.d/$iface <<EOF
11+
auto $iface
12+
iface $iface inet dhcp
13+
post-up ip route add default via 10.%(HK_HOST_NETWORK)s.0.1
14+
post-up ip route add 169.254.169.254 via 172.31.1.1
15+
EOF
16+
- rm -f /etc/resolv.conf
17+
- |
18+
cat > /etc/resolv.conf <<EOF
19+
nameserver 185.12.64.1
20+
nameserver 185.12.64.2
21+
edns edns0 trust-ad
22+
search .
23+
EOF
24+
- ip route add 169.254.0.0/16 via 172.31.1.1
25+
- ip route add default via 10.%(HK_HOST_NETWORK)s.0.1
26+
- echo "Done" > /.status

src/pyhk3/cache.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
nil = b'\x00'
2+
3+
4+
class Cache:
5+
active = True
6+
7+
def __init__(self):
8+
self.cache = {}
9+
10+
def clear(self, path=None):
11+
if path is None:
12+
return self.cache.clear()
13+
self.cache.pop(path, None)
14+
15+
def get(self, key):
16+
if not self.active:
17+
return nil
18+
return self.cache.get(key, nil)
19+
20+
def set(self, key, value):
21+
self.cache[key] = value
22+
23+
24+
cache = Cache()

0 commit comments

Comments
 (0)