GitOps deployment chain (hub → spokes)
This page explains why you see names like acm-hub-spoke, east-spoke-components, and industrial-edge-tst in ACM / Argo CD, and which YAML creates each link.
ACM Fleet → Applications lists Argo CD
Applicationresources. The ApplicationSetfleet-spoke-pushis a separate object. Search for it under Infrastructure → Clusters → GitOps (Argo CD UI) or withoc get applicationset fleet-spoke-push -n openshift-gitops.
End-to-end diagram
flowchart TB
subgraph git [Git repo hybrid-mesh-platform]
hubRegion[charts/region/hub/values.yaml]
eastRegion[charts/region/east/values.yaml]
westRegion[charts/region/west/values.yaml]
acmChart[charts/all/acm-hub-spoke]
spokePush[charts/all/spoke-meta-push]
components[charts/all/* components]
end
subgraph hubCluster [Hub cluster Argo CD openshift-gitops]
cgApp[hybrid-mesh-platform-hub Application]
acmApp[acm-hub-spoke Application]
appSet[fleet-spoke-push ApplicationSet]
eastParent[east-spoke-components Application]
westParent[west-spoke-components Application]
hubApps[kafka-console developer-hub etc]
end
subgraph eastCluster [East spoke Argo CD]
eastChild[industrial-edge-tst operators-edge ...]
eastWorkloads[Pods Routes Integrations]
end
cgApp --> acmApp
cgApp --> hubApps
acmApp --> appSet
appSet --> eastParent
appSet --> westParent
eastParent -->|remote sync chart east/| eastChild
eastChild --> eastWorkloads
eastChart --> eastParent
components --> eastChild
Layer 1 — Hub bootstrap (region chart)
What you run once on the hub (standalone or via RHDP field-content):
# Standalone — requires oc logged in as cluster-admin
./pattern.sh make install
# RHDP — three catalog orders; hub path charts/region/hub
# deployer.domain injected by Argo CD helm values (see rhdp-field-content.md)
What it renders: the charts/region/hub chart creates a multi-source Argo CD Application that pulls the VP clustergroup chart plus charts/region/hub/values.yaml:
charts/region/hub/templates/clustergroup-application.yaml:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: hybrid-mesh-platform-hub # -
namespace: openshift-gitops
spec:
sources:
- repoURL: https://github.com/.../hybrid-mesh-platform
ref: patternref
- repoURL: https://charts.validatedpatterns.io
chart: clustergroup
helm:
valueFiles:
- $patternref/values-global.yaml
- $patternref/charts/region/hub/values.yaml
The clustergroup chart loops clusterGroup.applications in charts/region/hub/values.yaml and creates one child Application per entry — including acm-hub-spoke, developer-hub, service-interconnect, etc.
Entry for ACM:
clusterGroup:
applications:
acm-hub-spoke:
name: acm-hub-spoke
namespace: openshift-gitops
argoProject: fleet
path: charts/all/acm-hub-spoke
syncWave: '6'
ACM UI / Argo CD: acm-hub-spoke on local-cluster — parent of the fleet ApplicationSet, not the ApplicationSet itself.
Layer 2 — ACM + ApplicationSet (charts/all/acm-hub-spoke)
When acm-hub-spoke syncs, it applies Placement, GitOpsCluster, ConfigMap acm-placement, and the ApplicationSet (sync waves 1–4).
2a — Placement selects east and west
charts/all/acm-hub-spoke/templates/placement.yaml:
apiVersion: cluster.open-cluster-management.io/v1beta1
kind: Placement
metadata:
name: hub-spoke-placement
namespace: openshift-gitops
labels:
cluster.open-cluster-management.io/placement: hub-spoke-placement
spec:
clusterSets:
- global
predicates:
- requiredClusterSelector:
labelSelector:
matchExpressions:
- key: region
operator: In
values: [east, west]
ACM creates PlacementDecision objects listing cluster names (east, west).
2b — ConfigMap tells the generator how to read decisions
charts/all/acm-hub-spoke/templates/acm-placement-configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: acm-placement
namespace: openshift-gitops
data:
apiVersion: cluster.open-cluster-management.io/v1beta1
kind: placementdecisions
matchKey: clusterName
statusListKey: decisions
2c — ApplicationSet (fleet root for spokes)
charts/all/acm-hub-spoke/templates/applicationset.yaml:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: fleet-spoke-push # <-- search this name in CLI / Argo CD
namespace: openshift-gitops
labels:
cluster.open-cluster-management.io/placement: hub-spoke-placement
spec:
generators:
- clusterDecisionResource:
configMapRef: acm-placement
labelSelector:
matchLabels:
cluster.open-cluster-management.io/placement: hub-spoke-placement
template:
metadata:
name: '-spoke-components' # east-spoke-components, west-spoke-components
spec:
source:
repoURL: https://github.com/.../hybrid-mesh-platform
path: '' # east/ or west/
destination:
name: '' # Argo CD cluster secret: east | west
namespace: openshift-gitops
syncPolicy:
automated:
selfHeal: true
prune: true
| Generator variable | Becomes in template |
|---|---|
| `` | east / west — folder and Argo cluster name |
| (from PlacementDecision) | One Application per selected cluster |
Important: This ApplicationSet is a normal manifest (sync-wave 4), not a PostSync hook, so it stays in the cluster and ACM can index it.
Layer 3 — Spoke GitOps (PULL + PUSH)
PUSH (hub → spoke): east-spoke-components / west-spoke-components (on hub Argo CD, destination east / west) sync charts/all/spoke-meta-push. That chart loops components[] and creates child Applications on the spoke (e.g. operators-ci-east).
PULL (spoke local): Each spoke runs RHDP field-content at charts/region/east or charts/region/west, which deploys clustergroup from charts/region/east/values.yaml (or west). Child apps point at charts/all/*:
# excerpt from charts/region/east/values.yaml → clusterGroup.applications
industrial-edge-tst:
name: industrial-edge-tst
path: charts/all/industrial-edge-tst
namespace: industrial-edge-tst-all
argoProject: industrial-edge
Example on east spoke Argo CD: industrial-edge-tst, operators-edge, spoke-gateway, … — names from clusterGroup.applications, not -east suffix (unless the app name field includes it).
Layer 4 — Workloads (charts/all/*)
Each child Application points at a Helm chart under charts/all/<name>/ (Kafka, Camel K, gateways, etc.).
Naming cheat sheet
| Name you see | Kind | Where it runs | Created by |
|---|---|---|---|
hybrid-mesh-platform-hub | Application (clustergroup root) | Hub | charts/region/hub bootstrap |
acm-hub-spoke | Application | Hub | charts/region/hub/values.yaml |
fleet-spoke-push | ApplicationSet | Hub | charts/all/acm-hub-spoke |
east-spoke-components | Application | Hub → deploys to east | ApplicationSet template (PUSH) |
industrial-edge-tst | Application | East spoke | charts/region/east/values.yaml (PULL) |
kafka-console | Application | Hub | charts/region/hub/values.yaml |
Sync order (hub ACM chart)
| Wave | Resource |
|---|---|
| 0 | RBAC for ApplicationSet controller |
| 1 | ManagedClusterSet binding |
| 2 | Placement + acm-placement ConfigMap |
| 3 | GitOpsCluster |
| 4 | ApplicationSet fleet-spoke-push |
Spoke sync waves are defined in charts/region/east|west/values.yaml (syncWave per app). See Architecture — Spoke sync-wave reference.
Verify the chain
# Hub — ApplicationSet must exist (not only child Applications)
oc config use-context hub
oc get applicationset fleet-spoke-push -n openshift-gitops
oc get placementdecisions -n openshift-gitops -l cluster.open-cluster-management.io/placement=hub-spoke-placement
oc get applications -n openshift-gitops | grep spoke-components
# East — child apps from east/ chart
oc config use-context east
oc get applications -n openshift-gitops | grep -E 'east$|east '
After changing charts/all/acm-hub-spoke:
oc annotate application acm-hub-spoke -n openshift-gitops \
argocd.argoproj.io/refresh=hard --overwrite