Rel v0.40.8 (#3198)
* Update deprecated yaml.v2->v3 * [Fix] fix issue with yaml sanitization when [] or [xxx] are present * Spring cleanup * fix#3192 * Column Blow Reloaded * Add ability to use alias when specifying custom viewsmine
parent
5e05221a26
commit
45c2137df8
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/slogs"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func infoCmd() *cobra.Command {
|
||||
|
|
|
|||
13
cmd/root.go
13
cmd/root.go
|
|
@ -20,7 +20,6 @@ import (
|
|||
"github.com/derailed/k9s/internal/view"
|
||||
|
||||
"github.com/lmittmann/tint"
|
||||
// "github.com/MatusOllah/slogcolor"
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
|
|
@ -134,6 +133,12 @@ func loadConfiguration() (*config.Config, error) {
|
|||
k9sCfg := config.NewConfig(k8sCfg)
|
||||
var errs error
|
||||
|
||||
conn, err := client.InitConnection(k8sCfg, slog.Default())
|
||||
if err != nil {
|
||||
errs = errors.Join(errs, err)
|
||||
}
|
||||
k9sCfg.SetConnection(conn)
|
||||
|
||||
if err := k9sCfg.Load(config.AppConfigFile, false); err != nil {
|
||||
errs = errors.Join(errs, err)
|
||||
}
|
||||
|
|
@ -143,10 +148,6 @@ func loadConfiguration() (*config.Config, error) {
|
|||
errs = errors.Join(errs, err)
|
||||
}
|
||||
|
||||
conn, err := client.InitConnection(k8sCfg, slog.Default())
|
||||
if err != nil {
|
||||
errs = errors.Join(errs, err)
|
||||
}
|
||||
// Try to access server version if that fail. Connectivity issue?
|
||||
if !conn.CheckConnectivity() {
|
||||
errs = errors.Join(errs, fmt.Errorf("cannot connect to context: %s", k9sCfg.K9s.ActiveContextName()))
|
||||
|
|
@ -157,7 +158,7 @@ func loadConfiguration() (*config.Config, error) {
|
|||
} else {
|
||||
slog.Info("✅ Kubernetes connectivity OK")
|
||||
}
|
||||
k9sCfg.SetConnection(conn)
|
||||
|
||||
if err := k9sCfg.Save(false); err != nil {
|
||||
slog.Error("K9s config save failed", slogs.Error, err)
|
||||
errs = errors.Join(errs, err)
|
||||
|
|
|
|||
3
go.mod
3
go.mod
|
|
@ -9,7 +9,6 @@ require (
|
|||
github.com/anchore/syft v1.20.0
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
github.com/cenkalti/backoff/v4 v4.3.0
|
||||
github.com/derailed/popeye v0.11.3
|
||||
github.com/derailed/tcell/v2 v2.3.1-rc.3
|
||||
github.com/derailed/tview v0.8.5
|
||||
github.com/fatih/color v1.18.0
|
||||
|
|
@ -27,7 +26,6 @@ require (
|
|||
github.com/stretchr/testify v1.10.0
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
golang.org/x/text v0.23.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
helm.sh/helm/v3 v3.17.1
|
||||
k8s.io/api v0.32.2
|
||||
|
|
@ -266,7 +264,6 @@ require (
|
|||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/rubenv/sql-migrate v1.7.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/saferwall/pe v1.5.6 // indirect
|
||||
|
|
|
|||
11
go.sum
11
go.sum
|
|
@ -418,7 +418,6 @@ github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9Fqctt
|
|||
github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
|
|
@ -432,8 +431,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
|
||||
github.com/derailed/popeye v0.11.3 h1:gQUp6zuSIRDBdyLS1Ln0nFs8FbQ+KGE+iQxe0w4Ug8M=
|
||||
github.com/derailed/popeye v0.11.3/go.mod h1:HygqX7A8BwidorJjJUnWDZ5AvbxHIU7uRwXgOtn9GwY=
|
||||
github.com/derailed/tcell/v2 v2.3.1-rc.3 h1:9s1fmyRcSPRlwr/C9tcpJKCujbrtmPpST6dcMUD2piY=
|
||||
github.com/derailed/tcell/v2 v2.3.1-rc.3/go.mod h1:nf68BEL8fjmXQHJT3xZjoZFs2uXOzyJcNAQqGUEMrFY=
|
||||
github.com/derailed/tview v0.8.5 h1:pogM/OnWlgDo6j4zyzdiIXh7E7+eT7D4CPfBnyaETug=
|
||||
|
|
@ -890,7 +887,6 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
|
|||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
|
|
@ -900,8 +896,6 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
|
|||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZMYDR+NGImiFvErt6VWfIRPuGM+vyjiEdkmIw=
|
||||
|
|
@ -1084,9 +1078,6 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
|
|||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/rubenv/sql-migrate v1.7.1 h1:f/o0WgfO/GqNuVg+6801K/KW3WdDSupzSjDYODmiUq4=
|
||||
github.com/rubenv/sql-migrate v1.7.1/go.mod h1:Ob2Psprc0/3ggbM6wCzyYVFFuc6FyZrb2AS+ezLDFb4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
|
|
@ -1539,11 +1530,9 @@ golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
|
|
|||
|
|
@ -54,11 +54,9 @@ func (c *Config) CallTimeout() time.Duration {
|
|||
|
||||
func (c *Config) RESTConfig() (*restclient.Config, error) {
|
||||
cfg, err := c.clientConfig().ClientConfig()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.proxy != nil {
|
||||
cfg.Proxy = c.proxy
|
||||
}
|
||||
|
|
@ -192,15 +190,11 @@ func (c *Config) GetContext(n string) (*api.Context, error) {
|
|||
return nil, fmt.Errorf("getcontext - invalid context specified: %q", n)
|
||||
}
|
||||
|
||||
// SetProxy sets the proxy function.
|
||||
func (c *Config) SetProxy(proxy func(*http.Request) (*url.URL, error)) {
|
||||
c.proxy = proxy
|
||||
}
|
||||
|
||||
func (c *Config) WithProxy(proxy func(*http.Request) (*url.URL, error)) *Config {
|
||||
c.SetProxy(proxy)
|
||||
return c
|
||||
}
|
||||
|
||||
// Contexts fetch all available contexts.
|
||||
func (c *Config) Contexts() (map[string]*api.Context, error) {
|
||||
cfg, err := c.RawConfig()
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/config/data"
|
||||
"github.com/derailed/k9s/internal/config/json"
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Alias tracks shortname to GVR mappings.
|
||||
|
|
@ -176,8 +176,6 @@ func (a *Aliases) loadDefaultAliases() {
|
|||
a.declare("help", "h", "?")
|
||||
a.declare("quit", "q", "q!", "qa", "Q")
|
||||
a.declare("aliases", "alias", "a")
|
||||
// !!BOZO!!
|
||||
// a.declare("popeye", "pop")
|
||||
a.declare("helm", "charts", "chart", "hm")
|
||||
a.declare("dir", "d")
|
||||
a.declare("contexts", "context", "ctx")
|
||||
|
|
@ -202,10 +200,6 @@ func (a *Aliases) SaveAliases(path string) error {
|
|||
if err := data.EnsureDirPath(path, data.DefaultDirMod); err != nil {
|
||||
return err
|
||||
}
|
||||
cfg, err := yaml.Marshal(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(path, cfg, data.DefaultFileMod)
|
||||
return data.SaveYAML(path, a)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// K9sBench the name of the benchmarks config file.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/config/json"
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
|
|
@ -289,14 +289,13 @@ func (c *Config) SaveFile(path string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
cfg, err := yaml.Marshal(c)
|
||||
if err != nil {
|
||||
if err := data.SaveYAML(path, c); err != nil {
|
||||
slog.Error("Unable to save K9s config file", slogs.Error, err)
|
||||
return err
|
||||
}
|
||||
|
||||
slog.Info("[CONFIG] Saving K9s config to disk", slogs.Path, path)
|
||||
return os.WriteFile(path, cfg, data.DefaultFileMod)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate the configuration.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal/config/json"
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
|
|
@ -74,12 +74,8 @@ func (d *Dir) Save(path string, c *Config) error {
|
|||
if err := EnsureDirPath(path, DefaultDirMod); err != nil {
|
||||
return err
|
||||
}
|
||||
cfg, err := yaml.Marshal(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(path, cfg, DefaultFileMod)
|
||||
return SaveYAML(path, c)
|
||||
}
|
||||
|
||||
func (d *Dir) loadConfig(path string) (*Config, error) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/config/data"
|
||||
"github.com/derailed/k9s/internal/config/mock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,14 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const envFGNodeShell = "K9S_FEATURE_GATE_NODE_SHELL"
|
||||
|
|
@ -48,3 +51,28 @@ func EnsureFullPath(path string, mod os.FileMode) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteYAML writes a yaml file to bytes.
|
||||
func WriteYAML(content any) ([]byte, error) {
|
||||
buff := bytes.NewBuffer(nil)
|
||||
ec := yaml.NewEncoder(buff)
|
||||
ec.SetIndent(2)
|
||||
|
||||
if err := ec.Encode(content); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buff.Bytes(), nil
|
||||
}
|
||||
|
||||
// SaveYAML writes a yaml file to disk.
|
||||
func SaveYAML(path string, content any) error {
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, DefaultFileMod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ec := yaml.NewEncoder(f)
|
||||
ec.SetIndent(2)
|
||||
|
||||
return ec.Encode(content)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,15 +59,15 @@ func (n *Namespace) merge(old *Namespace) {
|
|||
}
|
||||
|
||||
// Validate validates a namespace is setup correctly.
|
||||
func (n *Namespace) Validate(c client.Connection) {
|
||||
func (n *Namespace) Validate(conn client.Connection) {
|
||||
n.mx.RLock()
|
||||
defer n.mx.RUnlock()
|
||||
|
||||
if c == nil || !c.IsValidNamespace(n.Active) {
|
||||
if conn == nil || !conn.IsValidNamespace(n.Active) {
|
||||
return
|
||||
}
|
||||
for _, ns := range n.Favorites {
|
||||
if !c.IsValidNamespace(ns) {
|
||||
if !conn.IsValidNamespace(ns) {
|
||||
slog.Debug("Invalid favorite found",
|
||||
slogs.Namespace, ns,
|
||||
slogs.AllNS, n.isAllNamespaces(),
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/config/data"
|
||||
"github.com/derailed/k9s/internal/config/json"
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// HotKeys represents a collection of plugins.
|
||||
|
|
|
|||
|
|
@ -36,9 +36,6 @@ type K9s struct {
|
|||
Logger Logger `json:"logger" yaml:"logger"`
|
||||
Thresholds Threshold `json:"thresholds" yaml:"thresholds"`
|
||||
manualRefreshRate int
|
||||
manualHeadless *bool
|
||||
manualLogoless *bool
|
||||
manualCrumbsless *bool
|
||||
manualReadOnly *bool
|
||||
manualCommand *string
|
||||
manualScreenDumpDir *string
|
||||
|
|
@ -293,9 +290,9 @@ func (k *K9s) Override(k9sFlags *Flags) {
|
|||
k.manualRefreshRate = *k9sFlags.RefreshRate
|
||||
}
|
||||
|
||||
k.manualHeadless = k9sFlags.Headless
|
||||
k.manualLogoless = k9sFlags.Logoless
|
||||
k.manualCrumbsless = k9sFlags.Crumbsless
|
||||
k.UI.manualHeadless = k9sFlags.Headless
|
||||
k.UI.manualLogoless = k9sFlags.Logoless
|
||||
k.UI.manualCrumbsless = k9sFlags.Crumbsless
|
||||
if k9sFlags.ReadOnly != nil && *k9sFlags.ReadOnly {
|
||||
k.manualReadOnly = k9sFlags.ReadOnly
|
||||
}
|
||||
|
|
@ -309,7 +306,7 @@ func (k *K9s) Override(k9sFlags *Flags) {
|
|||
|
||||
// IsHeadless returns headless setting.
|
||||
func (k *K9s) IsHeadless() bool {
|
||||
if IsBoolSet(k.manualHeadless) {
|
||||
if IsBoolSet(k.UI.manualHeadless) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -318,7 +315,7 @@ func (k *K9s) IsHeadless() bool {
|
|||
|
||||
// IsLogoless returns logoless setting.
|
||||
func (k *K9s) IsLogoless() bool {
|
||||
if IsBoolSet(k.manualLogoless) {
|
||||
if IsBoolSet(k.UI.manualLogoless) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -327,7 +324,7 @@ func (k *K9s) IsLogoless() bool {
|
|||
|
||||
// IsCrumbsless returns crumbsless setting.
|
||||
func (k *K9s) IsCrumbsless() bool {
|
||||
if IsBoolSet(k.manualCrumbsless) {
|
||||
if IsBoolSet(k.UI.manualCrumbsless) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,17 +66,17 @@ func Test_k9sOverrides(t *testing.T) {
|
|||
ReadOnly: false,
|
||||
NoExitOnCtrlC: false,
|
||||
UI: UI{
|
||||
Headless: false,
|
||||
Logoless: false,
|
||||
Crumbsless: false,
|
||||
Headless: false,
|
||||
Logoless: false,
|
||||
Crumbsless: false,
|
||||
manualHeadless: &true,
|
||||
manualLogoless: &true,
|
||||
manualCrumbsless: &true,
|
||||
},
|
||||
SkipLatestRevCheck: false,
|
||||
DisablePodCounting: false,
|
||||
manualRefreshRate: 100,
|
||||
manualReadOnly: &true,
|
||||
manualHeadless: &true,
|
||||
manualLogoless: &true,
|
||||
manualCrumbsless: &true,
|
||||
manualCommand: &cmd,
|
||||
manualScreenDumpDir: &dir,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
|
|
@ -16,7 +17,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/config/data"
|
||||
"github.com/derailed/k9s/internal/config/json"
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const k9sPluginsDir = "k9s/plugins"
|
||||
|
|
@ -88,10 +89,14 @@ func (p Plugins) loadPluginDir(dir string) error {
|
|||
if err != nil {
|
||||
errs = errors.Join(errs, err)
|
||||
}
|
||||
|
||||
d := yaml.NewDecoder(bytes.NewReader(fileContent))
|
||||
d.KnownFields(true)
|
||||
|
||||
var plugin Plugin
|
||||
if err = yaml.UnmarshalStrict(fileContent, &plugin); err != nil {
|
||||
if err = d.Decode(&plugin); err != nil {
|
||||
var plugins Plugins
|
||||
if err = yaml.UnmarshalStrict(fileContent, &plugins); err != nil {
|
||||
if err = d.Decode(&plugins); err != nil {
|
||||
return fmt.Errorf("cannot parse %s into either a single plugin nor plugins: %w", fileName, err)
|
||||
}
|
||||
for name, plugin := range plugins.Plugins {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/config/json"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"github.com/derailed/tview"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// StyleListener represents a skin's listener.
|
||||
|
|
|
|||
|
|
@ -17,3 +17,9 @@ views:
|
|||
- AGE
|
||||
- NAME
|
||||
- IP
|
||||
|
||||
bozo:
|
||||
columns:
|
||||
- DUH
|
||||
- BLAH
|
||||
- BLEE
|
||||
|
|
|
|||
|
|
@ -34,4 +34,8 @@ type UI struct {
|
|||
|
||||
// DefaultsToFullScreen toggles fullscreen on views like logs, yaml, details.
|
||||
DefaultsToFullScreen bool `json:"defaultsToFullScreen" yaml:"defaultsToFullScreen"`
|
||||
|
||||
manualHeadless *bool
|
||||
manualLogoless *bool
|
||||
manualCrumbsless *bool
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,11 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/config/data"
|
||||
"github.com/derailed/k9s/internal/config/json"
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// ViewConfigListener represents a view config listener.
|
||||
|
|
@ -118,29 +119,65 @@ func (v *CustomView) Load(path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// AddListeners registers a new listener for various commands.
|
||||
func (v *CustomView) AddListeners(l ViewConfigListener, cmds ...string) {
|
||||
for _, cmd := range cmds {
|
||||
if cmd != "" {
|
||||
v.listeners[cmd] = l
|
||||
}
|
||||
}
|
||||
v.fireConfigChanged()
|
||||
}
|
||||
|
||||
// AddListener registers a new listener.
|
||||
func (v *CustomView) AddListener(gvr string, l ViewConfigListener) {
|
||||
v.listeners[gvr] = l
|
||||
func (v *CustomView) AddListener(cmd string, l ViewConfigListener) {
|
||||
v.listeners[cmd] = l
|
||||
v.fireConfigChanged()
|
||||
}
|
||||
|
||||
// RemoveListener unregister a listener.
|
||||
func (v *CustomView) RemoveListener(gvr string) {
|
||||
delete(v.listeners, gvr)
|
||||
}
|
||||
|
||||
func (v *CustomView) fireConfigChanged() {
|
||||
for gvr, list := range v.listeners {
|
||||
if vs := v.getVS(gvr, list.GetNamespace()); vs == nil {
|
||||
list.ViewSettingsChanged(nil)
|
||||
} else {
|
||||
slog.Debug("Reloading custom view settings", slogs.GVR, gvr)
|
||||
list.ViewSettingsChanged(vs)
|
||||
func (v *CustomView) RemoveListener(l ViewConfigListener) {
|
||||
for k, list := range v.listeners {
|
||||
if list == l {
|
||||
delete(v.listeners, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v *CustomView) fireConfigChanged() {
|
||||
cmds := slices.Collect(maps.Keys(v.listeners))
|
||||
slices.SortFunc(cmds, func(a, b string) int {
|
||||
switch {
|
||||
case strings.Contains(a, "/") && !strings.Contains(b, "/"):
|
||||
return 1
|
||||
case !strings.Contains(a, "/") && strings.Contains(b, "/"):
|
||||
return -1
|
||||
default:
|
||||
return strings.Compare(a, b)
|
||||
}
|
||||
})
|
||||
type tuple struct {
|
||||
cmd string
|
||||
vs *ViewSetting
|
||||
}
|
||||
var victim tuple
|
||||
for _, cmd := range cmds {
|
||||
if vs := v.getVS(cmd, v.listeners[cmd].GetNamespace()); vs != nil {
|
||||
slog.Debug("Reloading custom view settings", slogs.Command, cmd)
|
||||
victim = tuple{cmd, vs}
|
||||
break
|
||||
}
|
||||
victim = tuple{cmd, nil}
|
||||
}
|
||||
if victim.cmd != "" {
|
||||
v.listeners[victim.cmd].ViewSettingsChanged(victim.vs)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *CustomView) getVS(gvr, ns string) *ViewSetting {
|
||||
if client.IsAllNamespaces(ns) {
|
||||
ns = client.NamespaceAll
|
||||
}
|
||||
k := gvr
|
||||
kk := slices.Collect(maps.Keys(v.Views))
|
||||
slices.SortFunc(kk, func(s1, s2 string) int {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ func TestCustomView_getVS(t *testing.T) {
|
|||
}{
|
||||
"empty": {},
|
||||
|
||||
"miss": {
|
||||
gvr: "zorg",
|
||||
},
|
||||
|
||||
"gvr": {
|
||||
gvr: "v1/pods",
|
||||
e: &ViewSetting{
|
||||
|
|
@ -40,6 +44,13 @@ func TestCustomView_getVS(t *testing.T) {
|
|||
},
|
||||
},
|
||||
|
||||
"alias": {
|
||||
gvr: "bozo",
|
||||
e: &ViewSetting{
|
||||
Columns: []string{"DUH", "BLAH", "BLEE"},
|
||||
},
|
||||
},
|
||||
|
||||
"toast-no-ns": {
|
||||
gvr: "v1/pods",
|
||||
ns: "zorg",
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/config/data"
|
||||
"github.com/derailed/k9s/internal/render/helm"
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
"gopkg.in/yaml.v2"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
|
@ -83,7 +83,7 @@ func (h *HelmChart) GetValues(path string, allValues bool) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return yaml.Marshal(resp)
|
||||
return data.WriteYAML(resp)
|
||||
}
|
||||
|
||||
// Describe returns the chart notes.
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/config/data"
|
||||
"github.com/derailed/k9s/internal/render/helm"
|
||||
)
|
||||
|
||||
|
|
@ -126,10 +126,14 @@ func (h *HelmHistory) GetValues(path string, allValues bool) ([]byte, error) {
|
|||
return nil, fmt.Errorf("expected helm.ReleaseRes, but got %T", rel)
|
||||
}
|
||||
|
||||
var content any
|
||||
if allValues {
|
||||
return yaml.Marshal(resp.Release.Chart.Values)
|
||||
content = resp.Release.Chart.Values
|
||||
} else {
|
||||
content = resp.Release.Config
|
||||
}
|
||||
return yaml.Marshal(resp.Release.Config)
|
||||
|
||||
return data.WriteYAML(content)
|
||||
}
|
||||
|
||||
func (h *HelmHistory) Rollback(_ context.Context, path, rev string) error {
|
||||
|
|
|
|||
|
|
@ -1,142 +0,0 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Authors of K9s
|
||||
|
||||
package dao
|
||||
|
||||
// !!BOZO!! Popeye
|
||||
// import (
|
||||
// "bytes"
|
||||
// "context"
|
||||
// "encoding/json"
|
||||
// "errors"
|
||||
// "fmt"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "sort"
|
||||
// "time"
|
||||
|
||||
// "github.com/derailed/k9s/internal"
|
||||
// "github.com/derailed/k9s/internal/client"
|
||||
// cfg "github.com/derailed/k9s/internal/config"
|
||||
// "github.com/derailed/k9s/internal/render"
|
||||
// "github.com/derailed/popeye/pkg"
|
||||
// "github.com/derailed/popeye/pkg/config"
|
||||
// "github.com/derailed/popeye/types"
|
||||
// "k8s.io/apimachinery/pkg/runtime"
|
||||
// )
|
||||
|
||||
// var _ Accessor = (*Popeye)(nil)
|
||||
|
||||
// // Popeye tracks cluster sanitization.
|
||||
// type Popeye struct {
|
||||
// NonResource
|
||||
// }
|
||||
|
||||
// // NewPopeye returns a new set of aliases.
|
||||
// func NewPopeye(f Factory) *Popeye {
|
||||
// a := Popeye{}
|
||||
// a.Init(f, client.NewGVR("popeye"))
|
||||
|
||||
// return &a
|
||||
// }
|
||||
|
||||
// type readWriteCloser struct {
|
||||
// *bytes.Buffer
|
||||
// }
|
||||
|
||||
// // Close close read stream.
|
||||
// func (readWriteCloser) Close() error {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // List returns a collection of aliases.
|
||||
// func (p *Popeye) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||
// defer func(t time.Time) {
|
||||
// log.Debug().Msgf("Popeye -- Elapsed %v", time.Since(t))
|
||||
// if err := recover(); err != nil {
|
||||
// log.Debug().Msgf("POPEYE DIED!")
|
||||
// }
|
||||
// }(time.Now())
|
||||
|
||||
// flags, js := config.NewFlags(), "json"
|
||||
// flags.Output = &js
|
||||
// flags.ActiveNamespace = &ns
|
||||
|
||||
// if report, ok := ctx.Value(internal.KeyPath).(string); ok && report != "" {
|
||||
// ns, n := client.Namespaced(report)
|
||||
// sections := []string{n}
|
||||
// flags.Sections = §ions
|
||||
// flags.ActiveNamespace = &ns
|
||||
// }
|
||||
// spinach := filepath.Join(cfg.AppConfigDir, "spinach.yaml")
|
||||
// if c, err := p.getFactory().Client().Config().CurrentContextName(); err == nil {
|
||||
// spinach = filepath.Join(cfg.AppConfigDir, fmt.Sprintf("%s_spinach.yaml", c))
|
||||
// }
|
||||
// if _, err := os.Stat(spinach); err == nil {
|
||||
// flags.Spinach = &spinach
|
||||
// }
|
||||
|
||||
// popeye, err := pkg.NewPopeye(flags, &log.Logger)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// popeye.SetFactory(newPopeyeFactory(p.Factory))
|
||||
// if err = popeye.Init(); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// buff := readWriteCloser{Buffer: bytes.NewBufferString("")}
|
||||
// popeye.SetOutputTarget(buff)
|
||||
// if _, _, err = popeye.Sanitize(); err != nil {
|
||||
// log.Error().Err(err).Msgf("BOOM %#v", *flags.Sections)
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// var b render.Builder
|
||||
// if err = json.Unmarshal(buff.Bytes(), &b); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// oo := make([]runtime.Object, 0, len(b.Report.Sections))
|
||||
// sort.Sort(b.Report.Sections)
|
||||
// for _, s := range b.Report.Sections {
|
||||
// s.Tally.Count = len(s.Outcome)
|
||||
// if s.Tally.Sum() > 0 {
|
||||
// oo = append(oo, s)
|
||||
// }
|
||||
// }
|
||||
|
||||
// return oo, nil
|
||||
// }
|
||||
|
||||
// // Get retrieves a resource.
|
||||
// func (p *Popeye) Get(_ context.Context, _ string) (runtime.Object, error) {
|
||||
// return nil, errors.New("NYI!!")
|
||||
// }
|
||||
|
||||
// // ----------------------------------------------------------------------------
|
||||
// // Helpers...
|
||||
|
||||
// type popFactory struct {
|
||||
// Factory
|
||||
// }
|
||||
|
||||
// var _ types.Factory = (*popFactory)(nil)
|
||||
|
||||
// func newPopeyeFactory(f Factory) *popFactory {
|
||||
// return &popFactory{Factory: f}
|
||||
// }
|
||||
|
||||
// func (p *popFactory) Client() types.Connection {
|
||||
// return &popeyeConnection{Connection: p.Factory.Client()}
|
||||
// }
|
||||
|
||||
// type popeyeConnection struct {
|
||||
// client.Connection
|
||||
// }
|
||||
|
||||
// var _ types.Connection = (*popeyeConnection)(nil)
|
||||
|
||||
// func (c *popeyeConnection) Config() types.Config {
|
||||
// return c.Connection.Config()
|
||||
// }
|
||||
|
|
@ -242,21 +242,6 @@ func loadK9s(m ResourceMetas) {
|
|||
Verbs: []string{},
|
||||
Categories: []string{k9sCat},
|
||||
}
|
||||
m[client.NewGVR("popeye")] = metav1.APIResource{
|
||||
Name: "popeye",
|
||||
Kind: "Popeye",
|
||||
SingularName: "popeye",
|
||||
Namespaced: true,
|
||||
Verbs: []string{},
|
||||
Categories: []string{k9sCat},
|
||||
}
|
||||
m[client.NewGVR("sanitizer")] = metav1.APIResource{
|
||||
Name: "sanitizer",
|
||||
Kind: "Sanitizer",
|
||||
SingularName: "sanitizer",
|
||||
Verbs: []string{},
|
||||
Categories: []string{k9sCat},
|
||||
}
|
||||
m[client.NewGVR("contexts")] = metav1.APIResource{
|
||||
Name: "contexts",
|
||||
Kind: "Contexts",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -291,14 +292,15 @@ func makeC(n string) c {
|
|||
return c{name: n}
|
||||
}
|
||||
|
||||
func (c) InCmdMode() bool { return false }
|
||||
func (c c) Name() string { return c.name }
|
||||
func (c c) Hints() model.MenuHints { return nil }
|
||||
func (c c) HasFocus() bool { return false }
|
||||
func (c c) ExtraHints() map[string]string { return nil }
|
||||
func (c c) Draw(tcell.Screen) {}
|
||||
func (c c) InputHandler() func(*tcell.EventKey, func(tview.Primitive)) { return nil }
|
||||
func (c c) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
|
||||
func (c) InCmdMode() bool { return false }
|
||||
func (c c) Name() string { return c.name }
|
||||
func (c) SetCommand(*cmd.Interpreter) {}
|
||||
func (c) Hints() model.MenuHints { return nil }
|
||||
func (c) HasFocus() bool { return false }
|
||||
func (c) ExtraHints() map[string]string { return nil }
|
||||
func (c) Draw(tcell.Screen) {}
|
||||
func (c) InputHandler() func(*tcell.EventKey, func(tview.Primitive)) { return nil }
|
||||
func (c) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
|
||||
return nil
|
||||
}
|
||||
func (c c) SetRect(int, int, int, int) {}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/derailed/k9s/internal/model1"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/sahilm/fuzzy"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
|
@ -93,6 +94,13 @@ type Component interface {
|
|||
Hinter
|
||||
Commander
|
||||
Filterer
|
||||
Viewer
|
||||
}
|
||||
|
||||
// Viewer represents a resource viewer.
|
||||
type Viewer interface {
|
||||
// SetCommand sets the current command.
|
||||
SetCommand(*cmd.Interpreter)
|
||||
}
|
||||
|
||||
type Filterer interface {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ type Renderer interface {
|
|||
// ColorerFunc returns a row colorer function.
|
||||
ColorerFunc() ColorerFunc
|
||||
|
||||
// SetViewSetting sets custom view settings if any.
|
||||
SetViewSetting(vs *config.ViewSetting)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ func (b *Base) doHeader(dh model1.Header) model1.Header {
|
|||
return b.specs.Header(dh)
|
||||
}
|
||||
|
||||
// SetViewSetting sets custom view settings if any.
|
||||
func (b *Base) SetViewSetting(vs *config.ViewSetting) {
|
||||
var cols []string
|
||||
b.vs = vs
|
||||
|
|
|
|||
|
|
@ -1,195 +0,0 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Authors of K9s
|
||||
|
||||
package render
|
||||
|
||||
import "github.com/derailed/popeye/pkg/config"
|
||||
|
||||
// !!BOZO!! Popeye
|
||||
|
||||
// // Popeye renders a sanitizer to screen.
|
||||
// type Popeye struct {
|
||||
// Base
|
||||
// }
|
||||
|
||||
// // ColorerFunc colors a resource row.
|
||||
// func (Popeye) ColorerFunc() ColorerFunc {
|
||||
// return func(ns string, h Header, re *model1.RowEvent) tcell.Color {
|
||||
// c := DefaultColorer(ns, h, re)
|
||||
|
||||
// warnCol := h.IndexOf("WARNING", true)
|
||||
// status, _ := strconv.Atoi(strings.TrimSpace(re.Row.Fields[warnCol]))
|
||||
// if status > 0 {
|
||||
// c = tcell.ColorOrange
|
||||
// }
|
||||
// errCol := h.IndexOf("ERROR", true)
|
||||
// status, _ = strconv.Atoi(strings.TrimSpace(re.Row.Fields[errCol]))
|
||||
// if status > 0 {
|
||||
// c = ErrColor
|
||||
// }
|
||||
// return c
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Header returns a header row.
|
||||
// func (Popeye) Header(ns string) model1.Header {
|
||||
// return model1.Header{
|
||||
// model1.HeaderColumn{Name: "RESOURCE"},
|
||||
// model1.HeaderColumn{Name: "SCORE%", Align: tview.AlignRight},
|
||||
// model1.HeaderColumn{Name: "SCANNED", Align: tview.AlignRight},
|
||||
// model1.HeaderColumn{Name: "ERROR", Align: tview.AlignRight},
|
||||
// model1.HeaderColumn{Name: "WARNING", Align: tview.AlignRight},
|
||||
// model1.HeaderColumn{Name: "INFO", Align: tview.AlignRight},
|
||||
// model1.HeaderColumn{Name: "OK", Align: tview.AlignRight},
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Render renders a K8s resource to screen.
|
||||
// func (Popeye) Render(o interface{}, ns string, r *model1.Row) error {
|
||||
// s, ok := o.(Section)
|
||||
// if !ok {
|
||||
// return fmt.Errorf("expected Section, but got %T", o)
|
||||
// }
|
||||
|
||||
// r.ID = client.FQN(ns, s.Title)
|
||||
// r.Fields = append(r.Fields,
|
||||
// s.Title,
|
||||
// strconv.Itoa(s.Tally.Score()),
|
||||
// strconv.Itoa(s.Tally.OK+s.Tally.Info+s.Tally.Warning+s.Tally.Error),
|
||||
// strconv.Itoa(s.Tally.Error),
|
||||
// strconv.Itoa(s.Tally.Warning),
|
||||
// strconv.Itoa(s.Tally.Info),
|
||||
// strconv.Itoa(s.Tally.OK),
|
||||
// )
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // ----------------------------------------------------------------------------
|
||||
// // Helpers...
|
||||
|
||||
type (
|
||||
// // Builder represents a popeye report.
|
||||
// Builder struct {
|
||||
// Report Report `json:"popeye" yaml:"popeye"`
|
||||
// }
|
||||
|
||||
// // Report represents the output of a sanitization pass.
|
||||
// Report struct {
|
||||
// Score int `json:"score" yaml:"score"`
|
||||
// Grade string `json:"grade" yaml:"grade"`
|
||||
// Sections Sections `json:"sanitizers,omitempty" yaml:"sanitizers,omitempty"`
|
||||
// }
|
||||
|
||||
// Sections represents a collection of sections.
|
||||
Sections []Section
|
||||
|
||||
// Section represents a sanitizer pass.
|
||||
Section struct {
|
||||
Title string `json:"sanitizer" yaml:"sanitizer"`
|
||||
GVR string `yaml:"gvr" json:"gvr"`
|
||||
Tally *Tally `json:"tally" yaml:"tally"`
|
||||
Outcome Outcome `json:"issues,omitempty" yaml:"issues,omitempty"`
|
||||
}
|
||||
|
||||
// Outcome represents a classification of reports outcome.
|
||||
Outcome map[string]Issues
|
||||
|
||||
// Issues represents a collection of issues.
|
||||
Issues []Issue
|
||||
|
||||
// Issue represents a sanitization issue.
|
||||
Issue struct {
|
||||
Group string `yaml:"group" json:"group"`
|
||||
GVR string `yaml:"gvr" json:"gvr"`
|
||||
Level config.Level `yaml:"level" json:"level"`
|
||||
Message string `yaml:"message" json:"message"`
|
||||
}
|
||||
|
||||
// Tally tracks a section scores.
|
||||
|
||||
Tally struct {
|
||||
OK, Info, Warning, Error int
|
||||
Count int
|
||||
}
|
||||
)
|
||||
|
||||
// // Sum sums up tally counts.
|
||||
// func (t *Tally) Sum() int {
|
||||
// return t.OK + t.Info + t.Warning + t.Error
|
||||
// }
|
||||
|
||||
// // Score returns the overall sections score in percent.
|
||||
// func (t *Tally) Score() int {
|
||||
// oks := t.OK + t.Info
|
||||
// return toPerc(float64(oks), float64(oks+t.Warning+t.Error))
|
||||
// }
|
||||
|
||||
// func toPerc(v1, v2 float64) int {
|
||||
// if v2 == 0 {
|
||||
// return 0
|
||||
// }
|
||||
// return int(math.Floor((v1 / v2) * 100))
|
||||
// }
|
||||
|
||||
// // Len returns a section length.
|
||||
// func (s Sections) Len() int {
|
||||
// return len(s)
|
||||
// }
|
||||
|
||||
// // Swap swaps values.
|
||||
// func (s Sections) Swap(i, j int) {
|
||||
// s[i], s[j] = s[j], s[i]
|
||||
// }
|
||||
|
||||
// // Less compares section scores.
|
||||
// func (s Sections) Less(i, j int) bool {
|
||||
// t1, t2 := s[i].Tally, s[j].Tally
|
||||
// return t1.Score() < t2.Score()
|
||||
// }
|
||||
|
||||
// // GetObjectKind returns a schema object.
|
||||
// func (Section) GetObjectKind() schema.ObjectKind {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // DeepCopyObject returns a container copy.
|
||||
// func (s Section) DeepCopyObject() runtime.Object {
|
||||
// return s
|
||||
// }
|
||||
|
||||
// // MaxSeverity gather the max severity in a collection of issues.
|
||||
// func (s Section) MaxSeverity() config.Level {
|
||||
// max := config.OkLevel
|
||||
// for _, issues := range s.Outcome {
|
||||
// m := issues.MaxSeverity()
|
||||
// if m > max {
|
||||
// max = m
|
||||
// }
|
||||
// }
|
||||
|
||||
// return max
|
||||
// }
|
||||
|
||||
// // MaxSeverity gather the max severity in a collection of issues.
|
||||
// func (i Issues) MaxSeverity() config.Level {
|
||||
// max := config.OkLevel
|
||||
// for _, is := range i {
|
||||
// if is.Level > max {
|
||||
// max = is.Level
|
||||
// }
|
||||
// }
|
||||
|
||||
// return max
|
||||
// }
|
||||
|
||||
// // CountSeverity counts severity level instances.
|
||||
// func (i Issues) CountSeverity(l config.Level) int {
|
||||
// var count int
|
||||
// for _, is := range i {
|
||||
// if is.Level == l {
|
||||
// count++
|
||||
// }
|
||||
// }
|
||||
|
||||
// return count
|
||||
// }
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Authors of K9s
|
||||
|
||||
package render
|
||||
|
||||
// Level tracks lint check level.
|
||||
type Level int
|
||||
|
||||
const (
|
||||
// OkLevel denotes no linting issues.
|
||||
OkLevel Level = iota
|
||||
// InfoLevel denotes FIY linting issues.
|
||||
InfoLevel
|
||||
// WarnLevel denotes a warning issue.
|
||||
WarnLevel
|
||||
// ErrorLevel denotes a serious issue.
|
||||
ErrorLevel
|
||||
)
|
||||
|
||||
type (
|
||||
// Sections represents a collection of sections.
|
||||
Sections []Section
|
||||
|
||||
// Section represents a sanitizer pass.
|
||||
Section struct {
|
||||
Title string `json:"sanitizer" yaml:"sanitizer"`
|
||||
GVR string `yaml:"gvr" json:"gvr"`
|
||||
Outcome Outcome `json:"issues,omitempty" yaml:"issues,omitempty"`
|
||||
}
|
||||
|
||||
// Outcome represents a classification of reports outcome.
|
||||
Outcome map[string]Issues
|
||||
|
||||
// Issues represents a collection of issues.
|
||||
Issues []Issue
|
||||
|
||||
// Issue represents a sanitization issue.
|
||||
Issue struct {
|
||||
Group string `yaml:"group" json:"group"`
|
||||
GVR string `yaml:"gvr" json:"gvr"`
|
||||
Level Level `yaml:"level" json:"level"`
|
||||
Message string `yaml:"message" json:"message"`
|
||||
}
|
||||
)
|
||||
|
|
@ -32,11 +32,19 @@ type synchronizer interface {
|
|||
type Configurator struct {
|
||||
Config *config.Config
|
||||
Styles *config.Styles
|
||||
CustomView *config.CustomView
|
||||
customView *config.CustomView
|
||||
BenchFile string
|
||||
skinFile string
|
||||
}
|
||||
|
||||
func (c *Configurator) CustomView() *config.CustomView {
|
||||
if c.customView == nil {
|
||||
c.customView = config.NewCustomView()
|
||||
}
|
||||
|
||||
return c.customView
|
||||
}
|
||||
|
||||
// HasSkin returns true if a skin file was located.
|
||||
func (c *Configurator) HasSkin() bool {
|
||||
return c.skinFile != ""
|
||||
|
|
@ -82,13 +90,9 @@ func (c *Configurator) CustomViewsWatcher(ctx context.Context, s synchronizer) e
|
|||
|
||||
// RefreshCustomViews load view configuration changes.
|
||||
func (c *Configurator) RefreshCustomViews() error {
|
||||
if c.CustomView == nil {
|
||||
c.CustomView = config.NewCustomView()
|
||||
} else {
|
||||
c.CustomView.Reset()
|
||||
}
|
||||
c.CustomView().Reset()
|
||||
|
||||
return c.CustomView.Load(config.AppViewsFile)
|
||||
return c.CustomView().Load(config.AppViewsFile)
|
||||
}
|
||||
|
||||
// SkinsDirWatcher watches for skin directory file changes.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -39,23 +40,24 @@ func makeComponent(n string) c {
|
|||
return c{name: n}
|
||||
}
|
||||
|
||||
func (c) InCmdMode() bool { return false }
|
||||
func (c c) HasFocus() bool { return true }
|
||||
func (c c) Hints() model.MenuHints { return nil }
|
||||
func (c c) ExtraHints() map[string]string { return nil }
|
||||
func (c c) Name() string { return c.name }
|
||||
func (c c) Draw(tcell.Screen) {}
|
||||
func (c c) InputHandler() func(*tcell.EventKey, func(tview.Primitive)) { return nil }
|
||||
func (c c) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
|
||||
func (c) SetCommand(*cmd.Interpreter) {}
|
||||
func (c) InCmdMode() bool { return false }
|
||||
func (c) HasFocus() bool { return true }
|
||||
func (c) Hints() model.MenuHints { return nil }
|
||||
func (c) ExtraHints() map[string]string { return nil }
|
||||
func (c c) Name() string { return c.name }
|
||||
func (c) Draw(tcell.Screen) {}
|
||||
func (c) InputHandler() func(*tcell.EventKey, func(tview.Primitive)) { return nil }
|
||||
func (c) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
|
||||
return nil
|
||||
}
|
||||
func (c c) SetRect(int, int, int, int) {}
|
||||
func (c c) GetRect() (int, int, int, int) { return 0, 0, 0, 0 }
|
||||
func (c c) GetFocusable() tview.Focusable { return c }
|
||||
func (c c) Focus(func(tview.Primitive)) {}
|
||||
func (c c) Blur() {}
|
||||
func (c c) Start() {}
|
||||
func (c c) Stop() {}
|
||||
func (c c) Init(context.Context) error { return nil }
|
||||
func (c c) SetFilter(string) {}
|
||||
func (c c) SetLabelFilter(map[string]string) {}
|
||||
func (c) SetRect(int, int, int, int) {}
|
||||
func (c) GetRect() (int, int, int, int) { return 0, 0, 0, 0 }
|
||||
func (c c) GetFocusable() tview.Focusable { return c }
|
||||
func (c) Focus(func(tview.Primitive)) {}
|
||||
func (c) Blur() {}
|
||||
func (c) Start() {}
|
||||
func (c) Stop() {}
|
||||
func (c) Init(context.Context) error { return nil }
|
||||
func (c) SetFilter(string) {}
|
||||
func (c) SetLabelFilter(map[string]string) {}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ type Table struct {
|
|||
ctx context.Context
|
||||
mx sync.RWMutex
|
||||
readOnly bool
|
||||
noIcon bool
|
||||
}
|
||||
|
||||
// NewTable returns a new table view.
|
||||
|
|
@ -72,6 +73,15 @@ func NewTable(gvr client.GVR) *Table {
|
|||
}
|
||||
}
|
||||
|
||||
// SetNoIcon toggles no icon mode.
|
||||
func (t *Table) SetNoIcon(b bool) {
|
||||
t.mx.Lock()
|
||||
defer t.mx.Unlock()
|
||||
|
||||
t.noIcon = b
|
||||
}
|
||||
|
||||
// SetReadOnly toggles read-only mode.
|
||||
func (t *Table) SetReadOnly(ro bool) {
|
||||
t.mx.Lock()
|
||||
defer t.mx.Unlock()
|
||||
|
|
@ -114,7 +124,8 @@ func (t *Table) getMSort() bool {
|
|||
return t.manualSort
|
||||
}
|
||||
|
||||
func (t *Table) setViewSetting(vs *config.ViewSetting) bool {
|
||||
// SetViewSetting sets custom view config is present.
|
||||
func (t *Table) SetViewSetting(vs *config.ViewSetting) bool {
|
||||
t.mx.Lock()
|
||||
defer t.mx.Unlock()
|
||||
|
||||
|
|
@ -127,7 +138,8 @@ func (t *Table) setViewSetting(vs *config.ViewSetting) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (t *Table) getViewSetting() *config.ViewSetting {
|
||||
// GetViewSetting return current view settings if any.
|
||||
func (t *Table) GetViewSetting() *config.ViewSetting {
|
||||
t.mx.RLock()
|
||||
defer t.mx.RUnlock()
|
||||
|
||||
|
|
@ -161,7 +173,7 @@ func (t *Table) GVR() client.GVR { return t.gvr }
|
|||
|
||||
// ViewSettingsChanged notifies listener the view configuration changed.
|
||||
func (t *Table) ViewSettingsChanged(vs *config.ViewSetting) {
|
||||
if t.setViewSetting(vs) {
|
||||
if t.SetViewSetting(vs) {
|
||||
if vs == nil {
|
||||
if !t.getMSort() && !t.sortCol.IsSet() {
|
||||
t.setSortCol(model1.SortColumn{})
|
||||
|
|
@ -294,7 +306,7 @@ func (t *Table) doUpdate(data *model1.TableData) *model1.TableData {
|
|||
t.actions.Delete(KeyShiftP)
|
||||
}
|
||||
|
||||
t.setSortCol(data.ComputeSortCol(t.getViewSetting(), t.getSortCol(), t.getMSort()))
|
||||
t.setSortCol(data.ComputeSortCol(t.GetViewSetting(), t.getSortCol(), t.getMSort()))
|
||||
|
||||
return data
|
||||
}
|
||||
|
|
@ -537,9 +549,12 @@ func (t *Table) styleTitle() string {
|
|||
|
||||
var title string
|
||||
if ns == client.ClusterScope {
|
||||
title = SkinTitle(fmt.Sprintf(TitleFmt, ROIndicator(t.readOnly), t.gvr, render.AsThousands(rc)), t.styles.Frame())
|
||||
title = SkinTitle(fmt.Sprintf(TitleFmt, t.gvr, render.AsThousands(rc)), t.styles.Frame())
|
||||
} else {
|
||||
title = SkinTitle(fmt.Sprintf(NSTitleFmt, ROIndicator(t.readOnly), t.gvr, ns, render.AsThousands(rc)), t.styles.Frame())
|
||||
title = SkinTitle(fmt.Sprintf(NSTitleFmt, t.gvr, ns, render.AsThousands(rc)), t.styles.Frame())
|
||||
}
|
||||
if ic := ROIndicator(t.readOnly, t.noIcon); ic != "" {
|
||||
title = " " + ic + title
|
||||
}
|
||||
|
||||
buff := t.cmdBuff.GetText()
|
||||
|
|
@ -557,10 +572,14 @@ func (t *Table) styleTitle() string {
|
|||
}
|
||||
|
||||
// ROIndicator returns an icon showing whether the session is in readonly mode or not.
|
||||
func ROIndicator(ro bool) string {
|
||||
if ro {
|
||||
return LockedIC
|
||||
func ROIndicator(ro, noIC bool) string {
|
||||
if noIC {
|
||||
return ""
|
||||
}
|
||||
|
||||
return UnlockedIC
|
||||
if ro {
|
||||
return lockedIC
|
||||
}
|
||||
|
||||
return unlockedIC
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ const (
|
|||
SearchFmt = "<[filter:bg:r]/%s[fg:bg:-]> "
|
||||
|
||||
// NSTitleFmt represents a namespaced view title.
|
||||
NSTitleFmt = " %s [fg:bg:b]%s([hilite:bg:b]%s[fg:bg:-])[fg:bg:-][[count:bg:b]%s[fg:bg:-]][fg:bg:-] "
|
||||
NSTitleFmt = " [fg:bg:b]%s([hilite:bg:b]%s[fg:bg:-])[fg:bg:-][[count:bg:b]%s[fg:bg:-]][fg:bg:-] "
|
||||
|
||||
// TitleFmt represents a standard view title.
|
||||
TitleFmt = " %s [fg:bg:b]%s[fg:bg:-][[count:bg:b]%s[fg:bg:-]][fg:bg:-] "
|
||||
TitleFmt = " [fg:bg:b]%s[fg:bg:-][[count:bg:b]%s[fg:bg:-]][fg:bg:-] "
|
||||
|
||||
descIndicator = "↓"
|
||||
ascIndicator = "↑"
|
||||
|
|
|
|||
|
|
@ -16,11 +16,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
// UnlockedIC represents an unlocked icon.
|
||||
UnlockedIC = "🔓"
|
||||
|
||||
// LockedIC represents a locked icon.
|
||||
LockedIC = "🔒"
|
||||
unlockedIC = "🔓"
|
||||
lockedIC = "🔒"
|
||||
)
|
||||
|
||||
// Namespaceable represents a namespaceable model.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/slogs"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/ui/dialog"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
"github.com/derailed/tcell/v2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
|
@ -58,6 +59,12 @@ func (b *Browser) getUpdating() bool {
|
|||
return b.updating
|
||||
}
|
||||
|
||||
// SetCommand sets the current command.
|
||||
func (b *Browser) SetCommand(cmd *cmd.Interpreter) {
|
||||
b.GetTable().SetCommand(cmd)
|
||||
//b.Table.SetViewSetting(b.app.CustomView().VSFor(cmd)
|
||||
}
|
||||
|
||||
// Init watches all running pods in given namespace.
|
||||
func (b *Browser) Init(ctx context.Context) error {
|
||||
var err error
|
||||
|
|
@ -85,6 +92,7 @@ func (b *Browser) Init(ctx context.Context) error {
|
|||
b.app.CmdBuff().Reset()
|
||||
}
|
||||
b.Table.SetReadOnly(b.app.Config.IsReadOnly())
|
||||
b.Table.SetNoIcon(b.app.Config.K9s.UI.NoIcons)
|
||||
|
||||
b.bindKeys(b.Actions())
|
||||
for _, f := range b.bindKeysFn {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ package cmd
|
|||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
)
|
||||
|
||||
// Interpreter tracks user prompt input.
|
||||
|
|
@ -218,7 +220,7 @@ func (c *Interpreter) FuzzyArg() (string, bool) {
|
|||
func (c *Interpreter) NSArg() (string, bool) {
|
||||
ns, ok := c.args[nsKey]
|
||||
|
||||
return ns, ok && ns != ""
|
||||
return ns, ok && ns != client.BlankNamespace
|
||||
}
|
||||
|
||||
// HasContext returns the current context if any.
|
||||
|
|
|
|||
|
|
@ -329,7 +329,7 @@ func (c *Command) exec(p *cmd.Interpreter, gvr client.GVR, comp model.Component,
|
|||
slog.Error("Failure detected during command exec", slogs.Error, e)
|
||||
c.app.Content.Dump()
|
||||
slog.Debug("Dumping history buffer", slogs.CmdHist, c.app.cmdHistory.List())
|
||||
slog.Error("Dumping stack", slogs.Stack, debug.Stack())
|
||||
slog.Error("Dumping stack", slogs.Stack, string(debug.Stack()))
|
||||
|
||||
p := cmd.NewInterpreter("pod")
|
||||
cmds := c.app.cmdHistory.List()
|
||||
|
|
@ -344,6 +344,8 @@ func (c *Command) exec(p *cmd.Interpreter, gvr client.GVR, comp model.Component,
|
|||
if comp == nil {
|
||||
return fmt.Errorf("no component found for %s", gvr)
|
||||
}
|
||||
comp.SetCommand(p)
|
||||
|
||||
c.app.Flash().Infof("Viewing %s...", gvr)
|
||||
if clearStack {
|
||||
cmd := contextRX.ReplaceAllString(p.GetLine(), "")
|
||||
|
|
@ -352,10 +354,10 @@ func (c *Command) exec(p *cmd.Interpreter, gvr client.GVR, comp model.Component,
|
|||
if err := c.app.inject(comp, clearStack); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pushCmd {
|
||||
c.app.cmdHistory.Push(p.GetLine())
|
||||
}
|
||||
slog.Debug("History", slogs.Stack, c.app.cmdHistory.List())
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/sahilm/fuzzy"
|
||||
|
|
@ -58,6 +59,7 @@ func NewDetails(app *App, title, subject, contentType string, searchable bool) *
|
|||
return &d
|
||||
}
|
||||
|
||||
func (d *Details) SetCommand(*cmd.Interpreter) {}
|
||||
func (d *Details) SetFilter(string) {}
|
||||
func (d *Details) SetLabelFilter(map[string]string) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"github.com/derailed/tview"
|
||||
)
|
||||
|
|
@ -43,8 +44,9 @@ func NewHelp(app *App) *Help {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *Help) SetFilter(string) {}
|
||||
func (h *Help) SetLabelFilter(map[string]string) {}
|
||||
func (*Help) SetCommand(*cmd.Interpreter) {}
|
||||
func (*Help) SetFilter(string) {}
|
||||
func (*Help) SetLabelFilter(map[string]string) {}
|
||||
|
||||
// Init initializes the component.
|
||||
func (h *Help) Init(ctx context.Context) error {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
|
@ -55,8 +56,10 @@ func clipboardWrite(text string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var bracketRX = regexp.MustCompile(`\[(.+)\[\]`)
|
||||
|
||||
func sanitizeEsc(s string) string {
|
||||
return strings.ReplaceAll(s, "[]", "]")
|
||||
return bracketRX.ReplaceAllString(s, `[$1]`)
|
||||
}
|
||||
|
||||
func cpCmd(flash *model.Flash, v *tview.TextView) func(*tcell.EventKey) *tcell.EventKey {
|
||||
|
|
|
|||
|
|
@ -327,3 +327,26 @@ func Test_linesWithRegions(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_sanitizeEsc(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
s string
|
||||
e string
|
||||
}{
|
||||
"empty": {},
|
||||
"empty-brackets": {
|
||||
s: "[]",
|
||||
e: "[]",
|
||||
},
|
||||
"tag": {
|
||||
s: "[fred[]",
|
||||
e: "[fred]",
|
||||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, sanitizeEsc(u.s))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/sahilm/fuzzy"
|
||||
|
|
@ -61,6 +62,7 @@ func NewLiveView(app *App, title string, m model.ResourceViewer) *LiveView {
|
|||
return &v
|
||||
}
|
||||
|
||||
func (v *LiveView) SetCommand(*cmd.Interpreter) {}
|
||||
func (v *LiveView) SetFilter(string) {}
|
||||
func (v *LiveView) SetLabelFilter(map[string]string) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"github.com/derailed/tview"
|
||||
)
|
||||
|
|
@ -63,8 +64,9 @@ func NewLog(gvr client.GVR, opts *dao.LogOptions) *Log {
|
|||
return &l
|
||||
}
|
||||
|
||||
func (l *Log) SetFilter(string) {}
|
||||
func (l *Log) SetLabelFilter(map[string]string) {}
|
||||
func (*Log) SetCommand(*cmd.Interpreter) {}
|
||||
func (*Log) SetFilter(string) {}
|
||||
func (*Log) SetLabelFilter(map[string]string) {}
|
||||
|
||||
// Init initializes the viewer.
|
||||
func (l *Log) Init(ctx context.Context) (err error) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"github.com/derailed/tview"
|
||||
)
|
||||
|
|
@ -27,6 +28,7 @@ func NewPicker() *Picker {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Picker) SetCommand(*cmd.Interpreter) {}
|
||||
func (p *Picker) SetFilter(string) {}
|
||||
func (p *Picker) SetLabelFilter(map[string]string) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/tchart"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
)
|
||||
|
||||
// Graphable represents a graphic component.
|
||||
|
|
@ -77,6 +78,7 @@ func NewPulse(gvr client.GVR) ResourceViewer {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Pulse) SetCommand(*cmd.Interpreter) {}
|
||||
func (p *Pulse) SetFilter(string) {}
|
||||
func (p *Pulse) SetLabelFilter(map[string]string) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,9 +90,6 @@ func miscViewers(vv MetaViewers) {
|
|||
vv[client.NewGVR("pulses")] = MetaViewer{
|
||||
viewerFn: NewPulse,
|
||||
}
|
||||
vv[client.NewGVR("sanitizer")] = MetaViewer{
|
||||
viewerFn: NewSanitizer,
|
||||
}
|
||||
}
|
||||
|
||||
func appsViewers(vv MetaViewers) {
|
||||
|
|
|
|||
|
|
@ -1,436 +0,0 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Authors of K9s
|
||||
|
||||
package view
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/xray"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"github.com/derailed/tview"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var _ ResourceViewer = (*Sanitizer)(nil)
|
||||
|
||||
// Sanitizer represents a sanitizer tree view.
|
||||
type Sanitizer struct {
|
||||
*ui.Tree
|
||||
|
||||
app *App
|
||||
gvr client.GVR
|
||||
meta metav1.APIResource
|
||||
model *model.Tree
|
||||
cancelFn context.CancelFunc
|
||||
envFn EnvFunc
|
||||
contextFn ContextFunc
|
||||
}
|
||||
|
||||
// NewSanitizer returns a new view.
|
||||
func NewSanitizer(gvr client.GVR) ResourceViewer {
|
||||
return &Sanitizer{
|
||||
gvr: gvr,
|
||||
Tree: ui.NewTree(),
|
||||
model: model.NewTree(gvr),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Sanitizer) SetFilter(string) {}
|
||||
func (s *Sanitizer) SetLabelFilter(map[string]string) {}
|
||||
|
||||
// Init initializes the view.
|
||||
func (s *Sanitizer) Init(ctx context.Context) error {
|
||||
s.envFn = s.k9sEnv
|
||||
|
||||
if err := s.Tree.Init(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
s.SetKeyListenerFn(s.keyEntered)
|
||||
|
||||
var err error
|
||||
s.meta, err = dao.MetaAccess.MetaFor(s.gvr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.app, err = extractApp(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.bindKeys()
|
||||
s.SetBackgroundColor(s.app.Styles.Xray().BgColor.Color())
|
||||
s.SetBorderColor(s.app.Styles.Frame().Border.FgColor.Color())
|
||||
s.SetBorderFocusColor(s.app.Styles.Frame().Border.FocusColor.Color())
|
||||
s.SetGraphicsColor(s.app.Styles.Xray().GraphicColor.Color())
|
||||
s.SetTitle(cases.Title(language.Und, cases.NoLower).String(s.gvr.R()))
|
||||
|
||||
s.model.SetNamespace(client.CleanseNamespace(s.app.Config.ActiveNamespace()))
|
||||
s.model.AddListener(s)
|
||||
|
||||
s.SetChangedFunc(func(n *tview.TreeNode) {
|
||||
spec, ok := n.GetReference().(xray.NodeSpec)
|
||||
if !ok {
|
||||
slog.Error("No ref field found on node", slogs.FQN, n.GetText())
|
||||
return
|
||||
}
|
||||
s.SetSelectedItem(spec.AsPath())
|
||||
s.refreshActions()
|
||||
})
|
||||
s.refreshActions()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InCmdMode checks if prompt is active.
|
||||
func (*Sanitizer) InCmdMode() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ExtraHints returns additional hints.
|
||||
func (s *Sanitizer) ExtraHints() map[string]string {
|
||||
if s.app.Config.K9s.UI.NoIcons {
|
||||
return nil
|
||||
}
|
||||
return xray.EmojiInfo()
|
||||
}
|
||||
|
||||
// SetInstance sets specific resource instance.
|
||||
func (s *Sanitizer) SetInstance(string) {}
|
||||
|
||||
func (s *Sanitizer) bindKeys() {
|
||||
s.Actions().Bulk(ui.KeyMap{
|
||||
ui.KeySlash: ui.NewSharedKeyAction("Filter Mode", s.activateCmd, false),
|
||||
tcell.KeyEscape: ui.NewSharedKeyAction("Filter Reset", s.resetCmd, false),
|
||||
tcell.KeyEnter: ui.NewKeyAction("Goto", s.gotoCmd, true),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Sanitizer) keyEntered() {
|
||||
s.ClearSelection()
|
||||
s.update(s.filter(s.model.Peek()))
|
||||
}
|
||||
|
||||
func (s *Sanitizer) refreshActions() {}
|
||||
|
||||
// GetSelectedPath returns the current selection as string.
|
||||
func (s *Sanitizer) GetSelectedPath() string {
|
||||
spec := s.selectedSpec()
|
||||
if spec == nil {
|
||||
return ""
|
||||
}
|
||||
return spec.Path()
|
||||
}
|
||||
|
||||
func (s *Sanitizer) selectedSpec() *xray.NodeSpec {
|
||||
node := s.GetCurrentNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ref, ok := node.GetReference().(xray.NodeSpec)
|
||||
if !ok {
|
||||
slog.Error("Expecting a NodeSpec", slogs.RefType, fmt.Sprintf("%T", node.GetReference()))
|
||||
return nil
|
||||
}
|
||||
|
||||
return &ref
|
||||
}
|
||||
|
||||
// EnvFn returns an plugin env function if available.
|
||||
func (s *Sanitizer) EnvFn() EnvFunc {
|
||||
return s.envFn
|
||||
}
|
||||
|
||||
func (s *Sanitizer) k9sEnv() Env {
|
||||
env := k8sEnv(s.app.Conn().Config())
|
||||
|
||||
spec := s.selectedSpec()
|
||||
if spec == nil {
|
||||
return env
|
||||
}
|
||||
|
||||
env["FILTER"] = s.CmdBuff().GetText()
|
||||
if env["FILTER"] == "" {
|
||||
ns, n := client.Namespaced(spec.Path())
|
||||
env["NAMESPACE"], env["FILTER"] = ns, n
|
||||
}
|
||||
|
||||
switch spec.GVR() {
|
||||
case "containers":
|
||||
_, co := client.Namespaced(spec.Path())
|
||||
env["CONTAINER"] = co
|
||||
ns, n := client.Namespaced(*spec.ParentPath())
|
||||
env["NAMESPACE"], env["POD"], env["NAME"] = ns, n, co
|
||||
default:
|
||||
ns, n := client.Namespaced(spec.Path())
|
||||
env["NAMESPACE"], env["NAME"] = ns, n
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
// Aliases returns all available aliases.
|
||||
func (s *Sanitizer) Aliases() []string {
|
||||
return append(s.meta.ShortNames, s.meta.SingularName, s.meta.Name)
|
||||
}
|
||||
|
||||
func (s *Sanitizer) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if s.app.InCmdMode() {
|
||||
return evt
|
||||
}
|
||||
s.app.ResetPrompt(s.CmdBuff())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Sanitizer) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !s.CmdBuff().InCmdMode() {
|
||||
s.CmdBuff().Reset()
|
||||
return s.app.PrevCmd(evt)
|
||||
}
|
||||
s.CmdBuff().Reset()
|
||||
s.model.ClearFilter()
|
||||
s.Start()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Sanitizer) gotoCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if s.CmdBuff().IsActive() {
|
||||
if internal.IsLabelSelector(s.CmdBuff().GetText()) {
|
||||
s.Start()
|
||||
}
|
||||
s.CmdBuff().SetActive(false)
|
||||
s.GetRoot().ExpandAll()
|
||||
return nil
|
||||
}
|
||||
|
||||
spec := s.selectedSpec()
|
||||
if spec == nil {
|
||||
return nil
|
||||
}
|
||||
if len(spec.GVRs) <= 2 {
|
||||
return nil
|
||||
}
|
||||
path := strings.Replace(spec.Path(), "::", "/", 1)
|
||||
if strings.Contains(path, "[") {
|
||||
return nil
|
||||
}
|
||||
if len(strings.Split(path, "/")) == 1 && spec.GVR() != "node" {
|
||||
path = "-/" + path
|
||||
}
|
||||
s.app.gotoResource(client.NewGVR(spec.GVR()).R(), path, false, true)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Sanitizer) filter(root *xray.TreeNode) *xray.TreeNode {
|
||||
q := s.CmdBuff().GetText()
|
||||
if s.CmdBuff().Empty() || internal.IsLabelSelector(q) {
|
||||
return root
|
||||
}
|
||||
|
||||
s.UpdateTitle()
|
||||
if f, ok := internal.IsFuzzySelector(q); ok {
|
||||
return root.Filter(f, fuzzyFilter)
|
||||
}
|
||||
|
||||
if internal.IsInverseSelector(q) {
|
||||
return root.Filter(q, rxInverseFilter)
|
||||
}
|
||||
|
||||
return root.Filter(q, rxFilter)
|
||||
}
|
||||
|
||||
// TreeNodeSelected callback for node selection.
|
||||
func (s *Sanitizer) TreeNodeSelected() {
|
||||
s.app.QueueUpdateDraw(func() {
|
||||
n := s.GetCurrentNode()
|
||||
if n != nil {
|
||||
n.SetColor(s.app.Styles.Xray().CursorColor.Color())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TreeLoadFailed notifies the load failed.
|
||||
func (s *Sanitizer) TreeLoadFailed(err error) {
|
||||
s.app.Flash().Err(err)
|
||||
}
|
||||
|
||||
func (s *Sanitizer) update(node *xray.TreeNode) {
|
||||
root := makeTreeNode(node, s.ExpandNodes(), s.app.Config.K9s.UI.NoIcons, s.app.Styles)
|
||||
if node == nil {
|
||||
s.app.QueueUpdateDraw(func() {
|
||||
s.SetRoot(root)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
for _, c := range node.Children {
|
||||
s.hydrate(root, c)
|
||||
}
|
||||
if s.GetSelectedItem() == "" {
|
||||
s.SetSelectedItem(node.Spec().Path())
|
||||
}
|
||||
|
||||
s.app.QueueUpdateDraw(func() {
|
||||
s.SetRoot(root)
|
||||
root.Walk(func(node, parent *tview.TreeNode) bool {
|
||||
spec, ok := node.GetReference().(xray.NodeSpec)
|
||||
if !ok {
|
||||
slog.Error("Expecting a NodeSpec", slogs.RefType, fmt.Sprintf("%T", node.GetReference()))
|
||||
return false
|
||||
}
|
||||
// BOZO!! Figure this out expand/collapse but the root
|
||||
if parent != nil {
|
||||
node.SetExpanded(s.ExpandNodes())
|
||||
} else {
|
||||
node.SetExpanded(true)
|
||||
}
|
||||
|
||||
if spec.AsPath() == s.GetSelectedItem() {
|
||||
node.SetExpanded(true).SetSelectable(true)
|
||||
s.SetCurrentNode(node)
|
||||
}
|
||||
return true
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// TreeChanged notifies the model data changed.
|
||||
func (s *Sanitizer) TreeChanged(node *xray.TreeNode) {
|
||||
s.Count = node.Count(s.gvr.String())
|
||||
s.update(s.filter(node))
|
||||
s.UpdateTitle()
|
||||
}
|
||||
|
||||
func (s *Sanitizer) hydrate(parent *tview.TreeNode, n *xray.TreeNode) {
|
||||
node := makeTreeNode(n, s.ExpandNodes(), s.app.Config.K9s.UI.NoIcons, s.app.Styles)
|
||||
for _, c := range n.Children {
|
||||
s.hydrate(node, c)
|
||||
}
|
||||
parent.AddChild(node)
|
||||
}
|
||||
|
||||
// SetEnvFn sets the custom environment function.
|
||||
func (s *Sanitizer) SetEnvFn(EnvFunc) {}
|
||||
|
||||
// Refresh updates the view.
|
||||
func (s *Sanitizer) Refresh() {}
|
||||
|
||||
// BufferChanged indicates the buffer was changed.
|
||||
func (s *Sanitizer) BufferChanged(_, _ string) {}
|
||||
|
||||
// BufferCompleted indicates input was accepted.
|
||||
func (s *Sanitizer) BufferCompleted(_, _ string) {
|
||||
s.update(s.filter(s.model.Peek()))
|
||||
}
|
||||
|
||||
// BufferActive indicates the buff activity changed.
|
||||
func (s *Sanitizer) BufferActive(state bool, k model.BufferKind) {
|
||||
s.app.BufferActive(state, k)
|
||||
}
|
||||
|
||||
func (s *Sanitizer) defaultContext() context.Context {
|
||||
ctx := context.WithValue(context.Background(), internal.KeyFactory, s.app.factory)
|
||||
ctx = context.WithValue(ctx, internal.KeyFields, "")
|
||||
if s.CmdBuff().Empty() {
|
||||
ctx = context.WithValue(ctx, internal.KeyLabels, "")
|
||||
} else {
|
||||
ctx = context.WithValue(ctx, internal.KeyLabels, ui.TrimLabelSelector(s.CmdBuff().GetText()))
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Start initializes resource watch loop.
|
||||
func (s *Sanitizer) Start() {
|
||||
s.Stop()
|
||||
s.CmdBuff().AddListener(s)
|
||||
|
||||
ctx := s.defaultContext()
|
||||
ctx, s.cancelFn = context.WithCancel(ctx)
|
||||
if s.contextFn != nil {
|
||||
ctx = s.contextFn(ctx)
|
||||
}
|
||||
s.model.Refresh(ctx)
|
||||
s.UpdateTitle()
|
||||
}
|
||||
|
||||
// Stop terminates watch loop.
|
||||
func (s *Sanitizer) Stop() {
|
||||
if s.cancelFn == nil {
|
||||
return
|
||||
}
|
||||
s.cancelFn()
|
||||
s.cancelFn = nil
|
||||
s.CmdBuff().RemoveListener(s)
|
||||
}
|
||||
|
||||
// AddBindKeysFn sets up extra key bindings.
|
||||
func (s *Sanitizer) AddBindKeysFn(BindKeysFunc) {}
|
||||
|
||||
// SetContextFn sets custom context.
|
||||
func (s *Sanitizer) SetContextFn(f ContextFunc) {
|
||||
s.contextFn = f
|
||||
}
|
||||
|
||||
// Name returns the component name.
|
||||
func (s *Sanitizer) Name() string { return "report" }
|
||||
|
||||
// GetTable returns the underlying table.
|
||||
func (s *Sanitizer) GetTable() *Table { return nil }
|
||||
|
||||
// GVR returns a resource descriptor.
|
||||
func (s *Sanitizer) GVR() client.GVR { return s.gvr }
|
||||
|
||||
// App returns the current app handle.
|
||||
func (s *Sanitizer) App() *App {
|
||||
return s.app
|
||||
}
|
||||
|
||||
// UpdateTitle updates the view title.
|
||||
func (s *Sanitizer) UpdateTitle() {
|
||||
t := s.styleTitle()
|
||||
s.app.QueueUpdateDraw(func() {
|
||||
s.SetTitle(t)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Sanitizer) styleTitle() string {
|
||||
base := cases.Title(language.Und, cases.NoLower).String(s.gvr.R())
|
||||
ns := s.model.GetNamespace()
|
||||
if client.IsAllNamespaces(ns) {
|
||||
ns = client.NamespaceAll
|
||||
}
|
||||
|
||||
var title string
|
||||
if ns == client.ClusterScope {
|
||||
title = ui.SkinTitle(fmt.Sprintf(ui.TitleFmt, ui.ROIndicator(s.app.Config.IsReadOnly()), base, render.AsThousands(int64(s.Count))), s.app.Styles.Frame())
|
||||
} else {
|
||||
title = ui.SkinTitle(fmt.Sprintf(ui.NSTitleFmt, ui.ROIndicator(s.app.Config.IsReadOnly()), base, ns, render.AsThousands(int64(s.Count))), s.app.Styles.Frame())
|
||||
}
|
||||
|
||||
buff := s.CmdBuff().GetText()
|
||||
if buff == "" {
|
||||
return title
|
||||
}
|
||||
if internal.IsLabelSelector(buff) {
|
||||
buff = ui.TrimLabelSelector(buff)
|
||||
}
|
||||
|
||||
return title + ui.SkinTitle(fmt.Sprintf(ui.SearchFmt, buff), s.app.Styles.Frame())
|
||||
}
|
||||
|
|
@ -5,12 +5,12 @@ package view
|
|||
|
||||
import (
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/config/data"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// Secret presents a secret viewer.
|
||||
|
|
@ -57,7 +57,7 @@ func (s *Secret) decodeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
return nil
|
||||
}
|
||||
|
||||
raw, err := yaml.Marshal(d)
|
||||
raw, err := data.WriteYAML(d)
|
||||
if err != nil {
|
||||
s.App().Flash().Errf("Error decoding secret %s", err)
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
"github.com/derailed/tcell/v2"
|
||||
)
|
||||
|
||||
|
|
@ -28,6 +28,7 @@ type Table struct {
|
|||
enterFn EnterFunc
|
||||
envFn EnvFunc
|
||||
bindKeysFn []BindKeysFunc
|
||||
command *cmd.Interpreter
|
||||
}
|
||||
|
||||
// NewTable returns a new viewer.
|
||||
|
|
@ -48,11 +49,8 @@ func (t *Table) Init(ctx context.Context) (err error) {
|
|||
if t.app.Conn() != nil {
|
||||
ctx = context.WithValue(ctx, internal.KeyHasMetrics, t.app.Conn().HasMetrics())
|
||||
}
|
||||
if t.app.CustomView == nil {
|
||||
t.app.CustomView = config.NewCustomView()
|
||||
}
|
||||
ctx = context.WithValue(ctx, internal.KeyStyles, t.app.Styles)
|
||||
ctx = context.WithValue(ctx, internal.KeyViewConfig, t.app.CustomView)
|
||||
ctx = context.WithValue(ctx, internal.KeyViewConfig, t.app.CustomView())
|
||||
t.Table.Init(ctx)
|
||||
if !t.app.Config.K9s.UI.Reactive {
|
||||
if err := t.app.RefreshCustomViews(); err != nil {
|
||||
|
|
@ -68,6 +66,11 @@ func (t *Table) Init(ctx context.Context) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SetCommand sets the current command.
|
||||
func (t *Table) SetCommand(cmd *cmd.Interpreter) {
|
||||
t.command = cmd
|
||||
}
|
||||
|
||||
// HeaderIndex returns index of a given column or false if not found.
|
||||
func (t *Table) HeaderIndex(colName string) (int, bool) {
|
||||
for i := 0; i < t.GetColumnCount(); i++ {
|
||||
|
|
@ -145,14 +148,18 @@ func (t *Table) Start() {
|
|||
t.Stop()
|
||||
t.CmdBuff().AddListener(t)
|
||||
t.Styles().AddListener(t.Table)
|
||||
t.App().CustomView.AddListener(t.Table.GVR().String(), t.Table)
|
||||
cmds := []string{t.Table.GVR().String()}
|
||||
if t.command != nil {
|
||||
cmds = append(cmds, t.command.GetLine())
|
||||
}
|
||||
t.App().CustomView().AddListeners(t.Table, cmds...)
|
||||
}
|
||||
|
||||
// Stop terminates the component.
|
||||
func (t *Table) Stop() {
|
||||
t.CmdBuff().RemoveListener(t)
|
||||
t.Styles().RemoveListener(t.Table)
|
||||
t.App().CustomView.RemoveListener(t.GVR().String())
|
||||
t.App().CustomView().RemoveListener(t.Table)
|
||||
}
|
||||
|
||||
// SetEnterFn specifies the default enter behavior.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -95,6 +96,9 @@ type ResourceViewer interface {
|
|||
|
||||
// SetInstance sets a parent FQN
|
||||
SetInstance(string)
|
||||
|
||||
// SetCommand sets the current command.
|
||||
SetCommand(*cmd.Interpreter)
|
||||
}
|
||||
|
||||
// LogViewer represents a log viewer.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/slogs"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/ui/dialog"
|
||||
"github.com/derailed/k9s/internal/view/cmd"
|
||||
"github.com/derailed/k9s/internal/xray"
|
||||
"github.com/derailed/tcell/v2"
|
||||
"github.com/derailed/tview"
|
||||
|
|
@ -54,6 +55,7 @@ func NewXray(gvr client.GVR) ResourceViewer {
|
|||
}
|
||||
}
|
||||
|
||||
func (x *Xray) SetCommand(*cmd.Interpreter) {}
|
||||
func (x *Xray) SetFilter(string) {}
|
||||
func (x *Xray) SetLabelFilter(map[string]string) {}
|
||||
|
||||
|
|
@ -671,9 +673,12 @@ func (x *Xray) styleTitle() string {
|
|||
|
||||
var title string
|
||||
if ns == client.ClusterScope {
|
||||
title = ui.SkinTitle(fmt.Sprintf(ui.TitleFmt, ui.ROIndicator(x.app.Config.IsReadOnly()), base, render.AsThousands(int64(x.Count))), x.app.Styles.Frame())
|
||||
title = ui.SkinTitle(fmt.Sprintf(ui.TitleFmt, base, render.AsThousands(int64(x.Count))), x.app.Styles.Frame())
|
||||
} else {
|
||||
title = ui.SkinTitle(fmt.Sprintf(ui.NSTitleFmt, ui.ROIndicator(x.app.Config.IsReadOnly()), base, ns, render.AsThousands(int64(x.Count))), x.app.Styles.Frame())
|
||||
title = ui.SkinTitle(fmt.Sprintf(ui.NSTitleFmt, base, ns, render.AsThousands(int64(x.Count))), x.app.Styles.Frame())
|
||||
}
|
||||
if ic := ui.ROIndicator(x.app.Config.IsReadOnly(), x.app.Config.K9s.UI.NoIcons); ic != "" {
|
||||
title = " " + ic + title
|
||||
}
|
||||
|
||||
buff := x.CmdBuff().GetText()
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/popeye/pkg/config"
|
||||
)
|
||||
|
||||
// Section represents an xray renderer.
|
||||
|
|
@ -59,15 +58,15 @@ func (*Section) outcomeRefs(parent *TreeNode, section render.Section) {
|
|||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func colorize(s string, l config.Level) string {
|
||||
func colorize(s string, l render.Level) string {
|
||||
c := "green"
|
||||
// nolint:exhaustive
|
||||
switch l {
|
||||
case config.ErrorLevel:
|
||||
case render.ErrorLevel:
|
||||
c = "red"
|
||||
case config.WarnLevel:
|
||||
case render.WarnLevel:
|
||||
c = "orange"
|
||||
case config.InfoLevel:
|
||||
case render.InfoLevel:
|
||||
c = "blue"
|
||||
}
|
||||
return fmt.Sprintf("[%s::]%s", c, s)
|
||||
|
|
|
|||
Loading…
Reference in New Issue