Skip to content
114 changes: 103 additions & 11 deletions applications/wg-easy/Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ tasks:
- |
echo "Deleting CMX VM {{.CMX_VM_NAME}}..."
replicated vm rm {{.CMX_VM_NAME}}

cmx-vm-install:
desc: Download and install the app as Embedded Cluster on CMX VM
silent: true
Expand All @@ -589,6 +589,7 @@ tasks:
vars:
CHANNEL: '{{.CHANNEL | default "Unstable"}}'
SKIP_INSTALL: '{{.SKIP_INSTALL | default "false"}}'
AIRGAP: '{{.AIRGAP | default "false"}}'
ADMIN_CONSOLE_PASSWORD:
sh: uuidgen | cut -c1-13
deps:
Expand All @@ -610,34 +611,125 @@ tasks:
exit 1
fi

# Run airgap-build task if airgap mode is enabled
if [ "{{.AIRGAP}}" = "true" ]; then
echo "Airgap mode enabled, ensuring airgap build is ready..."
task airgap-build
fi

echo "SSH into the VM and download the app binary..."
SSH_BASE_CMD="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
if [ -n "{{.CMX_VM_PUBLIC_KEY}}" ]; then
SSH_BASE_CMD="$SSH_BASE_CMD -i {{.CMX_VM_PUBLIC_KEY}}"
fi
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)\"")

# Determine download URL based on airgap setting
DOWNLOAD_URL="https://replicated.app/embedded/{{.APP_SLUG}}/{{.CHANNEL}}"
if [ "{{.AIRGAP}}" = "true" ]; then
DOWNLOAD_URL="${DOWNLOAD_URL}?airgap=true"
fi

echo "SSH base command: $SSH_BASE_CMD"
$VM_SSH_CMD << 'EOF'
$VM_SSH_CMD << EOF
set -e
echo 'Downloading {{.APP_NAME}} installer...'
curl -f 'https://replicated.app/embedded/{{.APP_NAME}}/{{.CHANNEL}}' -H 'Authorization: {{.REPLICATED_LICENSE_ID}}' -o {{.APP_NAME}}-{{.CHANNEL}}.tgz
echo 'Downloading {{.APP_SLUG}} installer...'
curl -f '$DOWNLOAD_URL' -H 'Authorization: {{.REPLICATED_LICENSE_ID}}' -o {{.APP_SLUG}}-{{.CHANNEL}}.tgz

echo 'Extracting installer...'
tar -xvzf {{.APP_NAME}}-{{.CHANNEL}}.tgz
tar -xvzf {{.APP_SLUG}}-{{.CHANNEL}}.tgz

echo "Binary is available at ./{{.APP_SLUG}}"
EOF

if [ "{{.SKIP_INSTALL}}" = "false" ]; then
echo "Installing {{.APP_NAME}}..."
sudo ./{{.APP_NAME}} install --license license.yaml --admin-console-password {{.ADMIN_CONSOLE_PASSWORD}} --yes
else
echo "Skipping installation as requested. Binary is available at ./{{.APP_NAME}}"
if [ "{{.AIRGAP}}" = "true" ]; then
echo "Updating network policy for airgap installation..."
NETWORK_ID=$(replicated vm ls --output=json | jq -r ".[] | select(.name == \"{{.CMX_VM_NAME}}\") | .network_id")
replicated network update policy $NETWORK_ID --policy airgap
fi
EOF

if [ "{{.SKIP_INSTALL}}" = "false" ]; then
INSTALL_CMD="sudo ./{{.APP_SLUG}} install --license license.yaml --admin-console-password {{.ADMIN_CONSOLE_PASSWORD}} --yes"
if [ "{{.AIRGAP}}" = "true" ]; then
INSTALL_CMD="${INSTALL_CMD} --airgap-bundle {{.APP_SLUG}}.airgap"
fi

echo "Running installation command: $INSTALL_CMD"
$VM_SSH_CMD << EOF
$INSTALL_CMD
EOF

echo "Exposing port 30000 on the VM..."
replicated vm port expose --port 30000 {{.CMX_VM_NAME}}

echo "Visit above URL to access the Admin Console, password: {{.ADMIN_CONSOLE_PASSWORD}}"
fi

airgap-build:
desc: Check and build airgap bundle for the latest release
silent: true
cmds:
- |
echo "Checking if airgap build is available for latest release in channel {{.RELEASE_CHANNEL}}..."

