Skip to content

Commit ae3ecbf

Browse files
authored
feat: wg-easy airgap install (#70)
* update cmx-vm-install with airgap option * rewrite images for cert-manager chart * rewrite images for traefik chart * * rewrite images for wg-easy chart * add additionalImages for image in preflight in wg-easy * update HelmChart values mapping to match with bjw-s common chart values * rewrite images for Replicated SDK chart * rewrite images for image in wg-easy preflight * add imagepullsecrets * check if airgap is built before installing on CMX * trigger airgap build if not exist * add task airgap-build * use correct sequence
1 parent 40f9ec4 commit ae3ecbf

File tree

11 files changed

+203
-27
lines changed

11 files changed

+203
-27
lines changed

applications/wg-easy/Taskfile.yaml

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ tasks:
580580
- |
581581
echo "Deleting CMX VM {{.CMX_VM_NAME}}..."
582582
replicated vm rm {{.CMX_VM_NAME}}
583-
583+
584584
cmx-vm-install:
585585
desc: Download and install the app as Embedded Cluster on CMX VM
586586
silent: true
@@ -589,6 +589,7 @@ tasks:
589589
vars:
590590
CHANNEL: '{{.CHANNEL | default "Unstable"}}'
591591
SKIP_INSTALL: '{{.SKIP_INSTALL | default "false"}}'
592+
AIRGAP: '{{.AIRGAP | default "false"}}'
592593
ADMIN_CONSOLE_PASSWORD:
593594
sh: uuidgen | cut -c1-13
594595
deps:
@@ -610,34 +611,125 @@ tasks:
610611
exit 1
611612
fi
612613
614+
# Run airgap-build task if airgap mode is enabled
615+
if [ "{{.AIRGAP}}" = "true" ]; then
616+
echo "Airgap mode enabled, ensuring airgap build is ready..."
617+
task airgap-build
618+
fi
619+
613620
echo "SSH into the VM and download the app binary..."
614621
SSH_BASE_CMD="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
615622
if [ -n "{{.CMX_VM_PUBLIC_KEY}}" ]; then
616623
SSH_BASE_CMD="$SSH_BASE_CMD -i {{.CMX_VM_PUBLIC_KEY}}"
617624
fi
618625
VM_SSH_CMD=$(replicated vm ls --output=json | jq -r ".[] | select(.name == \"{{.CMX_VM_NAME}}\") | \"$SSH_BASE_CMD -p \(.direct_ssh_port) {{.CMX_VM_USER}}@\(.direct_ssh_endpoint)\"")
619626
627+
# Determine download URL based on airgap setting
628+
DOWNLOAD_URL="https://replicated.app/embedded/{{.APP_SLUG}}/{{.CHANNEL}}"
629+
if [ "{{.AIRGAP}}" = "true" ]; then
630+
DOWNLOAD_URL="${DOWNLOAD_URL}?airgap=true"
631+
fi
632+
620633
echo "SSH base command: $SSH_BASE_CMD"
621-
$VM_SSH_CMD << 'EOF'
634+
$VM_SSH_CMD << EOF
622635
set -e
623-
echo 'Downloading {{.APP_NAME}} installer...'
624-
curl -f 'https://replicated.app/embedded/{{.APP_NAME}}/{{.CHANNEL}}' -H 'Authorization: {{.REPLICATED_LICENSE_ID}}' -o {{.APP_NAME}}-{{.CHANNEL}}.tgz
636+
echo 'Downloading {{.APP_SLUG}} installer...'
637+
curl -f '$DOWNLOAD_URL' -H 'Authorization: {{.REPLICATED_LICENSE_ID}}' -o {{.APP_SLUG}}-{{.CHANNEL}}.tgz
625638
626639
echo 'Extracting installer...'
627-
tar -xvzf {{.APP_NAME}}-{{.CHANNEL}}.tgz
640+
tar -xvzf {{.APP_SLUG}}-{{.CHANNEL}}.tgz
641+
642+
echo "Binary is available at ./{{.APP_SLUG}}"
643+
EOF
628644
629-
if [ "{{.SKIP_INSTALL}}" = "false" ]; then
630-
echo "Installing {{.APP_NAME}}..."
631-
sudo ./{{.APP_NAME}} install --license license.yaml --admin-console-password {{.ADMIN_CONSOLE_PASSWORD}} --yes
632-
else
633-
echo "Skipping installation as requested. Binary is available at ./{{.APP_NAME}}"
645+
if [ "{{.AIRGAP}}" = "true" ]; then
646+
echo "Updating network policy for airgap installation..."
647+
NETWORK_ID=$(replicated vm ls --output=json | jq -r ".[] | select(.name == \"{{.CMX_VM_NAME}}\") | .network_id")
648+
replicated network update policy $NETWORK_ID --policy airgap
634649
fi
635-
EOF
636650
637651
if [ "{{.SKIP_INSTALL}}" = "false" ]; then
652+
INSTALL_CMD="sudo ./{{.APP_SLUG}} install --license license.yaml --admin-console-password {{.ADMIN_CONSOLE_PASSWORD}} --yes"
653+
if [ "{{.AIRGAP}}" = "true" ]; then
654+
INSTALL_CMD="${INSTALL_CMD} --airgap-bundle {{.APP_SLUG}}.airgap"
655+
fi
656+
657+
echo "Running installation command: $INSTALL_CMD"
658+
$VM_SSH_CMD << EOF
659+
$INSTALL_CMD
660+
EOF
661+
638662
echo "Exposing port 30000 on the VM..."
639663
replicated vm port expose --port 30000 {{.CMX_VM_NAME}}
640664
641665
echo "Visit above URL to access the Admin Console, password: {{.ADMIN_CONSOLE_PASSWORD}}"
642666
fi
643667
668+
airgap-build:
669+
desc: Check and build airgap bundle for the latest release
670+
silent: true
671+
cmds:
672+
- |
673+
echo "Checking if airgap build is available for latest release in channel {{.RELEASE_CHANNEL}}..."
674+
675+
# Get release list and extract app ID and channel ID
676+
RELEASE_DATA=$(replicated release ls -o json)
677+
APP_ID=$(echo "$RELEASE_DATA" | jq -r '.[0].appId')
678+
CHANNEL_ID=$(echo "$RELEASE_DATA" | jq -r '.[0].activeChannels[] | select(.name == "{{.RELEASE_CHANNEL}}") | .id')
679+
680+
if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then
681+
echo "Error: Could not retrieve app ID from latest releases"
682+
exit 1
683+
fi
684+
685+
if [ -z "$CHANNEL_ID" ] || [ "$CHANNEL_ID" = "null" ]; then
686+
echo "Error: Could not find channel ID for channel {{.RELEASE_CHANNEL}}"
687+
exit 1
688+
fi
689+
690+
echo "Found app ID: $APP_ID, channel ID: $CHANNEL_ID"
691+
692+
# Get channel releases and check airgap build status
693+
CHANNEL_RELEASES=$(replicated api get "v3/app/$APP_ID/channel/$CHANNEL_ID/releases")
694+
AIRGAP_BUILD_STATUS=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].airgapBuildStatus // "none"')
695+
AIRGAP_BUILD_ERROR=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].airgapBuildError // "none"')
696+
AIRGAP_BUNDLE_IMAGES=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].airgapBundleImages // "none"')
697+
AIRGAP_LATEST_SEQUENCE=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].channelSequence')
698+
699+
echo "Airgap build status: $AIRGAP_BUILD_STATUS"
700+
701+
if [ "$AIRGAP_BUILD_STATUS" = "built" ]; then
702+
echo "Airgap is already buit for sequence $AIRGAP_LATEST_SEQUENCE"
703+
echo "Airgap bundle images: $AIRGAP_BUNDLE_IMAGES"
704+
exit 0
705+
fi
706+
707+
if [ "$AIRGAP_BUILD_STATUS" = "metadata" ]; then
708+
echo "Airgap has not been built yet. Triggering build..."
709+
replicated api post "v3/app/$APP_ID/channel/$CHANNEL_ID/release/$AIRGAP_LATEST_SEQUENCE/airgap/build"
710+
fi
711+
712+
echo "Airgap build triggered. Polling every 10 seconds for up to 5 minutes..."
713+
for i in $(seq 1 30); do
714+
echo "Checking airgap build status... (attempt $i/30)"
715+
716+
CHANNEL_RELEASES=$(replicated api get "v3/app/$APP_ID/channel/$CHANNEL_ID/releases")
717+
AIRGAP_BUILD_STATUS=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].airgapBuildStatus // "none"')
718+
AIRGAP_BUILD_ERROR=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].airgapBuildError // "none"')
719+
AIRGAP_BUNDLE_IMAGES=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].airgapBundleImages // "none"')
720+
AIRGAP_LATEST_SEQUENCE=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].channelSequence')
721+
722+
echo "Airgap build current status: $AIRGAP_BUILD_STATUS"
723+
724+
if [ "$AIRGAP_BUILD_STATUS" = "built" ]; then
725+
echo "Airgap build completed successfully!"
726+
echo "Airgap bundle images: $AIRGAP_BUNDLE_IMAGES"
727+
exit 0
728+
fi
729+
sleep 10
730+
done
731+
732+
echo "Timeout: Airgap build did not complete within 5 minutes."
733+
echo "Last build status: $AIRGAP_BUILD_STATUS"
734+
echo "Last build error: $AIRGAP_BUILD_ERROR"
735+
exit 1

