Skip to content

Commit

Permalink
Merge pull request #2301 from rohantmp/destroyCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Kosiewski authored Dec 2, 2024
2 parents d222404 + 524671e commit e9d61cb
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 27 deletions.
34 changes: 33 additions & 1 deletion cmd/vclusterctl/cmd/platform/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"context"
"errors"
"fmt"
"os"

"github.com/loft-sh/log"
"github.com/loft-sh/log/survey"
"github.com/loft-sh/vcluster/pkg/cli/destroy"
"github.com/loft-sh/vcluster/pkg/cli/flags"
"github.com/loft-sh/vcluster/pkg/cli/start"
Expand Down Expand Up @@ -37,6 +39,9 @@ func NewDestroyCmd(globalFlags *flags.GlobalFlags) *cobra.Command {
Destroys a vCluster Platform instance in your Kubernetes cluster.
IMPORTANT: This action is done against the cluster the the kube-context is pointing to, and not the vCluster Platform instance that is logged in.
It does not require logging in to vCluster Platform.
Please make sure you meet the following requirements
before running this command:
Expand All @@ -59,14 +64,15 @@ VirtualClusterInstances managed with driver helm will be deleted, but the underl
destroyCmd.Flags().BoolVar(&cmd.DeleteNamespace, "delete-namespace", true, "Whether to delete the namespace or not")
destroyCmd.Flags().BoolVar(&cmd.IgnoreNotFound, "ignore-not-found", false, "Exit successfully if platform installation is not found")
destroyCmd.Flags().BoolVar(&cmd.Force, "force", false, "Try uninstalling even if the platform is not installed. '--namespace' is required if true")
destroyCmd.Flags().BoolVar(&cmd.NonInteractive, "non-interactive", false, "Will not prompt for confirmation")
destroyCmd.Flags().IntVar(&cmd.TimeoutMinutes, "timeout-minutes", 5, "How long to try deleting the platform before giving up")

return destroyCmd
}

func (cmd *DestroyCmd) Run(ctx context.Context) error {
// initialise clients, verify binaries exist, sanity-check context
err := cmd.Options.Prepare()
err := cmd.Options.Prepare(cmd.NonInteractive)
if err != nil {
return fmt.Errorf("failed to prepare clients: %w", err)
}
Expand Down Expand Up @@ -97,9 +103,35 @@ func (cmd *DestroyCmd) Run(ctx context.Context) error {
return fmt.Errorf("platform not installed in namespace %q", cmd.Namespace)
}

if !cmd.NonInteractive {
deleteOpt := "delete"
out, err := cmd.Log.Question(&survey.QuestionOptions{
Question: fmt.Sprintf("IMPORTANT! You are destroy the vCluster Platform in the namespace %q.\nThis may result in data loss. Please ensure your kube-context is pointed at the right cluster.\n Please type %q to continue:", cmd.Namespace, deleteOpt),
})
if err != nil {
return fmt.Errorf("failed to prompt for confirmation: %w", err)
}
if out != deleteOpt {
cmd.Log.Info("destroy cancelled")
return nil
}
}

err = destroy.Destroy(ctx, cmd.DeleteOptions)
if err != nil {
return fmt.Errorf("failed to destroy platform: %w", err)
}

cmd.Log.Infof("deleting platform config at %q", cmd.Config)
cliConfig := cmd.LoadedConfig(cmd.Log)
err = cliConfig.Delete()
if err != nil {
if errors.Is(err, os.ErrNotExist) && cmd.IgnoreNotFound {
cmd.Log.Info("no platform config detected")
return nil
}
return fmt.Errorf("failed to delete platform config: %w", err)
}

return nil
}
41 changes: 41 additions & 0 deletions pkg/cli/config/config.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package config

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -53,6 +55,45 @@ func (c *CLI) Save() error {
return Write(path, c)
}

func (c *CLI) Delete() error {
if c == nil || c.path == "" {
return errors.New("nil config path")
}

file, err := os.Open(c.path)
if err != nil {
return fmt.Errorf("failed to load vcluster configuration file from %q : %w", c.path, err)
}
stat, err := file.Stat()
if err != nil {
return fmt.Errorf("failed to load vcluster configuration file from %q: %w", c.path, err)
}
if stat.IsDir() {
return fmt.Errorf("failed to load vcluster configuration file %q", c.path)
}
defer file.Close()

fileBytes, err := io.ReadAll(file)
if err != nil {
return fmt.Errorf("read all: %w", err)
}

decoder := json.NewDecoder(bytes.NewReader(fileBytes))
decoder.DisallowUnknownFields()
tryRead := &CLI{}
err = decoder.Decode(tryRead)
if err != nil {
return fmt.Errorf("failed to unmarshall vcluster configuration from %q: %w", c.path, err)
}

// delete file at path
err = os.Remove(c.path)
if err != nil {
return fmt.Errorf("failed to delete configuration file at %q: %w", c.path, err)
}
return nil
}

