Release v0.27.3 (#1970)

- [Issue #1968](https://github.com/derailed/k9s/issues/1968) Some skins are missing the definitions for the help menu
- [Issue #1967](https://github.com/derailed/k9s/issues/1967) Helm cve-2023-25165
- [Issue #1964](https://github.com/derailed/k9s/issues/1964) logger.sinceSeconds config setting inconsistent with README
- [Issue #1955](https://github.com/derailed/k9s/issues/1955) K9s crashes with empty resources and/or verbs in RBAC
- [Issue #1954](https://github.com/derailed/k9s/issues/1954) Open very slow
- [Issue #1883](https://github.com/derailed/k9s/issues/1883) Fix force deletion
- [Issue #1788](https://github.com/derailed/k9s/issues/1788) Draining nodes cannot be forced
- [Issue #1150](https://github.com/derailed/k9s/issues/1150) Add a persistent popup for drain failures
mine
Fernand Galiana 2023-02-12 08:12:25 -07:00 committed by GitHub
parent d4ae1c7a74
commit 7c76691c38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 225 additions and 89 deletions

View File

@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
else else
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ") DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
endif endif
VERSION ?= v0.27.2 VERSION ?= v0.27.3
IMG_NAME := derailed/k9s IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION} IMAGE := ${IMG_NAME}:${VERSION}

View File

@ -258,7 +258,7 @@ K9s uses aliases to navigate most K8s resources.
| To view and switch to another Kubernetes namespace | `:`ns⏎ | | | To view and switch to another Kubernetes namespace | `:`ns⏎ | |
| To view all saved resources | `:`screendump or sd⏎ | | | To view all saved resources | `:`screendump or sd⏎ | |
| To delete a resource (TAB and ENTER to confirm) | `ctrl-d` | | | To delete a resource (TAB and ENTER to confirm) | `ctrl-d` | |
| To kill a resource (no confirmation dialog!) | `ctrl-k` | | | To kill a resource (no confirmation dialog, equivalent to kubectl delete --now) | `ctrl-k` | |
| Launch pulses view | `:`pulses or pu⏎ | | | Launch pulses view | `:`pulses or pu⏎ | |
| Launch XRay view | `:`xray RESOURCE [NAMESPACE]⏎ | RESOURCE can be one of po, svc, dp, rs, sts, ds, NAMESPACE is optional | | Launch XRay view | `:`xray RESOURCE [NAMESPACE]⏎ | RESOURCE can be one of po, svc, dp, rs, sts, ds, NAMESPACE is optional |
| Launch Popeye view | `:`popeye or pop⏎ | See [popeye](#popeye) | | Launch Popeye view | `:`popeye or pop⏎ | See [popeye](#popeye) |

View File

@ -0,0 +1,67 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
# Release v0.27.3
## Notes
Thank you to all that contributed with flushing out issues and enhancements for K9s! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make K9s better are, as ever, very much noted and appreciated! Also big thanks to all that have allocated their own time to help others on both slack and on this repo!!
As you may know, K9s is not pimped out by corps with deep pockets, thus if you feel K9s is helping your Kubernetes journey, please consider joining our [sponsorship program](https://github.com/sponsors/derailed) and/or make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
On Slack? Please join us [K9slackers](https://join.slack.com/t/k9sers/shared_invite/enQtOTA5MDEyNzI5MTU0LWQ1ZGI3MzliYzZhZWEyNzYxYzA3NjE0YTk1YmFmNzViZjIyNzhkZGI0MmJjYzhlNjdlMGJhYzE2ZGU1NjkyNTM)
---
## ♫ Sounds Behind The Release ♭
* [Bitches Brew - Miles Davis](https://www.youtube.com/watch?v=50fB5L1vmn8)
* [Sordid Affair - Röyksopp](https://www.youtube.com/watch?v=ECL5zO6ImsA)
* [Love Inc - Booka Shade](https://www.youtube.com/watch?v=sgLxTcok8kQ)
* [Twisted - Kaz James,Nick Morgan](https://www.youtube.com/watch?v=oOsYJ-Co8Y4)
---
## A Word From Our Sponsors...
To all the good folks below that opted to `pay it forward` and join our sponsorship program, I salute you!!
* [Astraea](https://github.com/s22)
* [Arnaud Bienvenu](https://github.com/abienvenu)
* [Eric Caleb](https://github.com/iamcaleberic)
* [Sean Williams](https://github.com/SeanThomasWilliams)
* [Federico Ragona](https://github.com/fedragon)
> Sponsorship cancellations since the last release: `7` ;(
---
## Maintenance Release
---
## Resolved Issues
* [Issue #1968](https://github.com/derailed/k9s/issues/1968) Some skins are missing the definitions for the help menu
* [Issue #1967](https://github.com/derailed/k9s/issues/1967) Helm cve-2023-25165
* [Issue #1964](https://github.com/derailed/k9s/issues/1964) logger.sinceSeconds config setting inconsistent with README
* [Issue #1955](https://github.com/derailed/k9s/issues/1955) K9s crashes with empty resources and/or verbs in RBAC
* [Issue #1954](https://github.com/derailed/k9s/issues/1954) Open very slow
* [Issue #1883](https://github.com/derailed/k9s/issues/1883) Fix force deletion
* [Issue #1788](https://github.com/derailed/k9s/issues/1788) Draining nodes cannot be forced
* [Issue #1150](https://github.com/derailed/k9s/issues/1150) Add a persistent popup for drain failures
---
## Contributed PRs
Please give `Big Thanks!` and `ATTA Girls/Boys!` to all the fine contributors for making K9s better for all of us!!
* [PR #1969](https://github.com/derailed/k9s/pull/1969) fix: Add missing help menu to gruvbox-dark skin
* [PR #1966](https://github.com/derailed/k9s/pull/1966) fix: Show meaningful error message when kubectl exec fails
* [PR #1965](https://github.com/derailed/k9s/pull/1965) set default sinceSeconds to 300
* [PR #1961](https://github.com/derailed/k9s/pull/1961) feat: Add sort by pod count on node view
* [PR #1960](https://github.com/derailed/k9s/pull/1960) [Misc] Add Nightfox-theme
---
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2022 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)

View File

@ -3,13 +3,14 @@ package cmd
import ( import (
"fmt" "fmt"
"os"
"github.com/derailed/k9s/internal/color" "github.com/derailed/k9s/internal/color"
"github.com/derailed/k9s/internal/config" "github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/ui" "github.com/derailed/k9s/internal/ui"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"os"
) )
func infoCmd() *cobra.Command { func infoCmd() *cobra.Command {
@ -56,5 +57,9 @@ func getScreenDumpDirForInfo() string {
log.Error().Err(err).Msgf("Unmarshal k9s config %v", err) log.Error().Err(err).Msgf("Unmarshal k9s config %v", err)
return config.K9sDefaultScreenDumpDir return config.K9sDefaultScreenDumpDir
} }
if cfg.K9s == nil {
cfg.K9s = config.NewK9s()
}
return cfg.K9s.GetScreenDumpDir() return cfg.K9s.GetScreenDumpDir()
} }

View File

@ -27,7 +27,7 @@ type Benchmark struct {
} }
// Delete nukes a resource. // Delete nukes a resource.
func (b *Benchmark) Delete(_ context.Context, path string, _ *metav1.DeletionPropagation, force bool) error { func (b *Benchmark) Delete(_ context.Context, path string, _ *metav1.DeletionPropagation, _ Grace) error {
return os.Remove(path) return os.Remove(path)
} }

View File

@ -12,6 +12,19 @@ import (
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
) )
type Grace int64
const (
// DefaultGrace uses delete default termination policy.
DefaultGrace Grace = -1
// ForceGrace sets delete grace-period to 0.
ForceGrace Grace = 0
// NowGrace set delete grace-period to 1,
NowGrace Grace = 1
)
var _ Describer = (*Generic)(nil) var _ Describer = (*Generic)(nil)
// Generic represents a generic resource. // Generic represents a generic resource.
@ -88,7 +101,7 @@ func (g *Generic) ToYAML(path string, showManaged bool) (string, error) {
} }
// Delete deletes a resource. // Delete deletes a resource.
func (g *Generic) Delete(ctx context.Context, path string, propagation *metav1.DeletionPropagation, force bool) error { func (g *Generic) Delete(ctx context.Context, path string, propagation *metav1.DeletionPropagation, grace Grace) error {
ns, n := client.Namespaced(path) ns, n := client.Namespaced(path)
auth, err := g.Client().CanI(ns, g.gvr.String(), []string{client.DeleteVerb}) auth, err := g.Client().CanI(ns, g.gvr.String(), []string{client.DeleteVerb})
if err != nil { if err != nil {
@ -98,13 +111,13 @@ func (g *Generic) Delete(ctx context.Context, path string, propagation *metav1.D
return fmt.Errorf("user is not authorized to delete %s", path) return fmt.Errorf("user is not authorized to delete %s", path)
} }
var gracePeriod *int64
if grace != DefaultGrace {
gracePeriod = (*int64)(&grace)
}
opts := metav1.DeleteOptions{ opts := metav1.DeleteOptions{
PropagationPolicy: propagation, PropagationPolicy: propagation,
} GracePeriodSeconds: gracePeriod,
if force {
var defaultKillGrace int64 = 1
opts.GracePeriodSeconds = &defaultKillGrace
} }
dial, err := g.dynClient() dial, err := g.dynClient()

View File

@ -111,7 +111,7 @@ func (h *Helm) ToYAML(path string, showManaged bool) (string, error) {
} }
// Delete uninstall a Helm. // Delete uninstall a Helm.
func (h *Helm) Delete(_ context.Context, path string, _ *metav1.DeletionPropagation, force bool) error { func (h *Helm) Delete(_ context.Context, path string, _ *metav1.DeletionPropagation, _ Grace) error {
ns, n := client.Namespaced(path) ns, n := client.Namespaced(path)
cfg, err := h.EnsureHelmConfig(ns) cfg, err := h.EnsureHelmConfig(ns)
if err != nil { if err != nil {

View File

@ -77,6 +77,7 @@ func (o DrainOptions) toDrainHelper(k kubernetes.Interface, w io.Writer) drain.H
IgnoreAllDaemonSets: o.IgnoreAllDaemonSets, IgnoreAllDaemonSets: o.IgnoreAllDaemonSets,
Out: w, Out: w,
ErrOut: w, ErrOut: w,
Force: o.Force,
} }
} }

View File

@ -26,7 +26,7 @@ type PortForward struct {
} }
// Delete deletes a portforward. // Delete deletes a portforward.
func (p *PortForward) Delete(_ context.Context, path string, _ *metav1.DeletionPropagation, _ bool) error { func (p *PortForward) Delete(_ context.Context, path string, _ *metav1.DeletionPropagation, _ Grace) error {
p.GetFactory().DeleteForwarder(path) p.GetFactory().DeleteForwarder(path)
return nil return nil

View File

@ -22,7 +22,7 @@ type ScreenDump struct {
} }
// Delete a ScreenDump. // Delete a ScreenDump.
func (d *ScreenDump) Delete(_ context.Context, path string, _ *metav1.DeletionPropagation, force bool) error { func (d *ScreenDump) Delete(_ context.Context, path string, _ *metav1.DeletionPropagation, _ Grace) error {
return os.Remove(path) return os.Remove(path)
} }

View File

@ -120,7 +120,7 @@ type Controller interface {
// Nuker represents a resource deleter. // Nuker represents a resource deleter.
type Nuker interface { type Nuker interface {
// Delete removes a resource from the api server. // Delete removes a resource from the api server.
Delete(ctx context.Context, path string, propagation *metav1.DeletionPropagation, force bool) error Delete(context.Context, string, *metav1.DeletionPropagation, Grace) error
} }
// Switchable represents a switchable resource. // Switchable represents a switchable resource.

View File

@ -111,7 +111,7 @@ func (t *Table) Get(ctx context.Context, path string) (runtime.Object, error) {
} }
// Delete deletes a resource. // Delete deletes a resource.
func (t *Table) Delete(ctx context.Context, path string, propagation *metav1.DeletionPropagation, force bool) error { func (t *Table) Delete(ctx context.Context, path string, propagation *metav1.DeletionPropagation, grace dao.Grace) error {
meta, err := getMeta(ctx, t.gvr) meta, err := getMeta(ctx, t.gvr)
if err != nil { if err != nil {
return err return err
@ -122,7 +122,7 @@ func (t *Table) Delete(ctx context.Context, path string, propagation *metav1.Del
return fmt.Errorf("no nuker for %q", meta.DAO.GVR()) return fmt.Errorf("no nuker for %q", meta.DAO.GVR())
} }
return nuker.Delete(ctx, path, propagation, force) return nuker.Delete(ctx, path, propagation, grace)
} }
// GetNamespace returns the model namespace. // GetNamespace returns the model namespace.

View File

@ -74,6 +74,9 @@ func (Policy) Render(o interface{}, gvr string, r *Row) error {
// Helpers... // Helpers...
func cleanseResource(r string) string { func cleanseResource(r string) string {
if r == "" {
return r
}
if r[0] == '/' { if r[0] == '/' {
return r return r
} }

View File

@ -6,11 +6,9 @@ import (
"github.com/derailed/tview" "github.com/derailed/tview"
) )
const confirmKey = "confirm" const dialogKey = "dialog"
type ( type confirmFunc func()
confirmFunc func()
)
// ShowConfirm pops a confirmation dialog. // ShowConfirm pops a confirmation dialog.
func ShowConfirm(styles config.Dialog, pages *ui.Pages, title, msg string, ack confirmFunc, cancel cancelFunc) { func ShowConfirm(styles config.Dialog, pages *ui.Pages, title, msg string, ack confirmFunc, cancel cancelFunc) {
@ -22,12 +20,12 @@ func ShowConfirm(styles config.Dialog, pages *ui.Pages, title, msg string, ack c
SetLabelColor(styles.LabelFgColor.Color()). SetLabelColor(styles.LabelFgColor.Color()).
SetFieldTextColor(styles.FieldFgColor.Color()) SetFieldTextColor(styles.FieldFgColor.Color())
f.AddButton("Cancel", func() { f.AddButton("Cancel", func() {
dismissConfirm(pages) dismiss(pages)
cancel() cancel()
}) })
f.AddButton("OK", func() { f.AddButton("OK", func() {
ack() ack()
dismissConfirm(pages) dismiss(pages)
cancel() cancel()
}) })
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
@ -43,13 +41,13 @@ func ShowConfirm(styles config.Dialog, pages *ui.Pages, title, msg string, ack c
modal.SetText(msg) modal.SetText(msg)
modal.SetTextColor(styles.FgColor.Color()) modal.SetTextColor(styles.FgColor.Color())
modal.SetDoneFunc(func(int, string) { modal.SetDoneFunc(func(int, string) {
dismissConfirm(pages) dismiss(pages)
cancel() cancel()
}) })
pages.AddPage(confirmKey, modal, false, false) pages.AddPage(dialogKey, modal, false, false)
pages.ShowPage(confirmKey) pages.ShowPage(dialogKey)
} }
func dismissConfirm(pages *ui.Pages) { func dismiss(pages *ui.Pages) {
pages.RemovePage(confirmKey) pages.RemovePage(dialogKey)
} }

View File

@ -22,9 +22,9 @@ func TestConfirmDialog(t *testing.T) {
} }
ShowConfirm(config.Dialog{}, p, "Blee", "Yo", ackFunc, caFunc) ShowConfirm(config.Dialog{}, p, "Blee", "Yo", ackFunc, caFunc)
d := p.GetPrimitive(confirmKey).(*tview.ModalForm) d := p.GetPrimitive(dialogKey).(*tview.ModalForm)
assert.NotNil(t, d) assert.NotNil(t, d)
dismissConfirm(p) dismiss(p)
assert.Nil(t, p.GetPrimitive(confirmKey)) assert.Nil(t, p.GetPrimitive(dialogKey))
} }

View File

@ -7,11 +7,15 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
const deleteKey = "delete" const (
noDeletePropagation = "None"
defaultPropagationIdx = 0
)
const noDeletePropagation = "None" type (
okFunc func(propagation *metav1.DeletionPropagation, force bool)
const defaultPropagationIdx = 0 cancelFunc func()
)
var propagationOptions []string = []string{ var propagationOptions []string = []string{
string(metav1.DeletePropagationBackground), string(metav1.DeletePropagationBackground),
@ -20,11 +24,6 @@ var propagationOptions []string = []string{
noDeletePropagation, noDeletePropagation,
} }
type (
okFunc func(propagation *metav1.DeletionPropagation, force bool)
cancelFunc func()
)
// ShowDelete pops a resource deletion dialog. // ShowDelete pops a resource deletion dialog.
func ShowDelete(styles config.Dialog, pages *ui.Pages, msg string, ok okFunc, cancel cancelFunc) { func ShowDelete(styles config.Dialog, pages *ui.Pages, msg string, ok okFunc, cancel cancelFunc) {
propagation, force := "", false propagation, force := "", false
@ -47,7 +46,7 @@ func ShowDelete(styles config.Dialog, pages *ui.Pages, msg string, ok okFunc, ca
force = checked force = checked
}) })
f.AddButton("Cancel", func() { f.AddButton("Cancel", func() {
dismissDelete(pages) dismiss(pages)
cancel() cancel()
}) })
f.AddButton("OK", func() { f.AddButton("OK", func() {
@ -58,7 +57,7 @@ func ShowDelete(styles config.Dialog, pages *ui.Pages, msg string, ok okFunc, ca
p := metav1.DeletionPropagation(propagation) p := metav1.DeletionPropagation(propagation)
ok(&p, force) ok(&p, force)
} }
dismissDelete(pages) dismiss(pages)
cancel() cancel()
}) })
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
@ -74,13 +73,9 @@ func ShowDelete(styles config.Dialog, pages *ui.Pages, msg string, ok okFunc, ca
confirm := tview.NewModalForm("<Delete>", f) confirm := tview.NewModalForm("<Delete>", f)
confirm.SetText(msg) confirm.SetText(msg)
confirm.SetDoneFunc(func(int, string) { confirm.SetDoneFunc(func(int, string) {
dismissDelete(pages) dismiss(pages)
cancel() cancel()
}) })
pages.AddPage(deleteKey, confirm, false, false) pages.AddPage(dialogKey, confirm, false, false)
pages.ShowPage(deleteKey) pages.ShowPage(dialogKey)
}
func dismissDelete(pages *ui.Pages) {
pages.RemovePage(deleteKey)
} }

View File

@ -22,9 +22,9 @@ func TestDeleteDialog(t *testing.T) {
} }
ShowDelete(config.Dialog{}, p, "Yo", okFunc, caFunc) ShowDelete(config.Dialog{}, p, "Yo", okFunc, caFunc)
d := p.GetPrimitive(deleteKey).(*tview.ModalForm) d := p.GetPrimitive(dialogKey).(*tview.ModalForm)
assert.NotNil(t, d) assert.NotNil(t, d)
dismissDelete(p) dismiss(p)
assert.Nil(t, p.GetPrimitive(deleteKey)) assert.Nil(t, p.GetPrimitive(dialogKey))
} }

View File

@ -10,7 +10,7 @@ import (
"github.com/derailed/tview" "github.com/derailed/tview"
) )
// ShowConfirm pops a confirmation dialog. // ShowError pops an error dialog.
func ShowError(styles config.Dialog, pages *ui.Pages, msg string) { func ShowError(styles config.Dialog, pages *ui.Pages, msg string) {
f := tview.NewForm() f := tview.NewForm()
f.SetItemPadding(0) f.SetItemPadding(0)
@ -20,7 +20,7 @@ func ShowError(styles config.Dialog, pages *ui.Pages, msg string) {
SetLabelColor(styles.LabelFgColor.Color()). SetLabelColor(styles.LabelFgColor.Color()).
SetFieldTextColor(tcell.ColorIndianRed) SetFieldTextColor(tcell.ColorIndianRed)
f.AddButton("Dismiss", func() { f.AddButton("Dismiss", func() {
dismissError(pages) dismiss(pages)
}) })
if b := f.GetButton(0); b != nil { if b := f.GetButton(0); b != nil {
b.SetBackgroundColorActivated(styles.ButtonFocusBgColor.Color()) b.SetBackgroundColorActivated(styles.ButtonFocusBgColor.Color())
@ -31,14 +31,10 @@ func ShowError(styles config.Dialog, pages *ui.Pages, msg string) {
modal.SetText(cowTalk(msg)) modal.SetText(cowTalk(msg))
modal.SetTextColor(tcell.ColorOrangeRed) modal.SetTextColor(tcell.ColorOrangeRed)
modal.SetDoneFunc(func(int, string) { modal.SetDoneFunc(func(int, string) {
dismissError(pages) dismiss(pages)
}) })
pages.AddPage(confirmKey, modal, false, false) pages.AddPage(dialogKey, modal, false, false)
pages.ShowPage(confirmKey) pages.ShowPage(dialogKey)
}
func dismissError(pages *ui.Pages) {
pages.RemovePage(confirmKey)
} }
func cowTalk(says string) string { func cowTalk(says string) string {

View File

@ -0,0 +1,21 @@
package dialog
import (
"testing"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tview"
"github.com/stretchr/testify/assert"
)
func TestErrorDialog(t *testing.T) {
p := ui.NewPages()
ShowError(config.Dialog{}, p, "Yo")
d := p.GetPrimitive(dialogKey).(*tview.ModalForm)
assert.NotNil(t, d)
dismiss(p)
assert.Nil(t, p.GetPrimitive(dialogKey))
}

View File

@ -8,6 +8,7 @@ import (
"github.com/derailed/k9s/internal" "github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config" "github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/model" "github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/render" "github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui" "github.com/derailed/k9s/internal/ui"
@ -80,7 +81,7 @@ func (t *mockModel) Get(ctx context.Context, path string) (runtime.Object, error
return nil, nil return nil, nil
} }
func (t *mockModel) Delete(ctx context.Context, path string, p *metav1.DeletionPropagation, f bool) error { func (t *mockModel) Delete(context.Context, string, *metav1.DeletionPropagation, dao.Grace) error {
return nil return nil
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"time" "time"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/model" "github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/render" "github.com/derailed/k9s/internal/render"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -78,5 +79,5 @@ type Tabular interface {
RemoveListener(model.TableListener) RemoveListener(model.TableListener)
// Delete a resource. // Delete a resource.
Delete(ctx context.Context, path string, propagation *metav1.DeletionPropagation, force bool) error Delete(context.Context, string, *metav1.DeletionPropagation, dao.Grace) error
} }

View File

@ -8,6 +8,7 @@ import (
"github.com/derailed/k9s/internal" "github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config" "github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/model" "github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/render" "github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui" "github.com/derailed/k9s/internal/ui"
@ -126,7 +127,7 @@ func (t *mockModel) Get(context.Context, string) (runtime.Object, error) {
return nil, nil return nil, nil
} }
func (t *mockModel) Delete(context.Context, string, *metav1.DeletionPropagation, bool) error { func (t *mockModel) Delete(context.Context, string, *metav1.DeletionPropagation, dao.Grace) error {
return nil return nil
} }

View File

@ -121,6 +121,7 @@ func (b *Browser) bindKeys(aa ui.KeyActions) {
tcell.KeyEscape: ui.NewSharedKeyAction("Filter Reset", b.resetCmd, false), tcell.KeyEscape: ui.NewSharedKeyAction("Filter Reset", b.resetCmd, false),
tcell.KeyEnter: ui.NewSharedKeyAction("Filter", b.filterCmd, false), tcell.KeyEnter: ui.NewSharedKeyAction("Filter", b.filterCmd, false),
tcell.KeyHelp: ui.NewSharedKeyAction("Help", b.helpCmd, false), tcell.KeyHelp: ui.NewSharedKeyAction("Help", b.helpCmd, false),
ui.KeyV: ui.NewSharedKeyAction("Zob", b.blahCmd, true),
}) })
} }
@ -350,6 +351,28 @@ func (b *Browser) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
return nil return nil
} }
func (b *Browser) blahCmd(evt *tcell.EventKey) *tcell.EventKey {
b.Stop()
defer b.Start()
{
v := NewDetails(b.app, "Results", "Blee", true)
if err := v.app.inject(v, false); err != nil {
v.app.Flash().Err(err)
}
for i := 0; i < 10; i++ {
j := i
b.app.QueueUpdateDraw(func() {
log.Debug().Msgf("YO %d", j)
fmt.Fprintf(v.GetWriter(), "Yo %d\n", j)
time.Sleep(1 * time.Second)
})
}
}
return nil
}
func (b *Browser) describeCmd(evt *tcell.EventKey) *tcell.EventKey { func (b *Browser) describeCmd(evt *tcell.EventKey) *tcell.EventKey {
path := b.GetSelectedItem() path := b.GetSelectedItem()
if path == "" { if path == "" {
@ -531,7 +554,7 @@ func (b *Browser) simpleDelete(selections []string, msg string) {
b.app.Flash().Errf("Invalid nuker %T", b.accessor) b.app.Flash().Errf("Invalid nuker %T", b.accessor)
continue continue
} }
if err := nuker.Delete(context.Background(), sel, nil, false); err != nil { if err := nuker.Delete(context.Background(), sel, nil, dao.DefaultGrace); err != nil {
b.app.Flash().Errf("Delete failed with `%s", err) b.app.Flash().Errf("Delete failed with `%s", err)
} else { } else {
b.app.factory.DeleteForwarder(sel) b.app.factory.DeleteForwarder(sel)
@ -551,7 +574,11 @@ func (b *Browser) resourceDelete(selections []string, msg string) {
b.app.Flash().Infof("Delete resource %s %s", b.GVR(), selections[0]) b.app.Flash().Infof("Delete resource %s %s", b.GVR(), selections[0])
} }
for _, sel := range selections { for _, sel := range selections {
if err := b.GetModel().Delete(b.defaultContext(), sel, propagation, force); err != nil { grace := dao.DefaultGrace
if force {
grace = dao.ForceGrace
}
if err := b.GetModel().Delete(b.defaultContext(), sel, propagation, grace); err != nil {
b.app.Flash().Errf("Delete failed with `%s", err) b.app.Flash().Errf("Delete failed with `%s", err)
} else { } else {
b.app.factory.DeleteForwarder(sel) b.app.factory.DeleteForwarder(sel)

View File

@ -3,6 +3,7 @@ package view
import ( import (
"context" "context"
"fmt" "fmt"
"io"
"strings" "strings"
"github.com/derailed/k9s/internal/config" "github.com/derailed/k9s/internal/config"
@ -159,6 +160,10 @@ func (d *Details) Update(buff string) *Details {
return d return d
} }
func (d *Details) GetWriter() io.Writer {
return d.text
}
// SetSubject updates the subject. // SetSubject updates the subject.
func (d *Details) SetSubject(s string) { func (d *Details) SetSubject(s string) {
d.subject = s d.subject = s

View File

@ -281,7 +281,6 @@ func (h *Help) resetTitle() {
func (h *Help) addSpacer(c int) { func (h *Help) addSpacer(c int) {
cell := tview.NewTableCell(render.Pad("", h.maxKey)) cell := tview.NewTableCell(render.Pad("", h.maxKey))
cell.SetBackgroundColor(h.App().Styles.BgColor())
cell.SetExpansion(1) cell.SetExpansion(1)
h.SetCell(0, c, cell) h.SetCell(0, c, cell)
} }

View File

@ -1,10 +1,8 @@
package view package view
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"strings"
"time" "time"
"github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/client"
@ -71,13 +69,11 @@ func (n *Node) drainCmd(evt *tcell.EventKey) *tcell.EventKey {
return evt return evt
} }
defaults := dao.DrainOptions{ opts := dao.DrainOptions{
GracePeriodSeconds: -1, GracePeriodSeconds: -1,
Timeout: 5 * time.Second, Timeout: 5 * time.Second,
DeleteEmptyDirData: false,
IgnoreAllDaemonSets: false,
} }
ShowDrain(n, path, defaults, drainNode) ShowDrain(n, path, opts, drainNode)
return nil return nil
} }
@ -94,19 +90,21 @@ func drainNode(v ResourceViewer, path string, opts dao.DrainOptions) {
return return
} }
buff := bytes.NewBufferString("") v.Stop()
if err := m.Drain(path, opts, buff); err != nil { defer v.Start()
{
d := NewDetails(v.App(), "Drain Progress", path, true)
if err := v.App().inject(d, false); err != nil {
v.App().Flash().Err(err)
}
if err := m.Drain(path, opts, d.GetWriter()); err != nil {
v.App().Flash().Err(err) v.App().Flash().Err(err)
return return
} }
lines := strings.Split(buff.String(), "\n")
for _, l := range lines {
if len(l) > 0 {
v.App().Flash().Info(l)
}
}
v.Refresh() v.Refresh()
} }
}
func (n *Node) toggleCordonCmd(cordon bool) func(evt *tcell.EventKey) *tcell.EventKey { func (n *Node) toggleCordonCmd(cordon bool) func(evt *tcell.EventKey) *tcell.EventKey {
return func(evt *tcell.EventKey) *tcell.EventKey { return func(evt *tcell.EventKey) *tcell.EventKey {

View File

@ -160,7 +160,7 @@ func (p *PortForward) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
for _, s := range selections { for _, s := range selections {
var pf dao.PortForward var pf dao.PortForward
pf.Init(p.App().factory, client.NewGVR("portforwards")) pf.Init(p.App().factory, client.NewGVR("portforwards"))
if err := pf.Delete(context.Background(), s, nil, true); err != nil { if err := pf.Delete(context.Background(), s, nil, dao.DefaultGrace); err != nil {
p.App().Flash().Err(err) p.App().Flash().Err(err)
return return
} }

View File

@ -200,7 +200,7 @@ func (p *Pod) killCmd(evt *tcell.EventKey) *tcell.EventKey {
} }
p.GetTable().ShowDeleted() p.GetTable().ShowDeleted()
for _, path := range selections { for _, path := range selections {
if err := nuker.Delete(context.Background(), path, nil, true); err != nil { if err := nuker.Delete(context.Background(), path, nil, dao.NowGrace); err != nil {
p.App().Flash().Errf("Delete failed with %s", err) p.App().Flash().Errf("Delete failed with %s", err)
} else { } else {
p.App().factory.DeleteForwarder(path) p.App().factory.DeleteForwarder(path)

View File

@ -10,6 +10,7 @@ import (
"github.com/derailed/k9s/internal" "github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config" "github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/model" "github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/render" "github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui" "github.com/derailed/k9s/internal/ui"
@ -140,7 +141,7 @@ func (t *mockTableModel) Get(context.Context, string) (runtime.Object, error) {
return nil, nil return nil, nil
} }
func (t *mockTableModel) Delete(context.Context, string, *metav1.DeletionPropagation, bool) error { func (t *mockTableModel) Delete(context.Context, string, *metav1.DeletionPropagation, dao.Grace) error {
return nil return nil
} }

View File

@ -672,7 +672,11 @@ func (x *Xray) resourceDelete(gvr client.GVR, spec *xray.NodeSpec, msg string) {
x.app.Flash().Errf("Invalid nuker %T", accessor) x.app.Flash().Errf("Invalid nuker %T", accessor)
return return
} }
if err := nuker.Delete(context.Background(), spec.Path(), nil, true); err != nil { grace := dao.DefaultGrace
if force {
grace = dao.ForceGrace
}
if err := nuker.Delete(context.Background(), spec.Path(), nil, grace); err != nil {
x.app.Flash().Errf("Delete failed with `%s", err) x.app.Flash().Errf("Delete failed with `%s", err)
} else { } else {
x.app.Flash().Infof("%s `%s deleted successfully", x.GVR(), spec.Path()) x.app.Flash().Infof("%s `%s deleted successfully", x.GVR(), spec.Path())