github.com/verrazzano/verrazzano@v1.7.0/platform-operator/namespacewatch/namespace_watcher.go (about)

     1  // Copyright (c) 2023, 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 namespacewatch
     5  
     6  import (
     7  	"context"
     8  	"github.com/verrazzano/verrazzano/pkg/log"
     9  	"go.uber.org/zap"
    10  	v1 "k8s.io/api/core/v1"
    11  	clipkg "sigs.k8s.io/controller-runtime/pkg/client"
    12  	"time"
    13  )
    14  
    15  const (
    16  	channelBufferSize = 100
    17  )
    18  
    19  // namespaceWatcher - holds global instance of NamespacesWatcher.  Required by namespaces util
    20  // functions that don't have access to the NamespacesWatcher context.
    21  var namespaceWatcher *NamespacesWatcher
    22  
    23  // NamespacesWatcher periodically checks if new namespaces are added
    24  type NamespacesWatcher struct {
    25  	client   clipkg.Client
    26  	tickTime time.Duration
    27  	log      *zap.SugaredLogger
    28  	shutdown chan int
    29  }
    30  
    31  // NewNamespaceWatcher - instantiate a NamespacesWatcher context
    32  func NewNamespaceWatcher(c clipkg.Client, duration time.Duration) *NamespacesWatcher {
    33  	namespaceWatcher = &NamespacesWatcher{
    34  		client:   c,
    35  		tickTime: duration,
    36  		log:      zap.S().With(log.FieldController, "namespacewatcher"),
    37  	}
    38  	return namespaceWatcher
    39  }
    40  
    41  func GetNamespaceWatcher() *NamespacesWatcher {
    42  	return namespaceWatcher
    43  }
    44  
    45  // Start starts the NamespacesWatcher if it is not already running.
    46  // It is safe to call Start multiple times, additional goroutines will not be created
    47  func (nw *NamespacesWatcher) Start() {
    48  	if nw.shutdown != nil {
    49  		// already running, so nothing to do
    50  		return
    51  	}
    52  	nw.shutdown = make(chan int, channelBufferSize)
    53  
    54  	// goroutine watches namespace resource every p.tickTime. If a shutdown signal is received (or channel is closed),
    55  	// the goroutine returns.
    56  	go func() {
    57  		ticker := time.NewTicker(nw.tickTime)
    58  		for {
    59  			select {
    60  			case <-ticker.C:
    61  				// timer event causes namespaces update
    62  				clusterName, rancherSystemProjectID, err := nw.getRancherSystemProjectID()
    63  				if err != nil {
    64  					nw.log.Errorf("%v", err)
    65  				}
    66  				if rancherSystemProjectID != "" && clusterName != "" {
    67  					err = nw.MoveSystemNamespacesToRancherSystemProject(rancherSystemProjectID, clusterName)
    68  					if err != nil {
    69  						nw.log.Errorf("%v", err)
    70  					}
    71  				}
    72  			case <-nw.shutdown:
    73  				// shutdown event causes termination
    74  				ticker.Stop()
    75  				return
    76  			}
    77  		}
    78  	}()
    79  }
    80  
    81  // MoveSystemNamespacesToRancherSystemProject Updates the label & annotation with Rancher System Project ID
    82  // For namespaces that have label "verrazzano.io/namespace"
    83  // And that does not have a label "management.cattle.io/system-namespace"
    84  // "management.cattle.io/system-namespace" namespace label indicates it is managed by Rancher
    85  func (nw *NamespacesWatcher) MoveSystemNamespacesToRancherSystemProject(rancherSystemProjectID string, clusterName string) error {
    86  
    87  	isRancherReady, err := nw.IsRancherReady()
    88  	if err != nil {
    89  		return err
    90  	}
    91  	if isRancherReady {
    92  		var rancherSystemProjectIDAnnotation = clusterName + ":" + rancherSystemProjectID
    93  
    94  		namespaceList := &v1.NamespaceList{}
    95  		err := nw.client.List(context.TODO(), namespaceList, &clipkg.ListOptions{})
    96  		if err != nil {
    97  			return err
    98  		}
    99  
   100  		for i := range namespaceList.Items {
   101  			if namespaceList.Items[i].Labels == nil {
   102  				namespaceList.Items[i].Labels = map[string]string{}
   103  			}
   104  			if namespaceList.Items[i].Annotations == nil {
   105  				namespaceList.Items[i].Annotations = map[string]string{}
   106  			}
   107  			_, rancherProjectIDAnnotationExists := namespaceList.Items[i].Annotations[RancherProjectIDLabelKey]
   108  			if isVerrazzanoManagedNamespace(&(namespaceList.Items[i])) && !rancherProjectIDAnnotationExists {
   109  				nw.log.Infof("Updating the Labels and Annotations of a Namespace %v with Rancher System Project ID %v", namespaceList.Items[i].Namespace, rancherSystemProjectID)
   110  				namespaceList.Items[i].Annotations[RancherProjectIDLabelKey] = rancherSystemProjectIDAnnotation
   111  				namespaceList.Items[i].Labels[RancherProjectIDLabelKey] = rancherSystemProjectID
   112  				if err = nw.client.Update(context.TODO(), &(namespaceList.Items[i]), &clipkg.UpdateOptions{}); err != nil {
   113  					return err
   114  				}
   115  			}
   116  		}
   117  	}
   118  	return nil
   119  }
   120  
   121  // Pause pauses the NamespaceWatch if it was running.
   122  // It is safe to call Pause multiple times
   123  func (nw *NamespacesWatcher) Pause() {
   124  	if nw.shutdown != nil {
   125  		close(nw.shutdown)
   126  		nw.shutdown = nil
   127  	}
   128  }