Skip to content
Merged
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
25 changes: 20 additions & 5 deletions internal/controller/bootstrap/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ func (b *Bootstrap) InitResources(instance *apiv3.CommonService, forceUpdateODLM
}

klog.Info("Installing/Updating OperandRegistry")
if err := b.InstallOrUpdateOpreg(forceUpdateODLMCRs, installPlanApproval); err != nil {
if err := b.InstallOrUpdateOpreg(installPlanApproval); err != nil {
return err
}

Expand Down Expand Up @@ -319,7 +319,7 @@ func (b *Bootstrap) InitResources(instance *apiv3.CommonService, forceUpdateODLM
}

klog.Info("Installing/Updating OperandRegistry")
if err := b.InstallOrUpdateOpreg(forceUpdateODLMCRs, installPlanApproval); err != nil {
if err := b.InstallOrUpdateOpreg(installPlanApproval); err != nil {
return err
}

Expand Down Expand Up @@ -769,14 +769,29 @@ func (b *Bootstrap) ResourceExists(dc discovery.DiscoveryInterface, apiGroupVers
}

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

if installPlanApproval != "" || b.CSData.ApprovalMode == string(olmv1alpha1.ApprovalManual) {
if err := b.updateApprovalMode(); err != nil {
return err
}
}

// Read channel list from cpp configmap
configMap := &corev1.ConfigMap{}
err := b.Client.Get(context.TODO(), types.NamespacedName{
Name: constant.IBMCPPCONFIG,
Namespace: b.CSData.ServicesNs,
}, configMap)

if err != nil {
if !errors.IsNotFound(err) {
return err
}
klog.Infof("ConfigMap %s not found in namespace %s, using default values", constant.IBMCPPCONFIG, b.CSData.ServicesNs)
configMap.Data = make(map[string]string)
}

var baseReg string
registries := []string{
constant.CSV4OpReg,
Expand All @@ -793,13 +808,13 @@ func (b *Bootstrap) InstallOrUpdateOpreg(forceUpdateODLMCRs bool, installPlanApp
baseReg = constant.CSV3OpReg
}

concatenatedReg, err := constant.ConcatenateRegistries(baseReg, registries, b.CSData)
concatenatedReg, err := constant.ConcatenateRegistries(baseReg, registries, b.CSData, configMap.Data)
if err != nil {
klog.Errorf("failed to concatenate OperandRegistry: %v", err)
return err
}

if err := b.renderTemplate(concatenatedReg, b.CSData, forceUpdateODLMCRs); err != nil {
if err := b.renderTemplate(concatenatedReg, b.CSData, true); err != nil {
return err
}
return nil
Expand Down
37 changes: 20 additions & 17 deletions internal/controller/commonservice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,15 +480,24 @@ func (r *CommonServiceReconciler) ReconcileNonConfigurableCR(ctx context.Context
return ctrl.Result{}, nil
}

func (r *CommonServiceReconciler) mappingToCsRequest() handler.MapFunc {
func (r *CommonServiceReconciler) mappingToCsRequestForConfigMaps() handler.MapFunc {
return func(object client.Object) []reconcile.Request {
CsInstance := []reconcile.Request{}
cmName := object.GetName()
cmNs := object.GetNamespace()
if cmName == constant.CsMapConfigMap && cmNs == "kube-public" {
CsInstance = append(CsInstance, reconcile.Request{NamespacedName: types.NamespacedName{Name: constant.MasterCR, Namespace: r.Bootstrap.CSData.OperatorNs}})
configMap, ok := object.(*corev1.ConfigMap)
if !ok {
return nil
}

// Check two configmaps: common-service-maps and ibm-cpp-config
if (configMap.Name == constant.CsMapConfigMap && configMap.Namespace == constant.CsMapConfigMapNs) ||
(configMap.Name == constant.IBMCPPCONFIG && configMap.Namespace == r.Bootstrap.CSData.ServicesNs) {
return []reconcile.Request{
{NamespacedName: types.NamespacedName{
Name: constant.MasterCR,
Namespace: r.Bootstrap.CSData.OperatorNs,
}},
}
}
return CsInstance
return nil
}
}

Expand Down Expand Up @@ -557,17 +566,11 @@ func (r *CommonServiceReconciler) SetupWithManager(mgr ctrl.Manager) error {
predicate.LabelChangedPredicate{}))).
Watches(
&source.Kind{Type: &corev1.ConfigMap{}},
handler.EnqueueRequestsFromMapFunc(r.mappingToCsRequest()),
handler.EnqueueRequestsFromMapFunc(r.mappingToCsRequestForConfigMaps()),
builder.WithPredicates(predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
return true
},
DeleteFunc: func(e event.DeleteEvent) bool {
return !e.DeleteStateUnknown
},
UpdateFunc: func(e event.UpdateEvent) bool {
return true
},
CreateFunc: func(e event.CreateEvent) bool { return true },
UpdateFunc: func(e event.UpdateEvent) bool { return true },
DeleteFunc: func(e event.DeleteEvent) bool { return !e.DeleteStateUnknown },
}))
if isOpregAPI, err := r.Bootstrap.CheckCRD(constant.OpregAPIGroupVersion, constant.OpregKind); err != nil {
klog.Errorf("Failed to check if OperandRegistry CRD exists: %v", err)
Expand Down
25 changes: 24 additions & 1 deletion internal/controller/configurationcollector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (

func Buildconfig(config map[string]string, bs *bootstrap.Bootstrap) map[string]string {
builder := configbuilder{data: config, bs: bs}
updatedConfig := builder.setDefaultStorageClass()
updatedConfig := builder.setDefaultStorageClass().setKeycloakOperatorChannels()
return updatedConfig.data
}

Expand Down Expand Up @@ -88,6 +88,29 @@ func (b *configbuilder) setDefaultStorageClass() *configbuilder {
return b
}

// setKeycloakOperatorChannels sets the keycloak operator channels in the config
func (b *configbuilder) setKeycloakOperatorChannels() *configbuilder {
if b.data == nil {
b.data = make(map[string]string)
}

keycloakChannels, exists := constant.DefaultChannels["keycloak-operator"]
if !exists || len(keycloakChannels) == 0 {
keycloakChannels = []string{"stable-v24", "stable-v22"}
}

var channelStr strings.Builder
for _, channel := range keycloakChannels {
channelStr.WriteString("- ")
channelStr.WriteString(channel)
channelStr.WriteString("\n")
}

b.data["keycloak-operator"] = channelStr.String()

return b
}

// CreateUpdateConfig deploys config builder for global cpp configmap
func CreateUpdateConfig(bs *bootstrap.Bootstrap) error {
config := &corev1.ConfigMap{}
Expand Down
7 changes: 7 additions & 0 deletions internal/controller/constant/constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ const (
ClusterOperatorNamespace = "openshift-operators"
// CS map configMap
CsMapConfigMap = "common-service-maps"
// CS map configMap namespace
CsMapConfigMapNs = "kube-public"
// CS Saas configMap
SaasConfigMap = "saas-config"
// Namespace Scope Operator resource name
Expand Down Expand Up @@ -122,6 +124,11 @@ const (
OpreqLabel string = "operator.ibm.com/opreq-control"
)

// DefaultChannels defines the default channels available for each operator
var DefaultChannels = map[string][]string{
"keycloak-operator": {"stable-v26", "stable-v24", "stable-v22"},
}

// CsOg is OperatorGroup constent for the common service operator
const CsOperatorGroup = `
apiVersion: operators.coreos.com/v1
Expand Down
104 changes: 96 additions & 8 deletions internal/controller/constant/odlm.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ package constant
import (
"bytes"
"fmt"
"regexp"
"sort"
"strconv"
"strings"
"text/template"

utilyaml "github.com/ghodss/yaml"
Expand Down Expand Up @@ -312,9 +316,8 @@ metadata:
status-monitored-services: {{ .StatusMonitoredServices }}
spec:
operators:
- channel: stable-v24
fallbackChannels:
- stable-v22
- channel: ""
fallbackChannels: []
installPlanApproval: {{ .ApprovalMode }}
name: keycloak-operator
namespace: "{{ .ServicesNs }}"
Expand Down Expand Up @@ -2490,12 +2493,12 @@ spec:
`

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

// unmarshal first OprandRegistry
// Unmarshal first OprandRegistry
if template, err = applyTemplate(baseRegistryTemplate, data); err != nil {
return "", err
}
Expand All @@ -2516,9 +2519,13 @@ func ConcatenateRegistries(baseRegistryTemplate string, insertedRegistryTemplate

newOperators = append(newOperators, insertedRegistry.Spec.Operators...)
}
// add new operators to baseRegistry
// Add new operators to baseRegistry
baseRegistry.Spec.Operators = append(baseRegistry.Spec.Operators, newOperators...)

// Update default and fallback channels with ConfigMap data
operatorNames := []string{"keycloak-operator"} // List of operators to process
processdDynamicChannels(&baseRegistry, cppdata, operatorNames)

opregBytes, err := utilyaml.Marshal(baseRegistry)
if err != nil {
return "", err
Expand All @@ -2533,7 +2540,7 @@ func ConcatenateConfigs(baseConfigTemplate string, insertedConfigTemplateList []
var template []byte
var err error

// unmarshal first OprandCongif
// unmarshal first OprandConfig
if template, err = applyTemplate(baseConfigTemplate, data); err != nil {
return "", err
}
Expand All @@ -2553,7 +2560,7 @@ func ConcatenateConfigs(baseConfigTemplate string, insertedConfigTemplateList []

newServices = append(newServices, insertedConfig.Spec.Services...)
}
// add new services to baseConfig
// Add new services to baseConfig
baseConfig.Spec.Services = append(baseConfig.Spec.Services, newServices...)

opconBytes, err := utilyaml.Marshal(baseConfig)
Expand All @@ -2573,3 +2580,84 @@ func applyTemplate(objectTemplate string, data interface{}) ([]byte, error) {

return buffer.Bytes(), nil
}

// processFallbackChannels updates operator entries with dynamic fallback channels based on ibm-cpp-config data
func processdDynamicChannels(registry *odlm.OperandRegistry, configMapData map[string]string, operatorNames []string) {
for i, operator := range registry.Spec.Operators {
// Check if this operator is in our list of operators to process
for _, opName := range operatorNames {
if operator.Name == opName {

channelList, exists := DefaultChannels[operator.Name]
if !exists || len(channelList) == 0 {
continue
}
highestVersion := channelList[0]

var currentChannel string
if operator.Name == "keycloak-operator" {
// For keycloak, check the keycloak_preferred_channel in ConfigMap or use a default
keycloakVersion, exists := configMapData["keycloak_preferred_channel"]
if exists {
currentChannel = keycloakVersion
} else {
currentChannel = "stable-v24" // Default for keycloak
}
} else {
continue
}

// If current channel is less than highest version in list
if compareVersions(currentChannel, highestVersion) < 0 {
registry.Spec.Operators[i].Channel = currentChannel
// Get all versions less than current channel
var fallbacks []string
for _, channel := range channelList {
if compareVersions(channel, currentChannel) < 0 {
fallbacks = append(fallbacks, channel)
}
}

// Sort fallbacks in descending order
sort.Slice(fallbacks, func(i, j int) bool {
return compareVersions(fallbacks[i], fallbacks[j]) > 0
})

// Update the operator's fallback channels
registry.Spec.Operators[i].FallbackChannels = fallbacks
} else {
registry.Spec.Operators[i].Channel = highestVersion
if len(channelList) > 1 {
registry.Spec.Operators[i].FallbackChannels = channelList[1:]
} else {
registry.Spec.Operators[i].FallbackChannels = []string{}
}
}
break
}
}
}
}

// compareVersions compares version strings (e.g., "stable-v22" vs "stable-v24")
// Returns -1 if v1 < v2; 0 if v1 == v2; 1 if v1 > v2
func compareVersions(v1, v2 string) int {
// Extract version numbers
re := regexp.MustCompile(`v(\d+)`)
v1Matches := re.FindStringSubmatch(v1)
v2Matches := re.FindStringSubmatch(v2)

if len(v1Matches) < 2 || len(v2Matches) < 2 {
return strings.Compare(v1, v2)
}

v1Num, _ := strconv.Atoi(v1Matches[1])
v2Num, _ := strconv.Atoi(v2Matches[1])

if v1Num < v2Num {
return -1
} else if v1Num > v2Num {
return 1
}
return 0
}
2 changes: 1 addition & 1 deletion internal/controller/no_olm_commonservice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func (r *CommonServiceReconciler) ReconcileNoOLMMasterCR(ctx context.Context, in
// Install/update Opreg and Opcon resources before installing ODLM if CRDs exist
if existOpreg && existOpcon {
klog.Info("Installing/Updating OperandRegistry")
if err := r.Bootstrap.InstallOrUpdateOpreg(forceUpdateODLMCRs, ""); err != nil {
if err := r.Bootstrap.InstallOrUpdateOpreg(""); err != nil {
klog.Errorf("Fail to Installing/Updating OperandConfig: %v", err)
return ctrl.Result{}, err
}
Expand Down
Loading