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
170 changes: 87 additions & 83 deletions src/internal/embedded/skills/pattern-author/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ allowed-tools: Read Bash(pattern *) Bash(helm *) Bash(find *) Bash(ls *)

# Validated Patterns Author

You are helping author Validated Patterns — GitOps-based deployment configurations for OpenShift built on the [clustergroup helm chart](https://github.com/validatedpatterns/clustergroup-chart/). A Pattern is a git repo containing values files that define what namespaces, operators, and applications to deploy on one or more OpenShift clusters via ArgoCD.
You are helping author Validated Patterns — GitOps-based deployment configurations for OpenShift built on the [clustergroup helm chart](https://github.com/validatedpatterns/clustergroup-chart/). A Pattern is a Git repository containing values files that define what namespaces, operators, and applications to deploy on one or more OpenShift clusters via ArgoCD.

For complete framework documentation, read [reference.md](reference.md) in this skill directory. Read it before your first Pattern authoring task in a session, or when you need details on a specific framework feature.

Expand All @@ -29,9 +29,9 @@ When creating a new Pattern:
3. **Define namespaces** — Add all required namespaces to the clustergroup values file (`values-<clusterGroupName>.yaml`). Include OperatorGroup configuration for operator namespaces.
4. **Define subscriptions** — Add operator subscriptions with at minimum the operator `name`. Set `namespace`, `channel`, and `source` as needed.
5. **Define applications** — Wire in helm charts as applications:
- Local charts: set `path` to the chart location in the repo
- Local charts: set `path` to the chart location in the repository
- VP-published charts: set `chart` and `chartVersion`
- External git charts: set `repoURL`, `path`, and `chartVersion` (git ref)
- External Git charts: set `repoURL`, `path`, and `chartVersion` (Git ref)
6. **Configure secrets** (if applicable) — Define secrets in `values-secret.yaml.template` and create corresponding ExternalSecret CRDs in chart templates.
7. **Set up hub/spoke** (if multi-cluster) — Add ACM subscription and `managedClusterGroups` to the hub. Create spoke values files.
8. **Add imperative jobs** (if needed) — Configure Ansible playbooks in the imperative framework for tasks that don't fit the declarative model.
Expand All @@ -43,12 +43,16 @@ When modifying an existing Pattern, read the current `values-global.yaml` and cl
These rules must always be followed:

- **Map form for namespaces** — Always define namespaces as a map, never a list. Maps merge across values files; lists override entirely.
- **No secrets in git** — Never put real secrets or credentials in the Pattern repo. Secrets belong in `~/values-secret-<pattern-name>.yaml` on the user's machine.
- **No secrets in Git** — Never put real secrets or credentials in the Pattern repository. Secrets belong in `~/values-secret-<pattern-name>.yaml` on the user's machine.
- **`singleArgoCD: true`** — Always set this for new Patterns.
- **`multiSourceConfig.enabled: true`** — Always set this for new Patterns.
- **Vault only on hub** — The Vault application and namespace belong only on the hub/main cluster. Spoke clusters need ESO only (no Vault). The VP `openshift-external-secrets` chart auto-configures spokes to use the hub's Vault.
- **Chart values stubs** — A chart's `values.yaml` must include default stubs for any `.Values.global.*` or `.Values.clusterGroup.*` values referenced in its templates, so `helm template` works standalone during development.
- **ESO backtick escaping** — In ExternalSecret templates, escape ESO template expressions with backticks to prevent Helm from interpreting them: `"{{ ` + "`" + `{{ .field_name }}` + "`" + ` }}"`.
- **ESO backtick escaping** — In ExternalSecret templates, escape ESO template expressions with backticks to prevent Helm from interpreting them:

```text
"{{ `{{ .field_name }}` }}"
```
- **Idempotent imperative jobs** — All imperative jobs run on a schedule (every 10 minutes by default) and must be idempotent.
- **Re-run `pattern init`** — After adding new local helm charts, re-run `pattern init` to wire them into the clustergroup values file. It is idempotent.

Expand Down Expand Up @@ -82,7 +86,7 @@ Not every operator needs a local chart. If the operator requires no additional c

### Adding a Local Helm Chart

Place the chart anywhere in the repo (convention: `charts/`). Run `pattern init` to auto-discover it, or manually add it to the clustergroup values:
Place the chart anywhere in the repository (convention: `charts/`). Run `pattern init` to auto-discover it, or manually add it to the clustergroup values:

```yaml
clusterGroup:
Expand All @@ -108,7 +112,7 @@ clusterGroup:
chartVersion: 0.1.*
```

### Adding a Chart from an External Git Repo
### Adding a Chart from an External Git Repository

```yaml
clusterGroup:
Expand All @@ -125,96 +129,96 @@ clusterGroup:

1. Define the secret in `values-secret.yaml.template`:

```yaml
version: "2.0"

secrets:
- name: my-secret
vaultPrefixes:
- global
fields:
- name: api-key
onMissingValue: prompt
- name: password
onMissingValue: generate
vaultPolicy: validatedPatternDefaultPolicy
```
```yaml
version: "2.0"

secrets:
- name: my-secret
vaultPrefixes:
- global
fields:
- name: api-key
onMissingValue: prompt
- name: password
onMissingValue: generate
vaultPolicy: validatedPatternDefaultPolicy
```

2. Add `secretStore` defaults to the chart's `values.yaml`:

```yaml
secretStore:
name: vault-backend
kind: ClusterSecretStore
```yaml
secretStore:
name: vault-backend
kind: ClusterSecretStore

mysecret:
key: secret/data/global/my-secret
refreshInterval: 2m0s
```
mysecret:
key: secret/data/global/my-secret
refreshInterval: 2m0s
```

3. Create an ExternalSecret template in the chart:

```yaml
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: my-secret
spec:
refreshInterval: {{ .Values.mysecret.refreshInterval }}
secretStoreRef:
name: {{ .Values.secretStore.name }}
kind: {{ .Values.secretStore.kind }}
target:
name: my-secret
template:
type: Opaque
data:
api-key: "{{ `{{ .api_key }}` }}"
password: "{{ `{{ .password }}` }}"
data:
- secretKey: api_key
remoteRef:
key: {{ .Values.mysecret.key }}
property: api-key
- secretKey: password
remoteRef:
key: {{ .Values.mysecret.key }}
property: password
```
```yaml
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: my-secret
spec:
refreshInterval: {{ .Values.mysecret.refreshInterval }}
secretStoreRef:
name: {{ .Values.secretStore.name }}
kind: {{ .Values.secretStore.kind }}
target:
name: my-secret
template:
type: Opaque
data:
api-key: "{{ `{{ .api_key }}` }}"
password: "{{ `{{ .password }}` }}"
data:
- secretKey: api_key
remoteRef:
key: {{ .Values.mysecret.key }}
property: api-key
- secretKey: password
remoteRef:
key: {{ .Values.mysecret.key }}
property: password
```

The Vault path is `secret/data/<vaultPrefix>/<secret-name>`. The `secretKey` values become the template variables in `target.template.data`.

### Adding a Spoke Cluster

1. Add ACM and `managedClusterGroups` to the hub clustergroup values:

```yaml
clusterGroup:
name: hub

namespaces:
open-cluster-management:

subscriptions:
acm:
name: advanced-cluster-management
namespace: open-cluster-management
channel: release-2.16

applications:
acm:
name: acm
namespace: open-cluster-management
chart: acm
chartVersion: 0.2.*

managedClusterGroups:
region-one:
name: group-one
acmlabels:
- name: clusterGroup
value: group-one
```
```yaml
clusterGroup:
name: hub

namespaces:
open-cluster-management:

subscriptions:
acm:
name: advanced-cluster-management
namespace: open-cluster-management
channel: release-2.16

applications:
acm:
name: acm
namespace: open-cluster-management
chart: acm
chartVersion: 0.2.*

managedClusterGroups:
region-one:
name: group-one
acmlabels:
- name: clusterGroup
value: group-one
```

2. Create `values-group-one.yaml` with the spoke's namespaces, subscriptions, and applications.

Expand Down
Loading
Loading