Describe the bug
Re-ordering entries in ManagedClusterAddOn.spec.configs does not update
the order in status.configReferences. The order is determined by the
first reconcile and never changes, making it impossible to alter config
precedence after initial creation.
Config precedence depends on index ordering — the documented contract on
GetAddOnDeploymentConfigValues (in addon-framework) states that the
highest-index entry takes precedence. However, changing which config
appears last in spec.configs has no effect because
status.configReferences retains its original order.
To Reproduce
Prerequisites: a hub cluster with OCM addon-manager running. This example
uses cluster1 as the managed cluster name, consistent with the
OCM contributing guidelines
for kind-based test environments.
- Create an
AddOnTemplate with a variable placeholder:
kubectl apply -f - <<'EOF'
apiVersion: addon.open-cluster-management.io/v1alpha1
kind: AddOnTemplate
metadata:
name: ordering-demo
spec:
addonName: ordering-demo
agentSpec:
workload:
manifests:
- apiVersion: v1
kind: ConfigMap
metadata:
name: ordering-demo
namespace: open-cluster-management-agent-addon
data:
greeting: "{{GREETING}}"
EOF
- Create two
AddOnDeploymentConfig resources with different values:
kubectl apply -f - <<'EOF'
apiVersion: addon.open-cluster-management.io/v1alpha1
kind: AddOnDeploymentConfig
metadata:
name: greeting-hello
namespace: cluster1
spec:
customizedVariables:
- name: GREETING
value: "hello"
---
apiVersion: addon.open-cluster-management.io/v1alpha1
kind: AddOnDeploymentConfig
metadata:
name: greeting-goodbye
namespace: cluster1
spec:
customizedVariables:
- name: GREETING
value: "goodbye"
EOF
- Create a
ClusterManagementAddOn referencing the template (no global
ADC default, so per-cluster configs are the only source):
kubectl apply -f - <<'EOF'
apiVersion: addon.open-cluster-management.io/v1alpha1
kind: ClusterManagementAddOn
metadata:
name: ordering-demo
annotations:
addon.open-cluster-management.io/lifecycle: "addon-manager"
spec:
addOnMeta:
displayName: Config Ordering Demo
installStrategy:
type: Manual
supportedConfigs:
- group: addon.open-cluster-management.io
resource: addontemplates
defaultConfig:
name: ordering-demo
- group: addon.open-cluster-management.io
resource: addondeploymentconfigs
EOF
- Create the
ManagedClusterAddOn with two ADC references — hello
first, goodbye second:
kubectl apply -f - <<'EOF'
apiVersion: addon.open-cluster-management.io/v1alpha1
kind: ManagedClusterAddOn
metadata:
name: ordering-demo
namespace: cluster1
spec:
configs:
- group: addon.open-cluster-management.io
resource: addondeploymentconfigs
name: greeting-hello
namespace: cluster1
- group: addon.open-cluster-management.io
resource: addondeploymentconfigs
name: greeting-goodbye
namespace: cluster1
EOF
- Wait for reconciliation, then inspect
status.configReferences:
kubectl get managedclusteraddons ordering-demo -n cluster1 \
-o jsonpath='{range .status.configReferences[*]}{.resource}: {.desiredConfig.name}{"\n"}{end}'
Note the order — greeting-hello appears before greeting-goodbye.
- Re-order the configs to put
goodbye first:
kubectl apply -f - <<'EOF'
apiVersion: addon.open-cluster-management.io/v1alpha1
kind: ManagedClusterAddOn
metadata:
name: ordering-demo
namespace: cluster1
spec:
configs:
- group: addon.open-cluster-management.io
resource: addondeploymentconfigs
name: greeting-goodbye
namespace: cluster1
- group: addon.open-cluster-management.io
resource: addondeploymentconfigs
name: greeting-hello
namespace: cluster1
EOF
- Wait for reconciliation and inspect again:
kubectl get managedclusteraddons ordering-demo -n cluster1 \
-o jsonpath='{range .status.configReferences[*]}{.resource}: {.desiredConfig.name}{"\n"}{end}'
The order in status.configReferences is unchanged — greeting-hello
still appears before greeting-goodbye, despite the spec now listing
goodbye first.
Cleanup:
kubectl delete clustermanagementaddons ordering-demo
kubectl delete addontemplates ordering-demo
kubectl delete addondeploymentconfigs greeting-hello greeting-goodbye -n cluster1
Expected behavior
When the user re-orders entries in ManagedClusterAddOn.spec.configs,
the order in status.configReferences should be updated to reflect the
new ordering, so that precedence changes take effect.
Environment ie: OCM version, Kubernetes version and provider:
- OCM addon-manager (all versions with multi-GK config support)
- Kubernetes 4.x / OpenShift 4.x
Additional context
The root cause is in mergeAddonConfig in
pkg/addon/controllers/addonconfiguration/addon_configuration_reconciler.go:
mergedConfigs := make(addonConfigMap)
// First go through the configReferences listed in mca status,
// if the existing config (gvk + namespace + name) is also in the desiredConfigMap,
// append it to mergedConfigs,
// this will save the LastAppliedConfig and LastObservedGeneration from mca status.
for _, configRef := range mcaCopy.Status.ConfigReferences {
gr := configRef.ConfigGroupResource
if _, ok := mergedConfigs[gr]; !ok {
mergedConfigs[gr] = []addonv1beta1.ConfigReference{}
}
if _, ok := desiredConfigMap.containsConfig(gr, configRef.DesiredConfig.ConfigReferent); ok {
mergedConfigs[gr] = append(mergedConfigs[gr], configRef)
}
}
// Then go through the desiredConfigMap ...
Because the old status.configReferences are iterated first and their
order is preserved, any re-ordering in the desired config map is ignored
for entries that already exist. New entries are appended at the end, but
existing entries keep their original positions.
The fix is to use the desiredConfigMap as the source of truth for
ordering, while preserving LastAppliedConfig and
LastObservedGeneration via a lookup map keyed by
group+resource+name+namespace:
// Build a lookup from old status to preserve rollout tracking fields
oldStatusByKey := map[configKey]addonv1beta1.ConfigReference{}
for _, configRef := range mcaCopy.Status.ConfigReferences {
key := configKey{...}
oldStatusByKey[key] = configRef
}
// Iterate desiredConfigMap in sorted GR order — source of truth for ordering
configRefs := []addonv1beta1.ConfigReference{}
for _, gr := range desiredConfigMap.orderedKeys() {
for _, configRef := range desiredConfigMap[gr] {
key := configKey{...}
if old, ok := oldStatusByKey[key]; ok {
old.DesiredConfig = configRef.DesiredConfig.DeepCopy()
configRefs = append(configRefs, old)
} else {
configRefs = append(configRefs, configRef)
}
}
}
Related issue: There is a separate bug in addon-framework where
GetAddOnConfigRef returns the first match instead of the last, which
compounds this issue. See open-cluster-management-io/addon-framework#387
Describe the bug
Re-ordering entries in
ManagedClusterAddOn.spec.configsdoes not updatethe order in
status.configReferences. The order is determined by thefirst reconcile and never changes, making it impossible to alter config
precedence after initial creation.
Config precedence depends on index ordering — the documented contract on
GetAddOnDeploymentConfigValues(in addon-framework) states that thehighest-index entry takes precedence. However, changing which config
appears last in
spec.configshas no effect becausestatus.configReferencesretains its original order.To Reproduce
Prerequisites: a hub cluster with OCM addon-manager running. This example
uses
cluster1as the managed cluster name, consistent with theOCM contributing guidelines
for kind-based test environments.
AddOnTemplatewith a variable placeholder:AddOnDeploymentConfigresources with different values:ClusterManagementAddOnreferencing the template (no globalADC default, so per-cluster configs are the only source):
ManagedClusterAddOnwith two ADC references —hellofirst,
goodbyesecond:status.configReferences:kubectl get managedclusteraddons ordering-demo -n cluster1 \ -o jsonpath='{range .status.configReferences[*]}{.resource}: {.desiredConfig.name}{"\n"}{end}'Note the order —
greeting-helloappears beforegreeting-goodbye.goodbyefirst:kubectl get managedclusteraddons ordering-demo -n cluster1 \ -o jsonpath='{range .status.configReferences[*]}{.resource}: {.desiredConfig.name}{"\n"}{end}'The order in
status.configReferencesis unchanged —greeting-hellostill appears before
greeting-goodbye, despite the spec now listinggoodbyefirst.Cleanup:
Expected behavior
When the user re-orders entries in
ManagedClusterAddOn.spec.configs,the order in
status.configReferencesshould be updated to reflect thenew ordering, so that precedence changes take effect.
Environment ie: OCM version, Kubernetes version and provider:
Additional context
The root cause is in
mergeAddonConfiginpkg/addon/controllers/addonconfiguration/addon_configuration_reconciler.go:Because the old
status.configReferencesare iterated first and theirorder is preserved, any re-ordering in the desired config map is ignored
for entries that already exist. New entries are appended at the end, but
existing entries keep their original positions.
The fix is to use the
desiredConfigMapas the source of truth forordering, while preserving
LastAppliedConfigandLastObservedGenerationvia a lookup map keyed bygroup+resource+name+namespace:
Related issue: There is a separate bug in addon-framework where
GetAddOnConfigRefreturns the first match instead of the last, whichcompounds this issue. See open-cluster-management-io/addon-framework#387