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  }