applications/wg-easy/charts/cert-manager/replicated/helmChart-cert-manager.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,30 @@ spec:
88
weight: 0
99
helmUpgradeFlags:
1010
- --wait
11+
values:
12+
cert-manager:
13+
global:
14+
imagePullSecrets:
15+
- name: '{{repl ImagePullSecretName }}'
16+
image:
17+
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "quay.io" }}'
18+
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "jetstack" }}/cert-manager-controller'
19+
webhook:
20+
image:
21+
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "quay.io" }}'
22+
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "jetstack" }}/cert-manager-webhook'
23+
cainjector:
24+
image:
25+
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "quay.io" }}'
26+
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "jetstack" }}/cert-manager-cainjector'
27+
acmesolver:
28+
image:
29+
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "quay.io" }}'
30+
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "jetstack" }}/cert-manager-acmesolver'
31+
startupapicheck:
32+
image:
33+
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "quay.io" }}'
34+
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "jetstack" }}/cert-manager-startupapicheck'
35+
1136
namespace: cert-manager
1237
builder: {}

applications/wg-easy/charts/cert-manager/values.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,45 @@ cert-manager:
44
# Override the namespace used to store the ConfigMap for leader election
55
namespace: "cert-manager"
66
installCRDs: true
7+
# Controller image configuration
8+
image:
9+
registry: quay.io
10+
repository: jetstack/cert-manager-controller
711
extraArgs:
812
- --cluster-resource-namespace=cert-manager
913
- --enable-certificate-owner-ref=true
1014
resources:
1115
requests:
1216
cpu: 5m
1317
memory: 45Mi
18+
# Webhook image configuration
1419
webhook:
20+
image:
21+
registry: quay.io
22+
repository: jetstack/cert-manager-webhook
1523
resources:
1624
requests:
1725
cpu: 5m
1826
memory: 22Mi
27+
# CA Injector image configuration
1928
cainjector:
29+
image:
30+
registry: quay.io
31+
repository: jetstack/cert-manager-cainjector
2032
resources:
2133
requests:
2234
cpu: 5m
2335
memory: 101Mi
36+
# ACME Solver image configuration
37+
acmesolver:
38+
image:
39+
registry: quay.io
40+
repository: jetstack/cert-manager-acmesolver
41+
# Startup API Check image configuration
42+
startupapicheck:
43+
image:
44+
registry: quay.io
45+
repository: jetstack/cert-manager-startupapicheck
2446
local:
2547
letsencrypt:
2648
production: false

