Skip to content

Commit 661c633

Browse files
authored
feat(example): add openstack-operator cdn overlay (#43)
Add a working example under example/openstack-operator: Red Hat CDN catalog ConfigMap, CSV pin via pin-version, remote architecture olm-openstack-subscriptions, and a TODO to switch the approve-installplan component to an HTTPS ref when available. Extract InstallPlan approval (job, rbac, subscription sync-wave) into reusable components/utilities/approve-installplan with README. Update example README table and openstack-operator docs. AI-Assist: Cursor; model=auto; mode=agent; origin=cursor Made-with: Cursor
1 parent 456d1ca commit 661c633

File tree

12 files changed

+360
-15
lines changed

12 files changed

+360
-15
lines changed

components/utilities/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Utilities
2+
3+
Shared Kustomize `Component` helpers for GitOps overlays. Add new utilities as subdirectories
4+
here (each subdirectory should contain a `kustomization.yaml` with `kind: Component`).
5+
6+
| Component | Role |
7+
| --------- | ---- |
8+
| [approve-installplan](./approve-installplan/) | RBAC, `Job`, and Subscription sync-wave patch for Manual OLM InstallPlan approval (OpenStack operator subscription from `architecture` `olm-openstack-subscriptions`). |
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# approve-installplan
2+
3+
Kustomize `Component` that adds:
4+
5+
- `ClusterRole` / `ClusterRoleBinding` so the OpenShift GitOps Argo CD application controller can patch `InstallPlan` objects and read related OpenStack APIs used by the Job script.
6+
- `Job` `approve-openstack-installplan` (sync-wave `1`) that approves a Manual InstallPlan and waits for operator install.
7+
- JSON6902 patch on `Subscription` named `openstack` (sync-wave `0`), matching
8+
[`architecture` `olm-openstack-subscriptions`](https://github.com/openstack-k8s-operators/architecture/tree/main/lib/olm-openstack-subscriptions).
9+
10+
## Usage
11+
12+
List this directory under `components` **after** the overlay that emits the `Subscription` (for
13+
example the remote `lib/olm-openstack-subscriptions/overlays/default` component). If this component
14+
runs first, the Subscription sync-wave patch will not apply.
15+
16+
```yaml
17+
components:
18+
- pin-version
19+
- https://github.com/openstack-k8s-operators/architecture/lib/olm-openstack-subscriptions/overlays/default?ref=COMMIT_SHA
20+
- https://github.com/openstack-k8s-operators/gitops/components/utilities/approve-installplan?ref=v0.1.0
21+
```
22+
23+
Images: `registry.redhat.io/openshift4/ose-tools-rhel9:latest` (Job).
24+
25+
In-tree overlays may reference this component with a local path until a release tag that contains
26+
`components/utilities/approve-installplan` is published; use the HTTPS URL above for reproducible
27+
builds outside the repository.
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
---
2+
apiVersion: batch/v1
3+
kind: Job
4+
metadata:
5+
name: approve-openstack-installplan
6+
namespace: openshift-gitops
7+
annotations:
8+
argocd.argoproj.io/sync-wave: "1"
9+
spec:
10+
backoffLimit: 1
11+
template:
12+
spec:
13+
containers:
14+
- name: installplan-approver
15+
image: registry.redhat.io/openshift4/ose-tools-rhel9:latest
16+
env:
17+
- name: OS_OPERATORS_NAMESPACE
18+
value: "openstack-operators"
19+
- name: OS_NAMESPACE
20+
value: "openstack"
21+
- name: RETRIES
22+
value: "30"
23+
- name: DELAY
24+
value: "10"
25+
- name: INSTALL_PLAN_RETRIES
26+
value: "60"
27+
- name: DEBUG
28+
value: "true"
29+
command:
30+
- /bin/bash
31+
- -c
32+
- |
33+
set -eo pipefail
34+
if [ "${DEBUG}" = "true" ]; then
35+
set -x
36+
fi
37+
# --- Configuration with defaults ---
38+
# Namespace where the OpenStack operators are installed.
39+
OS_OPERATORS_NAMESPACE=${OS_OPERATORS_NAMESPACE:-"openstack-operators"}
40+
# Namespace where the OpenStack control plane is deployed and the OpenStackVersion CR exists.
41+
OS_NAMESPACE=${OS_NAMESPACE:-"openstack"}
42+
# Default number of retries for most checks.
43+
RETRIES=${RETRIES:-60}
44+
# Number of retries specifically for waiting for the InstallPlan to complete.
45+
# This is longer because pulling operator images and starting pods can take a significant amount of time.
46+
INSTALL_PLAN_RETRIES=${INSTALL_PLAN_RETRIES:-60}
47+
# Delay in seconds between retries.
48+
DELAY=${DELAY:-10}
49+
# ---
50+
51+
log() {
52+
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) $@" >&2
53+
}
54+
55+
find_and_approve_installplan() {
56+
log "Waiting for unapproved InstallPlan..."
57+
for i in $(seq 1 $RETRIES); do
58+
install_plan_name=$(oc get installplan -n $OS_OPERATORS_NAMESPACE -o json | jq -r '.items[] | select(.spec.approval=="Manual" and .spec.approved==false) | .metadata.name' | head -n1) || true
59+
[ -n "$install_plan_name" ] && break
60+
log "Attempt $i/$RETRIES: No InstallPlan found, retrying..."
61+
sleep $DELAY
62+
done
63+
64+
if [ -z "$install_plan_name" ]; then
65+
log "Error: No manual InstallPlan found to approve within the time limit."
66+
exit 1
67+
fi
68+
69+
log "Found InstallPlan: $install_plan_name, approving..."
70+
oc patch installplan $install_plan_name -n $OS_OPERATORS_NAMESPACE --type merge -p '{"spec":{"approved":true}}' >&2
71+
echo "$install_plan_name"
72+
}
73+
74+
wait_for_installplan_completion() {
75+
local install_plan_name=$1
76+
for i in $(seq 1 $INSTALL_PLAN_RETRIES); do
77+
phase=$(oc get installplan $install_plan_name -n $OS_OPERATORS_NAMESPACE -o jsonpath='{.status.phase}')
78+
if [ "$phase" == "Complete" ]; then
79+
log "InstallPlan completed successfully."
80+
return 0
81+
fi
82+
if [ $i -eq $INSTALL_PLAN_RETRIES ]; then
83+
log "Error: InstallPlan did not complete in time."
84+
exit 1
85+
fi
86+
log "Attempt $i/$INSTALL_PLAN_RETRIES: Status is $phase, retrying..."
87+
sleep $DELAY
88+
done
89+
}
90+
91+
wait_for_crd() {
92+
log "Waiting for OpenStack CRD to become available..."
93+
for i in $(seq 1 $RETRIES); do
94+
if oc get crd openstacks.operator.openstack.org -o jsonpath='{.status.conditions[?(@.type=="Established")].status}' | grep -q True; then
95+
log "OpenStack CRD is Established."
96+
return 0
97+
fi
98+
if [ $i -eq $RETRIES ]; then
99+
log "Error: OpenStack CRD did not become Established in time."
100+
exit 1
101+
fi
102+
log "Attempt $i/$RETRIES: CRD not Established, retrying..."
103+
sleep $DELAY
104+
done
105+
}
106+
107+
wait_for_new_version() {
108+
log "Waiting for a new OpenStack version to become available..."
109+
110+
local deployed_version=""
111+
for i in $(seq 1 $RETRIES); do
112+
# There should be only one openstackversion CR
113+
deployed_version=$(oc get openstackversion -n $OS_NAMESPACE -o jsonpath='{.items[0].status.deployedVersion}' 2>/dev/null) || true
114+
if [ -n "$deployed_version" ]; then
115+
log "Current deployed version is: $deployed_version"
116+
break
117+
fi
118+
if [ $i -eq $RETRIES ]; then
119+
log "Error: Could not get current deployed version in time."
120+
exit 1
121+
fi
122+
log "Attempt $i/$RETRIES: Waiting for OpenStackVersion resource to get deployedVersion. Retrying..."
123+
sleep $DELAY
124+
done
125+
126+
for i in $(seq 1 $RETRIES); do
127+
available_version=$(oc get openstackversion -n $OS_NAMESPACE -o jsonpath='{.items[0].status.availableVersion}' 2>/dev/null) || true
128+
if [ -n "$available_version" ] && [ "$available_version" != "$deployed_version" ]; then
129+
log "New available version found: $available_version"
130+
return 0
131+
fi
132+
if [ $i -eq $RETRIES ]; then
133+
log "Error: New version did not become available in time."
134+
exit 1
135+
fi
136+
log "Attempt $i/$RETRIES: No new version available yet. Current available is '$available_version'. Retrying..."
137+
sleep $DELAY
138+
done
139+
}
140+
141+
# --- Main execution ---
142+
log "Starting install_plan_approval"
143+
install_plan_name=$(find_and_approve_installplan)
144+
145+
log "Waiting for InstallPlan '$install_plan_name' to complete..."
146+
147+
wait_for_installplan_completion "$install_plan_name"
148+
149+
log "Checking if this is an initial installation or an update..."
150+
if oc get openstack openstack -n $OS_OPERATORS_NAMESPACE; then
151+
log "OpenStack CR 'openstack' exists. This is an update."
152+
wait_for_new_version
153+
else
154+
log "OpenStack CR 'openstack' does not exist. This is an initial installation."
155+
wait_for_crd
156+
fi
157+
158+
log "Job completed successfully."
159+
restartPolicy: Never
160+
serviceAccountName: openshift-gitops-argocd-application-controller
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
# Apply this component after the Subscription exists in the build (e.g. after
3+
# openstack-k8s-operators/architecture lib/olm-openstack-subscriptions).
4+
apiVersion: kustomize.config.k8s.io/v1alpha1
5+
kind: Component
6+
resources:
7+
- job.yaml
8+
- rbac.yaml
9+
patches:
10+
- target:
11+
kind: Subscription
12+
name: openstack
13+
patch: |-
14+
- op: add
15+
path: /metadata/annotations/argocd.argoproj.io~1sync-wave
16+
value: "0"
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
# ClusterRole and binding so the OpenShift GitOps Argo CD application controller
3+
# can approve OLM InstallPlans and read resources used by the approval Job.
4+
apiVersion: rbac.authorization.k8s.io/v1
5+
kind: ClusterRole
6+
metadata:
7+
name: installplan-approver-role
8+
rules:
9+
- apiGroups:
10+
- operators.coreos.com
11+
resources:
12+
- installplans
13+
verbs:
14+
- get
15+
- list
16+
- patch
17+
- apiGroups:
18+
- operator.openstack.org
19+
resources:
20+
- openstacks
21+
verbs:
22+
- get
23+
- list
24+
- apiGroups:
25+
- core.openstack.org
26+
resources:
27+
- openstackversions
28+
verbs:
29+
- get
30+
- list
31+
- patch
32+
- apiGroups:
33+
- apiextensions.k8s.io
34+
resources:
35+
- customresourcedefinitions
36+
verbs:
37+
- get
38+
- list
39+
---
40+
apiVersion: rbac.authorization.k8s.io/v1
41+
kind: ClusterRoleBinding
42+
metadata:
43+
name: installplan-approver-binding
44+
roleRef:
45+
apiGroup: rbac.authorization.k8s.io
46+
kind: ClusterRole
47+
name: installplan-approver-role
48+
subjects:
49+
- kind: ServiceAccount
50+
name: openshift-gitops-argocd-application-controller
51+
namespace: openshift-gitops

example/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ where the overlay includes `components/argocd/annotations`).
1111

