blob: ede0b18c0e881aec8a06530ef7c188b0ce164ea6 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
# shuriken-sync: a SEPARATE CronJob that publishes the generated
# /data/shuriken.sh/<site>/dist trees to the public web servers (fishfinger +
# blowfish) over the rsync DAEMON protocol (no SSH, no key). The frontends run
# rsyncd via inetd with `hosts allow = *.wg0.wan.buetow.org,*.wg0,localhost`; the
# k3s pods run on r-nodes that have .wg0 (WireGuard) connectivity, so they match
# and can push over the mesh. The writable modules `irregular-ninja` and
# `alt-irregular-ninja` are declared in frontends/etc/rsyncd.conf.tpl (deploy
# with `rex -f frontends/Rexfile rsync`).
#
# It only publishes when a generation has COMPLETED since the last sync:
# shuriken deletes dist/status.json at the start of a run and writes it last on
# success, so status.json's presence+freshness is the "completed" signal. Most
# ticks are no-ops (one stat); a publish fires once after each successful daily
# generation. The shuriken `--sync` over SSH stays available as an option; this
# cron job just uses the rsync protocol instead.
#
# The nfs-check initContainer refuses to start if NFS is down on the node, so
# rsync --delete can never run against an empty/stale source and wipe the live
# public site.
apiVersion: batch/v1
kind: CronJob
metadata:
name: shuriken-sync
namespace: services
spec:
# Every 30 min -- most ticks skip (no fresh generation); a publish fires soon
# after the daily 04:00 generation completes. Cheap: one stat per tick.
schedule: "*/30 * * * *"
timeZone: Europe/Sofia
concurrencyPolicy: Forbid
startingDeadlineSeconds: 300
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 0
activeDeadlineSeconds: 3600
template:
spec:
restartPolicy: Never
hostAliases:
# The frontends are reached over the WireGuard mesh (.wg0); cluster
# DNS does not resolve *.wg0 names, so pin them to the mesh IPs.
- ip: 192.168.2.111
hostnames:
- fishfinger.wg0
- fishfinger.wg0.wan.buetow.org
- ip: 192.168.2.110
hostnames:
- blowfish.wg0
- blowfish.wg0.wan.buetow.org
initContainers:
- name: nfs-check
image: busybox:stable
command:
- sh
- -c
- |
test -f /mnt/shuriken.sh/.nfs-sentinel || (
echo "ERROR: NFS sentinel missing at /mnt/shuriken.sh/.nfs-sentinel"
echo "refusing to publish; NFS likely unmounted on this node"
echo "rsync --delete against an empty/stale source would wipe the live site"
exit 1
)
volumeMounts:
- name: data
mountPath: /mnt
readOnly: true
containers:
- name: shuriken-sync
image: registry.lan.buetow.org:30001/shuriken:0.14.0
imagePullPolicy: Always
command: ["/bin/bash", "-c"]
args:
- |
set -euo pipefail
D=/data/shuriken.sh
# Mutually exclude with the generation CronJob: don't publish
# while a generation is running. Non-blocking -- if the lock is
# held (generation in progress), skip this tick and let the next
# one publish once generation finishes. flock auto-releases if
# the generation pod dies, so a crash never wedges the sync.
exec 9>"$D/.lock"
if ! flock -n 9; then
echo "shuriken-sync: generation in progress (lock held); skipping"
exit 0
fi
# Only publish when a generation has completed since the last
# sync. status.json is deleted at the start of a run and
# written last on success, so its presence means "completed"
# and its freshness vs .last-sync means "not yet published".
gen=0
for f in \
"$D/irregular.ninja/dist/status.json" \
"$D/alt.irregular.ninja/dist/status.json"; do
if [ ! -f "$f" ]; then
echo "shuriken-sync: $f absent (generation in progress or failed); skipping"
exit 0
fi
m=$(stat -c %Y "$f")
[ "$m" -gt "$gen" ] && gen=$m
done
last=0
[ -f "$D/.last-sync" ] && last=$(stat -c %Y "$D/.last-sync")
if [ "$gen" -le "$last" ]; then
echo "shuriken-sync: no completed generation since last sync (gen=$gen last=$last); skipping"
exit 0
fi
echo "shuriken-sync: generation completed at $gen (last sync $last); publishing"
for pair in irregular.ninja:irregular-ninja alt.irregular.ninja:alt-irregular-ninja; do
site=${pair%%:*}
mod=${pair##*:}
for srv in fishfinger.wg0 blowfish.wg0; do
echo "shuriken-sync: $site -> rsync://$srv/$mod/"
rsync -a --delete --info=stats1 \
"$D/$site/dist/" "rsync://$srv/$mod/"
done
done
: > "$D/.last-sync"
echo "shuriken-sync: done"
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: "1"
memory: 512Mi
securityContext:
allowPrivilegeEscalation: false
runAsUser: 0
runAsGroup: 0
volumeMounts:
- name: data
mountPath: /data
- name: tmp
mountPath: /tmp
volumes:
- name: data
persistentVolumeClaim:
claimName: shuriken-data-pvc
- name: tmp
emptyDir:
sizeLimit: 256Mi
|