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 }