Skip to content
Open
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
16 changes: 15 additions & 1 deletion pkg/console/operator/sync_v400.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/url"
"os"
"slices"
"sort"
"strings"

// kube
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}

Expand Down
9 changes: 9 additions & 0 deletions pkg/console/subresource/configmap/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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
}

Expand All @@ -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
}

Expand Down