commit
ae648ddce5
|
|
@ -115,6 +115,8 @@ k9s info
|
|||
k9s -n mycoolns
|
||||
# Start K9s in an existing KubeConfig context
|
||||
k9s --context coolCtx
|
||||
# Start K9s in readonly mode - with all modification commands disabled
|
||||
k9s --readonly
|
||||
```
|
||||
|
||||
## Key Bindings
|
||||
|
|
@ -150,6 +152,8 @@ K9s uses aliases to navigate most K8s resources.
|
|||
k9s:
|
||||
# Indicates api-server poll intervals.
|
||||
refreshRate: 2
|
||||
# Indicates whether modification commands like delete/kill/edit are disabled. Default is false
|
||||
readOnly: false
|
||||
# Indicates log view maximum buffer size. Default 1k lines.
|
||||
logBufferSize: 200
|
||||
# Indicates how many lines of logs to retrieve from the api-server. Default 200 lines.
|
||||
|
|
|
|||
10
cmd/root.go
10
cmd/root.go
|
|
@ -113,6 +113,10 @@ func loadConfiguration() *config.Config {
|
|||
k9sCfg.K9s.OverrideHeadless(*k9sFlags.Headless)
|
||||
}
|
||||
|
||||
if k9sFlags.ReadOnly != nil {
|
||||
k9sCfg.K9s.OverrideReadOnly(*k9sFlags.ReadOnly)
|
||||
}
|
||||
|
||||
if k9sFlags.Command != nil {
|
||||
k9sCfg.K9s.OverrideCommand(*k9sFlags.Command)
|
||||
}
|
||||
|
|
@ -189,6 +193,12 @@ func initK9sFlags() {
|
|||
config.DefaultCommand,
|
||||
"Specify the default command to view when the application launches",
|
||||
)
|
||||
rootCmd.Flags().BoolVar(
|
||||
k9sFlags.ReadOnly,
|
||||
"readonly",
|
||||
false,
|
||||
"Disable all commands that modify the cluster",
|
||||
)
|
||||
}
|
||||
|
||||
func initK8sFlags() {
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ func TestConfigSaveFile(t *testing.T) {
|
|||
cfg.SetConnection(mc)
|
||||
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
|
||||
cfg.K9s.RefreshRate = 100
|
||||
cfg.K9s.ReadOnly = true
|
||||
cfg.K9s.LogBufferSize = 500
|
||||
cfg.K9s.LogRequestSize = 100
|
||||
cfg.K9s.CurrentContext = "blee"
|
||||
|
|
@ -260,6 +261,7 @@ func TestSetup(t *testing.T) {
|
|||
var expectedConfig = `k9s:
|
||||
refreshRate: 100
|
||||
headless: false
|
||||
readOnly: true
|
||||
logBufferSize: 500
|
||||
logRequestSize: 100
|
||||
currentContext: blee
|
||||
|
|
@ -300,6 +302,7 @@ var expectedConfig = `k9s:
|
|||
var resetConfig = `k9s:
|
||||
refreshRate: 2
|
||||
headless: false
|
||||
readOnly: false
|
||||
logBufferSize: 200
|
||||
logRequestSize: 200
|
||||
currentContext: blee
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ type Flags struct {
|
|||
Headless *bool
|
||||
Command *string
|
||||
AllNamespaces *bool
|
||||
ReadOnly *bool
|
||||
}
|
||||
|
||||
// NewFlags returns new configuration flags.
|
||||
|
|
@ -28,6 +29,7 @@ func NewFlags() *Flags {
|
|||
Headless: boolPtr(false),
|
||||
Command: strPtr(DefaultCommand),
|
||||
AllNamespaces: boolPtr(false),
|
||||
ReadOnly: boolPtr(false),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ const (
|
|||
defaultRefreshRate = 2
|
||||
defaultLogRequestSize = 200
|
||||
defaultLogBufferSize = 1000
|
||||
defaultReadOnly = false
|
||||
)
|
||||
|
||||
// K9s tracks K9s configuration options.
|
||||
type K9s struct {
|
||||
RefreshRate int `yaml:"refreshRate"`
|
||||
Headless bool `yaml:"headless"`
|
||||
ReadOnly bool `yaml:"readOnly"`
|
||||
LogBufferSize int `yaml:"logBufferSize"`
|
||||
LogRequestSize int `yaml:"logRequestSize"`
|
||||
CurrentContext string `yaml:"currentContext"`
|
||||
|
|
@ -20,6 +22,7 @@ type K9s struct {
|
|||
Clusters map[string]*Cluster `yaml:"clusters,omitempty"`
|
||||
manualRefreshRate int
|
||||
manualHeadless *bool
|
||||
manualReadOnly *bool
|
||||
manualCommand *string
|
||||
}
|
||||
|
||||
|
|
@ -27,6 +30,7 @@ type K9s struct {
|
|||
func NewK9s() *K9s {
|
||||
return &K9s{
|
||||
RefreshRate: defaultRefreshRate,
|
||||
ReadOnly: defaultReadOnly,
|
||||
LogBufferSize: defaultLogBufferSize,
|
||||
LogRequestSize: defaultLogRequestSize,
|
||||
Clusters: make(map[string]*Cluster),
|
||||
|
|
@ -43,6 +47,11 @@ func (k *K9s) OverrideHeadless(b bool) {
|
|||
k.manualHeadless = &b
|
||||
}
|
||||
|
||||
// OverrideReadOnly set the readonly mode manually.
|
||||
func (k *K9s) OverrideReadOnly(b bool) {
|
||||
k.manualReadOnly = &b
|
||||
}
|
||||
|
||||
// OverrideCommand set the command manually.
|
||||
func (k *K9s) OverrideCommand(cmd string) {
|
||||
k.manualCommand = &cmd
|
||||
|
|
@ -68,6 +77,15 @@ func (k *K9s) GetRefreshRate() int {
|
|||
return rate
|
||||
}
|
||||
|
||||
// GetReadOnly returns the readonly setting.
|
||||
func (k *K9s) GetReadOnly() bool {
|
||||
readOnly := k.ReadOnly
|
||||
if k.manualReadOnly != nil && *k.manualReadOnly {
|
||||
readOnly = *k.manualReadOnly
|
||||
}
|
||||
return readOnly
|
||||
}
|
||||
|
||||
// ActiveCluster returns the currently active cluster.
|
||||
func (k *K9s) ActiveCluster() *Cluster {
|
||||
if k.Clusters == nil {
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ func (b *Browser) defaultContext() context.Context {
|
|||
ctx = context.WithValue(ctx, internal.KeyLabels, ui.TrimLabelSelector(b.SearchBuff().String()))
|
||||
}
|
||||
ctx = context.WithValue(ctx, internal.KeyFields, "")
|
||||
ctx = context.WithValue(ctx, internal.KeyNamespace, client.CleanseNamespace((b.App().Config.ActiveNamespace())))
|
||||
ctx = context.WithValue(ctx, internal.KeyNamespace, client.CleanseNamespace(b.App().Config.ActiveNamespace()))
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
|
@ -370,11 +370,13 @@ func (b *Browser) refreshActions() {
|
|||
if b.app.ConOK() {
|
||||
b.namespaceActions(aa)
|
||||
|
||||
if client.Can(b.meta.Verbs, "edit") {
|
||||
aa[ui.KeyE] = ui.NewKeyAction("Edit", b.editCmd, true)
|
||||
}
|
||||
if client.Can(b.meta.Verbs, "delete") {
|
||||
aa[tcell.KeyCtrlD] = ui.NewKeyAction("Delete", b.deleteCmd, true)
|
||||
if !b.app.Config.K9s.GetReadOnly() {
|
||||
if client.Can(b.meta.Verbs, "edit") {
|
||||
aa[ui.KeyE] = ui.NewKeyAction("Edit", b.editCmd, true)
|
||||
}
|
||||
if client.Can(b.meta.Verbs, "delete") {
|
||||
aa[tcell.KeyCtrlD] = ui.NewKeyAction("Delete", b.deleteCmd, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,11 +40,21 @@ func NewContainer(gvr client.GVR) ResourceViewer {
|
|||
// Name returns the component name.
|
||||
func (c *Container) Name() string { return containerTitle }
|
||||
|
||||
func (c *Container) bindDangerousKeys(aa ui.KeyActions) {
|
||||
aa.Add(ui.KeyActions{
|
||||
ui.KeyS: ui.NewKeyAction("Shell", c.shellCmd, true),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Container) bindKeys(aa ui.KeyActions) {
|
||||
aa.Delete(tcell.KeyCtrlSpace, ui.KeySpace)
|
||||
|
||||
if !c.App().Config.K9s.GetReadOnly() {
|
||||
c.bindDangerousKeys(aa)
|
||||
}
|
||||
|
||||
aa.Add(ui.KeyActions{
|
||||
ui.KeyShiftF: ui.NewKeyAction("PortForward", c.portFwdCmd, true),
|
||||
ui.KeyS: ui.NewKeyAction("Shell", c.shellCmd, true),
|
||||
ui.KeyShiftC: ui.NewKeyAction("Sort CPU", c.GetTable().SortColCmd(6, false), false),
|
||||
ui.KeyShiftM: ui.NewKeyAction("Sort MEM", c.GetTable().SortColCmd(7, false), false),
|
||||
ui.KeyShiftX: ui.NewKeyAction("Sort %CPU (REQ)", c.GetTable().SortColCmd(8, false), false),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
|
|
@ -36,10 +35,19 @@ func NewPod(gvr client.GVR) ResourceViewer {
|
|||
return &p
|
||||
}
|
||||
|
||||
func (p *Pod) bindKeys(aa ui.KeyActions) {
|
||||
func (p *Pod) bindDangerousKeys(aa ui.KeyActions) {
|
||||
aa.Add(ui.KeyActions{
|
||||
tcell.KeyCtrlK: ui.NewKeyAction("Kill", p.killCmd, true),
|
||||
ui.KeyS: ui.NewKeyAction("Shell", p.shellCmd, true),
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Pod) bindKeys(aa ui.KeyActions) {
|
||||
if !p.App().Config.K9s.GetReadOnly() {
|
||||
p.bindDangerousKeys(aa)
|
||||
}
|
||||
|
||||
aa.Add(ui.KeyActions{
|
||||
ui.KeyShiftR: ui.NewKeyAction("Sort Ready", p.GetTable().SortColCmd(1, true), false),
|
||||
ui.KeyShiftS: ui.NewKeyAction("Sort Status", p.GetTable().SortColCmd(2, true), false),
|
||||
ui.KeyShiftT: ui.NewKeyAction("Sort Restart", p.GetTable().SortColCmd(3, false), false),
|
||||
|
|
|
|||
Loading…
Reference in New Issue