diff --git a/internal/dao/benchmark.go b/internal/dao/benchmark.go index 9d81223a5b..d7525ee981 100644 --- a/internal/dao/benchmark.go +++ b/internal/dao/benchmark.go @@ -10,6 +10,7 @@ import ( "github.com/derailed/k9s/internal" "github.com/derailed/k9s/internal/render" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -26,7 +27,7 @@ type Benchmark struct { } // Delete nukes a resource. -func (b *Benchmark) Delete(path string, cascade, force bool) error { +func (b *Benchmark) Delete(path string, _ *metav1.DeletionPropagation, force bool) error { return os.Remove(path) } diff --git a/internal/dao/generic.go b/internal/dao/generic.go index e7e0f36b23..2614580690 100644 --- a/internal/dao/generic.go +++ b/internal/dao/generic.go @@ -90,7 +90,7 @@ func (g *Generic) ToYAML(path string, showManaged bool) (string, error) { } // Delete deletes a resource. -func (g *Generic) Delete(path string, cascade, force bool) error { +func (g *Generic) Delete(path string, propagation *metav1.DeletionPropagation, force bool) error { ns, n := client.Namespaced(path) auth, err := g.Client().CanI(ns, g.gvr.String(), []string{client.DeleteVerb}) if err != nil { @@ -100,16 +100,12 @@ func (g *Generic) Delete(path string, cascade, force bool) error { return fmt.Errorf("user is not authorized to delete %s", path) } - p := metav1.DeletePropagationOrphan - if cascade { - p = metav1.DeletePropagationBackground - } var grace *int64 if force { grace = &defaultKillGrace } opts := metav1.DeleteOptions{ - PropagationPolicy: &p, + PropagationPolicy: propagation, GracePeriodSeconds: grace, } diff --git a/internal/dao/helm.go b/internal/dao/helm.go index 22525004d8..c8c7ed6305 100644 --- a/internal/dao/helm.go +++ b/internal/dao/helm.go @@ -10,6 +10,7 @@ import ( "github.com/rs/zerolog/log" "gopkg.in/yaml.v2" "helm.sh/helm/v3/pkg/action" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -107,7 +108,7 @@ func (c *Helm) ToYAML(path string, showManaged bool) (string, error) { } // Delete uninstall a Helm. -func (c *Helm) Delete(path string, cascade, force bool) error { +func (c *Helm) Delete(path string, _ *metav1.DeletionPropagation, force bool) error { ns, n := client.Namespaced(path) cfg, err := c.EnsureHelmConfig(ns) if err != nil { diff --git a/internal/dao/port_forward.go b/internal/dao/port_forward.go index 1c3cbb8db1..cd9b022162 100644 --- a/internal/dao/port_forward.go +++ b/internal/dao/port_forward.go @@ -11,6 +11,7 @@ import ( "github.com/derailed/k9s/internal/config" "github.com/derailed/k9s/internal/render" "github.com/rs/zerolog/log" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -25,7 +26,7 @@ type PortForward struct { } // Delete a portforward. -func (p *PortForward) Delete(path string, cascade, force bool) error { +func (p *PortForward) Delete(path string, _ *metav1.DeletionPropagation, force bool) error { p.Factory.DeleteForwarder(path) return nil diff --git a/internal/dao/screen_dump.go b/internal/dao/screen_dump.go index 84d63b2c42..7be5821c24 100644 --- a/internal/dao/screen_dump.go +++ b/internal/dao/screen_dump.go @@ -8,6 +8,7 @@ import ( "github.com/derailed/k9s/internal" "github.com/derailed/k9s/internal/render" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -25,7 +26,7 @@ type ScreenDump struct { } // Delete a ScreenDump. -func (d *ScreenDump) Delete(path string, cascade, force bool) error { +func (d *ScreenDump) Delete(path string, _ *metav1.DeletionPropagation, force bool) error { return os.Remove(path) } diff --git a/internal/dao/types.go b/internal/dao/types.go index dd3e2d61c8..a1841b13a2 100644 --- a/internal/dao/types.go +++ b/internal/dao/types.go @@ -120,7 +120,7 @@ type Controller interface { // Nuker represents a resource deleter. type Nuker interface { // Delete removes a resource from the api server. - Delete(path string, cascade, force bool) error + Delete(path string, propagation *metav1.DeletionPropagation, force bool) error } // Switchable represents a switchable resource. diff --git a/internal/model/table.go b/internal/model/table.go index 954a1c1860..1574cc37bc 100644 --- a/internal/model/table.go +++ b/internal/model/table.go @@ -13,6 +13,7 @@ import ( "github.com/derailed/k9s/internal/dao" "github.com/derailed/k9s/internal/render" "github.com/rs/zerolog/log" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" "k8s.io/apimachinery/pkg/runtime" ) @@ -110,7 +111,7 @@ func (t *Table) Get(ctx context.Context, path string) (runtime.Object, error) { } // Delete deletes a resource. -func (t *Table) Delete(ctx context.Context, path string, cascade, force bool) error { +func (t *Table) Delete(ctx context.Context, path string, propagation *metav1.DeletionPropagation, force bool) error { meta, err := getMeta(ctx, t.gvr) if err != nil { return err @@ -121,7 +122,7 @@ func (t *Table) Delete(ctx context.Context, path string, cascade, force bool) er return fmt.Errorf("no nuker for %q", meta.DAO.GVR()) } - return nuker.Delete(path, cascade, force) + return nuker.Delete(path, propagation, force) } // GetNamespace returns the model namespace. diff --git a/internal/ui/dialog/delete.go b/internal/ui/dialog/delete.go index b107560b3e..a24a2393a7 100644 --- a/internal/ui/dialog/delete.go +++ b/internal/ui/dialog/delete.go @@ -4,18 +4,30 @@ import ( "github.com/derailed/k9s/internal/config" "github.com/derailed/k9s/internal/ui" "github.com/derailed/tview" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const deleteKey = "delete" +const noDeletePropagation = "None" + +const defaultPropagationIdx = 0 + +var propagationOptions []string = []string{ + string(metav1.DeletePropagationBackground), + string(metav1.DeletePropagationForeground), + string(metav1.DeletePropagationOrphan), + noDeletePropagation, +} + type ( - okFunc func(cascade, force bool) + okFunc func(propagation *metav1.DeletionPropagation, force bool) cancelFunc func() ) // ShowDelete pops a resource deletion dialog. func ShowDelete(styles config.Dialog, pages *ui.Pages, msg string, ok okFunc, cancel cancelFunc) { - cascade, force := true, false + propagation, force := "", false f := tview.NewForm() f.SetItemPadding(0) f.SetButtonsAlign(tview.AlignCenter). @@ -23,8 +35,8 @@ func ShowDelete(styles config.Dialog, pages *ui.Pages, msg string, ok okFunc, ca SetButtonTextColor(styles.ButtonFgColor.Color()). SetLabelColor(styles.LabelFgColor.Color()). SetFieldTextColor(styles.FieldFgColor.Color()) - f.AddCheckbox("Cascade:", cascade, func(_ string, checked bool) { - cascade = checked + f.AddDropDown("Propagation:", propagationOptions, defaultPropagationIdx, func(_ string, optionIndex int) { + propagation = propagationOptions[optionIndex] }) f.AddCheckbox("Force:", force, func(_ string, checked bool) { force = checked @@ -34,7 +46,13 @@ func ShowDelete(styles config.Dialog, pages *ui.Pages, msg string, ok okFunc, ca cancel() }) f.AddButton("OK", func() { - ok(cascade, force) + switch propagation { + case noDeletePropagation: + ok(nil, force) + default: + p := metav1.DeletionPropagation(propagation) + ok(&p, force) + } dismissDelete(pages) cancel() }) diff --git a/internal/ui/dialog/delete_test.go b/internal/ui/dialog/delete_test.go index c31325b7f4..a81cdb00cf 100644 --- a/internal/ui/dialog/delete_test.go +++ b/internal/ui/dialog/delete_test.go @@ -7,13 +7,14 @@ import ( "github.com/derailed/k9s/internal/ui" "github.com/derailed/tview" "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestDeleteDialog(t *testing.T) { p := ui.NewPages() - okFunc := func(c, f bool) { - assert.True(t, c) + okFunc := func(p *metav1.DeletionPropagation, f bool) { + assert.Equal(t, propagationOptions[defaultPropagationIdx], p) assert.True(t, f) } caFunc := func() { diff --git a/internal/ui/table_test.go b/internal/ui/table_test.go index 7c0657d877..ba7fe8e60e 100644 --- a/internal/ui/table_test.go +++ b/internal/ui/table_test.go @@ -12,6 +12,7 @@ import ( "github.com/derailed/k9s/internal/render" "github.com/derailed/k9s/internal/ui" "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -79,7 +80,7 @@ func (t *mockModel) Get(ctx context.Context, path string) (runtime.Object, error return nil, nil } -func (t *mockModel) Delete(ctx context.Context, path string, c, f bool) error { +func (t *mockModel) Delete(ctx context.Context, path string, p *metav1.DeletionPropagation, f bool) error { return nil } diff --git a/internal/ui/types.go b/internal/ui/types.go index 229497c9e1..9c7db7c1e8 100644 --- a/internal/ui/types.go +++ b/internal/ui/types.go @@ -6,6 +6,7 @@ import ( "github.com/derailed/k9s/internal/model" "github.com/derailed/k9s/internal/render" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -77,5 +78,5 @@ type Tabular interface { RemoveListener(model.TableListener) // Delete a resource. - Delete(ctx context.Context, path string, cascade, force bool) error + Delete(ctx context.Context, path string, propagation *metav1.DeletionPropagation, force bool) error } diff --git a/internal/view/alias_test.go b/internal/view/alias_test.go index 3b207e9f96..657de412a0 100644 --- a/internal/view/alias_test.go +++ b/internal/view/alias_test.go @@ -15,6 +15,7 @@ import ( "github.com/gdamore/tcell/v2" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -125,7 +126,7 @@ func (t *mockModel) Get(context.Context, string) (runtime.Object, error) { return nil, nil } -func (t *mockModel) Delete(context.Context, string, bool, bool) error { +func (t *mockModel) Delete(context.Context, string, *metav1.DeletionPropagation, bool) error { return nil } diff --git a/internal/view/browser.go b/internal/view/browser.go index 12e74cd85c..cc6ae3965e 100644 --- a/internal/view/browser.go +++ b/internal/view/browser.go @@ -523,7 +523,7 @@ func (b *Browser) simpleDelete(selections []string, msg string) { b.app.Flash().Errf("Invalid nuker %T", b.accessor) continue } - if err := nuker.Delete(sel, true, true); err != nil { + if err := nuker.Delete(sel, nil, true); err != nil { b.app.Flash().Errf("Delete failed with `%s", err) } else { b.app.factory.DeleteForwarder(sel) @@ -535,7 +535,7 @@ func (b *Browser) simpleDelete(selections []string, msg string) { } func (b *Browser) resourceDelete(selections []string, msg string) { - dialog.ShowDelete(b.app.Styles.Dialog(), b.app.Content.Pages, msg, func(cascade, force bool) { + dialog.ShowDelete(b.app.Styles.Dialog(), b.app.Content.Pages, msg, func(propagation *metav1.DeletionPropagation, force bool) { b.ShowDeleted() if len(selections) > 1 { b.app.Flash().Infof("Delete %d marked %s", len(selections), b.GVR()) @@ -543,7 +543,7 @@ func (b *Browser) resourceDelete(selections []string, msg string) { b.app.Flash().Infof("Delete resource %s %s", b.GVR(), selections[0]) } for _, sel := range selections { - if err := b.GetModel().Delete(b.defaultContext(), sel, cascade, force); err != nil { + if err := b.GetModel().Delete(b.defaultContext(), sel, propagation, force); err != nil { b.app.Flash().Errf("Delete failed with `%s", err) } else { b.app.factory.DeleteForwarder(sel) diff --git a/internal/view/pf.go b/internal/view/pf.go index 377c0f0fd3..12f1299cf4 100644 --- a/internal/view/pf.go +++ b/internal/view/pf.go @@ -162,7 +162,7 @@ func (p *PortForward) deleteCmd(evt *tcell.EventKey) *tcell.EventKey { for _, s := range selections { var pf dao.PortForward pf.Init(p.App().factory, client.NewGVR("portforwards")) - if err := pf.Delete(s, true, true); err != nil { + if err := pf.Delete(s, nil, true); err != nil { p.App().Flash().Err(err) return } diff --git a/internal/view/pod.go b/internal/view/pod.go index a028f4be83..96942e8435 100644 --- a/internal/view/pod.go +++ b/internal/view/pod.go @@ -177,7 +177,7 @@ func (p *Pod) killCmd(evt *tcell.EventKey) *tcell.EventKey { } p.GetTable().ShowDeleted() for _, path := range selections { - if err := nuker.Delete(path, true, true); err != nil { + if err := nuker.Delete(path, nil, true); err != nil { p.App().Flash().Errf("Delete failed with %s", err) } else { p.App().factory.DeleteForwarder(path) diff --git a/internal/view/table_int_test.go b/internal/view/table_int_test.go index 54c40e6eb4..11bba71e30 100644 --- a/internal/view/table_int_test.go +++ b/internal/view/table_int_test.go @@ -16,6 +16,7 @@ import ( "github.com/derailed/tview" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -110,7 +111,7 @@ func (t *mockTableModel) Get(context.Context, string) (runtime.Object, error) { return nil, nil } -func (t *mockTableModel) Delete(context.Context, string, bool, bool) error { +func (t *mockTableModel) Delete(context.Context, string, *metav1.DeletionPropagation, bool) error { return nil } diff --git a/internal/view/xray.go b/internal/view/xray.go index 9f1ac05d6f..96525cd832 100644 --- a/internal/view/xray.go +++ b/internal/view/xray.go @@ -657,7 +657,7 @@ func (x *Xray) styleTitle() string { } func (x *Xray) resourceDelete(gvr client.GVR, spec *xray.NodeSpec, msg string) { - dialog.ShowDelete(x.app.Styles.Dialog(), x.app.Content.Pages, msg, func(cascade, force bool) { + dialog.ShowDelete(x.app.Styles.Dialog(), x.app.Content.Pages, msg, func(propagation *metav1.DeletionPropagation, force bool) { x.app.Flash().Infof("Delete resource %s %s", spec.GVR(), spec.Path()) accessor, err := dao.AccessorFor(x.app.factory, gvr) if err != nil { @@ -670,7 +670,7 @@ func (x *Xray) resourceDelete(gvr client.GVR, spec *xray.NodeSpec, msg string) { x.app.Flash().Errf("Invalid nuker %T", accessor) return } - if err := nuker.Delete(spec.Path(), true, true); err != nil { + if err := nuker.Delete(spec.Path(), nil, true); err != nil { x.app.Flash().Errf("Delete failed with `%s", err) } else { x.app.Flash().Infof("%s `%s deleted successfully", x.GVR(), spec.Path())