diff --git a/applications/wg-easy/Taskfile.yaml b/applications/wg-easy/Taskfile.yaml index 1c5eb454..5d589064 100644 --- a/applications/wg-easy/Taskfile.yaml +++ b/applications/wg-easy/Taskfile.yaml @@ -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 @@ -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: @@ -610,6 +611,12 @@ 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 @@ -617,27 +624,112 @@ tasks: 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].channelSequence') + + 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].channelSequence') + + 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 \ No newline at end of file diff --git a/applications/wg-easy/charts/cert-manager/replicated/helmChart-cert-manager.yaml b/applications/wg-easy/charts/cert-manager/replicated/helmChart-cert-manager.yaml index 94ebeb26..dffb6031 100644 --- a/applications/wg-easy/charts/cert-manager/replicated/helmChart-cert-manager.yaml +++ b/applications/wg-easy/charts/cert-manager/replicated/helmChart-cert-manager.yaml @@ -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: {} diff --git a/applications/wg-easy/charts/cert-manager/values.yaml b/applications/wg-easy/charts/cert-manager/values.yaml index 60c98f78..6df949dc 100644 --- a/applications/wg-easy/charts/cert-manager/values.yaml +++ b/applications/wg-easy/charts/cert-manager/values.yaml @@ -4,6 +4,10 @@ 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 @@ -11,16 +15,34 @@ cert-manager: 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 diff --git a/applications/wg-easy/charts/replicated/replicated/helmChart-replicated.yaml b/applications/wg-easy/charts/replicated/replicated/helmChart-replicated.yaml index 53e89300..3b5ecf3d 100644 --- a/applications/wg-easy/charts/replicated/replicated/helmChart-replicated.yaml +++ b/applications/wg-easy/charts/replicated/replicated/helmChart-replicated.yaml @@ -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: {} diff --git a/applications/wg-easy/charts/replicated/values.yaml b/applications/wg-easy/charts/replicated/values.yaml index bbed9490..3deb696a 100644 --- a/applications/wg-easy/charts/replicated/values.yaml +++ b/applications/wg-easy/charts/replicated/values.yaml @@ -1,3 +1,6 @@ # Values for replicated-sdk chart replicated: enabled: true + image: + registry: registry.replicated.com + repository: "library/replicated-sdk-image" diff --git a/applications/wg-easy/charts/traefik/replicated/helmChart-traefik.yaml b/applications/wg-easy/charts/traefik/replicated/helmChart-traefik.yaml index 74d08de8..722c7dc6 100644 --- a/applications/wg-easy/charts/traefik/replicated/helmChart-traefik.yaml +++ b/applications/wg-easy/charts/traefik/replicated/helmChart-traefik.yaml @@ -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: {} diff --git a/applications/wg-easy/charts/traefik/values.yaml b/applications/wg-easy/charts/traefik/values.yaml index e2567d7f..94113ada 100644 --- a/applications/wg-easy/charts/traefik/values.yaml +++ b/applications/wg-easy/charts/traefik/values.yaml @@ -4,6 +4,9 @@ certs: installStaging: false dnsNames: [] traefik: + image: + registry: docker.io + repository: traefik service: type: NodePort ports: diff --git a/applications/wg-easy/charts/wg-easy/replicated/helmChart-wg-easy.yaml b/applications/wg-easy/charts/wg-easy/replicated/helmChart-wg-easy.yaml index 9f5199c2..b163f89d 100644 --- a/applications/wg-easy/charts/wg-easy/replicated/helmChart-wg-easy.yaml +++ b/applications/wg-easy/charts/wg-easy/replicated/helmChart-wg-easy.yaml @@ -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: {} diff --git a/applications/wg-easy/charts/wg-easy/templates/_preflight.tpl b/applications/wg-easy/charts/wg-easy/templates/_preflight.tpl index f2a87cdc..454917fd 100644 --- a/applications/wg-easy/charts/wg-easy/templates/_preflight.tpl +++ b/applications/wg-easy/charts/wg-easy/templates/_preflight.tpl @@ -6,7 +6,7 @@ metadata: spec: collectors: - sysctl: - image: debian:buster-slim + image: {{ .Values.preflight.image.repository }} analyzers: - sysctl: checkName: IP forwarding enabled diff --git a/applications/wg-easy/charts/wg-easy/values.yaml b/applications/wg-easy/charts/wg-easy/values.yaml index 5349c109..46e8f035 100644 --- a/applications/wg-easy/charts/wg-easy/values.yaml +++ b/applications/wg-easy/charts/wg-easy/values.yaml @@ -129,3 +129,10 @@ persistence: retain: false globalMounts: - path: /etc/wireguard + +preflight: + image: + repository: "docker.io/library/debian:bookworm-slim" + +defaultPodOptions: + imagePullSecrets: [] \ No newline at end of file diff --git a/applications/wg-easy/replicated/application.yaml b/applications/wg-easy/replicated/application.yaml index eab11e5f..0f710d53 100644 --- a/applications/wg-easy/replicated/application.yaml +++ b/applications/wg-easy/replicated/application.yaml @@ -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