Skip to content

Commit 474e441

Browse files
committed
Add wg-easy application example with helmfile and taskfile flow
1 parent 1fc4c73 commit 474e441

28 files changed

+1169
-0
lines changed

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,8 @@ Thumbs.db
2828
*.pyo
2929
*.pyd
3030
__pycache__/
31+
32+
# wg-easy specific
33+
applications/wg-easy/release/
34+
applications/wg-easy/*/charts/
35+
applications/wg-easy/*/Chart.lock

applications/wg-easy/Taskfile.yaml

+300
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
version: "3"
2+
3+
includes:
4+
utils: ./taskfiles/utils.yml
5+
6+
vars:
7+
# Cluster configuration
8+
CLUSTER_NAME: '{{.CLUSTER_NAME | default "test-cluster"}}'
9+
K8S_VERSION: '{{.K8S_VERSION | default "1.32.2"}}'
10+
DISK_SIZE: '{{.DISK_SIZE | default "100"}}'
11+
INSTANCE_TYPE: '{{.INSTANCE_TYPE | default "r1.small"}}'
12+
DISTRIBUTION: '{{.DISTRIBUTION | default "k3s"}}'
13+
KUBECONFIG_FILE: './kubeconfig-{{.CLUSTER_NAME}}'
14+
15+
# Ports configuration
16+
EXPOSE_PORTS:
17+
- port: 443
18+
protocol: https
19+
- port: 80
20+
protocol: http
21+
22+
# GCP default configuration
23+
GCP_PROJECT: '{{.GCP_PROJECT | default "replicated-qa"}}'
24+
GCP_ZONE: '{{.GCP_ZONE | default "us-central1-a"}}'
25+
VM_NAME: '{{.VM_NAME | default (printf "%s-dev" (or (env "GUSER") "user"))}}'
26+
27+
tasks:
28+
default:
29+
desc: Show available tasks
30+
cmds:
31+
- task -s --list
32+
33+
create-cluster:
34+
desc: Create a test cluster using Replicated Compatibility Matrix (use EMBEDDED=true for embedded clusters)
35+
run: once
36+
silent: true
37+
vars:
38+
EMBEDDED: '{{.EMBEDDED | default "false"}}'
39+
LICENSE_ID: '{{if eq .EMBEDDED "true"}}{{.LICENSE_ID | default "2cmqT1dBVHZ3aSH21kPxWtgoYGr"}}{{end}}'
40+
TIMEOUT: '{{if eq .EMBEDDED "true"}}420{{else}}300{{end}}'
41+
status:
42+
- replicated cluster ls --output json | jq -e '.[] | select(.name == "{{.CLUSTER_NAME}}")' > /dev/null
43+
cmds:
44+
- |
45+
if [ "{{.EMBEDDED}}" = "true" ]; then
46+
echo "Creating embedded cluster {{.CLUSTER_NAME}} with license ID {{.LICENSE_ID}}..."
47+
replicated cluster create --distribution embedded-cluster --name {{.CLUSTER_NAME}} --license-id {{.LICENSE_ID}}
48+
else
49+
echo "Creating cluster {{.CLUSTER_NAME}} with distribution {{.DISTRIBUTION}}..."
50+
replicated cluster create --name {{.CLUSTER_NAME}} --distribution {{.DISTRIBUTION}} --version {{.K8S_VERSION}} --disk {{.DISK_SIZE}} --instance-type {{.INSTANCE_TYPE}}
51+
fi
52+
- task: utils:wait-for-cluster
53+
vars:
54+
TIMEOUT: "{{.TIMEOUT}}"
55+
56+
test:
57+
desc: Run a basic test suite
58+
silent: true
59+
cmds:
60+
- echo "Running basic tests..."
61+
- echo "This is a placeholder for actual tests"
62+
- sleep 5
63+
- echo "Tests completed!"
64+
65+
get-kubeconfig:
66+
desc: Get kubeconfig for the test cluster
67+
silent: true
68+
run: once
69+
cmds:
70+
- |
71+
echo "Getting kubeconfig for cluster {{.CLUSTER_NAME}}..."
72+
replicated cluster kubeconfig --name {{.CLUSTER_NAME}} --output-path {{.KUBECONFIG_FILE}}
73+
status:
74+
- test -f {{.KUBECONFIG_FILE}}
75+
deps:
76+
- create-cluster
77+
78+
update-dependencies:
79+
desc: Update Helm dependencies for all charts
80+
silent: true
81+
cmds:
82+
- echo "Updating Helm dependencies for all charts..."
83+
- |
84+
# Find all charts and update their dependencies
85+
for chart_dir in $(find . -maxdepth 2 -name "Chart.yaml" | xargs dirname); do
86+
echo "Updating dependency $chart_dir"
87+
helm dependency update "$chart_dir"
88+
done
89+
- echo "All dependencies updated!"
90+
91+
expose-ports:
92+
desc: Expose configured ports and capture exposed URLs
93+
silent: true
94+
run: once
95+
status:
96+
- |
97+
CLUSTER_ID=$(replicated cluster ls --output json | jq -r '.[] | select(.name == "{{.CLUSTER_NAME}}") | .id')
98+
if [ -z "$CLUSTER_ID" ]; then
99+
exit 1
100+
fi
101+
102+
# Check if all ports are already exposed
103+
expected_count={{len .EXPOSE_PORTS}}
104+
port_checks=""
105+
{{range $i, $port := .EXPOSE_PORTS}}
106+
port_checks="${port_checks}(.upstream_port == {{$port.port}} and .exposed_ports[0].protocol == \"{{$port.protocol}}\") or "
107+
{{end}}
108+
# Remove trailing "or "
109+
port_checks="${port_checks% or }"
110+
111+
PORT_COUNT=$(replicated cluster port ls $CLUSTER_ID --output json | jq -r ".[] | select($port_checks) | .upstream_port" | wc -l | tr -d ' ')
112+
[ "$PORT_COUNT" -eq "$expected_count" ]
113+
cmds:
114+
- task: utils:port-operations
115+
vars:
116+
OPERATION: "expose"
117+
deps:
118+
- create-cluster
119+
120+
deploy-helm:
121+
desc: Deploy all charts using helmfile
122+
silent: true
123+
cmds:
124+
- echo "Installing all charts via helmfile"
125+
- |
126+
# Get cluster ID
127+
CLUSTER_ID=$(replicated cluster ls --output json | jq -r '.[] | select(.name == "{{.CLUSTER_NAME}}") | .id')
128+
if [ -z "$CLUSTER_ID" ]; then
129+
echo "Error: Could not find cluster with name {{.CLUSTER_NAME}}"
130+
exit 1
131+
fi
132+
133+
# Get exposed URLs
134+
ENV_VARS=$(task utils:port-operations OPERATION=getenv CLUSTER_NAME={{.CLUSTER_NAME}})
135+
136+
# Deploy with helmfile
137+
echo "Using $ENV_VARS"
138+
KUBECONFIG={{.KUBECONFIG_FILE}} $ENV_VARS helmfile sync --wait
139+
- echo "All charts deployed!"
140+
deps:
141+
- get-kubeconfig
142+
- expose-ports
143+
- remove-k3s-traefik
144+
145+
remove-k3s-traefik:
146+
desc: Remove pre-installed Traefik from k3s clusters
147+
silent: true
148+
cmds:
149+
- |
150+
# Only run for k3s distributions
151+
if [ "{{.DISTRIBUTION}}" = "k3s" ]; then
152+
task utils:traefik-operations OPERATION=remove KUBECONFIG_FILE={{.KUBECONFIG_FILE}}
153+
else
154+
echo "Not a k3s cluster, skipping Traefik removal."
155+
fi
156+
deps:
157+
- get-kubeconfig
158+
159+
delete-cluster:
160+
desc: Delete all test clusters with matching name and clean up kubeconfig
161+
silent: true
162+
cmds:
163+
- echo "Deleting clusters named {{.CLUSTER_NAME}}..."
164+
- |
165+
CLUSTER_IDS=$(replicated cluster ls | grep "{{.CLUSTER_NAME}}" | awk '{print $1}')
166+
if [ -z "$CLUSTER_IDS" ]; then
167+
echo "No clusters found with name {{.CLUSTER_NAME}}"
168+
exit 0
169+
fi
170+
171+
for id in $CLUSTER_IDS; do
172+
echo "Deleting cluster ID: $id"
173+
replicated cluster rm "$id"
174+
done
175+
- |
176+
# Clean up kubeconfig file
177+
if [ -f "{{.KUBECONFIG_FILE}}" ]; then
178+
echo "Removing kubeconfig file {{.KUBECONFIG_FILE}}"
179+
rm "{{.KUBECONFIG_FILE}}"
180+
fi
181+
- echo "All matching clusters deleted and kubeconfig cleaned up!"
182+
183+
prepare-release:
184+
desc: Prepare release files by copying replicated YAML files and packaging Helm charts
185+
silent: true
186+
cmds:
187+
- echo "Preparing release files..."
188+
- rm -rf ./release
189+
- mkdir -p ./release
190+
191+
# Copy all non-config.yaml files
192+
- echo "Copying non-config YAML files to release folder..."
193+
- find . -path '*/replicated/*.yaml' -not -name 'config.yaml' -exec cp {} ./release/ \;
194+
- find ./replicated -name '*.yaml' -not -name 'config.yaml' -exec cp {} ./release/ \; 2>/dev/null || true
195+
196+
# Merge config.yaml files
197+
- echo "Merging config.yaml files..."
198+
- |
199+
# Start with an empty config file
200+
echo "{}" > ./release/config.yaml
201+
202+
# Merge all app config.yaml files first (excluding root replicated)
203+
for config_file in $(find . -path '*/replicated/config.yaml' | grep -v "^./replicated/"); do
204+
echo "Merging $config_file..."
205+
yq eval-all '. as $item ireduce ({}; . * $item)' ./release/config.yaml "$config_file" > ./release/config.yaml.new
206+
mv ./release/config.yaml.new ./release/config.yaml
207+
done
208+
209+
# Merge root config.yaml last
210+
if [ -f "./replicated/config.yaml" ]; then
211+
echo "Merging root config.yaml last..."
212+
yq eval-all '. as $item ireduce ({}; . * $item)' ./release/config.yaml "./replicated/config.yaml" > ./release/config.yaml.new
213+
mv ./release/config.yaml.new ./release/config.yaml
214+
fi
215+
216+
# Package Helm charts
217+
- echo "Packaging Helm charts..."
218+
- |
219+
# Find top-level directories containing Chart.yaml files
220+
for chart_dir in $(find . -maxdepth 2 -name "Chart.yaml" | xargs dirname); do
221+
echo "Packaging chart: $chart_dir"
222+
# Navigate to chart directory, package it, and move the resulting .tgz to release folder
223+
(cd "$chart_dir" && helm package . && mv *.tgz ../release/)
224+
done
225+
226+
- echo "Release files prepared in ./release/ directory"
227+
228+
release-create:
229+
desc: Create and promote a release for home-cluster using the Replicated CLI
230+
silent: true
231+
requiredVars:
232+
- CHANNEL
233+
vars:
234+
RELEASE_NOTES: '{{.RELEASE_NOTES | default "Release created via task release-create"}}'
235+
cmds:
236+
- echo "Creating and promoting release for home-cluster to channel {{.CHANNEL}}..."
237+
- |
238+
# Create and promote the release in one step
239+
echo "Creating release from files in ./release directory..."
240+
replicated release create --app home-cluster --yaml-dir ./release --release-notes "{{.RELEASE_NOTES}}" --promote {{.CHANNEL}}
241+
242+
echo "Release created and promoted to channel {{.CHANNEL}}"
243+
deps:
244+
- prepare-release
245+
246+
create-gcp-vm:
247+
desc: Create a simple GCP VM instance
248+
silent: true
249+
vars:
250+
GCP_MACHINE_TYPE: '{{.GCP_MACHINE_TYPE | default "e2-standard-2"}}'
251+
GCP_DISK_SIZE: '{{.GCP_DISK_SIZE | default "100"}}'
252+
GCP_DISK_TYPE: '{{.GCP_DISK_TYPE | default "pd-standard"}}'
253+
GCP_IMAGE_FAMILY: '{{.GCP_IMAGE_FAMILY | default "ubuntu-2204-lts"}}'
254+
GCP_IMAGE_PROJECT: '{{.GCP_IMAGE_PROJECT | default "ubuntu-os-cloud"}}'
255+
status:
256+
- gcloud compute instances describe {{.VM_NAME}} --project={{.GCP_PROJECT}} --zone={{.GCP_ZONE}} &>/dev/null
257+
cmds:
258+
- task: utils:gcp-operations
259+
vars:
260+
OPERATION: "create"
261+
262+
delete-gcp-vm:
263+
desc: Delete the GCP VM instance for K8s and VPN
264+
silent: true
265+
status:
266+
- "! gcloud compute instances describe {{.VM_NAME}} --project={{.GCP_PROJECT}} --zone={{.GCP_ZONE}} &>/dev/null"
267+
cmds:
268+
- task: utils:gcp-operations
269+
vars:
270+
OPERATION: "delete"
271+
272+
setup-embedded-cluster:
273+
desc: Setup Replicated embedded cluster on the GCP VM
274+
silent: true
275+
vars:
276+
CHANNEL: '{{.CHANNEL | default "unstable"}}'
277+
AUTH_TOKEN: '{{.AUTH_TOKEN | default "2uJbnRgxrmrhqBDF1Sd7wcYFDPh"}}'
278+
deps:
279+
- create-gcp-vm
280+
status:
281+
- |
282+
# Check if the home-cluster tarball has already been downloaded and extracted
283+
gcloud compute ssh {{.VM_NAME}} --project={{.GCP_PROJECT}} --zone={{.GCP_ZONE}} --command="test -d ./home-cluster" &>/dev/null
284+
cmds:
285+
- task: utils:gcp-operations
286+
vars:
287+
OPERATION: "setup-embedded"
288+
289+
full-test-cycle:
290+
desc: Create cluster, get kubeconfig, expose ports, update dependencies, deploy charts, test, and delete
291+
silent: true
292+
cmds:
293+
- task: create-cluster
294+
- task: get-kubeconfig
295+
- task: expose-ports
296+
- task: remove-k3s-traefik
297+
- task: update-dependencies
298+
- task: deploy-helm
299+
- task: test
300+
- task: delete-cluster
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: v2
2+
name: cert-manager-issuers
3+
description: A Helm chart for cert-manager issuers
4+
type: application
5+
version: 1.0.0
6+
appVersion: "1.0.0"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: kots.io/v1beta2
2+
kind: HelmChart
3+
metadata:
4+
name: cert-manager-issuers
5+
spec:
6+
chart:
7+
name: cert-manager-issuers
8+
chartVersion: '1.0.0'
9+
weight: 1
10+
helmUpgradeFlags:
11+
- --wait
12+
namespace: cert-manager
13+
builder: {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{{- if .Values.local.letsencrypt.staging }}
2+
---
3+
apiVersion: cert-manager.io/v1
4+
kind: ClusterIssuer
5+
metadata:
6+
name: letsencrypt-staging
7+
annotations:
8+
argocd.argoproj.io/sync-wave: "1"
9+
spec:
10+
acme:
11+
email: {{ .Values.local.letsencrypt.email }}
12+
server: https://acme-staging-v02.api.letsencrypt.org/directory
13+
privateKeySecretRef:
14+
name: letsencrypt-staging-account-key
15+
solvers:
16+
- dns01:
17+
cloudflare:
18+
apiTokenSecretRef:
19+
name: cloudflare-token
20+
key: token
21+
{{- end }}
22+
{{- if .Values.local.letsencrypt.production }}
23+
---
24+
apiVersion: cert-manager.io/v1
25+
kind: ClusterIssuer
26+
metadata:
27+
name: letsencrypt-production
28+
annotations:
29+
argocd.argoproj.io/sync-wave: "1"
30+
spec:
31+
acme:
32+
email: {{ .Values.local.letsencrypt.email }}
33+
server: https://acme-v02.api.letsencrypt.org/directory
34+
privateKeySecretRef:
35+
name: letsencrypt-production-account-key
36+
solvers:
37+
- dns01:
38+
cloudflare:
39+
apiTokenSecretRef:
40+
name: cloudflare-token
41+
key: token
42+
{{- end }}
43+
{{- if .Values.local.letsencrypt.selfSigned }}
44+
---
45+
apiVersion: cert-manager.io/v1
46+
kind: ClusterIssuer
47+
metadata:
48+
name: selfsigned-cluster-issuer
49+
annotations:
50+
argocd.argoproj.io/sync-wave: "1"
51+
spec:
52+
selfSigned: {}
53+
{{- end }}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
local:
2+
letsencrypt:
3+
production: false
4+
staging: false
5+
selfSigned: true
6+
7+
acme_host: 'dns.example.com'

0 commit comments

Comments
 (0)