From f648553dcf65a43ec0f19112cd1f6360d75b7775 Mon Sep 17 00:00:00 2001 From: ubombar Date: Thu, 21 Mar 2024 15:00:46 +0100 Subject: [PATCH] Add subnamespace controller functionality --- cmd/main.go | 7 ++- ...ultitenancy.edge-net.io_subnamespaces.yaml | 54 +++++++++++++++++ config/rbac/role.yaml | 26 +++++++++ .../multitenancy/subnamespace_controller.go | 41 ++++++++++++- internal/multitenancy/v1/multitenancy.go | 58 ++++++++++++++++++- internal/utils/utils.go | 6 ++ 6 files changed, 183 insertions(+), 9 deletions(-) create mode 100644 config/crd/bases/multitenancy.edge-net.io_subnamespaces.yaml diff --git a/cmd/main.go b/cmd/main.go index 4a3e8c6..056988f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -17,14 +17,14 @@ limitations under the License. package main import ( - "context" "crypto/tls" "flag" + "fmt" "os" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. - "google.golang.org/appengine/log" + _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/apimachinery/pkg/runtime" @@ -147,7 +147,8 @@ func main() { disableNodeLabeller := err != nil if err != nil { - log.Warningf(context.TODO(), "Cannot retrieve the MaxMind Account Token, running without the NodeLabeller") + fmt.Println("Skipping node labeller") + // log.Warningf(context.TODO(), "Cannot retrieve the MaxMind Account Token, running without the NodeLabeller") } // Setup reconcilers, we might want to add the list of reconcilers. This part is auto generated. diff --git a/config/crd/bases/multitenancy.edge-net.io_subnamespaces.yaml b/config/crd/bases/multitenancy.edge-net.io_subnamespaces.yaml new file mode 100644 index 0000000..97ec6fd --- /dev/null +++ b/config/crd/bases/multitenancy.edge-net.io_subnamespaces.yaml @@ -0,0 +1,54 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: subnamespaces.multitenancy.edge-net.io +spec: + group: multitenancy.edge-net.io + names: + kind: SubNamespace + listKind: SubNamespaceList + plural: subnamespaces + singular: subnamespace + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: SubNamespace is the Schema for the subnamespaces API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: SubNamespaceSpec defines the desired state of SubNamespace + properties: + foo: + description: Foo is an example field of SubNamespace. Edit subnamespace_types.go + to remove/update + type: string + type: object + status: + description: SubNamespaceStatus defines the observed state of SubNamespace + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 125be0f..b51aad3 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -39,6 +39,32 @@ rules: - patch - update - watch +- apiGroups: + - multitenancy.edge-net.io + resources: + - subnamespaces + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - multitenancy.edge-net.io + resources: + - subnamespaces/finalizers + verbs: + - update +- apiGroups: + - multitenancy.edge-net.io + resources: + - subnamespaces/status + verbs: + - get + - patch + - update - apiGroups: - multitenancy.edge-net.io resources: diff --git a/internal/controller/multitenancy/subnamespace_controller.go b/internal/controller/multitenancy/subnamespace_controller.go index eca6531..e8e4848 100644 --- a/internal/controller/multitenancy/subnamespace_controller.go +++ b/internal/controller/multitenancy/subnamespace_controller.go @@ -20,17 +20,21 @@ import ( "context" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" multitenancyv1 "github.com/edgenet-project/edgenet/api/multitenancy/v1" + "github.com/edgenet-project/edgenet/internal/multitenancy/v1" + "github.com/edgenet-project/edgenet/internal/utils" ) // SubNamespaceReconciler reconciles a SubNamespace object type SubNamespaceReconciler struct { client.Client - Scheme *runtime.Scheme + Scheme *runtime.Scheme + recorder record.EventRecorder } //+kubebuilder:rbac:groups=multitenancy.edge-net.io,resources=subnamespaces,verbs=get;list;watch;create;update;patch;delete @@ -47,15 +51,46 @@ type SubNamespaceReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.17.0/pkg/reconcile func (r *SubNamespaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = log.FromContext(ctx) + logger := log.FromContext(ctx) + sns := multitenancyv1.SubNamespace{} + isMarkedForDeletion, reconcileResult, err := utils.GetResourceWithFinalizer(ctx, r.Client, &sns, req.NamespacedName) - // TODO(user): your logic here + if !utils.IsObjectInitialized(&sns) { + return reconcileResult, err + } + multiTenancyManager, err := multitenancy.NewMultiTenancyManager(ctx, r.Client) + + if err != nil { + logger.Error(err, "cannot create multitenancy manager") + return ctrl.Result{}, err + } + + if isMarkedForDeletion { + // Do a cleanup and allow tenant object for deletion + if err := multiTenancyManager.SubNamespaceCleanup(ctx, &sns); err != nil { + utils.RecordEventError(r.recorder, &sns, "SubNamespace cleanup failed") + return ctrl.Result{Requeue: true}, err + } + + return utils.AllowObjectDeletion(ctx, r.Client, &sns) + } else { + // TODO: What to do now? + if err := multiTenancyManager.SetupSubNamespace(ctx, &sns); err != nil { + utils.RecordEventError(r.recorder, &sns, "SubNamespace setup failed") + return ctrl.Result{Requeue: true}, err + } + } + + utils.RecordEventInfo(r.recorder, &sns, "SubNamespace reconciliation successfull") return ctrl.Result{}, nil } // SetupWithManager sets up the controller with the Manager. func (r *SubNamespaceReconciler) SetupWithManager(mgr ctrl.Manager) error { + // Setup the event recorder + r.recorder = utils.GetEventRecorder(mgr) + return ctrl.NewControllerManagedBy(mgr). For(&multitenancyv1.SubNamespace{}). Complete(r) diff --git a/internal/multitenancy/v1/multitenancy.go b/internal/multitenancy/v1/multitenancy.go index 211bb48..29d5889 100644 --- a/internal/multitenancy/v1/multitenancy.go +++ b/internal/multitenancy/v1/multitenancy.go @@ -18,6 +18,7 @@ package multitenancy import ( "context" + "fmt" antreav1alpha1 "antrea.io/antrea/pkg/apis/crd/v1alpha1" multitenancyv1 "github.com/edgenet-project/edgenet/api/multitenancy/v1" @@ -45,14 +46,20 @@ type MultiTenancyManager interface { CreateCoreNamespace(context.Context, *multitenancyv1.Tenant, types.UID) error // Same as the CreateCoreNamespace except gets the UID from local cluster. - CreateCoreNamespaceLocal(ctx context.Context, t *multitenancyv1.Tenant) error + CreateCoreNamespaceLocal(context.Context, *multitenancyv1.Tenant) error // Creates a new tenant role binding with admin priviliages. Requires "edgenet:tenant-admin" role // to work. - CreateTenantAdminRoleBinding(ctx context.Context, t *multitenancyv1.Tenant) error + CreateTenantAdminRoleBinding(context.Context, *multitenancyv1.Tenant) error // Create the network policy. If specified creates the cluster network policy as well. - CreateTenantNetworkPolicy(ctx context.Context, t *multitenancyv1.Tenant) error + CreateTenantNetworkPolicy(context.Context, *multitenancyv1.Tenant) error + + // Cleanups the SubNamespace + SubNamespaceCleanup(context.Context, *multitenancyv1.SubNamespace) error + + // Creates and setups studd for the SubNamespace + SetupSubNamespace(context.Context, *multitenancyv1.SubNamespace) error } type multiTenancyManager struct { @@ -372,3 +379,48 @@ func (m *multiTenancyManager) CreateTenantNetworkPolicy(ctx context.Context, t * return nil } + +// Deletes the created child namespace. +func (m *multiTenancyManager) SubNamespaceCleanup(ctx context.Context, s *multitenancyv1.SubNamespace) error { + subNamespaceName := utils.ResolveSubNamespaceName(s) + + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: subNamespaceName, + }, + } + + // Try to delete the namespace, ignore if not found + if err := m.client.Delete(ctx, ns); err != nil && !errors.IsNotFound(err) { + return err + } + + fmt.Println("Deleted successfully") + + return nil +} + +// This creates a new namespace using the generated name. Then populates the namespace with the initial allocation. +// Then gives the current tenant admin the permissions. +func (m *multiTenancyManager) SetupSubNamespace(ctx context.Context, s *multitenancyv1.SubNamespace) error { + subNamespaceName := utils.ResolveSubNamespaceName(s) + + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: subNamespaceName, + Labels: map[string]string{ + "edge-net.io/generated": "true", + "edge-net.io/kind": "sub", + }, + }, + } + + // Try to create the namespace, continue even if it already exist + if err := m.client.Create(ctx, ns); err != nil && !errors.IsAlreadyExists(err) { + return err + } + + // TODO: Create the role bingind etc. + + return nil +} diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 121508b..bdeff72 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -28,6 +28,8 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + multitenancyv1 "github.com/edgenet-project/edgenet/api/multitenancy/v1" ) // Define a custom type that implements the flag.Value interface @@ -60,6 +62,10 @@ func ResolveCoreNamespaceName(tenantName string) string { return tenantName } +func ResolveSubNamespaceName(s *multitenancyv1.SubNamespace) string { + return fmt.Sprintf("%s-%s", s.GetName(), s.GetUID()) +} + // Check a string exists in a list of strings func containsFinalizer(finalizers []string, finalizer string) bool { for _, item := range finalizers {