Skip to content

Commit

Permalink
karmadactl unregister: force unregister pull mode member clusters wit…
Browse files Browse the repository at this point in the history
…h the flag --force

Signed-off-by: zhzhuang-zju <m17799853869@163.com>
  • Loading branch information
zhzhuang-zju committed Dec 11, 2024
1 parent ff7dc4a commit 10e98ac
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 65 deletions.
9 changes: 6 additions & 3 deletions pkg/karmadactl/unregister/unregister.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ type CommandUnregisterOption struct {

// rbacResources contains RBAC resources that grant the necessary permissions for the unregistering cluster to access to Karmada control plane.
rbacResources *register.RBACResources

forceDeletion bool
}

// AddFlags adds flags to the specified FlagSet.
Expand All @@ -152,6 +154,8 @@ func (j *CommandUnregisterOption) AddFlags(flags *pflag.FlagSet) {
flags.StringVar(&j.ClusterNamespace, "cluster-namespace", options.DefaultKarmadaClusterNamespace, "Namespace in the control plane where member cluster secrets are stored.")
flags.DurationVar(&j.Wait, "wait", 60*time.Second, "wait for the unjoin command execution process(default 60s), if there is no success after this time, timeout will be returned.")
flags.BoolVar(&j.DryRun, "dry-run", false, "Run the command in dry-run mode, without making any server requests.")
flags.BoolVar(&j.forceDeletion, "force", false,
"When set, the unregister command will attempt to clean up resources in the member cluster before deleting the Cluster object. If the cleanup fails within the timeout period, the Cluster object will still be deleted, potentially leaving some resources behind in the member cluster.")
}