# Get release list and extract app ID and channel ID
RELEASE_DATA=$(replicated release ls -o json)
APP_ID=$(echo "$RELEASE_DATA" | jq -r '.[0].appId')
CHANNEL_ID=$(echo "$RELEASE_DATA" | jq -r '.[0].activeChannels[] | select(.name == "{{.RELEASE_CHANNEL}}") | .id')

if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then
echo "Error: Could not retrieve app ID from latest releases"
exit 1
fi

if [ -z "$CHANNEL_ID" ] || [ "$CHANNEL_ID" = "null" ]; then
echo "Error: Could not find channel ID for channel {{.RELEASE_CHANNEL}}"
exit 1
fi

echo "Found app ID: $APP_ID, channel ID: $CHANNEL_ID"

# Get channel releases and check airgap build status
CHANNEL_RELEASES=$(replicated api get "v3/app/$APP_ID/channel/$CHANNEL_ID/releases")
AIRGAP_BUILD_STATUS=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].airgapBuildStatus // "none"')
AIRGAP_BUILD_ERROR=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].airgapBuildError // "none"')
AIRGAP_BUNDLE_IMAGES=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].airgapBundleImages // "none"')
AIRGAP_LATEST_SEQUENCE=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].sequence')

echo "Airgap build status: $AIRGAP_BUILD_STATUS"

if [ "$AIRGAP_BUILD_STATUS" = "built" ]; then
echo "Airgap is already buit for sequence $AIRGAP_LATEST_SEQUENCE"
echo "Airgap bundle images: $AIRGAP_BUNDLE_IMAGES"
exit 0
fi

if [ "$AIRGAP_BUILD_STATUS" = "metadata" ]; then
echo "Airgap has not been built yet. Triggering build..."
replicated api post "v3/app/$APP_ID/channel/$CHANNEL_ID/release/$AIRGAP_LATEST_SEQUENCE/airgap/build"
fi

echo "Airgap build triggered. Polling every 10 seconds for up to 5 minutes..."
for i in $(seq 1 30); do
echo "Checking airgap build status... (attempt $i/30)"

CHANNEL_RELEASES=$(replicated api get "v3/app/$APP_ID/channel/$CHANNEL_ID/releases")
AIRGAP_BUILD_STATUS=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].airgapBuildStatus // "none"')
AIRGAP_BUILD_ERROR=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].airgapBuildError // "none"')
AIRGAP_BUNDLE_IMAGES=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].airgapBundleImages // "none"')
AIRGAP_LATEST_SEQUENCE=$(echo "$CHANNEL_RELEASES" | jq -r '.releases[0].sequence')

echo "Airgap build current status: $AIRGAP_BUILD_STATUS"

if [ "$AIRGAP_BUILD_STATUS" = "built" ]; then
echo "Airgap build completed successfully!"
echo "Airgap bundle images: $AIRGAP_BUNDLE_IMAGES"
exit 0
fi
sleep 10
done

echo "Timeout: Airgap build did not complete within 5 minutes."
echo "Last build status: $AIRGAP_BUILD_STATUS"
echo "Last build error: $AIRGAP_BUILD_ERROR"
exit 1
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,30 @@ spec:
weight: 0
helmUpgradeFlags:
- --wait
values:
cert-manager:
global:
imagePullSecrets:
- name: '{{repl ImagePullSecretName }}'
image:
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "quay.io" }}'
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "jetstack" }}/cert-manager-controller'
webhook:
image:
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "quay.io" }}'
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "jetstack" }}/cert-manager-webhook'
cainjector:
image:
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "quay.io" }}'
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "jetstack" }}/cert-manager-cainjector'
acmesolver:
image:
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "quay.io" }}'
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "jetstack" }}/cert-manager-acmesolver'
startupapicheck:
image:
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "quay.io" }}'
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "jetstack" }}/cert-manager-startupapicheck'

