github.com/verrazzano/verrazzano@v1.7.0/application-operator/mcagent/mcagent_configmap.go (about)

     1  // Copyright (c) 2021, 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package mcagent
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/verrazzano/verrazzano/application-operator/constants"
    10  	vzconst "github.com/verrazzano/verrazzano/pkg/constants"
    11  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    12  
    13  	clustersv1alpha1 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1"
    14  	"github.com/verrazzano/verrazzano/application-operator/controllers/clusters"
    15  	"k8s.io/apimachinery/pkg/types"
    16  	"sigs.k8s.io/controller-runtime/pkg/client"
    17  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    18  )
    19  
    20  // Synchronize MultiClusterConfigMap objects to the local cluster
    21  func (s *Syncer) syncMCConfigMapObjects(namespace string) error {
    22  	// Get all the MultiClusterConfigMap objects from the admin cluster
    23  	allAdminMCConfigMaps := clustersv1alpha1.MultiClusterConfigMapList{}
    24  	listOptions := &client.ListOptions{Namespace: namespace}
    25  	err := s.AdminClient.List(s.Context, &allAdminMCConfigMaps, listOptions)
    26  	// When placements are changed a forbidden error can be returned.  In this case,
    27  	// we want to fall through and delete orphaned resources.
    28  	if err != nil && !apierrors.IsNotFound(err) && !apierrors.IsForbidden(err) {
    29  		return err
    30  	}
    31  
    32  	// Write each of the records that are targeted to this cluster
    33  	for _, mcConfigMap := range allAdminMCConfigMaps.Items {
    34  		if s.isThisCluster(mcConfigMap.Spec.Placement) {
    35  			_, err := s.createOrUpdateMCConfigMap(mcConfigMap)
    36  			if err != nil {
    37  				s.Log.Errorw(fmt.Sprintf("Failed syncing object: %v", err),
    38  					"MultiClusterConfigMap",
    39  					types.NamespacedName{Namespace: mcConfigMap.Namespace, Name: mcConfigMap.Name})
    40  			}
    41  		}
    42  	}
    43  
    44  	// Delete orphaned MultiClusterConfigMap resources.
    45  	// Get the list of MultiClusterConfigMap resources on the
    46  	// local cluster and compare to the list received from the admin cluster.
    47  	// The admin cluster is the source of truth.
    48  	allLocalMCConfigMaps := clustersv1alpha1.MultiClusterConfigMapList{}
    49  	err = s.LocalClient.List(s.Context, &allLocalMCConfigMaps, listOptions)
    50  	if err != nil {
    51  		s.Log.Errorf("Failed to list MultiClusterConfigMap on local cluster: %v", err)
    52  		return nil
    53  	}
    54  	for i, mcConfigMap := range allLocalMCConfigMaps.Items {
    55  		// Delete each MultiClusterConfigMap object that is not on the admin cluster or no longer placed on this cluster
    56  		if !s.configMapPlacedOnCluster(&allAdminMCConfigMaps, mcConfigMap.Name, mcConfigMap.Namespace) {
    57  			err := s.LocalClient.Delete(s.Context, &allLocalMCConfigMaps.Items[i])
    58  			if err != nil {
    59  				s.Log.Errorf("Failed to delete MultiClusterConfigMap with name %q and namespace %q: %v", mcConfigMap.Name, mcConfigMap.Namespace, err)
    60  			}
    61  		}
    62  	}
    63  
    64  	return nil
    65  }
    66  
    67  // Create or update a MultiClusterConfigMap
    68  func (s *Syncer) createOrUpdateMCConfigMap(mcConfigMap clustersv1alpha1.MultiClusterConfigMap) (controllerutil.OperationResult, error) {
    69  	var mcConfigMapNew clustersv1alpha1.MultiClusterConfigMap
    70  	mcConfigMapNew.Namespace = mcConfigMap.Namespace
    71  	mcConfigMapNew.Name = mcConfigMap.Name
    72  
    73  	// Create or update on the local cluster
    74  	return controllerutil.CreateOrUpdate(s.Context, s.LocalClient, &mcConfigMapNew, func() error {
    75  		mutateMCConfigMap(mcConfigMap, &mcConfigMapNew)
    76  		return nil
    77  	})
    78  }
    79  
    80  func (s *Syncer) updateMultiClusterConfigMapStatus(name types.NamespacedName, newCond clustersv1alpha1.Condition, newClusterStatus clustersv1alpha1.ClusterLevelStatus) error {
    81  	var fetched clustersv1alpha1.MultiClusterConfigMap
    82  	err := s.AdminClient.Get(s.Context, name, &fetched)
    83  	if err != nil {
    84  		return err
    85  	}
    86  	fetched.Status.Conditions = append(fetched.Status.Conditions, newCond)
    87  	clusters.SetClusterLevelStatus(&fetched.Status, newClusterStatus)
    88  	return s.AdminClient.Status().Update(s.Context, &fetched)
    89  }
    90  
    91  // mutateMCConfigMap mutates the MultiClusterConfigMap to reflect the contents of the parent MultiClusterConfigMap
    92  func mutateMCConfigMap(mcConfigMap clustersv1alpha1.MultiClusterConfigMap, mcConfigMapNew *clustersv1alpha1.MultiClusterConfigMap) {
    93  	mcConfigMapNew.Spec.Placement = mcConfigMap.Spec.Placement
    94  	mcConfigMapNew.Spec.Template = mcConfigMap.Spec.Template
    95  	mcConfigMapNew.Labels = mcConfigMap.Labels
    96  	// Mark the MC ConfigMap we synced from Admin cluster with verrazzano-managed=true, to
    97  	// distinguish from any (though unlikely) that the user might have created on managed cluster
    98  	if mcConfigMapNew.Labels == nil {
    99  		mcConfigMapNew.Labels = map[string]string{}
   100  	}
   101  	mcConfigMapNew.Labels[vzconst.VerrazzanoManagedLabelKey] = constants.LabelVerrazzanoManagedDefault
   102  
   103  }
   104  
   105  // configMapPlacedOnCluster returns boolean indicating if the list contains the object with the specified name and namespace
   106  func (s *Syncer) configMapPlacedOnCluster(mcAdminList *clustersv1alpha1.MultiClusterConfigMapList, name string, namespace string) bool {
   107  	for _, item := range mcAdminList.Items {
   108  		if item.Name == name && item.Namespace == namespace {
   109  			return s.isThisCluster(item.Spec.Placement)
   110  		}
   111  	}
   112  	return false
   113  }