diff --git a/Makefile b/Makefile
index 59c702504b..a1b57045fc 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
else
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
endif
-VERSION ?= v0.32.3
+VERSION ?= v0.32.4
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}
diff --git a/change_logs/release_v0.32.4.md b/change_logs/release_v0.32.4.md
new file mode 100644
index 0000000000..15d012321d
--- /dev/null
+++ b/change_logs/release_v0.32.4.md
@@ -0,0 +1,65 @@
+
+
+# Release v0.32.4
+
+## 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)
+
+## Maintenance Release!
+
+---
+
+## ♫ Sounds Behind The Release ♭
+
+Thinking of all you at KubeCon Paris!!
+May I suggest a nice glass of `cold Merlote` or other fine grape juices from my country?
+
+* [Le Gorille - George Brassens](https://www.youtube.com/watch?v=KVfwvk_yVyA)
+* [Les Funerailles D'antan (Love this guy!) - George Brassens](https://www.youtube.com/watch?v=bwb5k4k2EMc)
+* [Poinconneur Des Lilas - Serge Gainsbourg](https://www.youtube.com/watch?v=eWkWCFzkOvU)
+* [Mon Legionaire (Yup! same guy??) - Serge Gainsbourg](https://www.youtube.com/watch?v=gl8gopryqWI)
+* [Les Cornichons - Nino Ferrer](https://www.youtube.com/watch?v=N7JSW4NhM8I)
+* [Paris s'eveille - Jacques Dutronc](https://www.youtube.com/watch?v=3WcCg6rm3uM)
+
+---
+
+## Videos Are In The Can!
+
+Please dial [K9s Channel](https://www.youtube.com/channel/UC897uwPygni4QIjkPCpgjmw) for up coming content...
+
+* [K9s v0.31.0 Configs+Sneak peek](https://youtu.be/X3444KfjguE)
+* [K9s v0.30.0 Sneak peek](https://youtu.be/mVBc1XneRJ4)
+* [Vulnerability Scans](https://youtu.be/ULkl0MsaidU)
+
+---
+
+## Resolved Issues
+
+* [#2608](https://github.com/derailed/k9s/issues/2608) Make the sanitize feature easier to use
+* [#2605](https://github.com/derailed/k9s/issues/2605) Built-in shortcuts being overridden by plugins result in excessive logging
+* [#2604](https://github.com/derailed/k9s/issues/2604) Ability to mark a plugin as Dangerous/destructive
+* [#2592](https://github.com/derailed/k9s/issues/2592) "list access denied" when switching contexts within k9s since 0.32.0
+
+---
+
+## Contributed PRs
+
+Please be sure to give `Big Thanks!` and `ATTA Girls/Boys!` to all the fine contributors for making K9s better for all of us!!
+
+* [#2621](https://github.com/derailed/k9s/pull/2621) Fix snap build
+
+---
+
+
© 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
\ No newline at end of file
diff --git a/internal/client/client.go b/internal/client/client.go
index 9a39f583d7..e43a561aa6 100644
--- a/internal/client/client.go
+++ b/internal/client/client.go
@@ -217,10 +217,10 @@ func (a *APIClient) ServerVersion() (*version.Info, error) {
return info, nil
}
-func (a *APIClient) IsValidNamespace(n string) bool {
- ok, err := a.isValidNamespace(n)
+func (a *APIClient) IsValidNamespace(ns string) bool {
+ ok, err := a.isValidNamespace(ns)
if err != nil {
- log.Warn().Err(err).Msgf("namespace validation failed for: %q", n)
+ log.Warn().Err(err).Msgf("namespace validation failed for: %q", ns)
}
return ok
diff --git a/internal/config/data/ns.go b/internal/config/data/ns.go
index 57f9d3e678..819430356b 100644
--- a/internal/config/data/ns.go
+++ b/internal/config/data/ns.go
@@ -32,6 +32,7 @@ func NewActiveNamespace(n string) *Namespace {
if n == client.BlankNamespace {
n = client.DefaultNamespace
}
+
return &Namespace{
Active: n,
Favorites: []string{client.DefaultNamespace},
diff --git a/internal/config/json/schemas/plugins.json b/internal/config/json/schemas/plugins.json
index 5c41eb4883..8ef55509b5 100644
--- a/internal/config/json/schemas/plugins.json
+++ b/internal/config/json/schemas/plugins.json
@@ -14,6 +14,7 @@
"override": { "type": "boolean" },
"description": { "type": "string" },
"confirm": { "type": "boolean" },
+ "dangerous": { "type": "boolean" },
"scopes": {
"type": "array",
"items": { "type": "string" }
diff --git a/internal/config/plugin.go b/internal/config/plugin.go
index e57aa53a85..9d45fc14eb 100644
--- a/internal/config/plugin.go
+++ b/internal/config/plugin.go
@@ -36,6 +36,7 @@ type Plugin struct {
Command string `yaml:"command"`
Confirm bool `yaml:"confirm"`
Background bool `yaml:"background"`
+ Dangerous bool `yaml:"dangerous"`
}
func (p Plugin) String() string {
diff --git a/internal/dao/pod.go b/internal/dao/pod.go
index d65854498b..c836137dd6 100644
--- a/internal/dao/pod.go
+++ b/internal/dao/pod.go
@@ -207,19 +207,19 @@ func (p *Pod) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error)
return append(outs, tailLogs(ctx, p, opts)), nil
}
for _, co := range po.Spec.InitContainers {
- o := opts.Clone()
- o.Container = co.Name
- outs = append(outs, tailLogs(ctx, p, o))
+ cfg := opts.Clone()
+ cfg.Container = co.Name
+ outs = append(outs, tailLogs(ctx, p, cfg))
}
for _, co := range po.Spec.Containers {
- o := opts.Clone()
- o.Container = co.Name
- outs = append(outs, tailLogs(ctx, p, o))
+ cfg := opts.Clone()
+ cfg.Container = co.Name
+ outs = append(outs, tailLogs(ctx, p, cfg))
}
for _, co := range po.Spec.EphemeralContainers {
- o := opts.Clone()
- o.Container = co.Name
- outs = append(outs, tailLogs(ctx, p, o))
+ cfg := opts.Clone()
+ cfg.Container = co.Name
+ outs = append(outs, tailLogs(ctx, p, cfg))
}
return outs, nil
diff --git a/internal/render/sts.go b/internal/render/sts.go
index e35560ce04..c83ffa7ba6 100644
--- a/internal/render/sts.go
+++ b/internal/render/sts.go
@@ -59,16 +59,20 @@ func (s StatefulSet) Render(o interface{}, ns string, r *model1.Row) error {
podContainerNames(sts.Spec.Template.Spec, true),
podImageNames(sts.Spec.Template.Spec, true),
mapToStr(sts.Labels),
- AsStatus(s.diagnose(sts.Status.Replicas, sts.Status.ReadyReplicas)),
+ AsStatus(s.diagnose(sts.Spec.Replicas, sts.Status.Replicas, sts.Status.ReadyReplicas)),
ToAge(sts.GetCreationTimestamp()),
}
return nil
}
-func (StatefulSet) diagnose(d, r int32) error {
+func (StatefulSet) diagnose(w *int32, d, r int32) error {
if d != r {
- return fmt.Errorf("desiring %d replicas got %d available", d, r)
+ return fmt.Errorf("desired %d replicas got %d available", d, r)
}
+ if w != nil && *w != r {
+ return fmt.Errorf("want %d replicas got %d available", *w, r)
+ }
+
return nil
}
diff --git a/internal/view/actions.go b/internal/view/actions.go
index 7d76619889..234aa86961 100644
--- a/internal/view/actions.go
+++ b/internal/view/actions.go
@@ -80,7 +80,7 @@ func hotKeyActions(r Runner, aa *ui.KeyActions) error {
errs = errors.Join(errs, fmt.Errorf("duplicate hotkey found for %q in %q", hk.ShortCut, k))
continue
}
- log.Info().Msgf("Action %q has been overridden by hotkey in %q", hk.ShortCut, k)
+ log.Debug().Msgf("Action %q has been overridden by hotkey in %q", hk.ShortCut, k)
}
command, err := r.EnvFn()().Substitute(hk.Command)
@@ -110,7 +110,6 @@ func gotoCmd(r Runner, cmd, path string, clearStack bool) ui.ActionHandler {
}
func pluginActions(r Runner, aa *ui.KeyActions) error {
- pp := config.NewPlugins()
aa.Range(func(k tcell.Key, a ui.KeyAction) {
if a.Opts.Plugin {
aa.Delete(k)
@@ -121,12 +120,16 @@ func pluginActions(r Runner, aa *ui.KeyActions) error {
if err != nil {
return err
}
+ pp := config.NewPlugins()
if err := pp.Load(path); err != nil {
return err
}
- var errs error
- aliases := r.Aliases()
+ var (
+ errs error
+ aliases = r.Aliases()
+ ro = r.App().Config.K9s.IsReadOnly()
+ )
for k, plugin := range pp.Plugins {
if !inScope(plugin.Scopes, aliases) {
continue
@@ -141,15 +144,19 @@ func pluginActions(r Runner, aa *ui.KeyActions) error {
errs = errors.Join(errs, fmt.Errorf("duplicate plugin key found for %q in %q", plugin.ShortCut, k))
continue
}
- log.Info().Msgf("Action %q has been overridden by plugin in %q", plugin.ShortCut, k)
+ log.Debug().Msgf("Action %q has been overridden by plugin in %q", plugin.ShortCut, k)
}
+ if plugin.Dangerous && ro {
+ continue
+ }
aa.Add(key, ui.NewKeyActionWithOpts(
plugin.Description,
pluginAction(r, plugin),
ui.ActionOpts{
- Visible: true,
- Plugin: true,
+ Visible: true,
+ Plugin: true,
+ Dangerous: plugin.Dangerous,
},
))
}
diff --git a/internal/view/app.go b/internal/view/app.go
index fef223111a..055c95c8a6 100644
--- a/internal/view/app.go
+++ b/internal/view/app.go
@@ -111,10 +111,6 @@ func (a *App) Init(version string, rate int) error {
ns := a.Config.ActiveNamespace()
a.factory = watch.NewFactory(a.Conn())
- ok, err := a.isValidNS(ns)
- if !ok && err == nil {
- return fmt.Errorf("app-init - invalid namespace: %q", ns)
- }
a.initFactory(ns)
a.clusterModel = model.NewClusterInfo(a.factory, a.version, a.Config.K9s)
@@ -438,18 +434,6 @@ func (a *App) switchNS(ns string) error {
return a.factory.SetActiveNS(ns)
}
-func (a *App) isValidNS(ns string) (bool, error) {
- if ns == client.BlankNamespace || ns == client.NamespaceAll {
- return true, nil
- }
-
- if !a.Conn().IsValidNamespace(ns) {
- return false, fmt.Errorf("invalid namespace: %q", ns)
- }
-
- return true, nil
-}
-
func (a *App) switchContext(ci *cmd.Interpreter, force bool) error {
name, ok := ci.HasContext()
if !ok || a.Config.ActiveContextName() == name {
@@ -477,12 +461,13 @@ func (a *App) switchContext(ci *cmd.Interpreter, force bool) error {
}
ns := a.Config.ActiveNamespace()
if !a.Conn().IsValidNamespace(ns) {
- a.Flash().Errf("Unable to validate namespace %q. Using %q namespace", ns, client.DefaultNamespace)
- ns = client.DefaultNamespace
+ log.Warn().Msgf("Unable to validate namespace: %q. Using %q as active namespace", ns, ns)
if err := a.Config.SetActiveNamespace(ns); err != nil {
return err
}
}
+ a.Flash().Errf("Using %q namespace", ns)
+
if err := a.Config.Save(true); err != nil {
log.Error().Err(err).Msg("config save failed!")
} else {
diff --git a/internal/view/details.go b/internal/view/details.go
index c07c6171b3..235b5d2bcb 100644
--- a/internal/view/details.go
+++ b/internal/view/details.go
@@ -168,6 +168,7 @@ func (d *Details) StylesChanged(s *config.Styles) {
// Update updates the view content.
func (d *Details) Update(buff string) *Details {
d.model.SetText(buff)
+
return d
}
diff --git a/internal/view/dp.go b/internal/view/dp.go
index f9cd93d64d..6db577dd07 100644
--- a/internal/view/dp.go
+++ b/internal/view/dp.go
@@ -53,49 +53,16 @@ func (d *Deploy) logOptions(prev bool) (*dao.LogOptions, error) {
if path == "" {
return nil, errors.New("you must provide a selection")
}
-
- sts, err := d.dp(path)
+ dp, err := d.getInstance(path)
if err != nil {
return nil, err
}
- cc := sts.Spec.Template.Spec.Containers
- var (
- co, dco string
- allCos bool
- )
- if c, ok := dao.GetDefaultContainer(sts.Spec.Template.ObjectMeta, sts.Spec.Template.Spec); ok {
- co, dco = c, c
- } else if len(cc) == 1 {
- co = cc[0].Name
- } else {
- dco, allCos = cc[0].Name, true
- }
-
- cfg := d.App().Config.K9s.Logger
- opts := dao.LogOptions{
- Path: path,
- Container: co,
- Lines: int64(cfg.TailCount),
- SinceSeconds: cfg.SinceSeconds,
- SingleContainer: len(cc) == 1,
- AllContainers: allCos,
- ShowTimestamp: cfg.ShowTime,
- Previous: prev,
- }
- if co == "" {
- opts.AllContainers = true
- }
- opts.DefaultContainer = dco
-
- return &opts, nil
+ return podLogOptions(d.App(), path, prev, dp.ObjectMeta, dp.Spec.Template.Spec), nil
}
func (d *Deploy) showPods(app *App, model ui.Tabular, gvr client.GVR, fqn string) {
- var ddp dao.Deployment
- ddp.Init(d.App().factory, d.GVR())
-
- dp, err := ddp.GetInstance(fqn)
+ dp, err := d.getInstance(fqn)
if err != nil {
app.Flash().Err(err)
return
@@ -104,7 +71,7 @@ func (d *Deploy) showPods(app *App, model ui.Tabular, gvr client.GVR, fqn string
showPodsFromSelector(app, fqn, dp.Spec.Selector)
}
-func (d *Deploy) dp(fqn string) (*appsv1.Deployment, error) {
+func (d *Deploy) getInstance(fqn string) (*appsv1.Deployment, error) {
var dp dao.Deployment
dp.Init(d.App().factory, d.GVR())
diff --git a/internal/view/ds.go b/internal/view/ds.go
index a9e24abd73..6e89f4215b 100644
--- a/internal/view/ds.go
+++ b/internal/view/ds.go
@@ -4,9 +4,12 @@
package view
import (
+ "errors"
+
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/ui"
+ appsv1 "k8s.io/api/apps/v1"
)
// DaemonSet represents a daemon set custom viewer.
@@ -16,17 +19,16 @@ type DaemonSet struct {
// NewDaemonSet returns a new viewer.
func NewDaemonSet(gvr client.GVR) ResourceViewer {
- d := DaemonSet{
- ResourceViewer: NewPortForwardExtender(
- NewVulnerabilityExtender(
- NewRestartExtender(
- NewImageExtender(
- NewLogsExtender(NewBrowser(gvr), nil),
- ),
+ var d DaemonSet
+ d.ResourceViewer = NewPortForwardExtender(
+ NewVulnerabilityExtender(
+ NewRestartExtender(
+ NewImageExtender(
+ NewLogsExtender(NewBrowser(gvr), d.logOptions),
),
),
),
- }
+ )
d.AddBindKeysFn(d.bindKeys)
d.GetTable().SetEnterFn(d.showPods)
@@ -55,3 +57,23 @@ func (d *DaemonSet) showPods(app *App, model ui.Tabular, _ client.GVR, path stri
showPodsFromSelector(app, path, ds.Spec.Selector)
}
+
+func (d *DaemonSet) logOptions(prev bool) (*dao.LogOptions, error) {
+ path := d.GetTable().GetSelectedItem()
+ if path == "" {
+ return nil, errors.New("you must provide a selection")
+ }
+ ds, err := d.getInstance(path)
+ if err != nil {
+ return nil, err
+ }
+
+ return podLogOptions(d.App(), path, prev, ds.ObjectMeta, ds.Spec.Template.Spec), nil
+}
+
+func (d *DaemonSet) getInstance(fqn string) (*appsv1.DaemonSet, error) {
+ var ds dao.DaemonSet
+ ds.Init(d.App().factory, client.NewGVR("apps/v1/daemonsets"))
+
+ return ds.GetInstance(fqn)
+}
diff --git a/internal/view/job.go b/internal/view/job.go
index 09ff8d6854..e414e3e5c9 100644
--- a/internal/view/job.go
+++ b/internal/view/job.go
@@ -4,7 +4,10 @@
package view
import (
+ "errors"
+
"github.com/derailed/k9s/internal/client"
+ "github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/ui"
batchv1 "k8s.io/api/batch/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -19,7 +22,11 @@ type Job struct {
// NewJob returns a new viewer.
func NewJob(gvr client.GVR) ResourceViewer {
- j := Job{ResourceViewer: NewVulnerabilityExtender(NewLogsExtender(NewBrowser(gvr), nil))}
+ var j Job
+
+ j.ResourceViewer = NewVulnerabilityExtender(
+ NewLogsExtender(NewBrowser(gvr), j.logOptions),
+ )
j.GetTable().SetEnterFn(j.showPods)
j.GetTable().SetSortCol("AGE", true)
@@ -42,3 +49,23 @@ func (*Job) showPods(app *App, model ui.Tabular, gvr client.GVR, path string) {
showPodsFromSelector(app, path, job.Spec.Selector)
}
+
+func (j *Job) logOptions(prev bool) (*dao.LogOptions, error) {
+ path := j.GetTable().GetSelectedItem()
+ if path == "" {
+ return nil, errors.New("you must provide a selection")
+ }
+ job, err := j.getInstance(path)
+ if err != nil {
+ return nil, err
+ }
+
+ return podLogOptions(j.App(), path, prev, job.ObjectMeta, job.Spec.Template.Spec), nil
+}
+
+func (j *Job) getInstance(fqn string) (*batchv1.Job, error) {
+ var job dao.Job
+ job.Init(j.App().factory, client.NewGVR("batch/v1/jobs"))
+
+ return job.GetInstance(fqn)
+}
diff --git a/internal/view/logs_extender.go b/internal/view/logs_extender.go
index 95e452117d..d2a8ad4f39 100644
--- a/internal/view/logs_extender.go
+++ b/internal/view/logs_extender.go
@@ -8,6 +8,8 @@ import (
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
+ v1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// LogsExtender adds log actions to a given viewer.
@@ -91,3 +93,27 @@ func (l *LogsExtender) buildLogOpts(path, co string, prevLogs bool) *dao.LogOpti
return &opts
}
+
+func podLogOptions(app *App, fqn string, prev bool, m metav1.ObjectMeta, spec v1.PodSpec) *dao.LogOptions {
+ var (
+ cc = fetchContainers(m, spec, true)
+ cfg = app.Config.K9s.Logger
+ opts = dao.LogOptions{
+ Path: fqn,
+ Lines: int64(cfg.TailCount),
+ SinceSeconds: cfg.SinceSeconds,
+ SingleContainer: len(cc) == 1,
+ ShowTimestamp: cfg.ShowTime,
+ Previous: prev,
+ }
+ )
+ if c, ok := dao.GetDefaultContainer(m, spec); ok {
+ opts.Container, opts.DefaultContainer = c, c
+ } else if len(cc) == 1 {
+ opts.Container = cc[0]
+ } else {
+ opts.AllContainers = true
+ }
+
+ return &opts
+}
diff --git a/internal/view/pod.go b/internal/view/pod.go
index a1971682fc..418029e542 100644
--- a/internal/view/pod.go
+++ b/internal/view/pod.go
@@ -38,6 +38,7 @@ const (
trDownload = "Download"
pfIndicator = "[orange::b]Ⓕ"
defaultTxRetries = 999
+ magicPrompt = "Yes Please!"
)
// Pod represents a pod viewer.
@@ -146,24 +147,7 @@ func (p *Pod) logOptions(prev bool) (*dao.LogOptions, error) {
return nil, err
}
- cc, cfg := fetchContainers(pod.ObjectMeta, pod.Spec, true), p.App().Config.K9s.Logger
- opts := dao.LogOptions{
- Path: path,
- Lines: int64(cfg.TailCount),
- SinceSeconds: cfg.SinceSeconds,
- SingleContainer: len(cc) == 1,
- ShowTimestamp: cfg.ShowTime,
- Previous: prev,
- }
- if c, ok := dao.GetDefaultContainer(pod.ObjectMeta, pod.Spec); ok {
- opts.Container, opts.DefaultContainer = c, c
- } else if len(cc) == 1 {
- opts.Container = cc[0]
- } else {
- opts.AllContainers = true
- }
-
- return &opts, nil
+ return podLogOptions(p.App(), path, prev, pod.ObjectMeta, pod.Spec), nil
}
func (p *Pod) showContainers(app *App, _ ui.Tabular, _ client.GVR, _ string) {
@@ -287,9 +271,8 @@ func (p *Pod) sanitizeCmd(evt *tcell.EventKey) *tcell.EventKey {
return nil
}
- ack := "sanitize me pods!"
- msg := fmt.Sprintf("Sanitize deletes all pods in completed/error state\nPlease enter [orange::b]%s[-::-] to proceed.", ack)
- dialog.ShowConfirmAck(p.App().App, p.App().Content.Pages, ack, true, "Sanitize", msg, func() {
+ msg := fmt.Sprintf("Sanitize deletes all pods in completed/error state\nPlease enter [orange::b]%s[-::-] to proceed.", magicPrompt)
+ dialog.ShowConfirmAck(p.App().App, p.App().Content.Pages, magicPrompt, true, "Sanitize", msg, func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*p.App().Conn().Config().CallTimeout())
defer cancel()
total, err := s.Sanitize(ctx, p.GetTable().GetModel().GetNamespace())
diff --git a/internal/view/sts.go b/internal/view/sts.go
index 816dabb984..18636d9a2e 100644
--- a/internal/view/sts.go
+++ b/internal/view/sts.go
@@ -42,42 +42,12 @@ func (s *StatefulSet) logOptions(prev bool) (*dao.LogOptions, error) {
if path == "" {
return nil, errors.New("you must provide a selection")
}
-
sts, err := s.getInstance(path)
if err != nil {
return nil, err
}
- cc := sts.Spec.Template.Spec.Containers
- var (
- co, dco string
- allCos bool
- )
- if c, ok := dao.GetDefaultContainer(sts.Spec.Template.ObjectMeta, sts.Spec.Template.Spec); ok {
- co, dco = c, c
- } else if len(cc) == 1 {
- co = cc[0].Name
- } else {
- dco, allCos = cc[0].Name, true
- }
-
- cfg := s.App().Config.K9s.Logger
- opts := dao.LogOptions{
- Path: path,
- Container: co,
- Lines: int64(cfg.TailCount),
- SingleContainer: len(cc) == 1,
- SinceSeconds: cfg.SinceSeconds,
- AllContainers: allCos,
- ShowTimestamp: cfg.ShowTime,
- Previous: prev,
- }
- if co == "" {
- opts.AllContainers = true
- }
- opts.DefaultContainer = dco
-
- return &opts, nil
+ return podLogOptions(s.App(), path, prev, sts.ObjectMeta, sts.Spec.Template.Spec), nil
}
func (s *StatefulSet) bindKeys(aa *ui.KeyActions) {
@@ -96,5 +66,6 @@ func (s *StatefulSet) showPods(app *App, _ ui.Tabular, _ client.GVR, path string
func (s *StatefulSet) getInstance(path string) (*appsv1.StatefulSet, error) {
var sts dao.StatefulSet
+
return sts.GetInstance(s.App().factory, path)
}
diff --git a/plugins/debug-container.yaml b/plugins/debug-container.yaml
index f8561ca29b..aefba8801c 100644
--- a/plugins/debug-container.yaml
+++ b/plugins/debug-container.yaml
@@ -4,6 +4,7 @@ plugins:
debug:
shortCut: Shift-D
description: Add debug container
+ dangerous: true
scopes:
- containers
command: bash
diff --git a/plugins/helm-purge.yaml b/plugins/helm-purge.yaml
index 300053a7fa..c84a8eaa30 100644
--- a/plugins/helm-purge.yaml
+++ b/plugins/helm-purge.yaml
@@ -4,6 +4,7 @@ plugins:
helm-purge:
shortCut: Ctrl-P
description: Helm Purge
+ dangerous: true
scopes:
- po
command: kubectl
diff --git a/plugins/job-suspend.yaml b/plugins/job-suspend.yaml
index abee83bc52..799f884efd 100644
--- a/plugins/job-suspend.yaml
+++ b/plugins/job-suspend.yaml
@@ -3,6 +3,7 @@ plugins:
toggleCronjob:
shortCut: Ctrl-S
confirm: true
+ dangerous: true
scopes:
- cj
description: Toggle to suspend or resume a running cronjob
diff --git a/plugins/k3d-root-shell.yaml b/plugins/k3d-root-shell.yaml
index 79707b4b5a..d44304284c 100644
--- a/plugins/k3d-root-shell.yaml
+++ b/plugins/k3d-root-shell.yaml
@@ -3,6 +3,7 @@ plugins:
k3d-root-shell:
shortCut: Shift-S
confirm: false
+ dangerous: true
description: "Root Shell"
scopes:
- containers
diff --git a/plugins/liveMigration.yaml b/plugins/liveMigration.yaml
index 2ddd08cfc7..a0fbbe72b4 100644
--- a/plugins/liveMigration.yaml
+++ b/plugins/liveMigration.yaml
@@ -8,7 +8,7 @@ plugins:
# Require `virtctl` cli in your PATH,
# can be downloaded from Openshift `Command Line Tools` page
# or from kubevirt site https://kubevirt.io/user-guide/operations/virtctl_client_tool/
- #
+ #
#
liveMigration:
# Can be triggered from the VMI (VirtualMachineInstance) view, with shortcut `m`
@@ -17,6 +17,7 @@ plugins:
description: Live Migrate moves VM to another compute node
# Enable confirmation dialog
confirm: true
+ dangerous: true
# Collections of views that support this shortcut. (You can use `all`)
scopes:
- virtualmachineinstance
diff --git a/plugins/remove-finalizers.yaml b/plugins/remove-finalizers.yaml
index 4abb7d46ac..b7a83d67d5 100644
--- a/plugins/remove-finalizers.yaml
+++ b/plugins/remove-finalizers.yaml
@@ -11,11 +11,12 @@ plugins:
remove_finalizers:
shortCut: Ctrl-F
confirm: true
+ dangerous: true
scopes:
- all
description: |
Removes all finalizers from selected resource. Be careful when using it,
- it may leave dangling resources or delete them
+ it may leave dangling resources or delete them
command: kubectl
background: true
args:
diff --git a/plugins/rm-ns.yaml b/plugins/rm-ns.yaml
index 88509dc98c..a73592c040 100644
--- a/plugins/rm-ns.yaml
+++ b/plugins/rm-ns.yaml
@@ -3,6 +3,7 @@ plugins:
rm-ns:
shortCut: n
confirm: true
+ dangerous: true
description: Remove NS Finalizers
scopes:
- namespace
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 6fe232a669..233f9c0ac7 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,6 +1,6 @@
name: k9s
base: core22
-version: 'v0.32.3'
+version: 'v0.32.4'
summary: K9s is a CLI to view and manage your Kubernetes clusters.
description: |
K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of your clusters in a single powerful session.