applications/wg-easy/charts/replicated/replicated/helmChart-replicated.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ spec:
1515
- --history-max=15
1616
- --wait
1717

18-
values: {}
18+
values:
19+
replicated:
20+
image:
21+
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "registry.replicated.com" }}'
22+
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "library" }}/replicated-sdk-image'
23+
imagePullSecrets:
24+
- name: '{{repl ImagePullSecretName }}'
1925
namespace: replicated
2026
builder: {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
# Values for replicated-sdk chart
22
replicated:
33
enabled: true
4+
image:
5+
registry: registry.replicated.com
6+
repository: "library/replicated-sdk-image"

applications/wg-easy/charts/traefik/replicated/helmChart-traefik.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,13 @@ spec:
88
weight: 2
99
helmUpgradeFlags:
1010
- --wait
11+
values:
12+
traefik:
13+
image:
14+
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "docker.io" }}'
15+
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "library" }}/traefik'
16+
deployment:
17+
imagePullSecrets:
18+
- name: '{{repl ImagePullSecretName }}'
1119
namespace: traefik
1220
builder: {}

applications/wg-easy/charts/traefik/values.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ certs:
44
installStaging: false
55
dnsNames: []
66
traefik:
7+
image:
8+
registry: docker.io
9+
repository: traefik
710
service:
811
type: NodePort
912
ports:

