Skip to content

Commit ad7f975

Browse files
authored
fix: add --docker to vcluster platform destroy (loft-sh#3492)
1 parent f5959b4 commit ad7f975

File tree

3 files changed

+189
-1
lines changed

3 files changed

+189
-1
lines changed

cmd/vclusterctl/cmd/platform/destroy.go

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/loft-sh/log"
1010
"github.com/loft-sh/log/survey"
1111
"github.com/loft-sh/log/terminal"
12+
"github.com/loft-sh/vcluster/pkg/cli/config"
1213
"github.com/loft-sh/vcluster/pkg/cli/destroy"
1314
"github.com/loft-sh/vcluster/pkg/cli/flags"
1415
"github.com/loft-sh/vcluster/pkg/cli/start"
@@ -18,6 +19,7 @@ import (
1819

1920
type DestroyCmd struct {
2021
destroy.DeleteOptions
22+
Docker bool
2123
}
2224

2325
func NewDestroyCmd(globalFlags *flags.GlobalFlags) *cobra.Command {
@@ -38,7 +40,7 @@ func NewDestroyCmd(globalFlags *flags.GlobalFlags) *cobra.Command {
3840
############# vcluster platform destroy ##################
3941
########################################################
4042
41-
Destroys a vCluster Platform instance in your Kubernetes cluster.
43+
Destroys a vCluster Platform instance in your Kubernetes cluster or Docker.
4244
4345
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.
4446
It does not require logging in to vCluster Platform.
@@ -49,6 +51,7 @@ before running this command:
4951
1. Current kube-context has admin access to the cluster
5052
2. Helm v3 must be installed
5153
54+
If you installed vCluster platform using '--docker', use 'vcluster platform destroy --docker' to clean up the Docker container and volume.
5255
5356
VirtualClusterInstances managed with driver helm will be deleted, but the underlying virtual cluster will not be uninstalled.
5457
@@ -71,11 +74,24 @@ VirtualClusterInstances managed with driver helm will be deleted, but the underl
7174
destroyCmd.Flags().BoolVar(&cmd.NonInteractive, "non-interactive", false, "Will not prompt for confirmation")
7275
destroyCmd.Flags().IntVar(&cmd.TimeoutMinutes, "timeout-minutes", 5, "How long to try deleting the platform before giving up. May increase when removing finalizers if --force-remove-finalizers is used")
7376
destroyCmd.Flags().BoolVar(&cmd.ForceRemoveFinalizers, "force-remove-finalizers", false, "IMPORTANT! Removing finalizers may cause unintended behaviours like leaving resources behind, but will ensure the platform is uninstalled.")
77+
destroyCmd.Flags().BoolVar(&cmd.Docker, "docker", false, "If true, destroys the vCluster platform docker container and volume instead of the Kubernetes installation")
7478

7579
return destroyCmd
7680
}
7781

7882
func (cmd *DestroyCmd) Run(ctx context.Context) error {
83+
// automatically use docker mode if the driver is set to docker
84+
cfg := cmd.LoadedConfig(cmd.Log)
85+
if cfg.Driver.Type == config.DockerDriver && !cmd.Docker {
86+
cmd.Log.Info("Automatically using --docker flag because driver is set to 'docker'")
87+
cmd.Docker = true
88+
}
89+
90+
// handle docker destruction
91+
if cmd.Docker {
92+
return cmd.destroyDocker(ctx)
93+
}
94+
7995
// initialise clients, verify binaries exist, sanity-check context
8096
err := cmd.Options.Prepare()
8197
if err != nil {
@@ -153,3 +169,37 @@ func (cmd *DestroyCmd) Run(ctx context.Context) error {
153169

154170
return nil
155171
}
172+
173+
func (cmd *DestroyCmd) destroyDocker(ctx context.Context) error {
174+
if terminal.IsTerminalIn && !cmd.NonInteractive {
175+
deleteOpt := "delete"
176+
out, err := cmd.Log.Question(&survey.QuestionOptions{
177+
Question: fmt.Sprintf("IMPORTANT! You are destroying the vCluster Platform docker installation.\n This will remove the container and all associated data.\nPlease type %q to continue:", deleteOpt),
178+
})
179+
if err != nil {
180+
return fmt.Errorf("failed to prompt for confirmation: %w", err)
181+
}
182+
if out != deleteOpt {
183+
cmd.Log.Info("destroy cancelled")
184+
return nil
185+
}
186+
}
187+
188+
err := destroy.DestroyDocker(ctx, cmd.IgnoreNotFound, cmd.Log)
189+
if err != nil {
190+
return fmt.Errorf("failed to destroy docker platform: %w", err)
191+
}
192+
193+
cmd.Log.Infof("deleting platform config at %q", cmd.Config)
194+
cliConfig := cmd.LoadedConfig(cmd.Log)
195+
err = cliConfig.Delete()
196+
if err != nil {
197+
if errors.Is(err, os.ErrNotExist) && cmd.IgnoreNotFound {
198+
cmd.Log.Info("no platform config detected")
199+
return nil
200+
}
201+
return fmt.Errorf("failed to delete platform config: %w", err)
202+
}
203+
204+
return nil
205+
}

cmd/vclusterctl/cmd/platform/start.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/loft-sh/log"
1111
"github.com/loft-sh/log/survey"
1212
"github.com/loft-sh/log/terminal"
13+
"github.com/loft-sh/vcluster/pkg/cli/config"
1314
"github.com/loft-sh/vcluster/pkg/cli/email"
1415
"github.com/loft-sh/vcluster/pkg/cli/find"
1516
"github.com/loft-sh/vcluster/pkg/cli/flags"
@@ -86,6 +87,13 @@ before running this command:
8687
}
8788

8889
func (cmd *StartCmd) Run(ctx context.Context) error {
90+
// automatically use docker mode if the driver is set to docker
91+
cfg := cmd.LoadedConfig(cmd.Log)
92+
if cfg.Driver.Type == config.DockerDriver && !cmd.Docker {
93+
cmd.Log.Info("Automatically using --docker flag because driver is set to 'docker'")
94+
cmd.Docker = true
95+
}
96+
8997
// get the version to deploy
9098
if cmd.Version == "latest" || cmd.Version == "" {
9199
cmd.Version = platform.MinimumVersionTag

pkg/cli/destroy/destroy_docker.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package destroy
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"os/exec"
8+
"strings"
9+
10+
"github.com/loft-sh/log"
11+
)
12+
13+
const (
14+
// PlatformContainerName is the name of the docker container for vCluster platform
15+
PlatformContainerName = "vcluster-platform"
16+
// PlatformVolumeName is the name of the docker volume for vCluster platform
17+
PlatformVolumeName = "vcluster-platform"
18+
)
19+
20+
// ErrDockerPlatformNotFound is returned when no docker platform installation is found
21+
var ErrDockerPlatformNotFound = fmt.Errorf("no vCluster platform docker installation found (no container or volume)")
22+
23+
// DestroyDocker stops and removes the vCluster platform docker container and volume.
24+
// If ignoreNotFound is false and no container or volume is found, it returns ErrDockerPlatformNotFound.
25+
func DestroyDocker(ctx context.Context, ignoreNotFound bool, log log.Logger) error {
26+
// check if docker is available
27+
_, err := exec.LookPath("docker")
28+
if err != nil {
29+
return fmt.Errorf("docker is not installed or not available in PATH: %w", err)
30+
}
31+
32+
// check if docker daemon is running
33+
output, err := exec.CommandContext(ctx, "docker", "ps").CombinedOutput()
34+
if err != nil {
35+
return fmt.Errorf("docker daemon is not running or not accessible: %s", string(output))
36+
}
37+
38+
// track if we found anything to destroy
39+
foundContainer := false
40+
foundVolume := false
41+
42+
// check if container exists
43+
containerID, err := findContainer(ctx, PlatformContainerName)
44+
if err != nil {
45+
return fmt.Errorf("failed to find container: %w", err)
46+
}
47+
48+
if containerID != "" {
49+
foundContainer = true
50+
// stop the container
51+
log.Infof("Stopping vCluster platform container %s...", PlatformContainerName)
52+
out, err := exec.CommandContext(ctx, "docker", "stop", containerID).CombinedOutput()
53+
if err != nil {
54+
return fmt.Errorf("failed to stop container: %w: %s", err, string(out))
55+
}
56+
57+
// remove the container
58+
log.Infof("Removing vCluster platform container %s...", PlatformContainerName)
59+
out, err = exec.CommandContext(ctx, "docker", "rm", containerID).CombinedOutput()
60+
if err != nil {
61+
return fmt.Errorf("failed to remove container: %w: %s", err, string(out))
62+
}
63+
}
64+
65+
// check if volume exists and remove it
66+
hasVolume, err := volumeExists(ctx, PlatformVolumeName)
67+
if err != nil {
68+
return fmt.Errorf("failed to check if volume exists: %w", err)
69+
}
70+
71+
if hasVolume {
72+
foundVolume = true
73+
log.Infof("Removing vCluster platform volume %s...", PlatformVolumeName)
74+
out, err := exec.CommandContext(ctx, "docker", "volume", "rm", PlatformVolumeName).CombinedOutput()
75+
if err != nil {
76+
return fmt.Errorf("failed to remove volume: %w: %s", err, string(out))
77+
}
78+
}
79+
80+
// check if anything was found
81+
if !foundContainer && !foundVolume {
82+
if ignoreNotFound {
83+
log.Info("No vCluster platform docker installation found")
84+
return nil
85+
}
86+
return ErrDockerPlatformNotFound
87+
}
88+
89+
log.Info("Successfully destroyed vCluster platform docker installation")
90+
return nil
91+
}
92+
93+
// findContainer finds a container by name and returns its ID
94+
func findContainer(ctx context.Context, name string) (string, error) {
95+
out, err := exec.CommandContext(ctx, "docker", "ps", "-q", "-a", "-f", "name=^"+name+"$").CombinedOutput()
96+
if err != nil {
97+
return "", wrapCommandError(out, err)
98+
}
99+
100+
containerID := strings.TrimSpace(string(out))
101+
return containerID, nil
102+
}
103+
104+
// volumeExists checks if a docker volume exists
105+
func volumeExists(ctx context.Context, name string) (bool, error) {
106+
out, err := exec.CommandContext(ctx, "docker", "volume", "ls", "-q", "-f", "name=^"+name+"$").CombinedOutput()
107+
if err != nil {
108+
return false, wrapCommandError(out, err)
109+
}
110+
111+
return strings.TrimSpace(string(out)) != "", nil
112+
}
113+
114+
func wrapCommandError(stdout []byte, err error) error {
115+
if err == nil {
116+
return nil
117+
}
118+
119+
message := ""
120+
if len(stdout) > 0 {
121+
message += string(stdout) + "\n"
122+
}
123+
124+
var exitError *exec.ExitError
125+
if errors.As(err, &exitError) && exitError != nil && len(exitError.Stderr) > 0 {
126+
message += string(exitError.Stderr) + "\n"
127+
}
128+
129+
return fmt.Errorf("%s%w", message, err)
130+
}

0 commit comments

Comments
 (0)