Skip to content

Commit f245baa

Browse files
committed
feat(charts): add rhoso-apps helm chart and ci
Add Helm chart for Argo CD Applications (templates, values, values.schema.json), and a path-filtered GitHub Actions workflow (lint, template, package) with a TODO for publishing release artifacts. Refs: https://redhat.atlassian.net/browse/OSPRH-27658 AI-Assist: Cursor; model=Composer-2; mode=agent; origin=cursor Made-with: Cursor
1 parent 61cd3ac commit f245baa

9 files changed

Lines changed: 622 additions & 0 deletions

File tree

.github/workflows/helm-chart.yml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
---
2+
# Validate charts/rhoso-apps: lint (incl. values.schema.json), helm-unittest,
3+
# kubeconform on rendered CRs, package.
4+
# TODO: When release process is defined, persist and publish the chart artifact
5+
# (rhoso-apps-<version>.tgz from `helm package`)—e.g. GitHub Release asset, Helm
6+
# HTTP repo, or OCI registry—for downloadable installs.
7+
name: helm-chart
8+
permissions:
9+
contents: read
10+
on: # yamllint disable-line rule:truthy
11+
pull_request:
12+
branches:
13+
- main
14+
paths:
15+
- "charts/**"
16+
- ".github/workflows/helm-chart.yml"
17+
push:
18+
branches:
19+
- main
20+
paths:
21+
- "charts/**"
22+
- ".github/workflows/helm-chart.yml"
23+
jobs:
24+
validate:
25+
runs-on: ubuntu-latest
26+
env:
27+
# Pin tool versions (kubeconform: https://github.com/yannh/kubeconform/releases)
28+
KUBECONFORM_VERSION: v0.6.7
29+
# helm-unittest plugin: https://github.com/helm-unittest/helm-unittest/releases
30+
HELM_UNITTEST_VERSION: "0.7.0"
31+
# Kubernetes OpenAPI for built-in kinds; Argo Application uses Datree CRDs-catalog.
32+
KUBERNETES_SCHEMA_VERSION: "1.29.0"
33+
defaults:
34+
run:
35+
working-directory: charts/rhoso-apps
36+
steps:
37+
- name: Checkout
38+
uses: actions/checkout@v4
39+
40+
- name: Install Helm
41+
uses: azure/setup-helm@v4
42+
with:
43+
version: v3.16.3
44+
45+
- name: Install helm-unittest plugin
46+
run: helm plugin install https://github.com/helm-unittest/helm-unittest.git --version "${HELM_UNITTEST_VERSION}"
47+
48+
- name: Install kubeconform
49+
run: |
50+
set -euo pipefail
51+
mkdir -p "${HOME}/.local/bin"
52+
curl -sSL "https://github.com/yannh/kubeconform/releases/download/${KUBECONFORM_VERSION}/kubeconform-linux-amd64.tar.gz" | tar xz -C /tmp
53+
mv /tmp/kubeconform "${HOME}/.local/bin/kubeconform"
54+
echo "${HOME}/.local/bin" >> "${GITHUB_PATH}"
55+
56+
- name: Helm lint
57+
run: helm lint . -f values.yaml
58+
59+
- name: Helm unittest
60+
run: helm unittest .
61+
62+
- name: Helm template (kubeconform)
63+
run: |
64+
set -euo pipefail
65+
helm template rhoso-apps-test . -f values.yaml | kubeconform -summary \
66+
-kubernetes-version "${KUBERNETES_SCHEMA_VERSION}" \
67+
-schema-location default \
68+
-schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json'
69+
70+
# Produces rhoso-apps-*.tgz; publishing is TODO until release workflow exists (see file header).
71+
- name: Helm package
72+
run: helm package .

.yamllint.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ ignore:
55
- '*.env'
66
- '*.txt'
77
- '*.sh'
8+
# Helm templates are not valid YAML until rendered (Go templating).
9+
- 'charts/**/templates/**'
810

911
rules:
1012
line-length:

charts/rhoso-apps/.helmignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*.orig
18+
*~
19+
# Various IDEs
20+
.project
21+
.idea/
22+
*.tmproj
23+
.vscode/
24+
# helm-unittest suites (not part of the packaged chart)
25+
tests/