applications/wg-easy/charts/wg-easy/replicated/helmChart-wg-easy.yaml

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,31 @@ spec:
1616
- --wait
1717

1818
values:
19-
wg-easy:
20-
services:
21-
vpn:
22-
# TODO: Template for stand alone KOTS install?
23-
#type: NodePort
24-
ports:
25-
udp:
26-
nodePort: repl{{ ConfigOption `vpn-port` | ParseInt }}
27-
wireguard:
28-
password: repl{{ ConfigOption `password` }}
29-
host: repl{{ ConfigOption `domain` }}
30-
port: repl{{ ConfigOption `vpn-port` | ParseInt }}
19+
service:
20+
vpn:
21+
ports:
22+
udp:
23+
port: repl{{ ConfigOption `vpn-port` | ParseInt }}
24+
wireguard:
25+
password: repl{{ ConfigOption `password` }}
26+
host: repl{{ ConfigOption `domain` }}
27+
port: repl{{ ConfigOption `vpn-port` | ParseInt }}
3128
templates:
3229
traefikRoutes:
3330
web-tls:
3431
hostName: repl{{ ConfigOption `domain` }}
32+
controllers:
33+
wg-easy:
34+
containers:
35+
wg-container:
36+
image:
37+
repository: '{{repl HasLocalRegistry | ternary LocalRegistryHost "ghcr.io" }}/{{repl HasLocalRegistry | ternary LocalRegistryNamespace "wg-easy" }}/wg-easy'
38+
defaultPodOptions:
39+
imagePullSecrets:
40+
- name: '{{repl ImagePullSecretName }}'
41+
preflight:
42+
image:
43+
repository: '{{repl HasLocalRegistry | ternary LocalRegistryHost "docker.io" }}/{{repl HasLocalRegistry | ternary LocalRegistryNamespace "library" }}/debian:bookworm-slim'
44+
3545
namespace: wg-easy
3646
builder: {}

applications/wg-easy/charts/wg-easy/templates/_preflight.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ metadata:
66
spec:
77
collectors:
88
- sysctl:
9-
image: debian:buster-slim
9+
image: {{ .Values.preflight.image.repository }}
1010
analyzers:
1111
- sysctl:
1212
checkName: IP forwarding enabled

applications/wg-easy/charts/wg-easy/values.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,10 @@ persistence:
129129
retain: false
130130
globalMounts:
131131
- path: /etc/wireguard
132+
133+
preflight:
134+
image:
135+
repository: "docker.io/library/debian:bookworm-slim"
136+
137+
defaultPodOptions:
138+
imagePullSecrets: []

applications/wg-easy/replicated/application.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ spec:
88
icon: https://www.logo.wine/a/logo/WireGuard/WireGuard-Icon-Logo.wine.svg
99
#releaseNotes: These are our release notes
1010
allowRollback: true
11-
#additionalImages:
12-
# - jenkins/jenkins:lts
11+
additionalImages:
12+
- debian:buster-slim
1313
#additionalNamespaces should be populated by the Task file
1414
#ports:
1515
# - serviceName: wg-easy/web

0 commit comments

Comments
 (0)