diff --git a/pkg/namespace/controllers/reconciler.go b/pkg/namespace/controllers/reconciler.go index 5e300a0b6..ddeeee205 100644 --- a/pkg/namespace/controllers/reconciler.go +++ b/pkg/namespace/controllers/reconciler.go @@ -27,6 +27,7 @@ import ( "github.com/kubeslice/worker-operator/controllers" + cntrl "github.com/kubeslice/worker-operator/controllers" hub "github.com/kubeslice/worker-operator/pkg/hub/hubclient" "github.com/kubeslice/worker-operator/pkg/logger" "github.com/kubeslice/worker-operator/pkg/utils" @@ -103,6 +104,18 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu log.Error(err, "error while retrieving labels from namespace") return ctrl.Result{}, err } + if sliceName != "" { + isNsConfigured, err := cntrl.SliceAppNamespaceConfigured(ctx, sliceName, namespace.Name) + if err != nil { + log.Error(err, "Failed to get app namespace info for slice", + "slice", sliceName, "namespace", namespace.Name) + return ctrl.Result{}, nil + } + if !isNsConfigured { + log.Info("Namespace not part of slice", "namespace", namespace.Name, "slice", sliceName) + return ctrl.Result{}, nil + } + } *r.EventRecorder = (*r.EventRecorder).WithSlice(sliceName) err = hub.UpdateNamespaceInfoToHub(ctx, r.Hubclient, namespace.Name, sliceName) if err != nil { diff --git a/pkg/webhook/pod/webhook.go b/pkg/webhook/pod/webhook.go index 1bc7a1f73..d79461bcd 100644 --- a/pkg/webhook/pod/webhook.go +++ b/pkg/webhook/pod/webhook.go @@ -48,7 +48,18 @@ const ( ) var ( - log = logger.NewWrappedLogger().WithName("Webhook").V(1) + log = logger.NewWrappedLogger().WithName("Webhook").V(1) + LabelsToRemove = []string{ + "kubeslice.io/nsmIP", + "kubeslice.io/pod-type", + "kubeslice.io/slice", + } + + AnnotationsToRemove = []string{ + "kubeslice.io/status", + "ns.networkservicemesh.io", + "networkservicemesh.io", + } ) type SliceInfoProvider interface { @@ -80,6 +91,10 @@ func (wh *WebhookServer) Handle(ctx context.Context, req admission.Request) admi if mutate, sliceName := wh.MutationRequired(pod.ObjectMeta, ctx, req.Kind.Kind); !mutate { log.Info("mutation not required for pod", "pod metadata", pod.ObjectMeta.Name) + if offBoard := wh.OffboardRequired(pod.ObjectMeta, ctx, req.Kind.Kind, sliceName); offBoard { + log.Info("mutation to offboard required for pod", "pod metadata", pod.ObjectMeta.Name) + pod.ObjectMeta = wh.OffBoardObject(pod.ObjectMeta, ctx) + } } else { log.Info("mutating pod", "pod metadata", pod.ObjectMeta.Name) pod = MutatePod(pod, sliceName) @@ -100,9 +115,13 @@ func (wh *WebhookServer) Handle(ctx context.Context, req admission.Request) admi if mutate, sliceName := wh.MutationRequired(deploy.ObjectMeta, ctx, req.Kind.Kind); !mutate { log.Info("mutation not required for deployment", "pod metadata", deploy.Spec.Template.ObjectMeta) + if offBoard := wh.OffboardRequired(deploy.ObjectMeta, ctx, req.Kind.Kind, sliceName); offBoard { + log.Info("mutation to offboard required for deploy", "deploy metadata", deploy.ObjectMeta.Name) + deploy.ObjectMeta = wh.OffBoardObject(deploy.ObjectMeta, ctx) + } } else { deploy = MutateDeployment(deploy, sliceName) - log.Info("mutated deploy", "pod metadata", deploy.Spec.Template.ObjectMeta) + log.Info("mutated deploy", "deploy metadata", deploy.Spec.Template.ObjectMeta) } marshaled, err := json.Marshal(deploy) @@ -119,10 +138,14 @@ func (wh *WebhookServer) Handle(ctx context.Context, req admission.Request) admi log := logger.FromContext(ctx) if mutate, sliceName := wh.MutationRequired(statefulset.ObjectMeta, ctx, req.Kind.Kind); !mutate { - log.Info("mutation not required for statefulsets", "pod metadata", statefulset.Spec.Template.ObjectMeta) + log.Info("mutation not required for statefulsets", "statefulset metadata", statefulset.Spec.Template.ObjectMeta) + if offBoard := wh.OffboardRequired(statefulset.ObjectMeta, ctx, req.Kind.Kind, sliceName); offBoard { + log.Info("mutation to offboard required for statefulset", "statefulset metadata", statefulset.ObjectMeta.Name) + statefulset.ObjectMeta = wh.OffBoardObject(statefulset.ObjectMeta, ctx) + } } else { statefulset = MutateStatefulset(statefulset, sliceName) - log.Info("mutated statefulset", "pod metadata", statefulset.Spec.Template.ObjectMeta) + log.Info("mutated statefulset", "statefulset metadata", statefulset.Spec.Template.ObjectMeta) } marshaled, err := json.Marshal(statefulset) @@ -139,10 +162,14 @@ func (wh *WebhookServer) Handle(ctx context.Context, req admission.Request) admi log := logger.FromContext(ctx) if mutate, sliceName := wh.MutationRequired(daemonset.ObjectMeta, ctx, req.Kind.Kind); !mutate { - log.Info("mutation not required for daemonset", "pod metadata", daemonset.Spec.Template.ObjectMeta) + log.Info("mutation not required for daemonset", "daemonset metadata", daemonset.Spec.Template.ObjectMeta) + if offBoard := wh.OffboardRequired(daemonset.ObjectMeta, ctx, req.Kind.Kind, sliceName); offBoard { + log.Info("mutation to offboard required for daemonset", "daemonset metadata", daemonset.ObjectMeta.Name) + daemonset.ObjectMeta = wh.OffBoardObject(daemonset.ObjectMeta, ctx) + } } else { daemonset = MutateDaemonSet(daemonset, sliceName) - log.Info("mutated daemonset", "pod metadata", daemonset.Spec.Template.ObjectMeta) + log.Info("mutated daemonset", "daemonset metadata", daemonset.Spec.Template.ObjectMeta) } marshaled, err := json.Marshal(daemonset) @@ -177,6 +204,34 @@ func (wh *WebhookServer) Handle(ctx context.Context, req admission.Request) admi }} } +func (wh *WebhookServer) OffBoardObject(metadata metav1.ObjectMeta, ctx context.Context) metav1.ObjectMeta { + log := logger.FromContext(ctx).WithName("Webhook") + + annotations := metadata.GetAnnotations() + labels := metadata.GetLabels() + + // Remove kubeslice and nsm labels if present + LabelsToRemove := []string{"kubeslice.io/nsmIP", "kubeslice.io/pod-type", "kubeslice.io/slice"} + for _, labelKey := range LabelsToRemove { + if _, exists := labels[labelKey]; exists { + log.Info("Removing label", "labelKey", labelKey) + delete(labels, labelKey) + } + } + metadata.SetLabels(labels) + + // Remove annotations if necessary + AnnotationsToRemove := []string{"kubeslice.io/status", "ns.networkservicemesh.io", "networkservicemesh.io"} + for _, annotationKey := range AnnotationsToRemove { + if _, exists := annotations[annotationKey]; exists { + log.Info("Removing annotation", "annotationKey", annotationKey) + delete(annotations, annotationKey) + } + } + metadata.SetAnnotations(annotations) + return metadata +} + func MutatePod(pod *corev1.Pod, sliceName string) *corev1.Pod { // Add injection status to pod annotations if pod.ObjectMeta.Annotations == nil { @@ -317,9 +372,53 @@ func (wh *WebhookServer) ValidateServiceExport(svcex *v1beta1.ServiceExport, ctx return true, "", nil } +func (wh *WebhookServer) OffboardRequired(metadata metav1.ObjectMeta, ctx context.Context, kind, sliceName string) bool { + log := logger.FromContext(ctx).WithName("Webhook") + annotations := metadata.GetAnnotations() + labels := metadata.GetLabels() + + // Remove kubeslice and nsm labels if present + if sliceNameInNs, exists := labels[admissionWebhookSliceNamespaceSelectorKey]; exists { + log.Info("slice name in namespace exists", "sliceNameInNs", sliceNameInNs) + if sliceNameInNs != sliceName { + log.Info("slice name in namespace does not match sliceName", "sliceNameInNs", sliceNameInNs, "sliceName", sliceName) + nsConfigured, err := wh.SliceInfoClient.SliceAppNamespaceConfigured(context.Background(), sliceName, metadata.Namespace) + if err != nil { + log.Error(err, "Failed to get app namespace info for slice", + "slice", sliceName, "namespace", metadata.Namespace) + return false + } + if !nsConfigured { + log.Info("Namespace not part of slice", "namespace", metadata.Namespace, "slice", sliceName) + return true + } + return false + } + return false + } + + LabelsToRemove := []string{"kubeslice.io/nsmIP", "kubeslice.io/pod-type", "kubeslice.io/slice"} + for _, labelKey := range LabelsToRemove { + if _, exists := labels[labelKey]; exists { + log.Info("Found label", "labelKey", labelKey) + return true + } + } + + // Remove annotations if necessary + AnnotationsToRemove := []string{"kubeslice.io/status", "ns.networkservicemesh.io", "networkservicemesh.io"} + for _, annotationKey := range AnnotationsToRemove { + if _, exists := annotations[annotationKey]; exists { + log.Info("Found annotation", "annotationKey", annotationKey) + return true + } + } + return false +} + // returns mutationRequired bool, sliceName string func (wh *WebhookServer) MutationRequired(metadata metav1.ObjectMeta, ctx context.Context, kind string) (bool, string) { - log := logger.FromContext(ctx) + log := logger.FromContext(ctx).WithName("Webhook") annotations := metadata.GetAnnotations() labels := metadata.GetLabels() @@ -338,19 +437,6 @@ func (wh *WebhookServer) MutationRequired(metadata metav1.ObjectMeta, ctx contex return false, "" } - // do not inject if it is already injected - //TODO(rahulsawra): need better way to define injected status - if annotations[AdmissionWebhookAnnotationStatusKey] == "injected" { - log.Info("obj is already injected", "kind", kind) - return false, "" - } - - // Do not auto onboard control plane namespace. Ideally, we should not have any deployment/pod in the control plane ns connect to a slice - if metadata.Namespace == controlPlaneNamespace { - log.Info("namespace is same as controle plane") - return false, "" - } - nsLabels, err := wh.SliceInfoClient.GetNamespaceLabels(context.Background(), wh.Client, metadata.Namespace) if err != nil { log.Error(err, "Error getting namespace labels") @@ -367,25 +453,38 @@ func (wh *WebhookServer) MutationRequired(metadata metav1.ObjectMeta, ctx contex return false, "" } + // do not inject if it is already injected + //TODO(rahulsawra): need better way to define injected status + if annotations[AdmissionWebhookAnnotationStatusKey] == "injected" { + log.Info("obj is already injected", "kind", kind) + return false, sliceNameInNs + } + + // Do not auto onboard control plane namespace. Ideally, we should not have any deployment/pod in the control plane ns connect to a slice + if metadata.Namespace == controlPlaneNamespace { + log.Info("namespace is same as controle plane") + return false, sliceNameInNs + } + sliceNetworkType, err := wh.SliceInfoClient.GetSliceOverlayNetworkType(context.Background(), wh.Client, sliceNameInNs) if err != nil { log.Error(err, "Error getting slice overlay network type") - return false, "" + return false, sliceNameInNs } if sliceNetworkType != "" && sliceNetworkType != v1alpha1.SINGLENET { log.Info("Slice overlay type is not single-network. Skip pod mutation...") - return false, "" + return false, sliceNameInNs } nsConfigured, err := wh.SliceInfoClient.SliceAppNamespaceConfigured(context.Background(), sliceNameInNs, metadata.Namespace) if err != nil { log.Error(err, "Failed to get app namespace info for slice", "slice", sliceNameInNs, "namespace", metadata.Namespace) - return false, "" + return false, sliceNameInNs } if !nsConfigured { log.Info("Namespace not part of slice", "namespace", metadata.Namespace, "slice", sliceNameInNs) - return false, "" + return false, sliceNameInNs } // The annotation kubeslice.io/slice:SLICENAME is present, enable mutation return true, sliceNameInNs