github.com/verrazzano/verrazzano@v1.7.0/application-operator/mcagent/mcagent_project.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 clustersv1alpha1 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1" 10 "github.com/verrazzano/verrazzano/application-operator/constants" 11 "github.com/verrazzano/verrazzano/application-operator/controllers/clusters" 12 vzconst "github.com/verrazzano/verrazzano/pkg/constants" 13 "k8s.io/apimachinery/pkg/types" 14 "sigs.k8s.io/controller-runtime/pkg/client" 15 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 16 ) 17 18 // Check Project resources in the verrazzano-mc namespace on the admin cluster 19 // and create namespaces specified in the Project resources in the local cluster 20 func (s *Syncer) syncVerrazzanoProjects() error { 21 // Get all the Project objects from the admin cluster 22 allAdminProjects := &clustersv1alpha1.VerrazzanoProjectList{} 23 listOptions := &client.ListOptions{Namespace: constants.VerrazzanoMultiClusterNamespace} 24 err := s.AdminClient.List(s.Context, allAdminProjects, listOptions) 25 if err != nil { 26 return client.IgnoreNotFound(err) 27 } 28 29 // Write each of the records in verrazzano-mc namespace 30 for _, vp := range allAdminProjects.Items { 31 if vp.Namespace == constants.VerrazzanoMultiClusterNamespace { 32 if s.isThisCluster(vp.Spec.Placement) { 33 _, err := s.createOrUpdateVerrazzanoProject(vp) 34 if err != nil { 35 s.Log.Errorw(fmt.Sprintf("Failed syncing object: %v", err), 36 "VerrazzanoProject", 37 types.NamespacedName{Namespace: vp.Namespace, Name: vp.Name}) 38 } 39 } else { 40 // Remove the VerrazzanoProject resource if it is on the local cluster but no longer 41 // contains placements for this cluster. 42 vpLocal := clustersv1alpha1.VerrazzanoProject{} 43 err := s.LocalClient.Get(s.Context, types.NamespacedName{Namespace: vp.Namespace, Name: vp.Name}, &vpLocal) 44 if err == nil { 45 s.Log.Debugf("Deleting VerrazzanoProject %q from namespace %q because it is no longer targeted at this cluster", vp.Name, vp.Namespace) 46 err2 := s.LocalClient.Delete(s.Context, &vpLocal) 47 if err2 != nil { 48 s.Log.Errorf("failed to delete VerrazzanoProject with name %q and namespace %q: %v", vp.Name, vp.Namespace, err2) 49 } 50 } 51 } 52 } 53 } 54 55 // Delete orphaned VerrazzanoProject resources. 56 // Get the list of VerrazzanoProject resources on the 57 // local cluster and compare to the list received from the admin cluster. 58 // The admin cluster is the source of truth. 59 allLocalProjects := clustersv1alpha1.VerrazzanoProjectList{} 60 err = s.LocalClient.List(s.Context, &allLocalProjects, listOptions) 61 if err != nil { 62 s.Log.Errorf("Failed to list VerrazzanoProject on local cluster: %v", err) 63 return nil 64 } 65 for i, project := range allLocalProjects.Items { 66 // Delete each VerrazzanoProject object that is not on the admin cluster 67 if !projectListContains(allAdminProjects, project.Name, project.Namespace) { 68 err := s.LocalClient.Delete(s.Context, &allLocalProjects.Items[i]) 69 if err != nil { 70 s.Log.Errorf("Failed to delete VerrazzanoProject with name %q and namespace %q: %v", project.Name, project.Namespace, err) 71 } 72 } 73 } 74 75 // Update the list of namespaces being watched for multi-cluster objects 76 s.ProjectNamespaces, err = s.getManagedNamespaces() 77 if err != nil { 78 s.Log.Errorf("Failed to get the list of Verrazzano managed namespaces: %v", err) 79 return err 80 } 81 82 return nil 83 } 84 85 // Create or update a VerrazzanoProject 86 func (s *Syncer) createOrUpdateVerrazzanoProject(vp clustersv1alpha1.VerrazzanoProject) (controllerutil.OperationResult, error) { 87 var vpNew clustersv1alpha1.VerrazzanoProject 88 vpNew.Namespace = vp.Namespace 89 vpNew.Name = vp.Name 90 91 // Create or update on the local cluster 92 return controllerutil.CreateOrUpdate(s.Context, s.LocalClient, &vpNew, func() error { 93 mutateVerrazzanoProject(vp, &vpNew) 94 return nil 95 }) 96 } 97 98 func (s *Syncer) updateVerrazzanoProjectStatus(name types.NamespacedName, newCond clustersv1alpha1.Condition, newClusterStatus clustersv1alpha1.ClusterLevelStatus) error { 99 fetched := clustersv1alpha1.VerrazzanoProject{} 100 err := s.AdminClient.Get(s.Context, name, &fetched) 101 if err != nil { 102 return err 103 } 104 fetched.Status.Conditions = append(fetched.Status.Conditions, newCond) 105 clusters.SetClusterLevelStatus(&fetched.Status, newClusterStatus) 106 return s.AdminClient.Status().Update(s.Context, &fetched) 107 } 108 109 // mutateVerrazzanoProject mutates the VerrazzanoProject to reflect the contents of the parent VerrazzanoProject 110 func mutateVerrazzanoProject(vp clustersv1alpha1.VerrazzanoProject, vpNew *clustersv1alpha1.VerrazzanoProject) { 111 vpNew.Spec.Template = vp.Spec.Template 112 vpNew.Spec.Placement = vp.Spec.Placement 113 // Mark the VerrazzanoProject we synced from Admin cluster with verrazzano-managed=true, to 114 // distinguish from any (though unlikely) that the user might have created on managed cluster 115 if vpNew.Labels == nil { 116 vpNew.Labels = map[string]string{} 117 } 118 if vp.Labels != nil { 119 vpNew.Labels = vp.Labels 120 } 121 vpNew.Labels[vzconst.VerrazzanoManagedLabelKey] = constants.LabelVerrazzanoManagedDefault 122 vpNew.Annotations = vp.Annotations 123 } 124 125 // projectListContains returns boolean indicating if the list contains the object with the specified name and namespace 126 func projectListContains(projectList *clustersv1alpha1.VerrazzanoProjectList, name string, namespace string) bool { 127 for _, item := range projectList.Items { 128 if item.Name == name && item.Namespace == namespace { 129 return true 130 } 131 } 132 return false 133 }