namespace: cert-manager
builder: {}
22 changes: 22 additions & 0 deletions applications/wg-easy/charts/cert-manager/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,45 @@ cert-manager:
# Override the namespace used to store the ConfigMap for leader election
namespace: "cert-manager"
installCRDs: true
# Controller image configuration
image:
registry: quay.io
repository: jetstack/cert-manager-controller
extraArgs:
- --cluster-resource-namespace=cert-manager
- --enable-certificate-owner-ref=true
resources:
requests:
cpu: 5m
memory: 45Mi
# Webhook image configuration
webhook:
image:
registry: quay.io
repository: jetstack/cert-manager-webhook
resources:
requests:
cpu: 5m
memory: 22Mi
# CA Injector image configuration
cainjector:
image:
registry: quay.io
repository: jetstack/cert-manager-cainjector
resources:
requests:
cpu: 5m
memory: 101Mi
# ACME Solver image configuration
acmesolver:
image:
registry: quay.io
repository: jetstack/cert-manager-acmesolver
# Startup API Check image configuration
startupapicheck:
image:
registry: quay.io
repository: jetstack/cert-manager-startupapicheck
local:
letsencrypt:
production: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ spec:
- --history-max=15
- --wait

values: {}
values:
replicated:
image:
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "registry.replicated.com" }}'
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "library" }}/replicated-sdk-image'
imagePullSecrets:
- name: '{{repl ImagePullSecretName }}'
namespace: replicated
builder: {}
3 changes: 3 additions & 0 deletions applications/wg-easy/charts/replicated/values.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Values for replicated-sdk chart
replicated:
enabled: true
image:
registry: registry.replicated.com
repository: "library/replicated-sdk-image"
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,13 @@ spec:
weight: 2
helmUpgradeFlags:
- --wait
values:
traefik:
image:
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "docker.io" }}'
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "library" }}/traefik'
deployment:
imagePullSecrets:
- name: '{{repl ImagePullSecretName }}'
namespace: traefik
builder: {}
3 changes: 3 additions & 0 deletions applications/wg-easy/charts/traefik/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ certs:
installStaging: false
dnsNames: []
traefik:
image:
registry: docker.io
repository: traefik
service:
type: NodePort
ports:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,31 @@ spec:
- --wait

values:
wg-easy:
services:
vpn:
# TODO: Template for stand alone KOTS install?
#type: NodePort
ports:
udp:
nodePort: repl{{ ConfigOption `vpn-port` | ParseInt }}
wireguard:
password: repl{{ ConfigOption `password` }}
host: repl{{ ConfigOption `domain` }}
port: repl{{ ConfigOption `vpn-port` | ParseInt }}
service:
vpn:
ports:
udp:
port: repl{{ ConfigOption `vpn-port` | ParseInt }}
wireguard:
password: repl{{ ConfigOption `password` }}
host: repl{{ ConfigOption `domain` }}
port: repl{{ ConfigOption `vpn-port` | ParseInt }}
templates:
traefikRoutes:
web-tls:
hostName: repl{{ ConfigOption `domain` }}
controllers:
wg-easy:
containers:
wg-container:
image:
repository: '{{repl HasLocalRegistry | ternary LocalRegistryHost "ghcr.io" }}/{{repl HasLocalRegistry | ternary LocalRegistryNamespace "wg-easy" }}/wg-easy'
defaultPodOptions:
imagePullSecrets:
- name: '{{repl ImagePullSecretName }}'
preflight:
image:
repository: '{{repl HasLocalRegistry | ternary LocalRegistryHost "docker.io" }}/{{repl HasLocalRegistry | ternary LocalRegistryNamespace "library" }}/debian:bookworm-slim'

namespace: wg-easy
builder: {}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ metadata:
spec:
collectors:
- sysctl:
image: debian:buster-slim
image: {{ .Values.preflight.image.repository }}
analyzers:
- sysctl:
checkName: IP forwarding enabled
Expand Down
7 changes: 7 additions & 0 deletions applications/wg-easy/charts/wg-easy/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,10 @@ persistence:
retain: false
globalMounts:
- path: /etc/wireguard

preflight:
image:
repository: "docker.io/library/debian:bookworm-slim"

defaultPodOptions:
imagePullSecrets: []
4 changes: 2 additions & 2 deletions applications/wg-easy/replicated/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ spec:
icon: https://www.logo.wine/a/logo/WireGuard/WireGuard-Icon-Logo.wine.svg
#releaseNotes: These are our release notes
allowRollback: true
#additionalImages:
# - jenkins/jenkins:lts
additionalImages:
- debian:buster-slim
#additionalNamespaces should be populated by the Task file
#ports:
# - serviceName: wg-easy/web
Expand Down
Loading