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