posh-providers/k3d-io/k3d/command.go
Kevin Franklin Kim 2ac4f35ff5
feat: clear cache
2025-04-04 16:45:12 +02:00

421 lines
11 KiB
Go

package k3d
import (
"context"
"fmt"
"github.com/foomo/posh-providers/kubernets/kubectl"
"github.com/foomo/posh/pkg/cache"
"github.com/foomo/posh/pkg/command/tree"
"github.com/foomo/posh/pkg/env"
"github.com/foomo/posh/pkg/log"
"github.com/foomo/posh/pkg/prompt/goprompt"
"github.com/foomo/posh/pkg/readline"
"github.com/foomo/posh/pkg/shell"
"github.com/foomo/posh/pkg/util/files"
"github.com/foomo/posh/pkg/util/suggests"
)
type (
Command struct {
l log.Logger
k3d *K3d
name string
cache cache.Cache
kubectl *kubectl.Kubectl
commandTree tree.Root
}
CommandOption func(*Command) error
)
// ------------------------------------------------------------------------------------------------
// ~ Options
// ------------------------------------------------------------------------------------------------
func CommandWithName(v string) CommandOption {
return func(o *Command) error {
o.name = v
return nil
}
}
// ------------------------------------------------------------------------------------------------
// ~ Constructor
// ------------------------------------------------------------------------------------------------
func NewCommand(l log.Logger, k3d *K3d, cache cache.Cache, kubectl *kubectl.Kubectl, opts ...CommandOption) (*Command, error) {
inst := &Command{
l: l.Named("k3d"),
k3d: k3d,
cache: cache,
kubectl: kubectl,
name: "k3d",
}
for _, opt := range opts {
if opt != nil {
if err := opt(inst); err != nil {
return nil, err
}
}
}
nameArg := &tree.Arg{
Name: "name",
Description: "Cluster name",
Suggest: func(ctx context.Context, t tree.Root, r *readline.Readline) []goprompt.Suggest {
return suggests.List(inst.k3d.Config().ClusterNames())
},
}
chartArg := &tree.Arg{
Name: "chart",
Description: "Chart name",
Suggest: func(ctx context.Context, t tree.Root, r *readline.Readline) []goprompt.Suggest {
if value, err := inst.k3d.Config().Charts.Names(); err != nil {
inst.l.Debug(err.Error())
return nil
} else {
return suggests.List(value)
}
},
}
inst.commandTree = tree.New(&tree.Node{
Name: inst.name,
Description: "Execute K3d commands",
Nodes: tree.Nodes{
{
Name: "up",
Description: "Spin up configured cluster",
Args: tree.Args{nameArg},
Execute: inst.up,
},
{
Name: "pause",
Description: "Stop existing k3d cluster",
Args: tree.Args{nameArg},
Execute: inst.pause,
},
{
Name: "resume",
Description: "Start existing k3d cluster",
Args: tree.Args{nameArg},
Execute: inst.resume,
},
{
Name: "kubeconfig",
Description: "Retrieve kubeconfig from running cluster",
Args: tree.Args{nameArg},
Execute: inst.kubeconfig,
},
{
Name: "install",
Description: "Install predified charts",
Args: tree.Args{nameArg, chartArg},
Execute: inst.install,
},
{
Name: "uninstall",
Description: "Uninstall predefined charts",
Args: tree.Args{nameArg, chartArg},
Execute: inst.uninstall,
},
{
Name: "down",
Description: "Shut down configured cluster",
Args: tree.Args{nameArg},
Execute: inst.down,
},
},
})
return inst, nil
}
// ------------------------------------------------------------------------------------------------
// ~ Public methods
// ------------------------------------------------------------------------------------------------
func (c *Command) Name() string {
return c.commandTree.Node().Name
}
func (c *Command) Description() string {
return c.commandTree.Node().Description
}
func (c *Command) Complete(ctx context.Context, r *readline.Readline) []goprompt.Suggest {
return c.commandTree.Complete(ctx, r)
}
func (c *Command) Execute(ctx context.Context, r *readline.Readline) error {
return c.commandTree.Execute(ctx, r)
}
func (c *Command) Help(ctx context.Context, r *readline.Readline) string {
return c.commandTree.Help(ctx, r)
}
// ------------------------------------------------------------------------------------------------
// ~ Private methods
// ------------------------------------------------------------------------------------------------
/*
@k3d registry create $(CLUSTER_NAME)-registry --port 12345
@k3d cluster create $(CLUSTER_NAME) \
--image=rancher/k3s:v1.23.16-k3s1 \
--kubeconfig-update-default=false \
--kubeconfig-switch-context=false \
--registry-use k3d-$(CLUSTER_NAME)-registry:12345 \
-p "$(CLUSTER_PORT):443@loadbalancer" \
--k3s-arg "--disable=traefik@server:0"
--agents 1
@bin/helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
@bin/helm dependency update devops/helm/charts/base
@bin/helm dependency build devops/helm/charts/base
@bin/helm upgrade --install base devops/helm/charts/base
@kubectl create ns catalogue
@kubectl create ns checkout
@kubectl create ns general
@kubectl create ns msdconnect
@kubectl create ns profile
@kubectl create ns site
@kubectl create ns stock
@kubectl create ns toolbox
*/
func (c *Command) up(ctx context.Context, r *readline.Readline) error {
cfg := c.k3d.Config()
_, args := r.Args().Shift()
name, args := args.Shift()
// ensure registry
registry, err := c.k3d.Registry(ctx, cfg.Registry.Name)
if err != nil {
return err
}
if registry == nil {
// create registry
if err := shell.New(ctx, c.l, "k3d", "registry", "create", cfg.Registry.Name,
"--port", cfg.Registry.Port,
"--no-help",
).Run(); err != nil {
return err
}
}
clusterCfg, err := cfg.Cluster(name)
if err != nil {
return err
}
// ensure cluster
cluster, err := c.k3d.Cluster(ctx, clusterCfg.AliasName())
if err != nil {
return err
} else if cluster != nil {
c.l.Info("cluster already exists")
return nil
}
k3sArg := ""
if !clusterCfg.EnableTraefikRouter {
k3sArg = "--k3s-arg \"--disable=traefik@server:0\""
}
return shell.New(ctx, c.l, "k3d", "cluster", "create", clusterCfg.AliasName(),
"--image", clusterCfg.Image,
"--registry-use", fmt.Sprintf("%s:%s", cfg.Registry.Name, cfg.Registry.Port),
"--port", fmt.Sprintf("%s:443@loadbalancer", clusterCfg.Port),
k3sArg,
"--agents", "1",
).
Env(c.kubectl.Cluster(name).Env("")).
Args(args...).
Args(r.AdditionalArgs()...).
Args(r.AdditionalFlags()...).
Run()
}
func (c *Command) pause(ctx context.Context, r *readline.Readline) error {
cfg := c.k3d.Config()
_, args := r.Args().Shift()
name, args := args.Shift()
clusterCfg, err := cfg.Cluster(name)
if err != nil {
return err
}
cluster, err := c.k3d.Cluster(ctx, clusterCfg.AliasName())
if err != nil {
return err
} else if cluster == nil {
c.l.Info("cluster does not exists")
return nil
}
return shell.New(ctx, c.l, "k3d", "cluster", "stop", clusterCfg.AliasName()).
Env(c.kubectl.Cluster(name).Env("")).
Args(args...).
Args(r.AdditionalArgs()...).
Args(r.AdditionalFlags()...).
Run()
}
func (c *Command) resume(ctx context.Context, r *readline.Readline) error {
cfg := c.k3d.Config()
_, args := r.Args().Shift()
name, args := args.Shift()
clusterCfg, err := cfg.Cluster(name)
if err != nil {
return err
}
cluster, err := c.k3d.Cluster(ctx, clusterCfg.AliasName())
if err != nil {
return err
} else if cluster == nil {
c.l.Info("cluster does not exists")
return nil
}
return shell.New(ctx, c.l, "k3d", "cluster", "start", clusterCfg.AliasName()).
Env(c.kubectl.Cluster(name).Env("")).
Args(args...).
Args(r.AdditionalArgs()...).
Args(r.AdditionalFlags()...).
Run()
}
func (c *Command) install(ctx context.Context, r *readline.Readline) error {
var args []string
cfg := c.k3d.Config()
fs := r.FlagSets().Default()
cluster, name := r.Args().At(1), r.Args().At(2)
// allow values.override.yaml files
if err := files.Exists(env.Path(cfg.Charts.Path, name, "values.override.yaml")); err == nil {
args = append(args, "--values", env.Path(cfg.Charts.Path, name, "values.override.yaml"))
}
return shell.New(ctx, c.l, "helm",
"upgrade", name,
"--install",
"--dependency-update",
"--namespace", fmt.Sprintf("%s%s", cfg.Charts.Prefix, name),
"--create-namespace",
"--force",
env.Path(cfg.Charts.Path, name),
).
Env(c.kubectl.Cluster(cluster).Env("")).
Args(args...).
Args(fs.Visited().Args()...).
Args(r.AdditionalArgs()...).
Args(r.AdditionalFlags()...).
Run()
}
func (c *Command) uninstall(ctx context.Context, r *readline.Readline) error {
fs := r.FlagSets().Default()
cfg := c.k3d.Config()
cluster, name := r.Args().At(1), r.Args().At(2)
return shell.New(ctx, c.l, "helm",
"uninstall",
"--namespace", fmt.Sprintf("%s%s", cfg.Charts.Prefix, name),
name,
).
Env(c.kubectl.Cluster(cluster).Env("")).
Args(fs.Visited().Args()...).
Args(r.AdditionalArgs()...).
Args(r.AdditionalFlags()...).
Run()
}
func (c *Command) kubeconfig(ctx context.Context, r *readline.Readline) error {
cfg := c.k3d.Config()
name := r.Args().At(1)
clusterCfg, err := cfg.Cluster(name)
if err != nil {
return err
}
// ensure cluster
cluster, err := c.k3d.Cluster(ctx, clusterCfg.AliasName())
if err != nil {
return err
} else if cluster == nil {
c.l.Info("cluster does not exist")
return nil
}
// delete cluster
if err := shell.New(ctx, c.l, "k3d", "kubeconfig", "get", clusterCfg.AliasName()).
Args(">", c.kubectl.Cluster(name).Config("")).
Args(r.Flags()...).
Args(r.AdditionalArgs()...).
Args(r.AdditionalFlags()...).
Run(); err != nil {
return err
}
c.cache.Clear()
return nil
}
/*
cluster.down: export KUBECONFIG=devops/config/kubectl/$(CLUSTER_NAME).yaml
cluster.down:
@k3d cluster delete $(CLUSTER_NAME)
@k3d registry delete $(CLUSTER_NAME)-registry
@rm -f devops/config/kubectl/$(CLUSTER_NAME).yaml
*/
func (c *Command) down(ctx context.Context, r *readline.Readline) error {
cfg := c.k3d.Config()
name := r.Args().At(1)
clusterCfg, err := cfg.Cluster(name)
if err != nil {
return err
}
cluster, err := c.k3d.Cluster(ctx, clusterCfg.AliasName())
if err != nil {
return err
} else if cluster == nil {
c.l.Info("cluster does not exist")
return nil
}
// delete cluster
if err := shell.New(ctx, c.l, "k3d", "cluster", "delete", clusterCfg.AliasName()).
Env(c.kubectl.Cluster(name).Env("")).
Args(r.AdditionalArgs()...).
Args(r.AdditionalFlags()...).
Run(); err != nil {
return err
}
// delete config
if err := c.kubectl.Cluster(name).DeleteConfig(""); err != nil {
return err
}
// ensure registry
registry, err := c.k3d.Registry(ctx, cfg.Registry.Name)
if err != nil {
return err
}
if registry != nil {
// TODO check if empty
// delete registry
if err := shell.New(ctx, c.l, "k3d", "registry", "delete", cfg.Registry.Name).Run(); err != nil {
return err
}
}
return nil
}