charts/rhoso-apps/Chart.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
apiVersion: v2
3+
name: rhoso-apps
4+
description: Create and manage argocd applications to deploy RHOSO
5+
6+
# A chart can be either an 'application' or a 'library' chart.
7+
#
8+
# Application charts are a collection of templates that can be packaged into versioned archives
9+
# to be deployed.
10+
#
11+
# Library charts provide useful utilities or functions for the chart developer. They're included as
12+
# a dependency of application charts to inject those utilities and functions into the rendering
13+
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
14+
type: application
15+
16+
# This is the chart version. This version number should be incremented each time you make changes
17+
# to the chart and its templates, including the app version.
18+
# Versions are expected to follow Semantic Versioning (https://semver.org/)
19+
version: 0.1.0
20+
21+
# This is the version number of the application being deployed. This version number should be
22+
# incremented each time you make changes to the application. Versions are not expected to
23+
# follow Semantic Versioning. They should reflect the version the application is using.
24+
# It is recommended to use it with quotes.
25+
appVersion: "18.0.17"
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{{/*
2+
Namespace for Argo CD Application CRs (metadata.namespace).
3+
Pass root context ($) from inside range.
4+
*/}}
5+
{{- define "rhoso-apps.applicationNamespace" -}}
6+
{{- default "openshift-gitops" .Values.applicationNamespace | quote -}}
7+
{{- end }}
8+
9+
{{/*
10+
Default Kubernetes API server URL for spec.destination.server.
11+
Pass root context ($) from inside range.
12+
*/}}
13+
{{- define "rhoso-apps.destinationServer" -}}
14+
{{- default "https://kubernetes.default.svc" .Values.destinationServer | quote -}}
15+
{{- end }}
16+
17+
{{/*
18+
Argo CD AppProject name; empty string in values maps to "default".
19+
Pass dict with key "app" (per-application values map).
20+
*/}}
21+
{{- define "rhoso-apps.argocdProject" -}}
22+
{{- $app := .app -}}
23+
{{- default "default" $app.project | quote -}}
24+
{{- end }}
25+
26+
{{/*
27+
Repository path under spec.source.path.
28+
*/}}
29+
{{- define "rhoso-apps.sourcePath" -}}
30+
{{- $app := .app -}}
31+
{{- default "." $app.path | quote -}}
32+
{{- end }}
33+
34+
{{/*
35+
Git revision, branch, or tag for spec.source.targetRevision.
36+
*/}}
37+
{{- define "rhoso-apps.targetRevision" -}}
38+
{{- $app := .app -}}
39+
{{- default "HEAD" $app.targetRevision | quote -}}
40+
{{- end }}
41+
42+
{{/*
43+
Optional spec.source.kustomize (Argo CD Kustomize overrides).
44+
Pass dict with key "app" (per-application values map). Omitted if unset, non-map, or empty map.
45+
*/}}
46+
{{- define "rhoso-apps.sourceKustomize" -}}
47+
{{- $app := .app -}}
48+
{{- $k := $app.kustomize | default dict }}
49+
{{- if not (kindIs "map" $k) }}
50+
{{- $k = dict }}
51+
{{- end }}
52+
{{- if not (empty $k) }}
53+
kustomize:
54+
{{ toYaml $k | nindent 6 }}
55+
{{- end }}
56+
{{- end }}
57+
58+
{{/*
59+
Merge syncPolicy map with optional syncOptions; emit spec.syncPolicy block or nothing.
60+
Pass dict with key "app" (per-application values map).
61+
*/}}
62+
{{- define "rhoso-apps.syncPolicySpec" -}}
63+
{{- $app := .app -}}
64+
{{- $merged := $app.syncPolicy | default dict }}
65+
{{- if not (kindIs "map" $merged) }}
66+
{{- $merged = dict }}
67+
{{- end }}
68+
{{- if and $app.syncOptions (not (empty $app.syncOptions)) }}
69+
{{- $merged = merge $merged (dict "syncOptions" $app.syncOptions) }}
70+
{{- end }}
71+
{{- if not (empty $merged) }}
72+
syncPolicy:
73+
{{ toYaml $merged | indent 4 }}
74+
{{- end }}
75+
{{- end }}
76+
77+
{{/*
78+
Argo CD Application metadata.finalizers (resources finalizer: background vs foreground).
79+
Omitted finalizers default to background deletion.
80+
Pass dict with key "app" (per-application values map).
81+
*/}}
82+
{{- define "rhoso-apps.applicationFinalizers" -}}
83+
{{- $app := .app -}}
84+
{{- $f := default (list "resources-finalizer.argocd.argoproj.io/background") $app.finalizers }}
85+
{{- toYaml $f -}}
86+
{{- end }}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{{- range $name, $app := .Values.applications }}
2+
{{- if $app.enabled }}
3+
---
4+
apiVersion: argoproj.io/v1alpha1
5+
kind: Application
6+
metadata:
7+
name: {{ $name }}
8+
namespace: {{ include "rhoso-apps.applicationNamespace" $ }}
9+
finalizers:
10+
{{- include "rhoso-apps.applicationFinalizers" (dict "app" $app) | nindent 4 }}
11+
annotations:
12+
argocd.argoproj.io/sync-wave: {{ $app.syncWave | quote }}
13+
spec:
14+
project: {{ include "rhoso-apps.argocdProject" (dict "app" $app) }}
15+
source:
16+
repoURL: {{ $app.repoURL | quote }}
17+
path: {{ include "rhoso-apps.sourcePath" (dict "app" $app) }}
18+
targetRevision: {{ include "rhoso-apps.targetRevision" (dict "app" $app) }}
19+
{{ include "rhoso-apps.sourceKustomize" (dict "app" $app) }}
20+
destination:
21+
server: {{ include "rhoso-apps.destinationServer" $ }}
22+
{{ include "rhoso-apps.syncPolicySpec" (dict "app" $app) }}
23+
{{- end }}
24+
{{- end }}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
---
2+
# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json
3+
suite: rhoso-apps application template
4+
templates:
5+
- application.yaml
6+
tests:
7+
# Helm merges values; we cannot replace the whole applications map from tests.
8+
# Assert on one real Application from values.yaml via documentSelector.
9+
- it: renders openstack-controlplane Application from default values
10+
values:
11+
- ../values.yaml
12+
documentSelector:
13+
path: metadata.name
14+
value: openstack-controlplane
15+
asserts:
16+
- isKind:
17+
of: Application
18+
- equal:
19+
path: apiVersion
20+
value: argoproj.io/v1alpha1
21+
- equal:
22+
path: metadata.name
23+
value: openstack-controlplane
24+
- equal:
25+
path: metadata.namespace
26+
value: openshift-gitops
27+
- equal:
28+
path: metadata.finalizers[0]
29+
value: resources-finalizer.argocd.argoproj.io/foreground
30+
- equal:
31+
path: metadata.annotations["argocd.argoproj.io/sync-wave"]
32+
value: "10"
33+
- equal:
34+
path: spec.project
35+
value: default
36+
- equal:
37+
path: spec.source.repoURL
38+
value: https://github.com/openstack-k8s-operators/gitops
39+
- equal:
40+
path: spec.source.path
41+
value: examples/controlplane
42+
- equal:
43+
path: spec.source.targetRevision
44+
value: v0.1.0
45+
- equal:
46+
path: spec.destination.server
47+
value: https://kubernetes.default.svc
48+
- equal:
49+
path: spec.syncPolicy.syncOptions[0]
50+
value: Prune=true
51+
52+
- it: renders operator-dependencies with default background finalizer
53+
values:
54+
- ../values.yaml
55+
documentSelector:
56+
path: metadata.name
57+
value: operator-dependencies
58+
asserts:
59+
- equal:
60+
path: metadata.finalizers[0]
61+
value: resources-finalizer.argocd.argoproj.io/background
62+
63+
- it: omits spec.source.kustomize when not set in values
64+
values:
65+
- ../values.yaml
66+
documentSelector:
67+
path: metadata.name
68+
value: operator-dependencies
69+
asserts:
70+
- notExists:
71+
path: spec.source.kustomize
72+
73+
- it: renders spec.source.kustomize when applications entry sets kustomize
74+
set:
75+
applicationNamespace: openshift-gitops
76+
destinationServer: https://kubernetes.default.svc
77+
applications:
78+
kustom-test:
79+
enabled: true
80+
repoURL: https://github.com/example/repo
81+
path: examples/foo
82+
targetRevision: main
83+
syncWave: "0"
84+
syncOptions:
85+
- Prune=true
86+
kustomize:
87+
namePrefix: dev-
88+
components:
89+
- https://github.com/example/components/overlay
90+
documentSelector:
91+
path: metadata.name
92+
value: kustom-test
93+
asserts:
94+
- equal:
95+
path: spec.source.kustomize.namePrefix
96+
value: dev-
97+
- equal:
98+
path: spec.source.kustomize.components[0]
99+
value: https://github.com/example/components/overlay
100+
101+
- it: fails values schema when repoURL is missing
102+
set:
103+
applicationNamespace: openshift-gitops
104+
destinationServer: https://kubernetes.default.svc
105+
applications:
106+
bad:
107+
enabled: true
108+
path: "."
109+
targetRevision: main
110+
syncWave: "0"
111+
syncOptions: []
112+
asserts:
113+
- failedTemplate:
114+
errorPattern: repoURL
115+
116+
- it: fails values schema when destinationServer is not an https URI
117+
set:
118+
applicationNamespace: openshift-gitops
119+
destinationServer: http://insecure.example
120+
applications:
121+
a:
122+
enabled: true
123+
repoURL: https://github.com/example/repo
124+
path: "."
125+
targetRevision: main
126+
syncWave: "0"
127+
syncOptions: []
128+
asserts:
129+
- failedTemplate:
130+
errorPattern: destinationServer
131+
132+
- it: fails values schema when finalizer is not allowed
133+
set:
134+
applicationNamespace: openshift-gitops
135+
destinationServer: https://kubernetes.default.svc
136+
applications:
137+
bad:
138+
enabled: true
139+
repoURL: https://github.com/example/repo
140+
path: "."
141+
targetRevision: main
142+
syncWave: "0"
143+
syncOptions: []
144+
finalizers:
145+
- resources-finalizer.argocd.argoproj.io/invalid
146+
asserts:
147+
- failedTemplate:
148+
errorPattern: finalizers

0 commit comments

Comments
 (0)