1212
| Directory | Role |
1313
| --------- | ---- |
14-
| [openstack-operator](./openstack-operator/) | Foundational OpenStack operators, namespaces, subscription, install-plan approval (placeholder until a component exists). |
14+
| [openstack-operator](./openstack-operator/) | Foundational OpenStack operators: CDN catalog + pinned CSV, OLM subscription from `architecture` `olm-openstack-subscriptions`, InstallPlan approval Job, RBAC. |
1515
| [openstack-operator-cr](./openstack-operator-cr/) | Main OpenStack custom resource (placeholder until a component exists). |
1616
| [openstack-networks](./openstack-networks/) | Underlying networks: NNCP, NAD, NetConfig, MetalLB pools / advertisements, etc. |
1717
| [openstack-controlplane](./openstack-controlplane/) | `OpenStackControlPlane` and optional watcher service (networking is a separate overlay). |
Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,53 @@
11
# openstack-operator
22

3-
Placeholder overlay for the **openstack-operator** application described in the root
4-
`README.md` (*Application responsibilities and content*).
3+
Sample Kustomize overlay for the **openstack-operator** application described in the repository root
4+
`README.md` under *Application responsibilities and content*.
55

6-
There is no matching component under `components/rhoso/` yet. Replace this directory's
7-
`kustomization.yaml` with real `components` / `resources` when a slice is added.
6+
It installs the OpenStack operator from the **Red Hat Operator Hub** (`redhat-operators` catalog):
7+
namespaces, `OperatorGroup`, `CatalogSource`, `Subscription` with a pinned `startingCSV`, Manual
8+
InstallPlan approval, a post-sync `Job` that approves the InstallPlan, and RBAC for the OpenShift
9+
GitOps application controller. The OLM manifests are composed from the
10+
[`openstack-k8s-operators/architecture`](https://github.com/openstack-k8s-operators/architecture)
11+
`lib/olm-openstack-subscriptions` component; local pieces supply CDN catalog settings and the CSV
12+
pin.
813

9-
Upstream context: Red Hat documentation *Installing and preparing the OpenStack
10-
operator* (namespaces, subscription, install-plan approval job).
14+
See Red Hat documentation:
15+
[Installing and preparing the OpenStack operator](https://docs.redhat.com/en/documentation/red_hat_openstack_services_on_openshift/18.0/html/deploying_red_hat_openstack_services_on_openshift/assembly_installing-and-preparing-the-openstack-operator).
16+
17+
## Layout
18+
19+
| Path | Role |
20+
| ---- | ---- |
21+
| `catalog/values.yaml` | `ConfigMap` `olm-values` (channel, `redhat-operators`, index image, Manual approval) |
22+
| `pin-version/` | Adds `data.openstack-operator-version` for `spec.startingCSV` |
23+
| Remote component | `architecture` `olm-openstack-subscriptions/overlays/default` (pinned git ref in `kustomization.yaml`) |
24+
| [approve-installplan](https://github.com/openstack-k8s-operators/gitops/tree/v0.1.0/components/utilities/approve-installplan) (`?ref=v0.1.0`) | Shared utility: RBAC, InstallPlan approval `Job`, Subscription sync-wave |
25+
26+
Component order in `kustomization.yaml` matters: the version pin runs before the remote OLM
27+
component; the approval `Component` runs after it so the Subscription exists when the sync-wave patch
28+
is applied.
29+
30+
## Render
31+
32+
From the repository root (network access is required the first time to fetch the remote component):
33+
34+
```bash
35+
kustomize build example/openstack-operator
36+
```
37+
38+
## Tunables
39+
40+
- **`registry.redhat.io/redhat/redhat-operator-index` tag** in `catalog/values.yaml` (`openstack-operator-image`):
41+
use an index tag that matches your OpenShift cluster version (for example `v4.18` on OpenShift 4.18).
42+
- **`openstack-operator.v1.0.16`** in `pin-version/patch.yaml`: adjust the CSV string when you target
43+
a different RHOSO release available in the catalog.
44+
- **Remote `architecture` git ref** in `kustomization.yaml`: pinned to a commit SHA for reproducible
45+
builds; bump when you intentionally adopt newer `lib/olm-openstack-subscriptions` behavior.
46+
47+
Clusters must be able to pull images from `registry.redhat.io` (pull secret / global pull secret).
48+
49+
## Related
50+
51+
- [`components/utilities/`](../../components/utilities/) for shared helper components (including InstallPlan approval).
52+
- `openstack-operator-cr` example: main `OpenStack` CR (separate overlay).
53+
- `example/dependencies` for other OLM-related dependencies (MetalLB, NMState, cert-manager, etc.).
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
apiVersion: kustomize.config.k8s.io/v1beta1
3+
kind: Kustomization
4+
5+
resources:
6+
- values.yaml
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# local-config: referenced, but not emitted by kustomize
2+
---
3+
apiVersion: v1
4+
kind: ConfigMap
5+
metadata:
6+
name: olm-values
7+
annotations:
8+
config.kubernetes.io/local-config: 'true'
9+
data:
10+
openstack-operator-subscription-namespace: "openstack-operators"
11+
openstack-operator-channel: "stable-v1.0"
12+
openstack-operator-installplanapproval: "Manual"
13+
# --- CatalogSource Settings ---
14+
openstack-operator-catalog-source: "redhat-operators"
15+
openstack-catalog-namespace: "openshift-marketplace"
16+
openstack-operator-image: "registry.redhat.io/redhat/redhat-operator-index:v4.18"
17+
openstack-operator-display-name: "Red Hat Operators"
18+
openstack-operator-publisher: "Red Hat"

example/openstack-operator/kustomization.yaml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
apiVersion: kustomize.config.k8s.io/v1beta1
33
kind: Kustomization
44

5-
resources: []
5+
resources:
6+
- catalog
67

7-
# No gitops component yet for this slice. See README.md.
8-
# Future: add components (remote refs and local paths), for example:
9-
# components:
10-
# - https://github.com/openstack-k8s-operators/gitops/components/argocd/annotations?ref=TAG
11-
# - https://github.com/openstack-k8s-operators/gitops/components/rhoso/...?ref=TAG
12-
# Local paths (for "kustomize build" testing):
13-
# - ../../components/argocd/annotations
8+
# pin-version must run before the remote OLM component (olm-values feeds replacements).
9+
# approve-installplan must run after the remote component so the Subscription exists for the sync-wave patch.
10+
components:
11+
- pin-version
12+
- https://github.com/openstack-k8s-operators/architecture/lib/olm-openstack-subscriptions/overlays/default?ref=7da5f2e1dc2bfce99e269b0017783679ca405d8c
13+
# TODO: replace the local path with a git HTTPS URI, e.g.
14+
# https://github.com/openstack-k8s-operators/gitops/components/utilities/approve-installplan?ref=v0.1.0
15+
# once that tag includes this component (same pattern as other example overlays).
16+
- ../../components/utilities/approve-installplan

0 commit comments

Comments
 (0)