From a876b6d4eee63ca21bb9d4fd075559de18967f62 Mon Sep 17 00:00:00 2001 From: Jakub Hadvig Date: Mon, 9 Feb 2026 16:26:10 +0100 Subject: [PATCH] OCPBUGS-74872: Sort plugin list to make them deterministic --- pkg/console/operator/sync_v400.go | 16 +++++++++++++++- pkg/console/subresource/configmap/configmap.go | 9 +++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/pkg/console/operator/sync_v400.go b/pkg/console/operator/sync_v400.go index 59824c907..e915ce0e1 100644 --- a/pkg/console/operator/sync_v400.go +++ b/pkg/console/operator/sync_v400.go @@ -6,6 +6,7 @@ import ( "net/url" "os" "slices" + "sort" "strings" // kube @@ -17,6 +18,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/client-go/util/retry" "k8s.io/klog/v2" // openshift @@ -405,7 +407,15 @@ func (co *consoleOperator) SyncConfigMap( if err != nil { return nil, "FailedConsoleConfigBuilder", err } - cm, cmChanged, cmErr := resourceapply.ApplyConfigMap(ctx, co.configMapClient, recorder, defaultConfigmap) + var cm *corev1.ConfigMap + var cmChanged bool + var cmErr error + + // Retry on conflicts to handle concurrent ConfigMap updates + cmErr = retry.RetryOnConflict(retry.DefaultBackoff, func() error { + cm, cmChanged, cmErr = resourceapply.ApplyConfigMap(ctx, co.configMapClient, recorder, defaultConfigmap) + return cmErr + }) if cmErr != nil { return nil, "FailedApply", cmErr } @@ -731,6 +741,10 @@ func (co *consoleOperator) GetAvailablePlugins(enabledPluginsNames []string) []* } availablePlugins = append(availablePlugins, plugin) } + // Sort plugins by name to ensure deterministic processing order + sort.Slice(availablePlugins, func(i, j int) bool { + return availablePlugins[i].Name < availablePlugins[j].Name + }) return availablePlugins } diff --git a/pkg/console/subresource/configmap/configmap.go b/pkg/console/subresource/configmap/configmap.go index 8b36689e9..27a8b9125 100644 --- a/pkg/console/subresource/configmap/configmap.go +++ b/pkg/console/subresource/configmap/configmap.go @@ -179,6 +179,8 @@ func pluginsWithI18nNamespace(availablePlugins []*v1.ConsolePlugin) []string { i18nNamespaces = append(i18nNamespaces, fmt.Sprintf("plugin__%s", plugin.Name)) } } + // Sort to ensure deterministic YAML output + sort.Strings(i18nNamespaces) return i18nNamespaces } @@ -192,6 +194,9 @@ func getPluginsEndpointMap(availablePlugins []*v1.ConsolePlugin) map[string]stri klog.Errorf("unknown backend type for %q plugin: %q. Currently only %q backend type is supported.", plugin.Name, plugin.Spec.Backend.Type, v1.Service) } } + // Note: Here the YAML output is deterministic because: + // - availablePlugins are sorted by name in GetAvailablePlugins() + // - sigs.k8s.io/yaml uses json.Marshal which sorts map keys return pluginsEndpointMap } @@ -214,6 +219,10 @@ func getPluginsProxyServices(availablePlugins []*v1.ConsolePlugin) []consoleserv } } } + // Sort by ConsoleAPIPath to ensure deterministic YAML output + sort.Slice(proxyServices, func(i, j int) bool { + return proxyServices[i].ConsoleAPIPath < proxyServices[j].ConsoleAPIPath + }) return proxyServices }