Skip to content

Commit 5548c89

Browse files
authored
Implement dynamic default and fallback channels for operators in OperandRegistry (#2455)
* add process dynamic channel function Signed-off-by: YuChen <yuchen.shen@mail.utoronto.ca> * update cpp map with supported version Signed-off-by: YuChen <yuchen.shen@mail.utoronto.ca> * add ConfigMaps predicate functions in cs controller setup Signed-off-by: YuChen <yuchen.shen@mail.utoronto.ca> * polish the code Signed-off-by: YuChen <yuchen.shen@mail.utoronto.ca> * fix lint error Signed-off-by: YuChen <yuchen.shen@mail.utoronto.ca> * fix multi reconciliation problem Signed-off-by: YuChen <yuchen.shen@mail.utoronto.ca> --------- Signed-off-by: YuChen <yuchen.shen@mail.utoronto.ca>
1 parent 6054455 commit 5548c89

File tree

5 files changed

+167
-31
lines changed

5 files changed

+167
-31
lines changed

internal/controller/bootstrap/init.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ func (b *Bootstrap) InitResources(instance *apiv3.CommonService, forceUpdateODLM
240240
}
241241

242242
klog.Info("Installing/Updating OperandRegistry")
243-
if err := b.InstallOrUpdateOpreg(forceUpdateODLMCRs, installPlanApproval); err != nil {
243+
if err := b.InstallOrUpdateOpreg(installPlanApproval); err != nil {
244244
return err
245245
}
246246

@@ -279,7 +279,7 @@ func (b *Bootstrap) InitResources(instance *apiv3.CommonService, forceUpdateODLM
279279
}
280280

281281
klog.Info("Installing/Updating OperandRegistry")
282-
if err := b.InstallOrUpdateOpreg(forceUpdateODLMCRs, installPlanApproval); err != nil {
282+
if err := b.InstallOrUpdateOpreg(installPlanApproval); err != nil {
283283
return err
284284
}
285285

@@ -729,14 +729,29 @@ func (b *Bootstrap) ResourceExists(dc discovery.DiscoveryInterface, apiGroupVers
729729
}
730730

731731
// InstallOrUpdateOpreg will install or update OperandRegistry when Opreg CRD is existent
732-
func (b *Bootstrap) InstallOrUpdateOpreg(forceUpdateODLMCRs bool, installPlanApproval olmv1alpha1.Approval) error {
732+
func (b *Bootstrap) InstallOrUpdateOpreg(installPlanApproval olmv1alpha1.Approval) error {
733733

734734
if installPlanApproval != "" || b.CSData.ApprovalMode == string(olmv1alpha1.ApprovalManual) {
735735
if err := b.updateApprovalMode(); err != nil {
736736
return err
737737
}
738738
}
739739

740+
// Read channel list from cpp configmap
741+
configMap := &corev1.ConfigMap{}
742+
err := b.Client.Get(context.TODO(), types.NamespacedName{
743+
Name: constant.IBMCPPCONFIG,
744+
Namespace: b.CSData.ServicesNs,
745+
}, configMap)
746+
747+
if err != nil {
748+
if !errors.IsNotFound(err) {
749+
return err
750+
}
751+
klog.Infof("ConfigMap %s not found in namespace %s, using default values", constant.IBMCPPCONFIG, b.CSData.ServicesNs)
752+
configMap.Data = make(map[string]string)
753+
}
754+
740755
var baseReg string
741756
registries := []string{
742757
constant.CSV4OpReg,
@@ -753,13 +768,13 @@ func (b *Bootstrap) InstallOrUpdateOpreg(forceUpdateODLMCRs bool, installPlanApp
753768
baseReg = constant.CSV3OpReg
754769
}
755770

756-
concatenatedReg, err := constant.ConcatenateRegistries(baseReg, registries, b.CSData)
771+
concatenatedReg, err := constant.ConcatenateRegistries(baseReg, registries, b.CSData, configMap.Data)
757772
if err != nil {
758773
klog.Errorf("failed to concatenate OperandRegistry: %v", err)
759774
return err
760775
}
761776

762-
if err := b.renderTemplate(concatenatedReg, b.CSData, forceUpdateODLMCRs); err != nil {
777+
if err := b.renderTemplate(concatenatedReg, b.CSData, true); err != nil {
763778
return err
764779
}
765780
return nil

internal/controller/commonservice_controller.go

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -469,15 +469,24 @@ func (r *CommonServiceReconciler) ReconcileNonConfigurableCR(ctx context.Context
469469
return ctrl.Result{}, nil
470470
}
471471

472-
func (r *CommonServiceReconciler) mappingToCsRequest() handler.MapFunc {
472+
func (r *CommonServiceReconciler) mappingToCsRequestForConfigMaps() handler.MapFunc {
473473
return func(object client.Object) []reconcile.Request {
474-
CsInstance := []reconcile.Request{}
475-
cmName := object.GetName()
476-
cmNs := object.GetNamespace()
477-
if cmName == constant.CsMapConfigMap && cmNs == "kube-public" {
478-
CsInstance = append(CsInstance, reconcile.Request{NamespacedName: types.NamespacedName{Name: constant.MasterCR, Namespace: r.Bootstrap.CSData.OperatorNs}})
474+
configMap, ok := object.(*corev1.ConfigMap)
475+
if !ok {
476+
return nil
477+
}
478+
479+
// Check two configmaps: common-service-maps and ibm-cpp-config
480+
if (configMap.Name == constant.CsMapConfigMap && configMap.Namespace == constant.CsMapConfigMapNs) ||
481+
(configMap.Name == constant.IBMCPPCONFIG && configMap.Namespace == r.Bootstrap.CSData.ServicesNs) {
482+
return []reconcile.Request{
483+
{NamespacedName: types.NamespacedName{
484+
Name: constant.MasterCR,
485+
Namespace: r.Bootstrap.CSData.OperatorNs,
486+
}},
487+
}
479488
}
480-
return CsInstance
489+
return nil
481490
}
482491
}
483492

@@ -546,17 +555,11 @@ func (r *CommonServiceReconciler) SetupWithManager(mgr ctrl.Manager) error {
546555
predicate.LabelChangedPredicate{}))).
547556
Watches(
548557
&source.Kind{Type: &corev1.ConfigMap{}},
549-
handler.EnqueueRequestsFromMapFunc(r.mappingToCsRequest()),
558+
handler.EnqueueRequestsFromMapFunc(r.mappingToCsRequestForConfigMaps()),
550559
builder.WithPredicates(predicate.Funcs{
551-
CreateFunc: func(e event.CreateEvent) bool {
552-
return true
553-
},
554-
DeleteFunc: func(e event.DeleteEvent) bool {
555-
return !e.DeleteStateUnknown
556-
},
557-
UpdateFunc: func(e event.UpdateEvent) bool {
558-
return true
559-
},
560+
CreateFunc: func(e event.CreateEvent) bool { return true },
561+
UpdateFunc: func(e event.UpdateEvent) bool { return true },
562+
DeleteFunc: func(e event.DeleteEvent) bool { return !e.DeleteStateUnknown },
560563
}))
561564
if isOpregAPI, err := r.Bootstrap.CheckCRD(constant.OpregAPIGroupVersion, constant.OpregKind); err != nil {
562565
klog.Errorf("Failed to check if OperandRegistry CRD exists: %v", err)

internal/controller/configurationcollector/collector.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import (
3434

3535
func Buildconfig(config map[string]string, bs *bootstrap.Bootstrap) map[string]string {
3636
builder := configbuilder{data: config, bs: bs}
37-
updatedConfig := builder.setDefaultStorageClass()
37+
updatedConfig := builder.setDefaultStorageClass().setKeycloakOperatorChannels()
3838
return updatedConfig.data
3939
}
4040

@@ -88,6 +88,29 @@ func (b *configbuilder) setDefaultStorageClass() *configbuilder {
8888
return b
8989
}
9090

91+
// setKeycloakOperatorChannels sets the keycloak operator channels in the config
92+
func (b *configbuilder) setKeycloakOperatorChannels() *configbuilder {
93+
if b.data == nil {
94+
b.data = make(map[string]string)
95+
}
96+
97+
keycloakChannels, exists := constant.DefaultChannels["keycloak-operator"]
98+
if !exists || len(keycloakChannels) == 0 {
99+
keycloakChannels = []string{"stable-v24", "stable-v22"}
100+
}
101+
102+
var channelStr strings.Builder
103+
for _, channel := range keycloakChannels {
104+
channelStr.WriteString("- ")
105+
channelStr.WriteString(channel)
106+
channelStr.WriteString("\n")
107+
}
108+
109+
b.data["keycloak-operator"] = channelStr.String()
110+
111+
return b
112+
}
113+
91114
// CreateUpdateConfig deploys config builder for global cpp configmap
92115
func CreateUpdateConfig(bs *bootstrap.Bootstrap) error {
93116
config := &corev1.ConfigMap{}

internal/controller/constant/constant.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ const (
4242
ClusterOperatorNamespace = "openshift-operators"
4343
// CS map configMap
4444
CsMapConfigMap = "common-service-maps"
45+
// CS map configMap namespace
46+
CsMapConfigMapNs = "kube-public"
4547
// CS Saas configMap
4648
SaasConfigMap = "saas-config"
4749
// Namespace Scope Operator resource name
@@ -122,6 +124,11 @@ const (
122124
OpreqLabel string = "operator.ibm.com/opreq-control"
123125
)
124126

127+
// DefaultChannels defines the default channels available for each operator
128+
var DefaultChannels = map[string][]string{
129+
"keycloak-operator": {"stable-v26", "stable-v24", "stable-v22"},
130+
}
131+
125132
// CsOg is OperatorGroup constent for the common service operator
126133
const CsOperatorGroup = `
127134
apiVersion: operators.coreos.com/v1

internal/controller/constant/odlm.go

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ package constant
1919
import (
2020
"bytes"
2121
"fmt"
22+
"regexp"
23+
"sort"
24+
"strconv"
25+
"strings"
2226
"text/template"
2327

2428
utilyaml "github.com/ghodss/yaml"
@@ -240,9 +244,8 @@ metadata:
240244
status-monitored-services: {{ .StatusMonitoredServices }}
241245
spec:
242246
operators:
243-
- channel: stable-v24
244-
fallbackChannels:
245-
- stable-v22
247+
- channel: ""
248+
fallbackChannels: []
246249
installPlanApproval: {{ .ApprovalMode }}
247250
name: keycloak-operator
248251
namespace: "{{ .ServicesNs }}"
@@ -2269,12 +2272,12 @@ spec:
22692272
`
22702273

22712274
// ConcatenateRegistries concatenate the two YAML strings and return the new YAML string
2272-
func ConcatenateRegistries(baseRegistryTemplate string, insertedRegistryTemplateList []string, data interface{}) (string, error) {
2275+
func ConcatenateRegistries(baseRegistryTemplate string, insertedRegistryTemplateList []string, data interface{}, cppdata map[string]string) (string, error) {
22732276
baseRegistry := odlm.OperandRegistry{}
22742277
var template []byte
22752278
var err error
22762279

2277-
// unmarshal first OprandRegistry
2280+
// Unmarshal first OprandRegistry
22782281
if template, err = applyTemplate(baseRegistryTemplate, data); err != nil {
22792282
return "", err
22802283
}
@@ -2295,9 +2298,13 @@ func ConcatenateRegistries(baseRegistryTemplate string, insertedRegistryTemplate
22952298

22962299
newOperators = append(newOperators, insertedRegistry.Spec.Operators...)
22972300
}
2298-
// add new operators to baseRegistry
2301+
// Add new operators to baseRegistry
22992302
baseRegistry.Spec.Operators = append(baseRegistry.Spec.Operators, newOperators...)
23002303

2304+
// Update default and fallback channels with ConfigMap data
2305+
operatorNames := []string{"keycloak-operator"} // List of operators to process
2306+
processdDynamicChannels(&baseRegistry, cppdata, operatorNames)
2307+
23012308
opregBytes, err := utilyaml.Marshal(baseRegistry)
23022309
if err != nil {
23032310
return "", err
@@ -2312,7 +2319,7 @@ func ConcatenateConfigs(baseConfigTemplate string, insertedConfigTemplateList []
23122319
var template []byte
23132320
var err error
23142321

2315-
// unmarshal first OprandCongif
2322+
// unmarshal first OprandConfig
23162323
if template, err = applyTemplate(baseConfigTemplate, data); err != nil {
23172324
return "", err
23182325
}
@@ -2332,7 +2339,7 @@ func ConcatenateConfigs(baseConfigTemplate string, insertedConfigTemplateList []
23322339

23332340
newServices = append(newServices, insertedConfig.Spec.Services...)
23342341
}
2335-
// add new services to baseConfig
2342+
// Add new services to baseConfig
23362343
baseConfig.Spec.Services = append(baseConfig.Spec.Services, newServices...)
23372344

23382345
opconBytes, err := utilyaml.Marshal(baseConfig)
@@ -2352,3 +2359,84 @@ func applyTemplate(objectTemplate string, data interface{}) ([]byte, error) {
23522359

23532360
return buffer.Bytes(), nil
23542361
}
2362+
2363+
// processFallbackChannels updates operator entries with dynamic fallback channels based on ibm-cpp-config data
2364+
func processdDynamicChannels(registry *odlm.OperandRegistry, configMapData map[string]string, operatorNames []string) {
2365+
for i, operator := range registry.Spec.Operators {
2366+
// Check if this operator is in our list of operators to process
2367+
for _, opName := range operatorNames {
2368+
if operator.Name == opName {
2369+
2370+
channelList, exists := DefaultChannels[operator.Name]
2371+
if !exists || len(channelList) == 0 {
2372+
continue
2373+
}
2374+
highestVersion := channelList[0]
2375+
2376+
var currentChannel string
2377+
if operator.Name == "keycloak-operator" {
2378+
// For keycloak, check the keycloak_preferred_channel in ConfigMap or use a default
2379+
keycloakVersion, exists := configMapData["keycloak_preferred_channel"]
2380+
if exists {
2381+
currentChannel = keycloakVersion
2382+
} else {
2383+
currentChannel = "stable-v24" // Default for keycloak
2384+
}
2385+
} else {
2386+
continue
2387+
}
2388+
2389+
// If current channel is less than highest version in list
2390+
if compareVersions(currentChannel, highestVersion) < 0 {
2391+
registry.Spec.Operators[i].Channel = currentChannel
2392+
// Get all versions less than current channel
2393+
var fallbacks []string
2394+
for _, channel := range channelList {
2395+
if compareVersions(channel, currentChannel) < 0 {
2396+
fallbacks = append(fallbacks, channel)
2397+
}
2398+
}
2399+
2400+
// Sort fallbacks in descending order
2401+
sort.Slice(fallbacks, func(i, j int) bool {
2402+
return compareVersions(fallbacks[i], fallbacks[j]) > 0
2403+
})
2404+
2405+
// Update the operator's fallback channels
2406+
registry.Spec.Operators[i].FallbackChannels = fallbacks
2407+
} else {
2408+
registry.Spec.Operators[i].Channel = highestVersion
2409+
if len(channelList) > 1 {
2410+
registry.Spec.Operators[i].FallbackChannels = channelList[1:]
2411+
} else {
2412+
registry.Spec.Operators[i].FallbackChannels = []string{}
2413+
}
2414+
}
2415+
break
2416+
}
2417+
}
2418+
}
2419+
}
2420+
2421+
// compareVersions compares version strings (e.g., "stable-v22" vs "stable-v24")
2422+
// Returns -1 if v1 < v2; 0 if v1 == v2; 1 if v1 > v2
2423+
func compareVersions(v1, v2 string) int {
2424+
// Extract version numbers
2425+
re := regexp.MustCompile(`v(\d+)`)
2426+
v1Matches := re.FindStringSubmatch(v1)
2427+
v2Matches := re.FindStringSubmatch(v2)
2428+
2429+
if len(v1Matches) < 2 || len(v2Matches) < 2 {
2430+
return strings.Compare(v1, v2)
2431+
}
2432+
2433+
v1Num, _ := strconv.Atoi(v1Matches[1])
2434+
v2Num, _ := strconv.Atoi(v2Matches[1])
2435+
2436+
if v1Num < v2Num {
2437+
return -1
2438+
} else if v1Num > v2Num {
2439+
return 1
2440+
}
2441+
return 0
2442+
}

0 commit comments

Comments
 (0)