github.com/verrazzano/verrazzano@v1.7.1/application-operator/controllers/namespace/namespace_controller.go (about) 1 // Copyright (c) 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 namespace 5 6 import ( 7 "context" 8 "errors" 9 "github.com/verrazzano/verrazzano/application-operator/controllers/clusters" 10 vzlogInit "github.com/verrazzano/verrazzano/pkg/log" 11 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 12 "time" 13 14 "github.com/verrazzano/verrazzano/application-operator/constants" 15 "github.com/verrazzano/verrazzano/application-operator/controllers" 16 vzconst "github.com/verrazzano/verrazzano/pkg/constants" 17 vzstring "github.com/verrazzano/verrazzano/pkg/string" 18 "go.uber.org/zap" 19 appsv1 "k8s.io/api/apps/v1" 20 corev1 "k8s.io/api/core/v1" 21 "k8s.io/apimachinery/pkg/runtime" 22 "k8s.io/apimachinery/pkg/types" 23 ctrl "sigs.k8s.io/controller-runtime" 24 "sigs.k8s.io/controller-runtime/pkg/client" 25 "sigs.k8s.io/controller-runtime/pkg/controller" 26 "sigs.k8s.io/controller-runtime/pkg/reconcile" 27 ) 28 29 const ( 30 namespaceControllerFinalizer = "verrazzano.io/namespace" 31 namespaceField = "namespace" 32 controllerName = "namespace" 33 ) 34 35 // NamespaceController Reconciler reconciles a Verrazzano object 36 type NamespaceController struct { 37 client.Client 38 scheme *runtime.Scheme 39 controller controller.Controller 40 log *zap.SugaredLogger 41 } 42 43 // NewNamespaceController - Creates and configures the namespace controller 44 func NewNamespaceController(mgr ctrl.Manager, log *zap.SugaredLogger) (*NamespaceController, error) { 45 nc := &NamespaceController{ 46 Client: mgr.GetClient(), 47 scheme: mgr.GetScheme(), 48 log: log, 49 } 50 return nc, nc.setupWithManager(mgr) 51 } 52 53 // SetupWithManager creates a new controller and adds it to the manager 54 func (nc *NamespaceController) setupWithManager(mgr ctrl.Manager) error { 55 var err error 56 nc.controller, err = ctrl.NewControllerManagedBy(mgr). 57 WithOptions(controller.Options{ 58 RateLimiter: controllers.NewDefaultRateLimiter(), 59 }). 60 For(&corev1.Namespace{}). 61 Build(nc) 62 return err 63 } 64 65 // Reconcile - Watches for and manages namespace activity as it relates to Verrazzano platform services 66 func (nc *NamespaceController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 67 if ctx == nil { 68 return ctrl.Result{}, errors.New("context cannot be nil") 69 } 70 71 // We do not want any resource to get reconciled if it is in namespace kube-system 72 // This is due to a bug found in OKE, it should not affect functionality of any vz operators 73 // If this is the case then return success 74 if req.NamespacedName.Name == vzconst.KubeSystem { 75 log := zap.S().With(vzlogInit.FieldResourceNamespace, req.Namespace, vzlogInit.FieldResourceName, req.Name, vzlogInit.FieldController, controllerName) 76 log.Infof("Namespace resource %v should not be reconciled, ignoring", req.NamespacedName.Name) 77 return reconcile.Result{}, nil 78 } 79 80 // fetch the namespace 81 ns := corev1.Namespace{} 82 if err := nc.Client.Get(ctx, req.NamespacedName, &ns); err != nil { 83 return clusters.IgnoreNotFoundWithLog(err, zap.S()) 84 } 85 log, err := clusters.GetResourceLogger("namespace", req.NamespacedName, &ns) 86 if err != nil { 87 zap.S().Errorf("Failed to create controller logger for namespace resource: %v", err) 88 return clusters.NewRequeueWithDelay(), nil 89 } 90 log.Oncef("Reconciling namespace resource %v, generation %v", req.NamespacedName, ns.Generation) 91 92 res, err := nc.doReconcile(ctx, ns, log) 93 if clusters.ShouldRequeue(res) { 94 return res, nil 95 } 96 // Never return an error since it has already been logged and we don't want the 97 // controller runtime to log again (with stack trace). Just re-queue if there is an error. 98 if err != nil { 99 return clusters.NewRequeueWithDelay(), nil 100 } 101 102 log.Oncef("Finished reconciling namespace %v", req.NamespacedName) 103 104 return ctrl.Result{}, nil 105 } 106 107 // doReconcile performs the reconciliation operations for the namespace 108 func (nc *NamespaceController) doReconcile(ctx context.Context, ns corev1.Namespace, log vzlog.VerrazzanoLogger) (ctrl.Result, error) { 109 if !ns.ObjectMeta.DeletionTimestamp.IsZero() { 110 // Finalizer is present, perform any required cleanup and remove the finalizer 111 if vzstring.SliceContainsString(ns.Finalizers, namespaceControllerFinalizer) { 112 if err := nc.reconcileNamespaceDelete(ctx, &ns, log); err != nil { 113 return ctrl.Result{}, err 114 } 115 return nc.removeFinalizer(ctx, &ns, log) 116 } 117 return ctrl.Result{}, nil 118 } 119 120 return ctrl.Result{}, nc.reconcileNamespace(ctx, &ns, log) 121 } 122 123 // removeFinalizer - Remove the finalizer and update the namespace resource if the post-delete processing is successful 124 func (nc *NamespaceController) removeFinalizer(ctx context.Context, ns *corev1.Namespace, log vzlog.VerrazzanoLogger) (reconcile.Result, error) { 125 log.Debug("Removing finalizer") 126 ns.Finalizers = vzstring.RemoveStringFromSlice(ns.Finalizers, namespaceControllerFinalizer) 127 err := nc.Update(ctx, ns) 128 if err != nil { 129 return reconcile.Result{}, err 130 } 131 return reconcile.Result{}, nil 132 } 133 134 // reconcileNamespace - Reconcile any namespace changes 135 func (nc *NamespaceController) reconcileNamespace(ctx context.Context, ns *corev1.Namespace, log vzlog.VerrazzanoLogger) error { 136 if err := nc.reconcileOCILogging(ctx, ns, log); err != nil { 137 log.Errorf("Failed to reconcile OCI Logging: %v", err) 138 return err 139 } 140 log.Debugf("Reconciled namespace %s successfully", ns.Name) 141 return nil 142 } 143 144 // reconcileNamespaceDelete - Reconcile any post-delete changes required 145 func (nc *NamespaceController) reconcileNamespaceDelete(ctx context.Context, ns *corev1.Namespace, log vzlog.VerrazzanoLogger) error { 146 // Update the OCI Logging configuration to remove the namespace configuration 147 // If the annotation is not present, remove any existing logging configuration 148 return nc.removeOCILogging(ctx, ns, log) 149 } 150 151 // reconcileOCILogging - Configure OCI logging based on the annotation if present 152 func (nc *NamespaceController) reconcileOCILogging(ctx context.Context, ns *corev1.Namespace, log vzlog.VerrazzanoLogger) error { 153 // If the annotation is present, add the finalizer if necessary and update the logging configuration 154 if loggingOCID, ok := ns.Annotations[constants.OCILoggingIDAnnotation]; ok { 155 var added bool 156 if ns.Finalizers, added = vzstring.SliceAddString(ns.Finalizers, namespaceControllerFinalizer); added { 157 if err := nc.Update(ctx, ns); err != nil { 158 return err 159 } 160 } 161 log.Debugw("Updating logging configuration for namespace", namespaceField, ns.Name, "log-id", loggingOCID) 162 updated, err := addNamespaceLoggingFunc(ctx, nc.Client, ns.Name, loggingOCID) 163 if err != nil { 164 return err 165 } 166 if updated { 167 log.Debugw("Updated logging configuration for namespace", namespaceField, ns.Name) 168 err = nc.restartFluentd(ctx, log) 169 } 170 return err 171 } 172 // If the annotation is not present, remove any existing logging configuration 173 return nc.removeOCILogging(ctx, ns, log) 174 } 175 176 // removeOCILogging - Remove OCI logging if the namespace is deleted 177 func (nc *NamespaceController) removeOCILogging(ctx context.Context, ns *corev1.Namespace, log vzlog.VerrazzanoLogger) error { 178 removed, err := removeNamespaceLoggingFunc(ctx, nc.Client, ns.Name) 179 if err != nil { 180 return err 181 } 182 if removed { 183 log.Debugw("Removed logging configuration for namespace", namespaceField, ns.Name) 184 err = nc.restartFluentd(ctx, log) 185 } 186 return err 187 } 188 189 // restartFluentd - restarts the Fluentd pods by adding an annotation to the Fluentd daemonset. 190 func (nc *NamespaceController) restartFluentd(ctx context.Context, log vzlog.VerrazzanoLogger) error { 191 log.Debug("Restarting Fluentd") 192 daemonSet := &appsv1.DaemonSet{} 193 dsName := types.NamespacedName{Name: vzconst.FluentdDaemonSetName, Namespace: constants.VerrazzanoSystemNamespace} 194 195 if err := nc.Client.Get(ctx, dsName, daemonSet); err != nil { 196 return err 197 } 198 199 if daemonSet.Spec.Template.ObjectMeta.Annotations == nil { 200 daemonSet.Spec.Template.ObjectMeta.Annotations = make(map[string]string) 201 } 202 daemonSet.Spec.Template.ObjectMeta.Annotations[vzconst.VerrazzanoRestartAnnotation] = time.Now().Format(time.RFC3339) 203 204 if err := nc.Client.Update(ctx, daemonSet); err != nil { 205 return err 206 } 207 208 return nil 209 } 210 211 // addNamespaceLoggingFuncSig - Type for add namespace logging function, for unit testing 212 type addNamespaceLoggingFuncSig func(_ context.Context, _ client.Client, _ string, _ string) (bool, error) 213 214 // addNamespaceLoggingFunc - Variable to allow replacing add namespace logging func for unit tests 215 var addNamespaceLoggingFunc addNamespaceLoggingFuncSig = addNamespaceLogging 216 217 // removeNamespaceLoggingFuncSig - Type for remove namespace logging function, for unit testing 218 type removeNamespaceLoggingFuncSig func(_ context.Context, _ client.Client, _ string) (bool, error) 219 220 // removeNamespaceLoggingFunc - Variable to allow replacing remove namespace logging func for unit tests 221 var removeNamespaceLoggingFunc removeNamespaceLoggingFuncSig = removeNamespaceLogging