Skip to content

Commit 3f7a761

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 3f7a761

9 files changed

Lines changed: 491 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: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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+
Merge syncPolicy map with optional syncOptions; emit spec.syncPolicy block or nothing.
44+
Pass dict with key "app" (per-application values map).
45+
*/}}
46+
{{- define "rhoso-apps.syncPolicySpec" -}}
47+
{{- $app := .app -}}
48+
{{- $merged := $app.syncPolicy | default dict }}
49+
{{- if not (kindIs "map" $merged) }}
50+
{{- $merged = dict }}
51+
{{- end }}
52+
{{- if and $app.syncOptions (not (empty $app.syncOptions)) }}
53+
{{- $merged = merge $merged (dict "syncOptions" $app.syncOptions) }}
54+
{{- end }}
55+
{{- if not (empty $merged) }}
56+
syncPolicy:
57+
{{ toYaml $merged | indent 4 }}
58+
{{- end }}
59+
{{- end }}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
annotations:
10+
argocd.argoproj.io/sync-wave: {{ $app.syncWave | quote }}
11+
spec:
12+
project: {{ include "rhoso-apps.argocdProject" (dict "app" $app) }}
13+
source:
14+
repoURL: {{ $app.repoURL | quote }}
15+
path: {{ include "rhoso-apps.sourcePath" (dict "app" $app) }}
16+
targetRevision: {{ include "rhoso-apps.targetRevision" (dict "app" $app) }}
17+
destination:
18+
server: {{ include "rhoso-apps.destinationServer" $ }}
19+
{{ include "rhoso-apps.syncPolicySpec" (dict "app" $app) }}
20+
{{- end }}
21+
{{- end }}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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.annotations["argocd.argoproj.io/sync-wave"]
29+
value: "10"
30+
- equal:
31+
path: spec.project
32+
value: default
33+
- equal:
34+
path: spec.source.repoURL
35+
value: https://github.com/openstack-k8s-operators/gitops
36+
- equal:
37+
path: spec.source.path
38+
value: examples/controlplane
39+
- equal:
40+
path: spec.source.targetRevision
41+
value: v0.1.0
42+
- equal:
43+
path: spec.destination.server
44+
value: https://kubernetes.default.svc
45+
- equal:
46+
path: spec.syncPolicy.syncOptions[0]
47+
value: Prune=true
48+
49+
- it: fails values schema when repoURL is missing
50+
set:
51+
applicationNamespace: openshift-gitops
52+
destinationServer: https://kubernetes.default.svc
53+
applications:
54+
bad:
55+
enabled: true
56+
path: "."
57+
targetRevision: main
58+
syncWave: "0"
59+
syncOptions: []
60+
asserts:
61+
- failedTemplate:
62+
errorPattern: repoURL
63+
64+
- it: fails values schema when destinationServer is not an https URI
65+
set:
66+
applicationNamespace: openshift-gitops
67+
destinationServer: http://insecure.example
68+
applications:
69+
a:
70+
enabled: true
71+
repoURL: https://github.com/example/repo
72+
path: "."
73+
targetRevision: main
74+
syncWave: "0"
75+
syncOptions: []
76+
asserts:
77+
- failedTemplate:
78+
errorPattern: destinationServer
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "https://github.com/openstack-k8s-operators/gitops/charts/rhoso-apps/values.schema.json",
4+
"title": "rhoso-apps Helm chart values",
5+
"description": "Validated against templates/ and the documentation block in values.yaml.",
6+
"type": "object",
7+
"required": [
8+
"applicationNamespace",
9+
"destinationServer",
10+
"applications"
11+
],
12+
"additionalProperties": false,
13+
"properties": {
14+
"applicationNamespace": {
15+
"type": "string",
16+
"minLength": 1,
17+
"description": "Namespace for Argo CD Application CRs (metadata.namespace).",
18+
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"
19+
},
20+
"destinationServer": {
21+
"type": "string",
22+
"minLength": 1,
23+
"description": "Kubernetes API server URL for spec.destination.server.",
24+
"format": "uri",
25+
"pattern": "^https://"
26+
},
27+
"applications": {
28+
"type": "object",
29+
"minProperties": 1,
30+
"description": "Map of Application name to Argo CD Application settings.",
31+
"propertyNames": {
32+
"description": "DNS-1123 label: lowercase alphanumeric and hyphens (e.g. openstack-operator-cr).",
33+
"pattern": "^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$"
34+
},
35+
"additionalProperties": {
36+
"$ref": "#/definitions/application"
37+
}
38+
}
39+
},
40+
"definitions": {
41+
"application": {
42+
"type": "object",
43+
"description": "Per-application values under applications.<name>.",
44+
"required": [
45+
"enabled",
46+
"repoURL",
47+
"path",
48+
"targetRevision",
49+
"syncWave",
50+
"syncOptions"
51+
],
52+
"additionalProperties": false,
53+
"properties": {
54+
"enabled": {
55+
"type": "boolean",
56+
"description": "If true, render an Application CR."
57+
},
58+
"repoURL": {
59+
"type": "string",
60+
"minLength": 1,
61+
"description": "Git repository URL for spec.source.repoURL.",
62+
"format": "uri",
63+
"pattern": "^https://"
64+
},
65+
"path": {
66+
"type": "string",
67+
"description": "Path inside the repo; empty string uses chart default '.'."
68+
},
69+
"targetRevision": {
70+
"type": "string",
71+
"description": "Branch, tag, or commit; empty string uses chart default 'HEAD'."
72+
},
73+
"syncWave": {
74+
"type": "string",
75+
"description": "Value for argocd.argoproj.io/sync-wave annotation."
76+
},
77+
"syncOptions": {
78+
"type": "array",
79+
"description": "Argo CD sync option strings merged into spec.syncPolicy.syncOptions.",
80+
"minItems": 0,
81+
"items": {
82+
"type": "string",
83+
"minLength": 1
84+
}
85+
},
86+
"project": {
87+
"type": "string",
88+
"description": "Optional Argo CD AppProject; default in chart is 'default' if omitted."
89+
},
90+
"syncPolicy": {
91+
"type": "object",
92+
"description": "Optional map merged with syncOptions into spec.syncPolicy.",
93+
"additionalProperties": true
94+
}
95+
}
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)