// Complete ensures that options are valid and marshals them if necessary.
Expand Down Expand Up @@ -318,16 +322,15 @@ func (j *CommandUnregisterOption) RunUnregisterCluster() error {
// 1. delete the work object from the Karmada control plane
// When deleting a cluster, the deletion triggers the removal of executionSpace, which can lead to the deletion of RBAC roles related to work.
// Therefore, the deletion of work should be performed before deleting the cluster.
err := cmdutil.EnsureWorksDeleted(j.ControlPlaneClient, names.GenerateExecutionSpaceName(j.ClusterName), j.Wait)
err := cmdutil.EnsureWorksDeleted(j.ControlPlaneClient, names.GenerateExecutionSpaceName(j.ClusterName), j.Wait, j.forceDeletion)
if err != nil {
klog.Errorf("Failed to delete works object. cluster name: %s, error: %v", j.ClusterName, err)
return err
}
j.Wait = j.Wait - time.Since(start)

// 2. delete the cluster object from the Karmada control plane
//TODO: add flag --force to implement force deletion.
if err = cmdutil.DeleteClusterObject(j.ControlPlaneKubeClient, j.ControlPlaneClient, j.ClusterName, j.Wait, j.DryRun, false); err != nil {
if err = cmdutil.DeleteClusterObject(j.ControlPlaneKubeClient, j.ControlPlaneClient, j.ClusterName, j.Wait, j.DryRun, j.forceDeletion); err != nil {
klog.Errorf("Failed to delete cluster object. cluster name: %s, error: %v", j.ClusterName, err)
return err
}
Expand Down
64 changes: 3 additions & 61 deletions pkg/karmadactl/util/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,13 @@ import (
"fmt"
"time"

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/util/wait"
kubeclient "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
"github.com/karmada-io/karmada/pkg/util"
"github.com/karmada-io/karmada/pkg/util/names"
)

Expand Down Expand Up @@ -73,17 +70,17 @@ func DeleteClusterObject(controlPlaneKubeClient kubeclient.Interface, controlPla
klog.Warningf("Deleting the cluster object timed out. cluster name: %s, error: %v", clusterName, err)
klog.Infof("Start forced deletion. cluster name: %s", clusterName)
executionSpaceName := names.GenerateExecutionSpaceName(clusterName)
err = removeWorkFinalizer(executionSpaceName, controlPlaneKarmadaClient)
err = RemoveWorkFinalizer(executionSpaceName, controlPlaneKarmadaClient)
if err != nil {
klog.Errorf("Force deletion. Failed to remove the finalizer of Work, error: %v", err)
}

err = removeExecutionSpaceFinalizer(executionSpaceName, controlPlaneKubeClient)
err = RemoveExecutionSpaceFinalizer(executionSpaceName, controlPlaneKubeClient)
if err != nil {
klog.Errorf("Force deletion. Failed to remove the finalizer of Namespace(%s), error: %v", executionSpaceName, err)
}

err = removeClusterFinalizer(clusterName, controlPlaneKarmadaClient)
err = RemoveClusterFinalizer(clusterName, controlPlaneKarmadaClient)
if err != nil {
klog.Errorf("Force deletion. Failed to remove the finalizer of Cluster(%s), error: %v", clusterName, err)
}
Expand All @@ -94,58 +91,3 @@ func DeleteClusterObject(controlPlaneKubeClient kubeclient.Interface, controlPla

return err
}

// removeWorkFinalizer removes the finalizer of works in the executionSpace.
func removeWorkFinalizer(executionSpaceName string, controlPlaneKarmadaClient karmadaclientset.Interface) error {
list, err := controlPlaneKarmadaClient.WorkV1alpha1().Works(executionSpaceName).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return fmt.Errorf("failed to list work in executionSpace %s", executionSpaceName)
}

for i := range list.Items {
work := &list.Items[i]
if !controllerutil.ContainsFinalizer(work, util.ExecutionControllerFinalizer) {
continue
}
controllerutil.RemoveFinalizer(work, util.ExecutionControllerFinalizer)
_, err = controlPlaneKarmadaClient.WorkV1alpha1().Works(executionSpaceName).Update(context.TODO(), work, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("failed to remove the finalizer of work(%s/%s)", executionSpaceName, work.GetName())
}
}
return nil
}

// removeExecutionSpaceFinalizer removes the finalizer of executionSpace.
func removeExecutionSpaceFinalizer(executionSpaceName string, controlPlaneKubeClient kubeclient.Interface) error {
executionSpace, err := controlPlaneKubeClient.CoreV1().Namespaces().Get(context.TODO(), executionSpaceName, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get Namespace(%s)", executionSpaceName)
}

if !controllerutil.ContainsFinalizer(executionSpace, string(corev1.FinalizerKubernetes)) {
return nil
}

controllerutil.RemoveFinalizer(executionSpace, "kubernetes")
_, err = controlPlaneKubeClient.CoreV1().Namespaces().Update(context.TODO(), executionSpace, metav1.UpdateOptions{})

return err
}

// removeClusterFinalizer removes the finalizer of cluster object.
func removeClusterFinalizer(clusterName string, controlPlaneKarmadaClient karmadaclientset.Interface) error {
cluster, err := controlPlaneKarmadaClient.ClusterV1alpha1().Clusters().Get(context.TODO(), clusterName, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get Cluster(%s)", clusterName)
}

if !controllerutil.ContainsFinalizer(cluster, util.ClusterControllerFinalizer) {
return nil
}

controllerutil.RemoveFinalizer(cluster, util.ClusterControllerFinalizer)
_, err = controlPlaneKarmadaClient.ClusterV1alpha1().Clusters().Update(context.TODO(), cluster, metav1.UpdateOptions{})

return err
}
85 changes: 85 additions & 0 deletions pkg/karmadactl/util/finalizer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Copyright 2024 The Karmada Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
"context"
"fmt"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeclient "k8s.io/client-go/kubernetes"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
"github.com/karmada-io/karmada/pkg/util"
)

// RemoveWorkFinalizer removes the finalizer of works in the executionSpace.
func RemoveWorkFinalizer(executionSpaceName string, controlPlaneKarmadaClient karmadaclientset.Interface) error {
list, err := controlPlaneKarmadaClient.WorkV1alpha1().Works(executionSpaceName).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return fmt.Errorf("failed to list work in executionSpace %s", executionSpaceName)
}

for i := range list.Items {
work := &list.Items[i]
if !controllerutil.ContainsFinalizer(work, util.ExecutionControllerFinalizer) {
continue
}
controllerutil.RemoveFinalizer(work, util.ExecutionControllerFinalizer)
_, err = controlPlaneKarmadaClient.WorkV1alpha1().Works(executionSpaceName).Update(context.TODO(), work, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("failed to remove the finalizer of work(%s/%s)", executionSpaceName, work.GetName())
}
}
return nil
}

// RemoveExecutionSpaceFinalizer removes the finalizer of executionSpace.
func RemoveExecutionSpaceFinalizer(executionSpaceName string, controlPlaneKubeClient kubeclient.Interface) error {
executionSpace, err := controlPlaneKubeClient.CoreV1().Namespaces().Get(context.TODO(), executionSpaceName, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get Namespace(%s)", executionSpaceName)
}

if !controllerutil.ContainsFinalizer(executionSpace, string(corev1.FinalizerKubernetes)) {
return nil
}

controllerutil.RemoveFinalizer(executionSpace, "kubernetes")
_, err = controlPlaneKubeClient.CoreV1().Namespaces().Update(context.TODO(), executionSpace, metav1.UpdateOptions{})

return err
}

// RemoveClusterFinalizer removes the finalizer of cluster object.
func RemoveClusterFinalizer(clusterName string, controlPlaneKarmadaClient karmadaclientset.Interface) error {
cluster, err := controlPlaneKarmadaClient.ClusterV1alpha1().Clusters().Get(context.TODO(), clusterName, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get Cluster(%s)", clusterName)
}

if !controllerutil.ContainsFinalizer(cluster, util.ClusterControllerFinalizer) {
return nil
}

controllerutil.RemoveFinalizer(cluster, util.ClusterControllerFinalizer)
_, err = controlPlaneKarmadaClient.ClusterV1alpha1().Clusters().Update(context.TODO(), cluster, metav1.UpdateOptions{})

return err
}
Loading

0 comments on commit 10e98ac

Please sign in to comment.