diff --git a/pkg/controller/namespace/namespace_controller.go b/pkg/controller/namespace/namespace_controller.go index 31d27b340..4fba446d9 100644 --- a/pkg/controller/namespace/namespace_controller.go +++ b/pkg/controller/namespace/namespace_controller.go @@ -11,6 +11,7 @@ import ( "fmt" "github.com/go-logr/logr" + "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -76,7 +77,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu if controllerutil.ContainsFinalizer(namespace, constants.CascadingDeletionFinalizer) { controllerutil.RemoveFinalizer(namespace, constants.CascadingDeletionFinalizer) if err := r.Update(ctx, namespace); err != nil { - return ctrl.Result{}, fmt.Errorf("failed to remove finalizer: %v", err) + return ctrl.Result{}, errors.Wrapf(err, "failed to remove finalizer") } } // Our finalizer has finished, so the reconciler can do nothing. @@ -89,7 +90,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu // then lets add the finalizer and update the object. if workspaceLabelExists && !controllerutil.ContainsFinalizer(namespace, constants.CascadingDeletionFinalizer) { if err := r.initCreatorRoleBinding(ctx, namespace); err != nil { - return ctrl.Result{}, fmt.Errorf("failed to init creator role binding: %v", err) + return ctrl.Result{}, errors.Wrapf(err, "failed to init creator role binding") } updated := namespace.DeepCopy() // Remove legacy finalizer @@ -97,11 +98,20 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu // Remove legacy ownerReferences updated.OwnerReferences = make([]metav1.OwnerReference, 0) controllerutil.AddFinalizer(updated, constants.CascadingDeletionFinalizer) - return ctrl.Result{}, r.Patch(ctx, updated, client.MergeFrom(namespace)) + if err := r.Patch(ctx, updated, client.MergeFrom(namespace)); err != nil { + return ctrl.Result{}, errors.Wrapf(err, "failed to add finalizer") + } + return ctrl.Result{}, nil } - if err := r.initRoles(ctx, namespace); err != nil { - return ctrl.Result{}, fmt.Errorf("failed to init builtin roles: %v", err) + if workspaceLabelExists { + if err := r.initRoles(ctx, namespace); err != nil { + return ctrl.Result{}, errors.Wrapf(err, "failed to init roles in namespace %s", namespace.Name) + } + } else { + if err := r.cleanUp(ctx, namespace); err != nil { + return ctrl.Result{}, errors.Wrapf(err, "failed to clean up namespace %s", namespace.Name) + } } r.recorder.Event(namespace, corev1.EventTypeNormal, kscontroller.Synced, kscontroller.MessageResourceSynced) @@ -112,7 +122,7 @@ func (r *Reconciler) initRoles(ctx context.Context, namespace *corev1.Namespace) logger := klog.FromContext(ctx) var templates iamv1beta1.BuiltinRoleList if err := r.List(ctx, &templates, client.MatchingLabels{iamv1beta1.ScopeLabel: iamv1beta1.ScopeNamespace}); err != nil { - return fmt.Errorf("failed to list builtin roles: %v", err) + return errors.Wrapf(err, "failed to list builtin role templates") } for _, template := range templates.Items { selector, err := metav1.LabelSelectorAsSelector(&template.TargetSelector) @@ -135,7 +145,7 @@ func (r *Reconciler) initRoles(ctx context.Context, namespace *corev1.Namespace) return nil }) if err != nil { - return fmt.Errorf("failed to create or update builtin role: %v", err) + return errors.Wrapf(err, "failed to create or update builtin role") } logger.V(4).Info("builtin role initialized", "operation", op) } else if err != nil { @@ -176,8 +186,20 @@ func (r *Reconciler) initCreatorRoleBinding(ctx context.Context, namespace *core return nil }) if err != nil { - return err + return errors.Wrapf(err, "failed to create or update creator role binding") } klog.FromContext(ctx).V(4).Info("creator role binding initialized", "operation", op) return nil } + +func (r *Reconciler) cleanUp(ctx context.Context, namespace *corev1.Namespace) error { + role := &iamv1beta1.Role{} + if err := r.DeleteAllOf(ctx, role, client.InNamespace(namespace.Name)); err != nil { + return errors.Wrapf(err, "failed to delete roles") + } + roleBinding := &iamv1beta1.RoleBinding{} + if err := r.DeleteAllOf(ctx, roleBinding, client.InNamespace(namespace.Name)); err != nil { + return errors.Wrapf(err, "failed to delete role bindings") + } + return nil +} diff --git a/pkg/controller/workspace/workspace_controller.go b/pkg/controller/workspace/workspace_controller.go index 638ee783a..6255795f3 100644 --- a/pkg/controller/workspace/workspace_controller.go +++ b/pkg/controller/workspace/workspace_controller.go @@ -7,16 +7,14 @@ package workspace import ( "context" - "fmt" - - "kubesphere.io/kubesphere/pkg/constants" - - apierrors "k8s.io/apimachinery/pkg/api/errors" "github.com/go-logr/logr" + "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/retry" "k8s.io/klog/v2" tenantv1beta1 "kubesphere.io/api/tenant/v1beta1" ctrl "sigs.k8s.io/controller-runtime" @@ -25,6 +23,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "kubesphere.io/kubesphere/pkg/constants" kscontroller "kubesphere.io/kubesphere/pkg/controller" ) @@ -76,7 +75,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu controllerutil.RemoveFinalizer(expected, "finalizers.tenant.kubesphere.io") controllerutil.AddFinalizer(expected, constants.CascadingDeletionFinalizer) if err := r.Patch(ctx, expected, client.MergeFrom(workspace)); err != nil { - return ctrl.Result{}, fmt.Errorf("failed to add finalizer: %s", err) + return ctrl.Result{}, errors.Wrapf(err, "failed to add finalizer to workspace %s", workspace.Name) } workspaceOperation.WithLabelValues("create", workspace.Name).Inc() } @@ -84,12 +83,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu if controllerutil.ContainsFinalizer(workspace, constants.CascadingDeletionFinalizer) { ok, err := r.workspaceCascadingDeletion(ctx, workspace) if err != nil { - return ctrl.Result{}, fmt.Errorf("failed to delete workspace: %s", err) + return ctrl.Result{}, errors.Wrapf(err, "failed to delete workspace %s", workspace.Name) } if ok { controllerutil.RemoveFinalizer(workspace, constants.CascadingDeletionFinalizer) if err := r.Update(ctx, workspace); err != nil { - return ctrl.Result{}, fmt.Errorf("failed to remove finalizer: %s", err) + return ctrl.Result{}, errors.Wrapf(err, "failed to remove finalizer from workspace %s", workspace.Name) } workspaceOperation.WithLabelValues("delete", workspace.Name).Inc() } @@ -106,19 +105,42 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu // It returns a boolean indicating whether the deletion was successful and an error if any occurred. func (r *Reconciler) workspaceCascadingDeletion(ctx context.Context, workspace *tenantv1beta1.Workspace) (bool, error) { switch workspace.Annotations[constants.DeletionPropagationAnnotation] { - case string(metav1.DeletePropagationOrphan): - // If the deletion propagation policy is "Orphan", return true without deleting namespaces. - return true, nil - case string(metav1.DeletePropagationForeground), string(metav1.DeletePropagationBackground): - // If the deletion propagation policy is "Foreground" or "Background", delete the namespaces. - if err := r.deleteNamespaces(ctx, workspace); err != nil { - return false, fmt.Errorf("failed to delete namespaces in workspace %s: %s", workspace.Name, err) + case string(metav1.DeletePropagationOrphan), "": + if err := r.cleanUpNamespaces(ctx, workspace); err != nil { + return false, errors.Wrapf(err, "failed to clean up namespaces in workspace %s", workspace.Name) + } + case string(metav1.DeletePropagationForeground), string(metav1.DeletePropagationBackground): + if err := r.deleteNamespaces(ctx, workspace); err != nil { + return false, errors.Wrapf(err, "failed to delete namespaces in workspace %s", workspace.Name) } - return true, nil - default: - // If the deletion propagation policy is invalid, return an error. - return false, fmt.Errorf("invalid deletion propagation policy: %s", workspace.Annotations[constants.DeletionPropagationAnnotation]) } + return true, nil +} + +func (r *Reconciler) cleanUpNamespaces(ctx context.Context, workspace *tenantv1beta1.Workspace) error { + namespaces := &corev1.NamespaceList{} + if err := r.List(ctx, namespaces, client.MatchingLabels{tenantv1beta1.WorkspaceLabel: workspace.Name}); err != nil { + return errors.Wrapf(err, "failed to list namespaces in workspace %s", workspace.Name) + } + err := retry.RetryOnConflict(retry.DefaultRetry, func() error { + for _, ns := range namespaces.Items { + if err := r.Get(ctx, client.ObjectKeyFromObject(&ns), &ns); err != nil { + if apierrors.IsNotFound(err) { + continue + } + return err + } + delete(ns.Labels, tenantv1beta1.WorkspaceLabel) + if err := r.Update(ctx, &ns); err != nil { + return err + } + } + return nil + }) + if err != nil { + return errors.Wrapf(err, "failed to clean up namespaces in workspace %s", workspace.Name) + } + return nil } // deleteNamespaces deletes all namespaces associated with the given workspace. @@ -126,14 +148,14 @@ func (r *Reconciler) workspaceCascadingDeletion(ctx context.Context, workspace * func (r *Reconciler) deleteNamespaces(ctx context.Context, workspace *tenantv1beta1.Workspace) error { namespaces := &corev1.NamespaceList{} if err := r.List(ctx, namespaces, client.MatchingLabels{tenantv1beta1.WorkspaceLabel: workspace.Name}); err != nil { - return fmt.Errorf("failed to list namespaces in workspace %s: %s", workspace.Name, err) + return errors.Wrapf(err, "failed to list namespaces in workspace %s", workspace.Name) } for _, ns := range namespaces.Items { if err := r.Delete(ctx, &ns); err != nil { if apierrors.IsNotFound(err) { continue } - return fmt.Errorf("failed to delete namespace %s: %s", ns.Name, err) + return errors.Wrapf(err, "failed to delete namespace %s in workspace %s", ns.Name, workspace.Name) } } return nil diff --git a/pkg/controller/workspace/workspace_controller_suite_test.go b/pkg/controller/workspace/workspace_controller_suite_test.go index 32e712c94..eddb08dc6 100644 --- a/pkg/controller/workspace/workspace_controller_suite_test.go +++ b/pkg/controller/workspace/workspace_controller_suite_test.go @@ -11,19 +11,18 @@ import ( "testing" "time" - "kubesphere.io/kubesphere/pkg/controller/controllertest" - - kscontroller "kubesphere.io/kubesphere/pkg/controller" - . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "k8s.io/klog/v2" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + kscontroller "kubesphere.io/kubesphere/pkg/controller" + "kubesphere.io/kubesphere/pkg/controller/controllertest" "kubesphere.io/kubesphere/pkg/scheme" ) @@ -45,10 +44,9 @@ var _ = BeforeSuite(func() { logf.SetLogger(klog.NewKlogr()) By("bootstrapping test environment") - t := true if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" { testEnv = &envtest.Environment{ - UseExistingCluster: &t, + UseExistingCluster: ptr.To(true), } } else { crdDirPaths, err := controllertest.LoadCrdPath() diff --git a/pkg/controller/workspace/workspace_controller_test.go b/pkg/controller/workspace/workspace_controller_test.go index dd9ae9a47..0861bb1f2 100644 --- a/pkg/controller/workspace/workspace_controller_test.go +++ b/pkg/controller/workspace/workspace_controller_test.go @@ -9,17 +9,18 @@ import ( "context" "time" - "kubesphere.io/kubesphere/pkg/constants" - . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" tenantv1beta1 "kubesphere.io/api/tenant/v1beta1" + + "kubesphere.io/kubesphere/pkg/constants" ) var _ = Describe("Workspace", func() { - const timeout = time.Second * 30 const interval = time.Second * 1 @@ -28,16 +29,24 @@ var _ = Describe("Workspace", func() { // Avoid adding tests for vanilla CRUD operations because they would // test Kubernetes API server, which isn't the goal here. Context("Workspace Controller", func() { - It("Should create successfully", func() { - + It("DeletePropagationBackground", func() { workspace := &tenantv1beta1.Workspace{ ObjectMeta: metav1.ObjectMeta{ - Name: "workspace-test", + Name: "workspace-test1", + }, + } + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "namespace-test1", + Labels: map[string]string{ + tenantv1beta1.WorkspaceLabel: workspace.Name, + }, }, } // Create Expect(k8sClient.Create(context.Background(), workspace)).Should(Succeed()) + Expect(k8sClient.Create(context.Background(), namespace)).Should(Succeed()) By("Expecting to create workspace successfully") Eventually(func() bool { @@ -58,15 +67,6 @@ var _ = Describe("Workspace", func() { return workspace.Spec.Manager == "admin" }, timeout, interval).Should(BeTrue()) - // Delete - By("Expecting to delete workspace successfully") - Eventually(func() error { - if err := k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, workspace); err != nil { - return err - } - return k8sClient.Delete(context.Background(), workspace) - }, timeout, interval).Should(Succeed()) - // Update DeletionPropagation By("Expecting to delete workspace successfully") Eventually(func() error { @@ -80,10 +80,78 @@ var _ = Describe("Workspace", func() { return k8sClient.Update(context.Background(), workspace) }, timeout, interval).Should(Succeed()) - By("Expecting to delete workspace finish") + // Delete workspace + By("Expecting to delete workspace successfully") Eventually(func() error { - return k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, workspace) - }, timeout, interval).ShouldNot(Succeed()) + return k8sClient.Delete(context.Background(), workspace) + }, timeout, interval).Should(Succeed()) + + By("Expecting to cascading deletion finish") + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), types.NamespacedName{Name: namespace.Name}, namespace) + // Deleting a namespace will seem to succeed, but the namespace will just be put in a Terminating state, and never actually be reclaimed. + // Reference: https://book.kubebuilder.io/reference/envtest.html#namespace-usage-limitation + return namespace.Status.Phase == corev1.NamespaceTerminating + }, timeout, interval).Should(BeTrue()) + + By("Expecting to delete workspace finish") + Eventually(func() bool { + return apierrors.IsNotFound(k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, workspace)) + }, timeout, interval).Should(BeTrue()) + }) + It("DeletePropagationOrphan", func() { + workspace := &tenantv1beta1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "workspace-test2", + }, + } + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "namespace-test2", + Labels: map[string]string{ + tenantv1beta1.WorkspaceLabel: workspace.Name, + }, + }, + } + // Create + Expect(k8sClient.Create(context.Background(), workspace)).Should(Succeed()) + Expect(k8sClient.Create(context.Background(), namespace)).Should(Succeed()) + + By("Expecting to create workspace successfully") + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, workspace) + return len(workspace.Finalizers) > 0 + }, timeout, interval).Should(BeTrue()) + + // Update + updated := &tenantv1beta1.Workspace{} + Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, updated)).Should(Succeed()) + updated.Spec.Manager = "admin" + Expect(k8sClient.Update(context.Background(), updated)).Should(Succeed()) + + // List workspace role bindings + By("Expecting to update workspace successfully") + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, workspace) + return workspace.Spec.Manager == "admin" + }, timeout, interval).Should(BeTrue()) + + // Delete workspace + By("Expecting to delete workspace successfully") + Eventually(func() error { + return k8sClient.Delete(context.Background(), workspace) + }, timeout, interval).Should(Succeed()) + + By("Expecting to delete workspace finish") + Eventually(func() bool { + return apierrors.IsNotFound(k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, workspace)) + }, timeout, interval).Should(BeTrue()) + + By("Expecting to cascading deletion finish") + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), types.NamespacedName{Name: namespace.Name}, namespace) + return namespace.Labels[tenantv1beta1.WorkspaceLabel] == "" && namespace.Status.Phase != corev1.NamespaceTerminating + }, timeout, interval).Should(BeTrue()) }) }) }) diff --git a/pkg/controller/workspacetemplate/workspacetemplate_controller.go b/pkg/controller/workspacetemplate/workspacetemplate_controller.go index 7b0890958..36232cb4a 100644 --- a/pkg/controller/workspacetemplate/workspacetemplate_controller.go +++ b/pkg/controller/workspacetemplate/workspacetemplate_controller.go @@ -11,14 +11,13 @@ import ( "fmt" "strings" - "kubesphere.io/kubesphere/pkg/constants" - "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/tools/record" "k8s.io/klog/v2" @@ -33,11 +32,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "kubesphere.io/kubesphere/pkg/constants" kscontroller "kubesphere.io/kubesphere/pkg/controller" "kubesphere.io/kubesphere/pkg/controller/cluster/predicate" clusterutils "kubesphere.io/kubesphere/pkg/controller/cluster/utils" "kubesphere.io/kubesphere/pkg/controller/workspacetemplate/utils" "kubesphere.io/kubesphere/pkg/utils/clusterclient" + "kubesphere.io/kubesphere/pkg/utils/hashutil" ) const ( @@ -231,7 +232,7 @@ func (r *Reconciler) initWorkspaceRoles(ctx context.Context, workspaceTemplate * builtinWorkspaceRole.Kind == iamv1beta1.ResourceKindWorkspaceRole { target := &iamv1beta1.WorkspaceRole{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%s", workspaceTemplate.Name, builtinWorkspaceRole.Name), + Name: ensureWorkspaceRoleName(workspaceTemplate.Name, builtinWorkspaceRole.Name), }, } op, err := controllerutil.CreateOrUpdate(ctx, r.Client, target, func() error { @@ -256,12 +257,21 @@ func (r *Reconciler) initWorkspaceRoles(ctx context.Context, workspaceTemplate * return nil } +func ensureWorkspaceRoleName(workspace, role string) string { + workspaceRoleName := fmt.Sprintf("%s-%s", workspace, role) + if len(workspaceRoleName) <= validation.LabelValueMaxLength { + return workspaceRoleName + } + hashedWorkspaceName := hashutil.FNVString([]byte(workspace)) + return fmt.Sprintf("%s.%s", role, hashedWorkspaceName) +} + func (r *Reconciler) initManagerRoleBinding(ctx context.Context, workspaceTemplate *tenantv1beta1.WorkspaceTemplate) error { manager := workspaceTemplate.Spec.Template.Spec.Manager if manager == "" { return nil } - workspaceAdminRoleName := fmt.Sprintf("%s-admin", workspaceTemplate.Name) + workspaceAdminRoleName := ensureWorkspaceRoleName(workspaceTemplate.Name, "admin") existWorkspaceRoleBinding := &iamv1beta1.WorkspaceRoleBinding{ObjectMeta: metav1.ObjectMeta{Name: workspaceAdminRoleName}} if _, err := ctrl.CreateOrUpdate(ctx, r.Client, existWorkspaceRoleBinding, func() error { existWorkspaceRoleBinding.Labels = map[string]string{ @@ -269,7 +279,6 @@ func (r *Reconciler) initManagerRoleBinding(ctx context.Context, workspaceTempla iamv1beta1.UserReferenceLabel: manager, iamv1beta1.RoleReferenceLabel: workspaceAdminRoleName, } - existWorkspaceRoleBinding.RoleRef = rbacv1.RoleRef{ APIGroup: iamv1beta1.SchemeGroupVersion.Group, Kind: iamv1beta1.ResourceKindWorkspaceRole, @@ -290,13 +299,6 @@ func (r *Reconciler) initManagerRoleBinding(ctx context.Context, workspaceTempla } func (r *Reconciler) workspaceTemplateCascadingDeletion(ctx context.Context, workspaceTemplate *tenantv1beta1.WorkspaceTemplate) (bool, error) { - switch workspaceTemplate.Annotations[constants.DeletionPropagationAnnotation] { - case string(metav1.DeletePropagationOrphan), string(metav1.DeletePropagationForeground), string(metav1.DeletePropagationBackground): - default: - klog.FromContext(ctx).V(4).Info(fmt.Sprintf("waiting for deletion propagation update, invalid deletion propagation policy found: %s", workspaceTemplate.Annotations[constants.DeletionPropagationAnnotation])) - return false, nil - } - clusters, err := r.clusterClientSet.ListClusters(ctx) if err != nil { return false, fmt.Errorf("failed to list clusters: %s", err) @@ -330,12 +332,7 @@ func (r *Reconciler) workspaceCascadingDeletion(ctx context.Context, clusterName if err := clusterClient.Get(ctx, types.NamespacedName{Name: workspaceTemplate.Name}, workspace); err != nil { return client.IgnoreNotFound(err) } - if workspace.DeletionTimestamp.IsZero() { - if err := clusterClient.Delete(ctx, workspace); err != nil { - return fmt.Errorf("failed to delete workspace %s in cluster %s: %s", workspace.Name, clusterName, err) - } - } - if workspace.Annotations[constants.DeletionPropagationAnnotation] == workspaceTemplate.Annotations[constants.DeletionPropagationAnnotation] { + if !workspace.DeletionTimestamp.IsZero() { return nil } if workspace.Annotations == nil { @@ -345,5 +342,8 @@ func (r *Reconciler) workspaceCascadingDeletion(ctx context.Context, clusterName if err := clusterClient.Update(ctx, workspace); err != nil { return fmt.Errorf("failed to update workspace %s in cluster %s: %s", workspace.Name, clusterName, err) } + if err := clusterClient.Delete(ctx, workspace); err != nil { + return fmt.Errorf("failed to delete workspace %s in cluster %s: %s", workspace.Name, clusterName, err) + } return nil } diff --git a/pkg/kapis/tenant/v1beta1/handler.go b/pkg/kapis/tenant/v1beta1/handler.go index f84c69793..ea392a64d 100644 --- a/pkg/kapis/tenant/v1beta1/handler.go +++ b/pkg/kapis/tenant/v1beta1/handler.go @@ -48,7 +48,7 @@ func (h *handler) ListWorkspaces(req *restful.Request, resp *restful.Response) { return } - resp.WriteEntity(result) + _ = resp.WriteEntity(result) } func (h *handler) GetWorkspace(request *restful.Request, response *restful.Response) { @@ -63,7 +63,7 @@ func (h *handler) GetWorkspace(request *restful.Request, response *restful.Respo return } - response.WriteEntity(workspace) + _ = response.WriteEntity(workspace) } func (h *handler) CreateWorkspaceTemplate(req *restful.Request, resp *restful.Response) { @@ -99,23 +99,18 @@ func (h *handler) CreateWorkspaceTemplate(req *restful.Request, resp *restful.Re return } - resp.WriteEntity(created) + _ = resp.WriteEntity(created) } func (h *handler) DeleteWorkspaceTemplate(request *restful.Request, response *restful.Response) { workspace := request.PathParameter("workspace") - opts := metav1.DeleteOptions{} - - err := request.ReadEntity(&opts) - if err != nil { - opts = *metav1.NewDeleteOptions(0) + if err := request.ReadEntity(&opts); err != nil { + api.HandleBadRequest(response, request, err) + return } - err = h.tenant.DeleteWorkspaceTemplate(workspace, opts) - - if err != nil { - klog.Error(err) + if err := h.tenant.DeleteWorkspaceTemplate(workspace, opts); err != nil { if errors.IsNotFound(err) { api.HandleNotFound(response, request, err) return @@ -124,7 +119,7 @@ func (h *handler) DeleteWorkspaceTemplate(request *restful.Request, response *re return } - response.WriteEntity(servererr.None) + _ = response.WriteEntity(servererr.None) } func (h *handler) UpdateWorkspaceTemplate(req *restful.Request, resp *restful.Response) { @@ -173,7 +168,7 @@ func (h *handler) UpdateWorkspaceTemplate(req *restful.Request, resp *restful.Re return } - resp.WriteEntity(updated) + _ = resp.WriteEntity(updated) } func (h *handler) DescribeWorkspaceTemplate(request *restful.Request, response *restful.Response) { @@ -187,7 +182,7 @@ func (h *handler) DescribeWorkspaceTemplate(request *restful.Request, response * api.HandleInternalError(response, request, err) return } - response.WriteEntity(workspace) + _ = response.WriteEntity(workspace) } func (h *handler) PatchWorkspaceTemplate(req *restful.Request, resp *restful.Response) { @@ -227,7 +222,7 @@ func (h *handler) PatchWorkspaceTemplate(req *restful.Request, resp *restful.Res return } - resp.WriteEntity(patched) + _ = resp.WriteEntity(patched) } func (h *handler) ListWorkspaceTemplates(req *restful.Request, resp *restful.Response) { @@ -248,5 +243,5 @@ func (h *handler) ListWorkspaceTemplates(req *restful.Request, resp *restful.Res return } - resp.WriteEntity(result) + _ = resp.WriteEntity(result) }