Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions applications/wg-easy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ The `task release-prepare` command walks all `charts/*/replicated/` directories,

Charts are deployed in dependency order via `helmfile.yaml.gotmpl`. The same helmfile serves two purposes through Helmfile environments:

- **`default` environment**: Charts are installed from local paths (`./charts/cert-manager`, `./charts/wg-easy`, etc.). The Replicated SDK is disabled. This is the inner development loop -- validate chart changes against a test cluster without touching the Replicated platform.
- **`default` environment**: Charts are installed from local paths (`./charts/cert-manager`, `./charts/wg-easy`, etc.). This is the inner development loop -- validate chart changes against a test cluster without touching the Replicated platform.

- **`replicated` environment**: Charts are pulled from the Replicated OCI registry (`oci://registry.replicated.com/<app>/<channel>/<chart>`), authenticated with a customer license ID. Container images are routed through the Replicated registry proxy. The Replicated SDK is enabled. This simulates what an end customer's installation looks like.
- **`replicated` environment**: Charts are pulled from the Replicated OCI registry (`oci://registry.replicated.com/<app>/<channel>/<chart>`), authenticated with a customer license ID. Container images are routed through the Replicated registry proxy. This simulates what an end customer's installation looks like.

```bash
# Local development -- charts from disk
Expand Down Expand Up @@ -89,12 +89,11 @@ applications/wg-easy/
├── charts/
│ ├── cert-manager/ # Wrapped cert-manager chart
│ ├── cert-manager-issuers/ # Chart for cert-manager issuers
│ ├── replicated-sdk/ # Replicated SDK chart
│ ├── templates/ # Common templates shared across charts
│ ├── traefik/ # Wrapped Traefik chart
│ └── wg-easy/ # Main application chart
│ └── wg-easy/ # Main application chart (includes Replicated SDK as dependency)
├── replicated/ # Root Replicated configuration
├── taskfiles/ # Task utility functions
├── development-config-values.yaml # ConfigValues for headless installs
├── helmfile.yaml.gotmpl # Defines chart installation order
└── Taskfile.yaml # Main task definitions
```
Expand Down
106 changes: 105 additions & 1 deletion applications/wg-easy/Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -668,10 +668,11 @@ tasks:
- dependencies-update

chart-validate:
desc: Validate all Helm charts (lint + template + helmfile)
desc: Validate all Helm charts (lint + template + helmfile + config contract)
cmds:
- task: chart-lint-all
- task: chart-template-all
- task: config-validate
- echo "Validating helmfile template..."
- |
if [ -f "helmfile.yaml.gotmpl" ]; then
Expand All @@ -689,6 +690,109 @@ tasks:
echo "No helmfile.yaml.gotmpl found, skipping helmfile validation"
fi

config-validate:
desc: Validate the four-way contract between values.yaml, HelmChart CR, KOTS Config, and ConfigValues
cmds:
- echo "Validating configuration contract..."
- |
ERRORS=0

# 1. Collect all config item names from KOTS Config screens
CONFIG_ITEMS=$(find . -path '*/replicated/config.yaml' -exec yq -r '.spec.groups[].items[].name' {} \; 2>/dev/null | sort -u)
ROOT_CONFIG_ITEMS=""
if [ -f "./replicated/config.yaml" ]; then
ROOT_CONFIG_ITEMS=$(yq -r '.spec.groups[].items[].name' ./replicated/config.yaml 2>/dev/null | sort -u)
fi
ALL_CONFIG_ITEMS=$(echo -e "${CONFIG_ITEMS}\n${ROOT_CONFIG_ITEMS}" | sort -u | grep -v '^$')

echo "=== KOTS Config items ==="
echo "$ALL_CONFIG_ITEMS"
echo ""

# 2. Collect all ConfigOption references from HelmChart CRs
HELMCHART_REFS=$(find . -path '*/replicated/helmChart-*.yaml' -exec grep -o 'ConfigOption [^}]*' {} \; 2>/dev/null | sed 's/ConfigOption [`"'"'"']//' | sed 's/[`"'"'"'].*//' | sed 's/ConfigOption //' | sort -u)

echo "=== ConfigOption references in HelmChart CRs ==="
echo "$HELMCHART_REFS"
echo ""

# 3. Check that every ConfigOption reference has a matching Config item
for ref in $HELMCHART_REFS; do
if ! echo "$ALL_CONFIG_ITEMS" | grep -qx "$ref"; then
echo "ERROR: HelmChart references ConfigOption '$ref' but no matching Config item found"
ERRORS=$((ERRORS + 1))
fi
done

# 4. Check that every Config item is referenced in at least one HelmChart CR
for item in $ALL_CONFIG_ITEMS; do
if ! echo "$HELMCHART_REFS" | grep -qx "$item"; then
echo "WARNING: Config item '$item' is not referenced by any HelmChart CR"
fi
done

