Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce reset flag and routine #65

Merged
merged 2 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ARCH="$(shell go env GOARCH)"
ARM=""
VERSION="latest"

.PHONY: pre dev build release image image-arm-6 image-arm-7 image-multiarch clean
.PHONY: pre dev build release image image-arm-6 image-arm-7 image-multiarch clean reset

dist := dist
bin := $(shell basename $(CURDIR))
Expand Down Expand Up @@ -53,4 +53,7 @@ image-multiarch:
clean:
rm -rf $(dist)/*
rm -rf /opt/dev-toolkit/k2d/*
rm -rf /var/lib/k2d
rm -rf /var/lib/k2d

reset: build
$(dist)/$(bin) -reset
34 changes: 34 additions & 0 deletions cmd/k2d.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package main

import (
"context"
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"path"

restfulspec "github.com/emicklei/go-restful-openapi/v2"
Expand All @@ -24,6 +26,7 @@ import (
"github.com/portainer/k2d/internal/token"
"github.com/portainer/k2d/internal/types"
"github.com/portainer/k2d/pkg/network"
"github.com/portainer/k2d/pkg/prompt"
"github.com/sethvargo/go-envconfig"
)

Expand All @@ -38,6 +41,9 @@ func getAdvertiseIpAddr(advertiseAddr string) (net.IP, error) {
func main() {
ctx := context.Background()

resetMode := flag.Bool("reset", false, "Reset this host by removing all resources created by k2d and created via k2d")
flag.Parse()

var cfg config.Config
if err := envconfig.Process(ctx, &cfg); err != nil {
log.Fatalf("unable to parse configuration: %s", err)
Expand All @@ -49,6 +55,34 @@ func main() {
}
defer logger.Sync()

if *resetMode {
fmt.Println("Are you sure you want to this host? This will remove everything created by or via k2d including workload and data. y/N")
confirm, err := prompt.AskForConfirmation()
if err != nil {
logger.Fatalf("unable to ask for confirmation: %s", err)
}

if confirm {
kubeDockerAdapterOptions := &adapter.KubeDockerAdapterOptions{
K2DConfig: &cfg,
Logger: logger,
ServerConfiguration: nil,
}

kubeDockerAdapter, err := adapter.NewKubeDockerAdapter(kubeDockerAdapterOptions)
if err != nil {
logger.Fatalf("unable to create docker adapter: %s", err)
}

err = kubeDockerAdapter.ExecuteResetRoutine(ctx, cfg.DataPath)
if err != nil {
logger.Fatalf("an error occured during reset routine: %s", err)
}
}

os.Exit(0)
}

// We add the logger to the main context
ctx = logging.ContextWithLogger(ctx, logger)

Expand Down
177 changes: 177 additions & 0 deletions internal/adapter/reset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package adapter

import (
"context"
"fmt"

"github.com/portainer/k2d/pkg/filesystem"
"k8s.io/apimachinery/pkg/labels"
)

// ExecuteResetRoutine performs a cleanup routine that removes all k2d resources from the host,
// as well as content within the specified k2d data directory.
// This function is intended to be used as a "reset mode" operation for cleaning up any resources
// managed by k2d on the host.
//
// Parameters:
// - ctx context.Context: The context for carrying out the reset routine.
// - k2dDataPath string: The path to the k2d data directory that needs to be cleaned up.
//
// Returns:
// - error: Returns an error if any of the resource removal or file system operations fail.
//
// Steps:
// 1. Removes all workload resources (like deployments, pods) by invoking removeAllWorkloads.
// 2. Removes all Persistent Volumes and Persistent Volume Claims by invoking removeAllPersistentVolumeAndClaims.
// 3. Removes all ConfigMaps and Secrets by invoking removeAllConfigMapsAndSecrets.
// 4. Removes all namespaces by invoking removeAllNamespaces.
// 5. Removes all content in the k2d data directory by invoking filesystem.RemoveAllContent.
func (adapter *KubeDockerAdapter) ExecuteResetRoutine(ctx context.Context, k2dDataPath string) error {
adapter.logger.Infoln("reset mode enabled, removing all k2d resources on this host")

err := adapter.removeAllWorkloads(ctx)
if err != nil {
return fmt.Errorf("unable to remove workloads: %w", err)
}

err = adapter.removeAllPersistentVolumeAndClaims(ctx)
if err != nil {
return fmt.Errorf("unable to remove persistent volumes and persistent volume claims: %w", err)
}

err = adapter.removeAllConfigMapsAndSecrets(ctx)
if err != nil {
return fmt.Errorf("unable to remove configmaps and secrets: %w", err)
}

err = adapter.removeAllNamespaces(ctx)
if err != nil {
return fmt.Errorf("unable to remove namespaces: %w", err)
}

adapter.logger.Infoln("removing k2d data directory content...")

err = filesystem.RemoveAllContent(k2dDataPath)
if err != nil {
return fmt.Errorf("unable to remove k2d data directory content: %w", err)
}

adapter.logger.Infoln("reset routine completed")
return nil
}

func (adapter *KubeDockerAdapter) removeAllWorkloads(ctx context.Context) error {
adapter.logger.Infoln("removing all workloads (deployments, pods)...")

deployments, err := adapter.ListDeployments(ctx, "")
if err != nil {
return fmt.Errorf("unable to list deployments: %w", err)
}

for _, deployment := range deployments.Items {
adapter.logger.Infof("removing deployment %s/%s", deployment.Namespace, deployment.Name)
adapter.DeleteContainer(ctx, deployment.Name, deployment.Namespace)
}

pods, err := adapter.ListPods(ctx, "")
if err != nil {
return fmt.Errorf("unable to list pods: %w", err)
}

for _, pod := range pods.Items {
adapter.logger.Infof("removing pod %s/%s", pod.Namespace, pod.Name)
adapter.DeleteContainer(ctx, pod.Name, pod.Namespace)
}

return nil
}

func (adapter *KubeDockerAdapter) removeAllPersistentVolumeAndClaims(ctx context.Context) error {
adapter.logger.Infoln("removing all persistent volumes and persistent volume claims...")

persistentVolumes, err := adapter.ListPersistentVolumes(ctx)
if err != nil {
return fmt.Errorf("unable to list persistent volumes: %w", err)
}

for _, persistentVolume := range persistentVolumes.Items {
adapter.logger.Infof("removing persistent volume %s", persistentVolume.Name)

err = adapter.DeletePersistentVolume(ctx, persistentVolume.Name)
if err != nil {
adapter.logger.Warnf("unable to remove persistent volume %s: %s", persistentVolume.Name, err)
}
}

persistentVolumeClaims, err := adapter.ListPersistentVolumeClaims(ctx, "")
if err != nil {
return fmt.Errorf("unable to list persistent volume claims: %w", err)
}

for _, persistentVolumeClaim := range persistentVolumeClaims.Items {
adapter.logger.Infof("removing persistent volume claim %s/%s", persistentVolumeClaim.Namespace, persistentVolumeClaim.Name)

err = adapter.DeletePersistentVolumeClaim(ctx, persistentVolumeClaim.Name, persistentVolumeClaim.Namespace)
if err != nil {
adapter.logger.Warnf("unable to remove persistent volume claim %s/%s: %s", persistentVolumeClaim.Namespace, persistentVolumeClaim.Name, err)
}
}

return nil
}

func (adapter *KubeDockerAdapter) removeAllConfigMapsAndSecrets(ctx context.Context) error {
adapter.logger.Infoln("removing all configmaps...")

configMaps, err := adapter.ListConfigMaps("")
if err != nil {
return fmt.Errorf("unable to list configmaps: %w", err)
}

for _, configMap := range configMaps.Items {
adapter.logger.Infof("removing configmap %s/%s", configMap.Namespace, configMap.Name)

err = adapter.DeleteConfigMap(configMap.Name, configMap.Namespace)
if err != nil {
adapter.logger.Warnf("unable to remove configmap %s/%s: %s", configMap.Namespace, configMap.Name, err)
}
}

adapter.logger.Infoln("removing all secrets...")

secrets, err := adapter.ListSecrets("", labels.NewSelector())
if err != nil {
return fmt.Errorf("unable to list secrets: %w", err)
}

for _, secret := range secrets.Items {
adapter.logger.Infof("removing secret %s/%s", secret.Namespace, secret.Name)

err = adapter.DeleteSecret(secret.Name, secret.Namespace)
if err != nil {
adapter.logger.Warnf("unable to remove secret %s/%s: %s", secret.Namespace, secret.Name, err)
}
}

return nil
}

func (adapter *KubeDockerAdapter) removeAllNamespaces(ctx context.Context) error {
adapter.logger.Infoln("removing all namespaces...")

namespaces, err := adapter.ListNamespaces(ctx)
if err != nil {
return fmt.Errorf("unable to list namespaces: %w", err)
}

for _, namespace := range namespaces.Items {
adapter.logger.Infof("removing namespace %s", namespace.Name)

err = adapter.DeleteNamespace(ctx, namespace.Name)
if err != nil {
adapter.logger.Warnf("unable to remove namespace %s: %s", namespace.Name, err)
}
}

return nil
}
Loading