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 }