// Read returns the current config by trying to read it from the given config path.
// It returns a new default config if there have been any errors during the read.
func Read(path string, log log.Logger) *CLI {
Expand Down
33 changes: 17 additions & 16 deletions pkg/cli/destroy/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type DeleteOptions struct {
DeleteNamespace bool
IgnoreNotFound bool
Force bool
NonInteractive bool
TimeoutMinutes int
}

Expand Down Expand Up @@ -152,22 +153,6 @@ func Destroy(ctx context.Context, opts DeleteOptions) error {
return err
}

for _, name := range clihelper.DefaultClusterRoles {
name := name + "-binding"
opts.Log.Infof("deleting clusterrolebinding %q", name)
err := opts.KubeClient.RbacV1().ClusterRoleBindings().Delete(ctx, name+"-binding", metav1.DeleteOptions{})
if err != nil && !kerrors.IsNotFound(err) {
return fmt.Errorf("failed to delete clusterrole: %w", err)
}
}
for _, name := range clihelper.DefaultClusterRoles {
opts.Log.Infof("deleting clusterrole %q", name)
err := opts.KubeClient.RbacV1().ClusterRoles().Delete(ctx, name, metav1.DeleteOptions{})
if err != nil && !kerrors.IsNotFound(err) {
return fmt.Errorf("failed to delete clusterrole: %w", err)
}
}

opts.Log.Info("deleting CRDS")
err = wait.ExponentialBackoffWithContext(ctx, wait.Backoff{Duration: time.Second, Factor: backoffFactor, Cap: time.Duration(opts.TimeoutMinutes) * time.Minute, Steps: math.MaxInt32}, func(ctx context.Context) (bool, error) {
list, err := apiextensionclientset.ApiextensionsV1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{})
Expand Down Expand Up @@ -236,6 +221,22 @@ func Destroy(ctx context.Context, opts DeleteOptions) error {
}
}

for _, name := range clihelper.DefaultClusterRoles {
name := name + "-binding"
opts.Log.Infof("deleting clusterrolebinding %q", name)
err := opts.KubeClient.RbacV1().ClusterRoleBindings().Delete(ctx, name, metav1.DeleteOptions{})
if err != nil && !kerrors.IsNotFound(err) {
return fmt.Errorf("failed to delete clusterrole: %w", err)
}
}
for _, name := range clihelper.DefaultClusterRoles {
opts.Log.Infof("deleting clusterrole %q", name)
err := opts.KubeClient.RbacV1().ClusterRoles().Delete(ctx, name, metav1.DeleteOptions{})
if err != nil && !kerrors.IsNotFound(err) {
return fmt.Errorf("failed to delete clusterrole: %w", err)
}
}

return nil
}

Expand Down
21 changes: 12 additions & 9 deletions pkg/cli/start/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func (l *LoftStarter) Start(ctx context.Context) error {
l.LocalPort = "9898"
}

err := l.Prepare()
err := l.Prepare(false)
if err != nil {
return err
}
Expand Down Expand Up @@ -141,7 +141,7 @@ func (l *LoftStarter) Start(ctx context.Context) error {
}

// Prepare initializes clients, verifies the existense of binaries, and ensures we are starting with the right kube context
func (l *Options) Prepare() error {
func (l *Options) Prepare(cliNonInteractive bool) error {
platformClient := platform.NewClientFromConfig(l.LoadedConfig(l.Log))

platformConfig := platformClient.Config().Platform
Expand All @@ -160,15 +160,18 @@ func (l *Options) Prepare() error {
if l.Context != "" {
contextToLoad = l.Context
} else if platformConfig.LastInstallContext != "" && platformConfig.LastInstallContext != contextToLoad {
contextToLoad, err = l.Log.Question(&survey.QuestionOptions{
Question: product.Replace(fmt.Sprintf("Seems like you try to use 'vcluster %s' with a different kubernetes context than before. Please choose which kubernetes context you want to use", l.CommandName)),
DefaultValue: contextToLoad,
Options: []string{contextToLoad, platformConfig.LastInstallContext},
})
if err != nil {
return err
if !cliNonInteractive {
contextToLoad, err = l.Log.Question(&survey.QuestionOptions{
Question: product.Replace(fmt.Sprintf("Seems like you try to use 'vcluster %s' with a different kubernetes context than before. Please choose which kubernetes context you want to use", l.CommandName)),
DefaultValue: contextToLoad,
Options: []string{contextToLoad, platformConfig.LastInstallContext},
})
if err != nil {
return err
}
}
}

l.Context = contextToLoad

platformConfig.LastInstallContext = contextToLoad
Expand Down
4 changes: 4 additions & 0 deletions pkg/platform/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ func (c *client) Save() error {
return c.config.Save()
}

func (c *client) Delete() error {
return c.config.Delete()
}

func (c *client) ManagementConfig() (*rest.Config, error) {
return c.restConfig("/kubernetes/management")
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/platform/clihelper/clihelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ var defaultDeploymentName = "loft"

var DefaultClusterRoles = []string{
"loft-agent-cluster",
"loft-runnner-cluster",
"loft-runner-cluster",
"loft-vcluster-cluster",
"loft-cluster-authenticated",
"loft-management-authenticated",
}

func Timeout() time.Duration {
Expand Down

0 comments on commit e9d61cb

Please sign in to comment.