# 5. Validate ConfigValues file if present
if [ -f "development-config-values.yaml" ]; then
echo "=== Validating development-config-values.yaml ==="
CV_ITEMS=$(yq -r '.spec.values | keys | .[]' development-config-values.yaml 2>/dev/null | sort -u)
echo "ConfigValues items: $CV_ITEMS"
echo ""

# Every ConfigValues item should match a Config item
for cv_item in $CV_ITEMS; do
if ! echo "$ALL_CONFIG_ITEMS" | grep -qx "$cv_item"; then
echo "ERROR: ConfigValues contains '$cv_item' but no matching Config item found"
ERRORS=$((ERRORS + 1))
fi
done

# Every required Config item should have a value in ConfigValues
REQUIRED_ITEMS=$(find . -path '*/replicated/config.yaml' -exec yq -r '.spec.groups[].items[] | select(.required == true) | .name' {} \; 2>/dev/null | sort -u)
if [ -f "./replicated/config.yaml" ]; then
ROOT_REQUIRED=$(yq -r '.spec.groups[].items[] | select(.required == true) | .name' ./replicated/config.yaml 2>/dev/null | sort -u)
REQUIRED_ITEMS=$(echo -e "${REQUIRED_ITEMS}\n${ROOT_REQUIRED}" | sort -u | grep -v '^$')
fi

for req_item in $REQUIRED_ITEMS; do
CV_VALUE=$(yq -r ".spec.values.\"$req_item\".value" development-config-values.yaml 2>/dev/null)
if [ -z "$CV_VALUE" ]; then
echo "ERROR: Required Config item '$req_item' has no value in development-config-values.yaml"
ERRORS=$((ERRORS + 1))
fi
done
else
echo "WARNING: No development-config-values.yaml found, skipping ConfigValues validation"
fi

# 6. Validate HelmChart value keys exist in chart values.yaml
echo ""
echo "=== Validating HelmChart values against chart values.yaml ==="
find ./charts -maxdepth 2 -mindepth 2 -type d -name replicated | while read chartDir; do
parent=$(basename $(dirname $chartDir))
helmChartFile="$chartDir/helmChart-$parent.yaml"
valuesFile="$(dirname $chartDir)/values.yaml"

if [ -f "$helmChartFile" ] && [ -f "$valuesFile" ]; then
# Extract top-level value keys from HelmChart CR (excluding template functions)
HC_KEYS=$(yq -r '.spec.values | keys | .[]' "$helmChartFile" 2>/dev/null | sort -u)
VALUES_KEYS=$(yq -r 'keys | .[]' "$valuesFile" 2>/dev/null | sort -u)

for key in $HC_KEYS; do
if ! echo "$VALUES_KEYS" | grep -qx "$key"; then
echo "INFO: HelmChart $parent sets top-level key '$key' not in $valuesFile (may be subchart value)"
fi
done
fi
done

echo ""
if [ $ERRORS -gt 0 ]; then
echo "FAILED: $ERRORS error(s) found in configuration contract"
exit 1
else
echo "Configuration contract validation passed!"
fi

chart-package-all:
desc: Package all Helm charts for distribution
cmds:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ metadata:
spec:
collectors:
- logs:
namespace: {{ .Release.Namespace }}
namespaces: {{ .Release.Namespace }}
selector:
- app.kubernetes.io/instance=cert-manager
{{- end -}}
{{- end -}}
9 changes: 0 additions & 9 deletions applications/wg-easy/charts/replicated/Chart.lock

This file was deleted.

10 changes: 0 additions & 10 deletions applications/wg-easy/charts/replicated/Chart.yaml

This file was deleted.

This file was deleted.

6 changes: 0 additions & 6 deletions applications/wg-easy/charts/replicated/values.yaml

This file was deleted.

7 changes: 5 additions & 2 deletions applications/wg-easy/charts/wg-easy/Chart.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ dependencies:
- name: templates
repository: file://../templates
version: 1.1.0
digest: sha256:b31a8b14ce1e7d0bb2452ff43d6e5433bd438c86cff3138c4a028902950e9884
generated: "2025-06-25T10:58:36.514573-04:00"
- name: replicated
repository: oci://registry.replicated.com/library
version: 1.16.0
digest: sha256:5370f364086a8743fd2aca9559f6effbd2a0d87eb793d31c3938360e82cd8bf3
generated: "2026-02-24T13:52:16.626819-05:00"
3 changes: 3 additions & 0 deletions applications/wg-easy/charts/wg-easy/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ dependencies:
- name: templates
version: '*'
repository: file://../templates
- name: replicated
version: 1.16.0
repository: oci://registry.replicated.com/library
description: Simple wireguard with web configuration management
home: https://github.com/replicatedhq/platform-examples/
maintainers:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ spec:
chart:
name: wg-easy
weight: 3

