K9s/release v0.32.4 (#2637)
* [Bug] fix #2605 * [Bug] fix #2604 * [Bug] fix #2592 * [Bug] fix #2608 * [Bug] Fix #2612 * Rel v0.32.4mine
parent
c31a48fbee
commit
d3027c8f29
2
Makefile
2
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}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||
|
||||
# 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
|
||||
|
||||
---
|
||||
|
||||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
|
|
@ -70,6 +71,9 @@ func (c *Context) Validate(conn client.Connection, ks KubeSettings) {
|
|||
c.mx.Lock()
|
||||
defer c.mx.Unlock()
|
||||
|
||||
if a := os.Getenv(envPFAddress); a != "" {
|
||||
c.PortForwardAddress = a
|
||||
}
|
||||
if c.PortForwardAddress == "" {
|
||||
c.PortForwardAddress = defaultPFAddress()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ func NewActiveNamespace(n string) *Namespace {
|
|||
if n == client.BlankNamespace {
|
||||
n = client.DefaultNamespace
|
||||
}
|
||||
|
||||
return &Namespace{
|
||||
Active: n,
|
||||
Favorites: []string{client.DefaultNamespace},
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"override": { "type": "boolean" },
|
||||
"description": { "type": "string" },
|
||||
"confirm": { "type": "boolean" },
|
||||
"dangerous": { "type": "boolean" },
|
||||
"scopes": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ plugins:
|
|||
debug:
|
||||
shortCut: Shift-D
|
||||
description: Add debug container
|
||||
dangerous: true
|
||||
scopes:
|
||||
- containers
|
||||
command: bash
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ plugins:
|
|||
helm-purge:
|
||||
shortCut: Ctrl-P
|
||||
description: Helm Purge
|
||||
dangerous: true
|
||||
scopes:
|
||||
- po
|
||||
command: kubectl
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ plugins:
|
|||
toggleCronjob:
|
||||
shortCut: Ctrl-S
|
||||
confirm: true
|
||||
dangerous: true
|
||||
scopes:
|
||||
- cj
|
||||
description: Toggle to suspend or resume a running cronjob
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ plugins:
|
|||
k3d-root-shell:
|
||||
shortCut: Shift-S
|
||||
confirm: false
|
||||
dangerous: true
|
||||
description: "Root Shell"
|
||||
scopes:
|
||||
- containers
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ plugins:
|
|||
remove_finalizers:
|
||||
shortCut: Ctrl-F
|
||||
confirm: true
|
||||
dangerous: true
|
||||
scopes:
|
||||
- all
|
||||
description: |
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ plugins:
|
|||
rm-ns:
|
||||
shortCut: n
|
||||
confirm: true
|
||||
dangerous: true
|
||||
description: Remove NS Finalizers
|
||||
scopes:
|
||||
- namespace
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue