From 42df2726ea29c4b5fb7574f34a0c7329fb11adcb Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Sun, 16 Jul 2023 21:16:17 -0700 Subject: [PATCH] Refactor rm Signed-off-by: Darren Shepherd --- .../100-reference/01-command-line/acorn.md | 2 +- .../01-command-line/acorn_container.md | 2 +- .../01-command-line/acorn_exec.md | 2 +- .../01-command-line/acorn_logs.md | 2 +- .../01-command-line/acorn_port-forward.md | 2 +- .../100-reference/01-command-line/acorn_ps.md | 2 +- .../100-reference/01-command-line/acorn_rm.md | 17 +- .../01-command-line/acorn_start.md | 2 +- .../01-command-line/acorn_stop.md | 2 +- .../01-command-line/acorn_update.md | 2 +- .../01-command-line/acorn_wait.md | 2 +- pkg/cli/all.go | 2 +- pkg/cli/apiserver.go | 2 +- pkg/cli/containers.go | 2 +- pkg/cli/containers_test.go | 10 +- pkg/cli/exec.go | 2 +- pkg/cli/log.go | 2 +- pkg/cli/port_forward.go | 2 +- pkg/cli/ps.go | 2 +- pkg/cli/rm.go | 60 +-- pkg/cli/rm_helper.go | 147 +++--- pkg/cli/rm_test.go | 485 ------------------ pkg/cli/start.go | 2 +- pkg/cli/stop.go | 2 +- pkg/cli/testdata/acorn/acorn_test_info.txt | 2 +- pkg/cli/testdata/all/all_test.txt | 4 +- pkg/cli/testdata/all/all_test_i.txt | 4 +- pkg/cli/testdata/all/all_test_json.txt | 2 +- pkg/cli/testdata/all/all_test_yaml.txt | 2 +- pkg/cli/update.go | 2 +- pkg/cli/wait.go | 2 +- pkg/tables/tables.go | 2 +- 32 files changed, 122 insertions(+), 655 deletions(-) delete mode 100644 pkg/cli/rm_test.go diff --git a/docs/docs/100-reference/01-command-line/acorn.md b/docs/docs/100-reference/01-command-line/acorn.md index cf8d8b39a..f30ec1fb5 100644 --- a/docs/docs/100-reference/01-command-line/acorn.md +++ b/docs/docs/100-reference/01-command-line/acorn.md @@ -48,7 +48,7 @@ acorn [flags] * [acorn pull](acorn_pull.md) - Pull an image from a remote registry * [acorn push](acorn_push.md) - Push an image to a remote registry * [acorn render](acorn_render.md) - Evaluate and display an Acornfile with args -* [acorn rm](acorn_rm.md) - Delete an app, container, secret or volume +* [acorn rm](acorn_rm.md) - Delete an acorn, optionally with it's associated secrets and volumes * [acorn run](acorn_run.md) - Run an app from an image or Acornfile * [acorn secret](acorn_secret.md) - Manage secrets * [acorn start](acorn_start.md) - Start an app diff --git a/docs/docs/100-reference/01-command-line/acorn_container.md b/docs/docs/100-reference/01-command-line/acorn_container.md index 78df3484c..9fbde18ca 100644 --- a/docs/docs/100-reference/01-command-line/acorn_container.md +++ b/docs/docs/100-reference/01-command-line/acorn_container.md @@ -6,7 +6,7 @@ title: "acorn container" Manage containers ``` -acorn container [flags] [APP_NAME|CONTAINER_NAME...] +acorn container [flags] [ACORN_NAME|CONTAINER_NAME...] ``` ### Examples diff --git a/docs/docs/100-reference/01-command-line/acorn_exec.md b/docs/docs/100-reference/01-command-line/acorn_exec.md index 37126a3aa..0f0ffb163 100644 --- a/docs/docs/100-reference/01-command-line/acorn_exec.md +++ b/docs/docs/100-reference/01-command-line/acorn_exec.md @@ -10,7 +10,7 @@ Run a command in a container Run a command in a container ``` -acorn exec [flags] APP_NAME|CONTAINER_NAME CMD +acorn exec [flags] ACORN_NAME|CONTAINER_NAME CMD ``` ### Options diff --git a/docs/docs/100-reference/01-command-line/acorn_logs.md b/docs/docs/100-reference/01-command-line/acorn_logs.md index 2fa1b3a37..3741d4933 100644 --- a/docs/docs/100-reference/01-command-line/acorn_logs.md +++ b/docs/docs/100-reference/01-command-line/acorn_logs.md @@ -6,7 +6,7 @@ title: "acorn logs" Log all workloads from an app ``` -acorn logs [flags] [APP_NAME|CONTAINER_REPLICA_NAME] +acorn logs [flags] [ACORN_NAME|CONTAINER_REPLICA_NAME] ``` ### Options diff --git a/docs/docs/100-reference/01-command-line/acorn_port-forward.md b/docs/docs/100-reference/01-command-line/acorn_port-forward.md index d0b933ff8..4689e9403 100644 --- a/docs/docs/100-reference/01-command-line/acorn_port-forward.md +++ b/docs/docs/100-reference/01-command-line/acorn_port-forward.md @@ -10,7 +10,7 @@ Forward a container port locally Forward a container port locally ``` -acorn port-forward [flags] APP_NAME|CONTAINER_NAME PORT +acorn port-forward [flags] ACORN_NAME|CONTAINER_NAME PORT ``` ### Options diff --git a/docs/docs/100-reference/01-command-line/acorn_ps.md b/docs/docs/100-reference/01-command-line/acorn_ps.md index 6d1a54200..b0502b7fa 100644 --- a/docs/docs/100-reference/01-command-line/acorn_ps.md +++ b/docs/docs/100-reference/01-command-line/acorn_ps.md @@ -6,7 +6,7 @@ title: "acorn ps" List or get apps ``` -acorn ps [flags] [APP_NAME...] +acorn ps [flags] [ACORN_NAME...] ``` ### Examples diff --git a/docs/docs/100-reference/01-command-line/acorn_rm.md b/docs/docs/100-reference/01-command-line/acorn_rm.md index 98bc562e5..e1f21e600 100644 --- a/docs/docs/100-reference/01-command-line/acorn_rm.md +++ b/docs/docs/100-reference/01-command-line/acorn_rm.md @@ -3,28 +3,29 @@ title: "acorn rm" --- ## acorn rm -Delete an app, container, secret or volume +Delete an acorn, optionally with it's associated secrets and volumes ``` -acorn rm [flags] APP_NAME [APP_NAME...] +acorn rm [flags] ACORN_NAME [ACORN_NAME...] ``` ### Examples ``` -acorn rm APP_NAME -acorn rm -t volume,container APP_NAME +acorn rm ACORN_NAME +acorn rm --volumes --secrets ACORN_NAME ``` ### Options ``` - -a, --all Delete all types - -f, --force Force Delete + -a, --all Delete all associated resources (volumes, secrets) + -f, --force Do not prompt for delete -h, --help help for rm - --ignore-cleanup Ignore delete jobs - -t, --type strings Delete by type (container,app,volume,secret or c,a,v,s) + --ignore-cleanup Delete acorns without running delete jobs + -s, --secrets Delete acorn and associated secrets + -v, --volumes Delete acorn and associated volumes ``` ### Options inherited from parent commands diff --git a/docs/docs/100-reference/01-command-line/acorn_start.md b/docs/docs/100-reference/01-command-line/acorn_start.md index 715429a8c..7f5cc94cc 100644 --- a/docs/docs/100-reference/01-command-line/acorn_start.md +++ b/docs/docs/100-reference/01-command-line/acorn_start.md @@ -6,7 +6,7 @@ title: "acorn start" Start an app ``` -acorn start [flags] [APP_NAME...] +acorn start [flags] [ACORN_NAME...] ``` ### Examples diff --git a/docs/docs/100-reference/01-command-line/acorn_stop.md b/docs/docs/100-reference/01-command-line/acorn_stop.md index abad7405d..c77a31ff3 100644 --- a/docs/docs/100-reference/01-command-line/acorn_stop.md +++ b/docs/docs/100-reference/01-command-line/acorn_stop.md @@ -6,7 +6,7 @@ title: "acorn stop" Stop an app ``` -acorn stop [flags] [APP_NAME...] +acorn stop [flags] [ACORN_NAME...] ``` ### Examples diff --git a/docs/docs/100-reference/01-command-line/acorn_update.md b/docs/docs/100-reference/01-command-line/acorn_update.md index 62f9caf90..185ad6c59 100644 --- a/docs/docs/100-reference/01-command-line/acorn_update.md +++ b/docs/docs/100-reference/01-command-line/acorn_update.md @@ -6,7 +6,7 @@ title: "acorn update" Update a deployed Acorn ``` -acorn update [flags] APP_NAME [deploy flags] +acorn update [flags] ACORN_NAME [deploy flags] ``` ### Examples diff --git a/docs/docs/100-reference/01-command-line/acorn_wait.md b/docs/docs/100-reference/01-command-line/acorn_wait.md index 7f7eb70ad..7ea98d6e6 100644 --- a/docs/docs/100-reference/01-command-line/acorn_wait.md +++ b/docs/docs/100-reference/01-command-line/acorn_wait.md @@ -6,7 +6,7 @@ title: "acorn wait" Wait an app to be ready then exit with status code 0 ``` -acorn wait [flags] APP_NAME +acorn wait [flags] ACORN_NAME ``` ### Options diff --git a/pkg/cli/all.go b/pkg/cli/all.go index 011faff3e..d0ad7be1a 100644 --- a/pkg/cli/all.go +++ b/pkg/cli/all.go @@ -29,7 +29,7 @@ type All struct { func (a *All) Run(cmd *cobra.Command, args []string) error { if !a.Quiet { fmt.Println("") - fmt.Println("APPS:") + fmt.Println("ACORNS:") } ps := &Ps{ Quiet: a.Quiet, diff --git a/pkg/cli/apiserver.go b/pkg/cli/apiserver.go index b8fb46bb3..e59ca6c2c 100644 --- a/pkg/cli/apiserver.go +++ b/pkg/cli/apiserver.go @@ -15,7 +15,7 @@ var ( func NewApiServer(c CommandContext) *cobra.Command { api := &APIServer{client: c.ClientFactory} cmd := cli.Command(api, cobra.Command{ - Use: "api-server [flags] [APP_NAME...]", + Use: "api-server [flags] [ACORN_NAME...]", SilenceUsage: true, Short: "Run api-server", Hidden: true, diff --git a/pkg/cli/containers.go b/pkg/cli/containers.go index a1a166e6b..8deb6c7a6 100644 --- a/pkg/cli/containers.go +++ b/pkg/cli/containers.go @@ -13,7 +13,7 @@ import ( func NewContainer(c CommandContext) *cobra.Command { cmd := cli.Command(&Container{client: c.ClientFactory}, cobra.Command{ - Use: "container [flags] [APP_NAME|CONTAINER_NAME...]", + Use: "container [flags] [ACORN_NAME|CONTAINER_NAME...]", Aliases: []string{"containers", "c"}, Example: ` acorn containers`, diff --git a/pkg/cli/containers_test.go b/pkg/cli/containers_test.go index e605bd7f7..1847b3da2 100644 --- a/pkg/cli/containers_test.go +++ b/pkg/cli/containers_test.go @@ -117,7 +117,7 @@ func TestContainer(t *testing.T) { client: mClient, }, wantErr: false, - wantOut: "NAME APP IMAGE STATE RESTARTCOUNT CREATED MESSAGE\nfound.container1 0 10y ago \n", + wantOut: "NAME ACORN IMAGE STATE RESTARTCOUNT CREATED MESSAGE\nfound.container1 0 10y ago \n", }, { name: "acorn container -a", fields: fields{ @@ -138,7 +138,7 @@ func TestContainer(t *testing.T) { client: mClient, }, wantErr: false, - wantOut: "NAME APP IMAGE STATE RESTARTCOUNT CREATED MESSAGE\nfound.container1 0 10y ago \nfound.container2 stopped 0 10y ago \n", + wantOut: "NAME ACORN IMAGE STATE RESTARTCOUNT CREATED MESSAGE\nfound.container1 0 10y ago \nfound.container2 stopped 0 10y ago \n", }, { name: "acorn container found.container1", fields: fields{ @@ -159,7 +159,7 @@ func TestContainer(t *testing.T) { client: mClient, }, wantErr: false, - wantOut: "NAME APP IMAGE STATE RESTARTCOUNT CREATED MESSAGE\nfound.container1 0 10y ago \n", + wantOut: "NAME ACORN IMAGE STATE RESTARTCOUNT CREATED MESSAGE\nfound.container1 0 10y ago \n", }, { name: "acorn container found", fields: fields{ @@ -180,7 +180,7 @@ func TestContainer(t *testing.T) { client: mClient, }, wantErr: false, - wantOut: "NAME APP IMAGE STATE RESTARTCOUNT CREATED MESSAGE\nfound.container1 0 10y ago \n", + wantOut: "NAME ACORN IMAGE STATE RESTARTCOUNT CREATED MESSAGE\nfound.container1 0 10y ago \n", }, { name: "acorn container found -a", fields: fields{ @@ -201,7 +201,7 @@ func TestContainer(t *testing.T) { client: mClient, }, wantErr: false, - wantOut: "NAME APP IMAGE STATE RESTARTCOUNT CREATED MESSAGE\nfound.container1 0 10y ago \nfound.container2 stopped 0 10y ago \n", + wantOut: "NAME ACORN IMAGE STATE RESTARTCOUNT CREATED MESSAGE\nfound.container1 0 10y ago \nfound.container2 stopped 0 10y ago \n", }, { name: "acorn container kill found.container1", fields: fields{ diff --git a/pkg/cli/exec.go b/pkg/cli/exec.go index f2ef31359..653520127 100644 --- a/pkg/cli/exec.go +++ b/pkg/cli/exec.go @@ -18,7 +18,7 @@ import ( func NewExec(c CommandContext) *cobra.Command { exec := &Exec{client: c.ClientFactory} cmd := cli.Command(exec, cobra.Command{ - Use: "exec [flags] APP_NAME|CONTAINER_NAME CMD", + Use: "exec [flags] ACORN_NAME|CONTAINER_NAME CMD", SilenceUsage: true, Short: "Run a command in a container", Long: "Run a command in a container", diff --git a/pkg/cli/log.go b/pkg/cli/log.go index b6b6bb31d..4333aaa00 100644 --- a/pkg/cli/log.go +++ b/pkg/cli/log.go @@ -12,7 +12,7 @@ import ( func NewLogs(c CommandContext) *cobra.Command { logs := &Logs{client: c.ClientFactory} return cli.Command(logs, cobra.Command{ - Use: "logs [flags] [APP_NAME|CONTAINER_REPLICA_NAME]", + Use: "logs [flags] [ACORN_NAME|CONTAINER_REPLICA_NAME]", SilenceUsage: true, Short: "Log all workloads from an app", Args: cobra.MaximumNArgs(1), diff --git a/pkg/cli/port_forward.go b/pkg/cli/port_forward.go index 80ad58b23..ee2bce6f1 100644 --- a/pkg/cli/port_forward.go +++ b/pkg/cli/port_forward.go @@ -9,7 +9,7 @@ import ( func NewPortForward(c CommandContext) *cobra.Command { exec := &PortForward{client: c.ClientFactory} cmd := cli.Command(exec, cobra.Command{ - Use: "port-forward [flags] APP_NAME|CONTAINER_NAME PORT", + Use: "port-forward [flags] ACORN_NAME|CONTAINER_NAME PORT", SilenceUsage: true, Short: "Forward a container port locally", Long: "Forward a container port locally", diff --git a/pkg/cli/ps.go b/pkg/cli/ps.go index db004fabf..865609611 100644 --- a/pkg/cli/ps.go +++ b/pkg/cli/ps.go @@ -11,7 +11,7 @@ import ( func NewPs(c CommandContext) *cobra.Command { return cli.Command(&Ps{client: c.ClientFactory}, cobra.Command{ - Use: "ps [flags] [APP_NAME...]", + Use: "ps [flags] [ACORN_NAME...]", Aliases: []string{"app", "apps", "a"}, Example: ` acorn ps`, diff --git a/pkg/cli/rm.go b/pkg/cli/rm.go index 23303c279..9bf6ca0c5 100644 --- a/pkg/cli/rm.go +++ b/pkg/cli/rm.go @@ -7,77 +7,49 @@ import ( func NewRm(c CommandContext) *cobra.Command { return cli.Command(&Rm{client: c.ClientFactory}, cobra.Command{ - Use: "rm [flags] APP_NAME [APP_NAME...]", + Use: "rm [flags] ACORN_NAME [ACORN_NAME...]", Example: ` -acorn rm APP_NAME -acorn rm -t volume,container APP_NAME`, +acorn rm ACORN_NAME +acorn rm --volumes --secrets ACORN_NAME`, SilenceUsage: true, - Short: "Delete an app, container, secret or volume", + Short: "Delete an acorn, optionally with it's associated secrets and volumes", ValidArgsFunction: newCompletion(c.ClientFactory, appsCompletion).complete, Args: cobra.MinimumNArgs(1), }) } type Rm struct { - All bool `usage:"Delete all types" short:"a"` - Type []string `usage:"Delete by type (container,app,volume,secret or c,a,v,s)" short:"t"` - Force bool `usage:"Force Delete" short:"f"` - IgnoreCleanup bool `usage:"Ignore delete jobs"` + All bool `usage:"Delete all associated resources (volumes, secrets)" short:"a"` + Volumes bool `usage:"Delete acorn and associated volumes" short:"v"` + Secrets bool `usage:"Delete acorn and associated secrets" short:"s"` + Force bool `usage:"Do not prompt for delete" short:"f"` + IgnoreCleanup bool `usage:"Delete acorns without running delete jobs"` client ClientFactory } -type RmObjects struct { - App bool - Container bool - Secret bool - Volume bool -} func (a *Rm) Run(cmd *cobra.Command, args []string) error { - var rmObjects RmObjects - c, err := a.client.CreateDefault() if err != nil { return err } if a.All { - rmObjects = RmObjects{ - App: true, - Secret: true, - Volume: true, - } - } else if len(a.Type) > 0 { - for _, obj := range a.Type { - addRmObject(&rmObjects, obj) - } - } else { // If nothing is set default to App - rmObjects = RmObjects{ - App: true, - } + a.Volumes = true + a.Secrets = true } - // Do not prompt when deleting non-nested resource - a.Force = a.Force || rmObjects.App && !rmObjects.Secret && !rmObjects.Volume for _, arg := range args { - if rmObjects.App { - err := removeApp(arg, c, cmd, a.Force, a.IgnoreCleanup, rmObjects.Container || rmObjects.Volume || rmObjects.Secret) - if err != nil { - return err - } - } - if rmObjects.Container { - err := removeContainer(arg, c, cmd, a.Force) - if err != nil { - return err - } + err := removeAcorn(cmd.Context(), c, arg, a.IgnoreCleanup, a.Volumes || a.Secrets) + if err != nil { + return err } - if rmObjects.Volume { + if a.Volumes { err := removeVolume(arg, c, cmd, a.Force) if err != nil { return err } } - if rmObjects.Secret { + if a.Secrets { err := removeSecret(arg, c, cmd, a.Force) if err != nil { return err diff --git a/pkg/cli/rm_helper.go b/pkg/cli/rm_helper.go index 66465b8f5..05966ce4f 100644 --- a/pkg/cli/rm_helper.go +++ b/pkg/cli/rm_helper.go @@ -1,6 +1,7 @@ package cli import ( + "context" "fmt" "strings" "time" @@ -14,29 +15,6 @@ import ( "github.com/spf13/cobra" ) -func addRmObject(rmObjects *RmObjects, obj string) { - switch strings.ToLower(obj) { - case "app": - rmObjects.App = true - case "container": - rmObjects.Container = true - case "secret": - rmObjects.Secret = true - case "volume": - rmObjects.Volume = true - case "a": - rmObjects.App = true - case "c": - rmObjects.Container = true - case "s": - rmObjects.Secret = true - case "v": - rmObjects.Volume = true - default: - pterm.Warning.Printf("%s is not a valid type\n", obj) - } -} - func getSecretsToRemove(arg string, client client.Client, cmd *cobra.Command) ([]string, error) { var result []string secrets, err := client.SecretList(cmd.Context()) @@ -51,6 +29,7 @@ func getSecretsToRemove(arg string, client client.Client, cmd *cobra.Command) ([ } return result, nil } + func getVolumesToDelete(arg string, client client.Client, cmd *cobra.Command) ([]string, error) { var result []string volumes, err := client.VolumeList(cmd.Context()) @@ -65,55 +44,13 @@ func getVolumesToDelete(arg string, client client.Client, cmd *cobra.Command) ([ } return result, nil } -func getContainersToDelete(arg string, client client.Client, cmd *cobra.Command) ([]string, error) { - var result []string - containers, err := client.ContainerReplicaList(cmd.Context(), nil) - if err != nil { - return nil, err - } - - for _, container := range containers { - if arg == strings.Split(container.Name, ".")[0] { - result = append(result, container.Name) - } - } - return result, nil -} -func removeContainer(arg string, c client.Client, cmd *cobra.Command, force bool) error { - conToDel, err := getContainersToDelete(arg, c, cmd) - if len(conToDel) == 0 { - pterm.Info.Println("No containers associated with " + arg) - return nil - } - if !force { - for _, con := range conToDel { - pterm.FgRed.Println(con) - } - err := prompt.Remove("containers") - if err != nil { - return err - } - } - if err != nil { - return err - } - for _, con := range conToDel { - _, err := c.ContainerReplicaDelete(cmd.Context(), con) - if err != nil { - return fmt.Errorf("deleting container %s: %w", con, err) - } - fmt.Println("Removed: " + con) - } - return nil -} func removeVolume(arg string, c client.Client, cmd *cobra.Command, force bool) error { volToDel, err := getVolumesToDelete(arg, c, cmd) if err != nil { return err } if len(volToDel) == 0 { - pterm.Info.Println("No volumes associated with " + arg) return nil } if !force { @@ -135,7 +72,7 @@ func removeVolume(arg string, c client.Client, cmd *cobra.Command, force bool) e return fmt.Errorf("deleting volume %s: %w", arg, err) } if v != nil { - fmt.Println("Removed: " + vol) + fmt.Println("Removed volume: " + vol) continue } else { fmt.Printf("Error: No such volume: %s\n", vol) @@ -143,26 +80,67 @@ func removeVolume(arg string, c client.Client, cmd *cobra.Command, force bool) e } return nil } -func removeApp(arg string, c client.Client, cmd *cobra.Command, force, ignoreCleanup, wait bool) error { - if !force { - pterm.FgRed.Println(arg) - err := prompt.Remove("app") - if err != nil { - return err + +func deleteOthers(ctx context.Context, c client.Client, arg string) (bool, error) { + _, err := c.ContainerReplicaGet(ctx, arg) + if apierrors.IsNotFound(err) { + } else if err != nil { + return false, err + } else { + _, err = c.ContainerReplicaDelete(ctx, arg) + if err == nil { + fmt.Println("Removed container: " + arg) + } + return true, err + } + + _, err = c.VolumeGet(ctx, arg) + if apierrors.IsNotFound(err) { + } else if err != nil { + return false, err + } else { + _, err = c.VolumeDelete(ctx, arg) + if err == nil { + fmt.Println("Removed volume: " + arg) } + return true, err } - app, err := c.AppDelete(cmd.Context(), arg) + + _, err = c.SecretGet(ctx, arg) + if apierrors.IsNotFound(err) { + } else if err != nil { + return false, err + } else { + _, err = c.SecretDelete(ctx, arg) + if err == nil { + fmt.Println("Removed secret: " + arg) + } + return true, err + } + + return false, nil +} + +func removeAcorn(ctx context.Context, c client.Client, arg string, ignoreCleanup, wait bool) error { + app, err := c.AppDelete(ctx, arg) if err != nil { return fmt.Errorf("deleting app %s: %w", arg, err) } if app == nil { + if strings.Contains(arg, ".") { + if ok, err := deleteOthers(ctx, c, arg); err != nil { + return err + } else if ok { + return nil + } + } fmt.Printf("Error: No such app: %s\n", arg) return nil } if ignoreCleanup { - if err := c.AppIgnoreDeleteCleanup(cmd.Context(), arg); err != nil { + if err := c.AppIgnoreDeleteCleanup(ctx, arg); err != nil { return fmt.Errorf("skipping cleanup for app %s: %w", arg, err) } } @@ -173,22 +151,23 @@ func removeApp(arg string, c client.Client, cmd *cobra.Command, force, ignoreCle fmt.Println("Removed: " + arg) } }() + if wait { - fmt.Printf("Waiting for app %s to be removed...\n", arg) for { + if _, err = c.AppGet(ctx, arg); apierrors.IsNotFound(err) { + return nil + } else if err != nil { + logrus.Debugf("Error getting app for removal check: %v", err) + } select { - case <-cmd.Context().Done(): - return cmd.Context().Err() - default: - if _, err = c.AppGet(cmd.Context(), arg); apierrors.IsNotFound(err) { - return nil - } else if err != nil { - logrus.Debugf("Error getting app for removal check: %v", err) - } + case <-ctx.Done(): + return ctx.Err() + case <-time.After(time.Second): } - time.Sleep(time.Second) + fmt.Printf("Waiting for app %s to be removed...\n", arg) } } + return nil } diff --git a/pkg/cli/rm_test.go b/pkg/cli/rm_test.go deleted file mode 100644 index a60c4fff6..000000000 --- a/pkg/cli/rm_test.go +++ /dev/null @@ -1,485 +0,0 @@ -package cli - -import ( - "fmt" - "io" - "os" - "strings" - "testing" - - api_acorn_io "github.com/acorn-io/runtime/pkg/apis/api.acorn.io" - apiv1 "github.com/acorn-io/runtime/pkg/apis/api.acorn.io/v1" - "github.com/acorn-io/runtime/pkg/cli/testdata" - "github.com/acorn-io/runtime/pkg/labels" - "github.com/acorn-io/runtime/pkg/mocks" - "github.com/acorn-io/runtime/pkg/prompt" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -func TestAppRm(t *testing.T) { - type fields struct { - All bool - Type []string - Force bool - } - tests := []struct { - name string - fields fields - args []string - prepare func(client *mocks.MockClient) - stdin string - wantErr bool - wantOut string - }{ - { - name: "does not exist default type", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().AppDelete(gomock.Any(), "dne").Return( - nil, fmt.Errorf("error: app dne does not exist"), - ) - }, - stdin: "y\n", - args: []string{"dne"}, - wantErr: true, - wantOut: "deleting app dne: error: app dne does not exist", - }, - { - name: "does not exist force default type", - fields: fields{ - All: false, - Type: nil, - Force: true, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().AppDelete(gomock.Any(), "dne").Return( - nil, fmt.Errorf("error: app dne does not exist"), - ) - }, - args: []string{"-f", "dne"}, - wantErr: true, - wantOut: "deleting app dne: error: app dne does not exist", - }, - { - name: "does not exist container type", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().ContainerReplicaList(gomock.Any(), nil).Return( - nil, nil, - ) - }, - stdin: "y\n", - args: []string{"-tc", "dne"}, - wantErr: false, - wantOut: "", - }, - { - name: "does not exist volume type", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().VolumeList(gomock.Any()).Return( - nil, nil, - ) - }, - stdin: "y\n", - args: []string{"-tv", "dne"}, - wantErr: false, - wantOut: "", - }, - { - name: "does exist default type", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().AppDelete(gomock.Any(), "found").Return( - &apiv1.App{ - ObjectMeta: metav1.ObjectMeta{ - Name: "found", - }, - }, nil, - ) - }, - stdin: "y\n", - args: []string{"found"}, - wantErr: false, - wantOut: "Removed: found\n", - }, - { - name: "does exist force default type", - fields: fields{ - All: false, - Type: nil, - Force: true, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().AppDelete(gomock.Any(), "found").Return( - &apiv1.App{ - ObjectMeta: metav1.ObjectMeta{ - Name: "found", - }, - }, nil, - ) - }, - args: []string{"-f", "found"}, - wantErr: false, - wantOut: "Removed: found\n", - }, - { - name: "does exist container type short", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().ContainerReplicaList(gomock.Any(), nil).Return( - []apiv1.ContainerReplica{{ - ObjectMeta: metav1.ObjectMeta{Name: "found.container"}, - Spec: apiv1.ContainerReplicaSpec{AppName: "found"}, - }}, nil, - ) - f.EXPECT().ContainerReplicaDelete(gomock.Any(), "found.container").Return( - &apiv1.ContainerReplica{ - ObjectMeta: metav1.ObjectMeta{Name: "found.container"}, - Spec: apiv1.ContainerReplicaSpec{AppName: "found"}, - }, nil, - ) - }, - stdin: "y\n", - args: []string{"-tc", "found"}, - wantErr: false, - wantOut: "Removed: found.container\n", - }, - { - name: "does exist container type long", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().ContainerReplicaList(gomock.Any(), nil).Return( - []apiv1.ContainerReplica{{ - ObjectMeta: metav1.ObjectMeta{Name: "found.container"}, - Spec: apiv1.ContainerReplicaSpec{AppName: "found"}, - }}, nil, - ) - f.EXPECT().ContainerReplicaDelete(gomock.Any(), "found.container").Return( - &apiv1.ContainerReplica{ - ObjectMeta: metav1.ObjectMeta{Name: "found.container"}, - Spec: apiv1.ContainerReplicaSpec{AppName: "found"}, - }, nil, - ) - }, - stdin: "y\n", - args: []string{"-tcontainer", "found"}, - wantErr: false, - wantOut: "Removed: found.container\n", - }, - { - name: "does exist app type short", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().AppDelete(gomock.Any(), "found").Return( - &apiv1.App{ - ObjectMeta: metav1.ObjectMeta{ - Name: "found", - }, - }, nil, - ) - }, - stdin: "y\n", - args: []string{"-ta", "found"}, - wantErr: false, - wantOut: "Removed: found\n", - }, - { - name: "does exist app type long", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().AppDelete(gomock.Any(), "found").Return( - &apiv1.App{ - ObjectMeta: metav1.ObjectMeta{ - Name: "found", - }, - }, nil, - ) - }, - stdin: "y\n", - args: []string{"-tapp", "found"}, - wantErr: false, - wantOut: "Removed: found\n", - }, - { - name: "does exist volume type short", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().VolumeList(gomock.Any()).Return( - []apiv1.Volume{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "found.vol", - Labels: map[string]string{ - labels.AcornVolumeName: "vol", - labels.AcornAppName: "found", - }}, - Status: apiv1.VolumeStatus{ - AppPublicName: "found", - AppName: "found", - VolumeName: "vol", - }, - }}, nil, - ) - f.EXPECT().VolumeDelete(gomock.Any(), "found.vol").Return( - &apiv1.Volume{ - ObjectMeta: metav1.ObjectMeta{ - Name: "found.vol", - }, - }, nil, - ) - }, - stdin: "y\n", - args: []string{"-tv", "found"}, - wantErr: false, - wantOut: "Removed: found.vol\n", - }, - { - name: "does exist volume type long", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().VolumeList(gomock.Any()).Return( - []apiv1.Volume{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "found.vol", - Labels: map[string]string{ - labels.AcornVolumeName: "vol", - labels.AcornAppName: "found", - }}, - Status: apiv1.VolumeStatus{ - AppPublicName: "found", - AppName: "found", - VolumeName: "vol", - }, - }}, nil, - ) - f.EXPECT().VolumeDelete(gomock.Any(), "found.vol").Return( - &apiv1.Volume{ - ObjectMeta: metav1.ObjectMeta{ - Name: "found.vol", - }, - }, nil, - ) - }, - stdin: "y\n", - args: []string{"-tvolume", "found"}, - wantErr: false, - wantOut: "Removed: found.vol\n", - }, - { - name: "does exist secret type short", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().SecretList(gomock.Any()).Return( - []apiv1.Secret{{ - ObjectMeta: metav1.ObjectMeta{Name: "found.secret"}, - }}, nil, - ) - f.EXPECT().SecretDelete(gomock.Any(), "found.secret").Return( - &apiv1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "found.secret"}, - }, nil, - ) - }, - stdin: "y\n", - args: []string{"-ts", "found"}, - wantErr: false, - wantOut: "Removed: found.secret\n", - }, - { - name: "does exist secret type long", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().SecretList(gomock.Any()).Return( - []apiv1.Secret{{ - ObjectMeta: metav1.ObjectMeta{Name: "found.secret"}, - }}, nil, - ) - f.EXPECT().SecretDelete(gomock.Any(), "found.secret").Return( - &apiv1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "found.secret"}, - }, nil, - ) - }, - stdin: "y\n", - args: []string{"-tsecret", "found"}, - wantErr: false, - wantOut: "Removed: found.secret\n", - }, - { - name: "does exist all type", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - prepare: func(f *mocks.MockClient) { - f.EXPECT().AppDelete(gomock.Any(), "found").Return( - &apiv1.App{ - ObjectMeta: metav1.ObjectMeta{ - Name: "found", - }, - }, nil, - ) - gomock.InOrder( - f.EXPECT().AppGet(gomock.Any(), "found").Return( - &apiv1.App{ - ObjectMeta: metav1.ObjectMeta{ - Name: "found", - }, - }, nil, - ), - f.EXPECT().AppGet(gomock.Any(), "found").Return( - nil, apierrors.NewNotFound(schema.GroupResource{ - Group: api_acorn_io.Group, - Resource: "apps", - }, "found"), - ), - ) - f.EXPECT().VolumeList(gomock.Any()).Return( - []apiv1.Volume{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "found.vol", - Labels: map[string]string{ - labels.AcornVolumeName: "vol", - labels.AcornAppName: "found", - }}, - Status: apiv1.VolumeStatus{ - AppPublicName: "found", - AppName: "found", - VolumeName: "vol", - }, - }}, nil, - ) - f.EXPECT().VolumeDelete(gomock.Any(), "found.vol").Return( - &apiv1.Volume{ - ObjectMeta: metav1.ObjectMeta{ - Name: "found.vol", - }, - }, nil, - ) - f.EXPECT().SecretList(gomock.Any()).Return( - []apiv1.Secret{{ - ObjectMeta: metav1.ObjectMeta{Name: "found.secret"}, - }}, nil, - ) - f.EXPECT().SecretDelete(gomock.Any(), "found.secret").Return( - &apiv1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "found.secret"}, - }, nil, - ) - }, - stdin: "y\n", - args: []string{"-a", "found"}, - wantErr: false, - wantOut: "Waiting for app found to be removed...\nRemoved: found\nRemoved: found.vol\nRemoved: found.secret\n", - }, - { - name: "no app name arg default type", - fields: fields{ - All: false, - Type: nil, - Force: false, - }, - stdin: "y\n", - args: []string{}, - wantErr: true, - wantOut: "requires at least 1 arg(s), only received 0", - }, - { - name: "no app name arg force default type", - fields: fields{ - All: false, - Type: nil, - Force: true, - }, - args: []string{"-f"}, - wantErr: true, - wantOut: "requires at least 1 arg(s), only received 0", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ctrl := gomock.NewController(t) - mClient := mocks.NewMockClient(ctrl) - if tt.prepare != nil { - tt.prepare(mClient) - } - - r, w, _ := os.Pipe() - os.Stdout = w - cmd := NewRm(CommandContext{ - ClientFactory: &testdata.MockClientFactoryManual{ - Client: mClient, - }, - StdOut: w, - StdErr: w, - StdIn: strings.NewReader(tt.stdin), - }) - cmd.SetArgs(tt.args) - prompt.NoPromptRemove = true - - err := cmd.Execute() - if err != nil && !tt.wantErr { - assert.Failf(t, "got err when err not expected", "got err: %s", err.Error()) - } else if err != nil && tt.wantErr { - assert.Equal(t, tt.wantOut, err.Error()) - } else { - w.Close() - out, _ := io.ReadAll(r) - assert.Equal(t, tt.wantOut, string(out)) - } - }) - } -} diff --git a/pkg/cli/start.go b/pkg/cli/start.go index 8efc6abde..6ad797b6c 100644 --- a/pkg/cli/start.go +++ b/pkg/cli/start.go @@ -9,7 +9,7 @@ import ( func NewStart(c CommandContext) *cobra.Command { return cli.Command(&Start{client: c.ClientFactory}, cobra.Command{ - Use: "start [flags] [APP_NAME...]", + Use: "start [flags] [ACORN_NAME...]", Example: ` acorn start my-app diff --git a/pkg/cli/stop.go b/pkg/cli/stop.go index 62e8d5053..94d3b796c 100644 --- a/pkg/cli/stop.go +++ b/pkg/cli/stop.go @@ -9,7 +9,7 @@ import ( func NewStop(c CommandContext) *cobra.Command { return cli.Command(&Stop{client: c.ClientFactory}, cobra.Command{ - Use: "stop [flags] [APP_NAME...]", + Use: "stop [flags] [ACORN_NAME...]", Example: ` acorn stop my-app diff --git a/pkg/cli/testdata/acorn/acorn_test_info.txt b/pkg/cli/testdata/acorn/acorn_test_info.txt index 1bd0a9d98..e349b3576 100644 --- a/pkg/cli/testdata/acorn/acorn_test_info.txt +++ b/pkg/cli/testdata/acorn/acorn_test_info.txt @@ -29,7 +29,7 @@ Available Commands: pull Pull an image from a remote registry push Push an image to a remote registry render Evaluate and display an Acornfile with args - rm Delete an app, container, secret or volume + rm Delete an acorn, optionally with it's associated secrets and volumes run Run an app from an image or Acornfile secret Manage secrets start Start an app diff --git a/pkg/cli/testdata/all/all_test.txt b/pkg/cli/testdata/all/all_test.txt index 8735300f8..55b9dffe1 100644 --- a/pkg/cli/testdata/all/all_test.txt +++ b/pkg/cli/testdata/all/all_test.txt @@ -1,10 +1,10 @@ -APPS: +ACORNS: NAME IMAGE DIGEST HEALTHY UP-TO-DATE CREATED ENDPOINTS MESSAGE found 292y ago CONTAINERS: -NAME APP IMAGE STATE RESTARTCOUNT CREATED MESSAGE +NAME ACORN IMAGE STATE RESTARTCOUNT CREATED MESSAGE found.container 0 292y ago VOLUMES: diff --git a/pkg/cli/testdata/all/all_test_i.txt b/pkg/cli/testdata/all/all_test_i.txt index 7cd18af9a..dbea12c6a 100644 --- a/pkg/cli/testdata/all/all_test_i.txt +++ b/pkg/cli/testdata/all/all_test_i.txt @@ -1,10 +1,10 @@ -APPS: +ACORNS: NAME IMAGE DIGEST HEALTHY UP-TO-DATE CREATED ENDPOINTS MESSAGE found 292y ago CONTAINERS: -NAME APP IMAGE STATE RESTARTCOUNT CREATED MESSAGE +NAME ACORN IMAGE STATE RESTARTCOUNT CREATED MESSAGE found.container 0 292y ago VOLUMES: diff --git a/pkg/cli/testdata/all/all_test_json.txt b/pkg/cli/testdata/all/all_test_json.txt index 2f1d01d3e..a85f04d30 100644 --- a/pkg/cli/testdata/all/all_test_json.txt +++ b/pkg/cli/testdata/all/all_test_json.txt @@ -1,5 +1,5 @@ -APPS: +ACORNS: { "items": [ { diff --git a/pkg/cli/testdata/all/all_test_yaml.txt b/pkg/cli/testdata/all/all_test_yaml.txt index fd17fc5d6..01ee7240d 100644 --- a/pkg/cli/testdata/all/all_test_yaml.txt +++ b/pkg/cli/testdata/all/all_test_yaml.txt @@ -1,5 +1,5 @@ -APPS: +ACORNS: --- items: - metadata: diff --git a/pkg/cli/update.go b/pkg/cli/update.go index 2e2020e96..5d05e7711 100644 --- a/pkg/cli/update.go +++ b/pkg/cli/update.go @@ -13,7 +13,7 @@ var hideUpdateFlags = []string{"dangerous", "memory", "target-namespace", "secre func NewUpdate(c CommandContext) *cobra.Command { cmd := cli.Command(&Update{out: c.StdOut, client: c.ClientFactory}, cobra.Command{ - Use: "update [flags] APP_NAME [deploy flags]", + Use: "update [flags] ACORN_NAME [deploy flags]", SilenceUsage: true, Short: "Update a deployed Acorn", ValidArgsFunction: newCompletion(c.ClientFactory, appsCompletion).withShouldCompleteOptions(onlyNumArgs(1)).complete, diff --git a/pkg/cli/wait.go b/pkg/cli/wait.go index 47ce48290..a030b6863 100644 --- a/pkg/cli/wait.go +++ b/pkg/cli/wait.go @@ -8,7 +8,7 @@ import ( func NewWait(c CommandContext) *cobra.Command { return cli.Command(&Wait{client: c.ClientFactory}, cobra.Command{ - Use: "wait [flags] APP_NAME", + Use: "wait [flags] ACORN_NAME", SilenceUsage: true, Short: "Wait an app to be ready then exit with status code 0", Args: cobra.ExactArgs(1), diff --git a/pkg/tables/tables.go b/pkg/tables/tables.go index b23dd888d..a6ff93171 100644 --- a/pkg/tables/tables.go +++ b/pkg/tables/tables.go @@ -74,7 +74,7 @@ var ( Container = [][]string{ {"Name", "{{ . | name }}"}, - {"App", "Status.Columns.App"}, + {"Acorn", "Status.Columns.App"}, {"Image", "Spec.Image"}, {"State", "Status.Columns.State"}, {"RestartCount", "Status.RestartCount"},