github.com/verrazzano/verrazzano@v1.7.0/application-operator/mcagent/mcagent_component.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 MultiClusterComponent objects to the local cluster
    21  func (s *Syncer) syncMCComponentObjects(namespace string) error {
    22  	// Get all the MultiClusterComponent objects from the admin cluster
    23  	allAdminMCComponents := clustersv1alpha1.MultiClusterComponentList{}
    24  	listOptions := &client.ListOptions{Namespace: namespace}
    25  	err := s.AdminClient.List(s.Context, &allAdminMCComponents, 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 _, mcComponent := range allAdminMCComponents.Items {
    34  		if s.isThisCluster(mcComponent.Spec.Placement) {
    35  			_, err := s.createOrUpdateMCComponent(mcComponent)
    36  			if err != nil {
    37  				s.Log.Errorw(fmt.Sprintf("Failed syncing object: %v", err),
    38  					"MultiClusterComponent",
    39  					types.NamespacedName{Namespace: mcComponent.Namespace, Name: mcComponent.Name})
    40  			}
    41  		}
    42  	}
    43  
    44  	// Delete orphaned MultiClusterComponent resources.
    45  	// Get the list of MultiClusterComponent 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  	allLocalMCComponents := clustersv1alpha1.MultiClusterComponentList{}
    49  	err = s.LocalClient.List(s.Context, &allLocalMCComponents, listOptions)
    50  	if err != nil {
    51  		s.Log.Errorf("Failed to list MultiClusterComponent on local cluster: %v", err)
    52  		return nil
    53  	}
    54  	for i, mcComponent := range allLocalMCComponents.Items {
    55  		// Delete each MultiClusterComponent object that is not on the admin cluster or no longer placed on this cluster
    56  		if !s.componentPlacedOnCluster(&allAdminMCComponents, mcComponent.Name, mcComponent.Namespace) {
    57  			err := s.LocalClient.Delete(s.Context, &allLocalMCComponents.Items[i])
    58  			if err != nil {
    59  				s.Log.Errorf("Failed to delete MultiClusterComponent with name %q and namespace %q: %v", mcComponent.Name, mcComponent.Namespace, err)
    60  			}
    61  		}
    62  	}
    63  
    64  	return nil
    65  }
    66  
    67  // Create or update a MultiClusterComponent
    68  func (s *Syncer) createOrUpdateMCComponent(mcComponent clustersv1alpha1.MultiClusterComponent) (controllerutil.OperationResult, error) {
    69  	var mcComponentNew clustersv1alpha1.MultiClusterComponent
    70  	mcComponentNew.Namespace = mcComponent.Namespace
    71  	mcComponentNew.Name = mcComponent.Name
    72  
    73  	// Create or update on the local cluster
    74  	return controllerutil.CreateOrUpdate(s.Context, s.LocalClient, &mcComponentNew, func() error {
    75  		mutateMCComponent(mcComponent, &mcComponentNew)
    76  		return nil
    77  	})
    78  }
    79  
    80  func (s *Syncer) updateMultiClusterComponentStatus(name types.NamespacedName, newCond clustersv1alpha1.Condition, newClusterStatus clustersv1alpha1.ClusterLevelStatus) error {
    81  	var fetched clustersv1alpha1.MultiClusterComponent
    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  // mutateMCComponent mutates the MultiClusterComponent to reflect the contents of the parent MultiClusterComponent
    92  func mutateMCComponent(mcComponent clustersv1alpha1.MultiClusterComponent, mcComponentNew *clustersv1alpha1.MultiClusterComponent) {
    93  	mcComponentNew.Spec.Placement = mcComponent.Spec.Placement
    94  	mcComponentNew.Spec.Template = mcComponent.Spec.Template
    95  	mcComponentNew.Labels = mcComponent.Labels
    96  	// Mark the MC component 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 mcComponentNew.Labels == nil {
    99  		mcComponentNew.Labels = map[string]string{}
   100  	}
   101  	mcComponentNew.Labels[vzconst.VerrazzanoManagedLabelKey] = constants.LabelVerrazzanoManagedDefault
   102  
   103  }
   104  
   105  // componentPlacedOnCluster returns boolean indicating if the list contains the object with the specified name and namespace
   106  func (s *Syncer) componentPlacedOnCluster(mcAdminList *clustersv1alpha1.MultiClusterComponentList, 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  }