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
6 changes: 6 additions & 0 deletions controllers/constant/constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ const (
//OpreqLabel is the label used to label the subscription/CR managed by ODLM
OpreqLabel string = "operator.ibm.com/opreq-control"

//ODLMReferenceLabel is the label used to label the resources used for ODLM operand value reference
ODLMReferenceLabel string = "operator.ibm.com/referenced-by-odlm-resource"

//ODLMWatchedLabel is the label used to label the resources watched by ODLM for value reference
ODLMWatchedLabel string = "operator.ibm.com/watched-by-odlm"

//OpbiNsLabel is the label used to add OperandBindInfo namespace to the secrets/configmaps watched by ODLM
OpbiNsLabel string = "operator.ibm.com/watched-by-opbi-with-namespace"

Expand Down
43 changes: 10 additions & 33 deletions controllers/operandbindinfo/operandbindinfo_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -250,6 +249,7 @@ func (r *Reconciler) copySecret(ctx context.Context, sourceName, targetName, sou
}
secretLabel[bindInfoInstance.Namespace+"."+bindInfoInstance.Name+"/bindinfo"] = "true"
secretLabel[constant.OpbiTypeLabel] = "copy"
secretLabel[constant.ODLMWatchedLabel] = "true"
secretCopy := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: targetName,
Expand All @@ -274,7 +274,7 @@ func (r *Reconciler) copySecret(ctx context.Context, sourceName, targetName, sou
if err := r.Client.Get(ctx, types.NamespacedName{Namespace: targetNs, Name: targetName}, existingSecret); err != nil {
return false, errors.Wrapf(err, "failed to get secret %s/%s", targetNs, targetName)
}
if needUpdate := compareSecret(secretCopy, existingSecret); needUpdate {
if needUpdate := util.CompareSecret(secretCopy, existingSecret); needUpdate {
podRefreshment = true
if err := r.Update(ctx, secretCopy); err != nil {
return false, errors.Wrapf(err, "failed to update secret %s/%s", targetNs, targetName)
Expand All @@ -290,9 +290,10 @@ func (r *Reconciler) copySecret(ctx context.Context, sourceName, targetName, sou
}
}

ensureLabelsForSecret(secret, map[string]string{
util.EnsureLabelsForSecret(secret, map[string]string{
bindInfoInstance.Namespace + "." + bindInfoInstance.Name + "/bindinfo": "true",
constant.OpbiTypeLabel: "original",
constant.OpbiTypeLabel: "original",
constant.ODLMWatchedLabel: "true",
})

// Update the operand Secret
Expand Down Expand Up @@ -342,6 +343,7 @@ func (r *Reconciler) copyConfigmap(ctx context.Context, sourceName, targetName,
}
cmLabel[bindInfoInstance.Namespace+"."+bindInfoInstance.Name+"/bindinfo"] = "true"
cmLabel[constant.OpbiTypeLabel] = "copy"
cmLabel[constant.ODLMWatchedLabel] = "true"
cmCopy := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: targetName,
Expand All @@ -365,7 +367,7 @@ func (r *Reconciler) copyConfigmap(ctx context.Context, sourceName, targetName,
if err := r.Client.Get(ctx, types.NamespacedName{Namespace: targetNs, Name: targetName}, existingCm); err != nil {
return false, errors.Wrapf(err, "failed to get ConfigMap %s/%s", targetNs, targetName)
}
if needUpdate := compareConfigMap(cmCopy, existingCm); needUpdate {
if needUpdate := util.CompareConfigMap(cmCopy, existingCm); needUpdate {
podRefreshment = true
if err := r.Update(ctx, cmCopy); err != nil {
return false, errors.Wrapf(err, "failed to update ConfigMap %s/%s", targetNs, sourceName)
Expand All @@ -383,9 +385,10 @@ func (r *Reconciler) copyConfigmap(ctx context.Context, sourceName, targetName,
}

// Set the OperandBindInfo label for the ConfigMap
ensureLabelsForConfigMap(cm, map[string]string{
util.EnsureLabelsForConfigMap(cm, map[string]string{
bindInfoInstance.Namespace + "." + bindInfoInstance.Name + "/bindinfo": "true",
constant.OpbiTypeLabel: "original",
constant.OpbiTypeLabel: "original",
constant.ODLMWatchedLabel: "true",
})

// Update the operand Configmap
Expand Down Expand Up @@ -769,29 +772,3 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
builder.WithPredicates(opregPredicates),
).Complete(r)
}

func ensureLabelsForSecret(secret *corev1.Secret, labels map[string]string) {
if secret.Labels == nil {
secret.Labels = make(map[string]string)
}
for k, v := range labels {
secret.Labels[k] = v
}
}

func ensureLabelsForConfigMap(cm *corev1.ConfigMap, labels map[string]string) {
if cm.Labels == nil {
cm.Labels = make(map[string]string)
}
for k, v := range labels {
cm.Labels[k] = v
}
}

func compareSecret(secret *corev1.Secret, existingSecret *corev1.Secret) (needUpdate bool) {
return !equality.Semantic.DeepEqual(secret.GetLabels(), existingSecret.GetLabels()) || !equality.Semantic.DeepEqual(secret.Type, existingSecret.Type) || !equality.Semantic.DeepEqual(secret.Data, existingSecret.Data) || !equality.Semantic.DeepEqual(secret.StringData, existingSecret.StringData)
}

func compareConfigMap(configMap *corev1.ConfigMap, existingConfigMap *corev1.ConfigMap) (needUpdate bool) {
return !equality.Semantic.DeepEqual(configMap.GetLabels(), existingConfigMap.GetLabels()) || !equality.Semantic.DeepEqual(configMap.Data, existingConfigMap.Data) || !equality.Semantic.DeepEqual(configMap.BinaryData, existingConfigMap.BinaryData)
}
63 changes: 62 additions & 1 deletion controllers/operandrequest/operandrequest_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,69 @@ func (r *Reconciler) getConfigToRequestMapper() handler.MapFunc {
}
}

func (r *Reconciler) getReferenceToRequestMapper() handler.MapFunc {
ctx := context.Background()
return func(object client.Object) []ctrl.Request {
labels := object.GetLabels()
if labels == nil {
return []ctrl.Request{}
}
odlmReference, ok := labels[constant.ODLMReferenceLabel]
if !ok {
return []ctrl.Request{}
}
odlmReferenceSlices := strings.Split(odlmReference, ".")
if len(odlmReferenceSlices) != 3 {
return []ctrl.Request{}
}

var requestList []operatorv1alpha1.OperandRequest
if odlmReferenceSlices[0] == "OperandConfig" {
requestList, _ = r.ListOperandRequestsByConfig(ctx, types.NamespacedName{Namespace: odlmReferenceSlices[1], Name: odlmReferenceSlices[2]})
} else if odlmReferenceSlices[0] == "OperandRegistry" {
requestList, _ = r.ListOperandRequestsByRegistry(ctx, types.NamespacedName{Namespace: odlmReferenceSlices[1], Name: odlmReferenceSlices[2]})
} else {
return []ctrl.Request{}
}

requests := []ctrl.Request{}
for _, request := range requestList {
namespaceName := types.NamespacedName{Name: request.Name, Namespace: request.Namespace}
req := ctrl.Request{NamespacedName: namespaceName}
requests = append(requests, req)
}
return requests
}
}

// SetupWithManager adds OperandRequest controller to the manager.
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
options := controller.Options{
MaxConcurrentReconciles: r.MaxConcurrentReconciles, // Set the desired value for max concurrent reconciles.
}
ReferencePredicates := predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
labels := e.Object.GetLabels()
for labelKey, labelValue := range labels {
if labelKey == constant.ODLMWatchedLabel {
return labelValue == "true"
}
}
return false
},
UpdateFunc: func(e event.UpdateEvent) bool {
labels := e.ObjectNew.GetLabels()
for labelKey, labelValue := range labels {
if labelKey == constant.ODLMWatchedLabel {
return labelValue == "true"
}
}
return false
},
DeleteFunc: func(e event.DeleteEvent) bool {
return true
},
}
return ctrl.NewControllerManagedBy(mgr).
WithOptions(options).
For(&operatorv1alpha1.OperandRequest{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Expand Down Expand Up @@ -342,5 +400,8 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
newObject := e.ObjectNew.(*operatorv1alpha1.OperandConfig)
return !reflect.DeepEqual(oldObject.Spec, newObject.Spec)
},
})).Complete(r)
})).
Watches(&source.Kind{Type: &corev1.ConfigMap{}}, handler.EnqueueRequestsFromMapFunc(r.getReferenceToRequestMapper()), builder.WithPredicates(ReferencePredicates)).
Watches(&source.Kind{Type: &corev1.Secret{}}, handler.EnqueueRequestsFromMapFunc(r.getReferenceToRequestMapper()), builder.WithPredicates(ReferencePredicates)).
Complete(r)
}
13 changes: 12 additions & 1 deletion controllers/operandrequest/operandrequest_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ var _ = Describe("OperandRequest controller", func() {
namespace = "ibm-cloudpak"
registryName1 = "common-service"
registryName2 = "common-service-2"
registryNamespace = "ibm-common-services"
registryNamespace = "data-ns"
operatorNamespace = "ibm-operators"
)

Expand Down Expand Up @@ -319,6 +319,12 @@ var _ = Describe("OperandRequest controller", func() {
return err
}, testutil.Timeout, testutil.Interval).Should(Succeed())

Eventually(func() error {
jaegerConfigMap := &corev1.ConfigMap{}
err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: "jaeger-configmap-reference", Namespace: registryNamespaceName}, jaegerConfigMap)
return err
}, testutil.Timeout, testutil.Interval).Should(Succeed())