# helmUpgradeFlags specifies additional flags to pass to the `helm upgrade` command.
helmUpgradeFlags:
- --skip-crds
Expand All @@ -16,6 +16,13 @@ spec:
- --wait

values:
replicated:
createPullSecret: true
image:
registry: '{{repl HasLocalRegistry | ternary LocalRegistryHost "registry.replicated.com" }}'
repository: '{{repl HasLocalRegistry | ternary LocalRegistryNamespace "library" }}/replicated-sdk-image'
imagePullSecrets:
- name: '{{repl ImagePullSecretName }}'
service:
vpn:
ports:
Expand Down Expand Up @@ -43,4 +50,6 @@ spec:
repository: '{{repl HasLocalRegistry | ternary LocalRegistryHost "docker.io" }}/{{repl HasLocalRegistry | ternary LocalRegistryNamespace "library" }}/debian:bookworm-slim'

namespace: wg-easy
builder: {}
builder:
replicated:
enabled: true
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ metadata:
name: wg-easy-supportbundle
spec:
collectors:
- clusterResources:
namespaces:
- {{ .Release.Namespace }}
- logs:
namespace: {{ .Release.Namespace }}
selector:
Expand All @@ -21,4 +24,4 @@ spec:
- pass:
when: 'net.ipv4.ip_forward == 1'
message: "IP forwarding is enabled."
{{- end -}}
{{- end -}}
2 changes: 1 addition & 1 deletion applications/wg-easy/charts/wg-easy/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ wireguard:
port: 51820 # This is used in the postUp
defaultAddress: 10.10.10.x
defaultDns: 1.1.1.1
allowedIps: 0.0.0.0/5, 8.0.0.0/7, 11.0.0.0/8, 12.0.0.0/6, 16.0.0.0/4, 32.0.0.0/3, 64.0.0.0/2, 128.0.0.0/3, 160.0.0.0/5, 168.0.0.0/6, 172.0.0.0/12, 172.32.0.0/11, 172.64.0.0/10, 172.128.0.0/9, 173.0.0.0/8, 174.0.0.0/7, 176.0.0.0/4, 192.0.0.0/9, 192.128.0.0/11, 192.160.0.0/13, 192.169.0.0/16, 192.170.0.0/15, 192.172.0.0/14, 192.176.0.0/12, 192.192.0.0/10, 193.0.0.0/8, 194.0.0.0/7, 196.0.0.0/6, 200.0.0.0/5, 208.0.0.0/4, 224.0.0.0/3
allowedIps: 0.0.0.0/0
postUp: iptables -A FORWARD -i wg0 -o eth0 -d 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8 -j DROP; iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE; iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT

# Shared templates for Traefik routes
Expand Down
15 changes: 15 additions & 0 deletions applications/wg-easy/development-config-values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
apiVersion: kots.io/v1beta1
kind: ConfigValues
metadata:
name: wg-easy
spec:
values:
# Wireguard Settings
password:
value: "testpassword123"
domain:
value: "10.0.0.11"
vpn-port:
default: "20000"
value: "20000"
20 changes: 11 additions & 9 deletions applications/wg-easy/docs/chart-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@ This document explains the modular chart approach used in the WG-Easy Helm chart

```
applications/wg-easy/
├── charts/templates/ # Common templates shared across charts
│ ├── traefik-routes.yaml # Templates for Traefik IngressRoutes
│ └── traefik-route-tcp.yaml # Templates for Traefik TCP routes
├── cert-manager/ # Wrapped cert-manager chart
├── cert-manager-issuers/ # Chart for cert-manager issuers
├── charts/
│ ├── templates/ # Common templates shared across charts
│ │ ├── traefik-routes.yaml # Templates for Traefik IngressRoutes
│ │ └── traefik-route-tcp.yaml # Templates for Traefik TCP routes
│ ├── cert-manager/ # Wrapped cert-manager chart
│ ├── cert-manager-issuers/ # Chart for cert-manager issuers
│ ├── traefik/ # Wrapped Traefik chart
│ └── wg-easy/ # Main application chart (includes Replicated SDK as dependency)
├── replicated/ # Root Replicated configuration
├── replicated-sdk/ # Replicated SDK chart
├── traefik/ # Wrapped Traefik chart
├── wg-easy/ # Main application chart
├── helmfile.yaml.gotmpl # Defines chart installation order
├── helmfile.yaml.gotmpl # Defines chart installation order
└── Taskfile.yaml # Main task definitions
```

The Replicated SDK is included as a dependency of the wg-easy chart (declared in `charts/wg-easy/Chart.yaml`) rather than deployed as a standalone chart. This means the SDK runs in the same namespace as the application and its values are configured through the `replicated` key in the wg-easy HelmChart CR.

## Chart Wrapping Concept

Chart wrapping is a core technique in this pattern where upstream Helm charts are encapsulated in local charts rather than used directly. This provides several key benefits:
Expand Down
Loading
Loading