By("Disabling the jaeger operator from first OperandRequest")
requestInstance1 := &operatorv1alpha1.OperandRequest{}
Expect(k8sClient.Get(ctx, requestKey1, requestInstance1)).Should(Succeed())
Expand Down Expand Up @@ -364,6 +370,11 @@ var _ = Describe("OperandRequest controller", func() {
err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: "jaeger-configmap", Namespace: registryNamespaceName}, jaegerConfigMap)
return err != nil && errors.IsNotFound(err)
}, testutil.Timeout, testutil.Interval).Should(BeTrue())
Eventually(func() bool {
jaegerConfigMap := &corev1.ConfigMap{}
err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: "jaeger-configmap-reference", Namespace: registryNamespaceName}, jaegerConfigMap)
return err != nil && errors.IsNotFound(err)
}, testutil.Timeout, testutil.Interval).Should(BeTrue())

By("Checking operators have been deleted")
Eventually(func() bool {
Expand Down
52 changes: 43 additions & 9 deletions controllers/operandrequest/reconcile_operand.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func (r *Reconciler) reconcileOperand(ctx context.Context, requestInstance *oper
klog.V(2).Infof("There is no service: %s from the OperandConfig instance: %s/%s, Skip reconciling Operands", operand.Name, registryKey.Namespace, req.Registry)
continue
}
err = r.reconcileCRwithConfig(ctx, opdConfig, configInstance.Namespace, csv, requestInstance, operand.Name, sub.Namespace, &r.Mutex)
err = r.reconcileCRwithConfig(ctx, opdConfig, configInstance.Name, configInstance.Namespace, csv, requestInstance, operand.Name, sub.Namespace, &r.Mutex)
if err != nil {
merr.Add(err)
requestInstance.SetMemberStatus(operand.Name, "", operatorv1alpha1.ServiceFailed, &r.Mutex)
Expand Down Expand Up @@ -202,12 +202,13 @@ func (r *Reconciler) reconcileOperand(ctx context.Context, requestInstance *oper
}

// reconcileCRwithConfig merge and create custom resource base on OperandConfig and CSV alm-examples
func (r *Reconciler) reconcileCRwithConfig(ctx context.Context, service *operatorv1alpha1.ConfigService, namespace string, csv *olmv1alpha1.ClusterServiceVersion, requestInstance *operatorv1alpha1.OperandRequest, operandName string, operatorNamespace string, mu sync.Locker) error {
func (r *Reconciler) reconcileCRwithConfig(ctx context.Context, service *operatorv1alpha1.ConfigService, opConfigName, opConfigNs string, csv *olmv1alpha1.ClusterServiceVersion, requestInstance *operatorv1alpha1.OperandRequest, operandName string, operatorNamespace string, mu sync.Locker) error {
merr := &util.MultiErr{}

// Create k8s resources required by service
if service.Resources != nil {
for _, res := range service.Resources {
for i := range service.Resources {
res := service.Resources[i]
if res.APIVersion == "" {
return fmt.Errorf("The APIVersion of k8s resource is empty for operator " + service.Name)
}
Expand All @@ -220,11 +221,27 @@ func (r *Reconciler) reconcileCRwithConfig(ctx context.Context, service *operato
}
var k8sResNs string
if res.Namespace == "" {
k8sResNs = namespace
k8sResNs = opConfigNs
} else {
k8sResNs = res.Namespace
}

resObject, err := util.ObjectToNewUnstructured(&res)
if err != nil {
klog.Errorf("Failed to convert %s %s/%s object to unstructured.Unstructured object", res.Kind, k8sResNs, res.Name)
return err
}

if err := r.ParseValueReferenceInObject(ctx, "data", resObject.Object["data"], resObject.Object, "OperandConfig", opConfigName, opConfigNs); err != nil {
klog.Errorf("Failed to parse value reference in resource %s/%s: %v", k8sResNs, res.Name, err)
return err
}
// cover unstructured.Unstructured object to original OperandConfig object
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(resObject.Object, &res); err != nil {
klog.Errorf("Failed to convert unstructured.Unstructured object to %s %s/%s object", res.Kind, k8sResNs, res.Name)
return err
}

var k8sRes unstructured.Unstructured
k8sRes.SetAPIVersion(res.APIVersion)
k8sRes.SetKind(res.Kind)
Expand Down Expand Up @@ -271,14 +288,30 @@ func (r *Reconciler) reconcileCRwithConfig(ctx context.Context, service *operato
var almExampleList []interface{}
err := json.Unmarshal([]byte(almExamples), &almExampleList)
if err != nil {
return errors.Wrapf(err, "failed to convert alm-examples in the Subscription %s/%s to slice", namespace, service.Name)
return errors.Wrapf(err, "failed to convert alm-examples in the Subscription %s/%s to slice", opConfigNs, service.Name)
}

foundMap := make(map[string]bool)
for cr := range service.Spec {
foundMap[cr] = false
}

serviceObject, err := util.ObjectToNewUnstructured(service)
if err != nil {
klog.Errorf("Failed to convert OperandConfig service object %s to unstructured.Unstructured object", service.Name)
return err
}

if err := r.ParseValueReferenceInObject(ctx, "spec", serviceObject.Object["spec"], serviceObject.Object, "OperandConfig", opConfigName, opConfigNs); err != nil {
klog.Errorf("Failed to parse value reference for service %s: %v", service.Name, err)
return err
}
// cover unstructured.Unstructured object to original OperandConfig object
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(serviceObject.Object, service); err != nil {
klog.Errorf("Failed to convert unstructured.Unstructured object to service object %s", service.Name)
return err
}

// Merge OperandConfig and ClusterServiceVersion alm-examples
for _, almExample := range almExampleList {
// Create an unstructured object for CR and check its value
Expand All @@ -293,7 +326,7 @@ func (r *Reconciler) reconcileCRwithConfig(ctx context.Context, service *operato

err := r.Client.Get(ctx, types.NamespacedName{
Name: name,
Namespace: namespace,
Namespace: opConfigNs,
}, &crFromALM)

foundInConfig := false
Expand All @@ -308,19 +341,20 @@ func (r *Reconciler) reconcileCRwithConfig(ctx context.Context, service *operato
klog.Warningf("%v in the alm-example doesn't exist in the OperandConfig for %v", crFromALM.GetKind(), csv.GetName())
continue
}

if err != nil && !apierrors.IsNotFound(err) {
merr.Add(errors.Wrapf(err, "failed to get the custom resource %s/%s", namespace, name))
merr.Add(errors.Wrapf(err, "failed to get the custom resource %s/%s", opConfigNs, name))
continue
} else if apierrors.IsNotFound(err) {
// Create Custom Resource
if err := r.compareConfigandExample(ctx, crFromALM, service, namespace); err != nil {
if err := r.compareConfigandExample(ctx, crFromALM, service, opConfigNs); err != nil {
merr.Add(err)
continue
}
} else {
if r.CheckLabel(crFromALM, map[string]string{constant.OpreqLabel: "true"}) {
// Update or Delete Custom Resource
if err := r.existingCustomResource(ctx, crFromALM, spec.(map[string]interface{}), service, namespace); err != nil {
if err := r.existingCustomResource(ctx, crFromALM, spec.(map[string]interface{}), service, opConfigNs); err != nil {
merr.Add(err)
continue
}
Expand Down
Loading