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/slogs"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func infoCmd() *cobra.Command {
|
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/derailed/k9s/internal/view"
|
||||||
|
|
||||||
"github.com/lmittmann/tint"
|
"github.com/lmittmann/tint"
|
||||||
// "github.com/MatusOllah/slogcolor"
|
|
||||||
"github.com/mattn/go-colorable"
|
"github.com/mattn/go-colorable"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
|
|
@ -134,6 +133,12 @@ func loadConfiguration() (*config.Config, error) {
|
||||||
k9sCfg := config.NewConfig(k8sCfg)
|
k9sCfg := config.NewConfig(k8sCfg)
|
||||||
var errs error
|
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 {
|
if err := k9sCfg.Load(config.AppConfigFile, false); err != nil {
|
||||||
errs = errors.Join(errs, err)
|
errs = errors.Join(errs, err)
|
||||||
}
|
}
|
||||||
|
|
@ -143,10 +148,6 @@ func loadConfiguration() (*config.Config, error) {
|
||||||
errs = errors.Join(errs, err)
|
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?
|
// Try to access server version if that fail. Connectivity issue?
|
||||||
if !conn.CheckConnectivity() {
|
if !conn.CheckConnectivity() {
|
||||||
errs = errors.Join(errs, fmt.Errorf("cannot connect to context: %s", k9sCfg.K9s.ActiveContextName()))
|
errs = errors.Join(errs, fmt.Errorf("cannot connect to context: %s", k9sCfg.K9s.ActiveContextName()))
|
||||||
|
|
@ -157,7 +158,7 @@ func loadConfiguration() (*config.Config, error) {
|
||||||
} else {
|
} else {
|
||||||
slog.Info("✅ Kubernetes connectivity OK")
|
slog.Info("✅ Kubernetes connectivity OK")
|
||||||
}
|
}
|
||||||
k9sCfg.SetConnection(conn)
|
|
||||||
if err := k9sCfg.Save(false); err != nil {
|
if err := k9sCfg.Save(false); err != nil {
|
||||||
slog.Error("K9s config save failed", slogs.Error, err)
|
slog.Error("K9s config save failed", slogs.Error, err)
|
||||||
errs = errors.Join(errs, 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/anchore/syft v1.20.0
|
||||||
github.com/atotto/clipboard v0.1.4
|
github.com/atotto/clipboard v0.1.4
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0
|
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/tcell/v2 v2.3.1-rc.3
|
||||||
github.com/derailed/tview v0.8.5
|
github.com/derailed/tview v0.8.5
|
||||||
github.com/fatih/color v1.18.0
|
github.com/fatih/color v1.18.0
|
||||||
|
|
@ -27,7 +26,6 @@ require (
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0
|
github.com/xeipuuv/gojsonschema v1.2.0
|
||||||
golang.org/x/text v0.23.0
|
golang.org/x/text v0.23.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
helm.sh/helm/v3 v3.17.1
|
helm.sh/helm/v3 v3.17.1
|
||||||
k8s.io/api v0.32.2
|
k8s.io/api v0.32.2
|
||||||
|
|
@ -266,7 +264,6 @@ require (
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // 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/rubenv/sql-migrate v1.7.1 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/saferwall/pe v1.5.6 // 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/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-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.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.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
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=
|
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/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 h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
|
||||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
|
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 h1:9s1fmyRcSPRlwr/C9tcpJKCujbrtmPpST6dcMUD2piY=
|
||||||
github.com/derailed/tcell/v2 v2.3.1-rc.3/go.mod h1:nf68BEL8fjmXQHJT3xZjoZFs2uXOzyJcNAQqGUEMrFY=
|
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=
|
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.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.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.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 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
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=
|
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.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.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.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 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
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=
|
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.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 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
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 h1:f/o0WgfO/GqNuVg+6801K/KW3WdDSupzSjDYODmiUq4=
|
||||||
github.com/rubenv/sql-migrate v1.7.1/go.mod h1:Ob2Psprc0/3ggbM6wCzyYVFFuc6FyZrb2AS+ezLDFb4=
|
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=
|
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-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-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-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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.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.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 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
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=
|
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) {
|
func (c *Config) RESTConfig() (*restclient.Config, error) {
|
||||||
cfg, err := c.clientConfig().ClientConfig()
|
cfg, err := c.clientConfig().ClientConfig()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.proxy != nil {
|
if c.proxy != nil {
|
||||||
cfg.Proxy = c.proxy
|
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)
|
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)) {
|
func (c *Config) SetProxy(proxy func(*http.Request) (*url.URL, error)) {
|
||||||
c.proxy = proxy
|
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.
|
// Contexts fetch all available contexts.
|
||||||
func (c *Config) Contexts() (map[string]*api.Context, error) {
|
func (c *Config) Contexts() (map[string]*api.Context, error) {
|
||||||
cfg, err := c.RawConfig()
|
cfg, err := c.RawConfig()
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/config/data"
|
"github.com/derailed/k9s/internal/config/data"
|
||||||
"github.com/derailed/k9s/internal/config/json"
|
"github.com/derailed/k9s/internal/config/json"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Alias tracks shortname to GVR mappings.
|
// Alias tracks shortname to GVR mappings.
|
||||||
|
|
@ -176,8 +176,6 @@ func (a *Aliases) loadDefaultAliases() {
|
||||||
a.declare("help", "h", "?")
|
a.declare("help", "h", "?")
|
||||||
a.declare("quit", "q", "q!", "qa", "Q")
|
a.declare("quit", "q", "q!", "qa", "Q")
|
||||||
a.declare("aliases", "alias", "a")
|
a.declare("aliases", "alias", "a")
|
||||||
// !!BOZO!!
|
|
||||||
// a.declare("popeye", "pop")
|
|
||||||
a.declare("helm", "charts", "chart", "hm")
|
a.declare("helm", "charts", "chart", "hm")
|
||||||
a.declare("dir", "d")
|
a.declare("dir", "d")
|
||||||
a.declare("contexts", "context", "ctx")
|
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 {
|
if err := data.EnsureDirPath(path, data.DefaultDirMod); err != nil {
|
||||||
return err
|
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"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// K9sBench the name of the benchmarks config file.
|
// 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/config/json"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"github.com/derailed/k9s/internal/view/cmd"
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -289,14 +289,13 @@ func (c *Config) SaveFile(path string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := yaml.Marshal(c)
|
if err := data.SaveYAML(path, c); err != nil {
|
||||||
if err != nil {
|
|
||||||
slog.Error("Unable to save K9s config file", slogs.Error, err)
|
slog.Error("Unable to save K9s config file", slogs.Error, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Info("[CONFIG] Saving K9s config to disk", slogs.Path, path)
|
slog.Info("[CONFIG] Saving K9s config to disk", slogs.Path, path)
|
||||||
return os.WriteFile(path, cfg, data.DefaultFileMod)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the configuration.
|
// Validate the configuration.
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
"k8s.io/client-go/tools/clientcmd/api"
|
"k8s.io/client-go/tools/clientcmd/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import (
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/config/json"
|
"github.com/derailed/k9s/internal/config/json"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
"k8s.io/client-go/tools/clientcmd/api"
|
"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 {
|
if err := EnsureDirPath(path, DefaultDirMod); err != nil {
|
||||||
return err
|
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) {
|
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/data"
|
||||||
"github.com/derailed/k9s/internal/config/mock"
|
"github.com/derailed/k9s/internal/config/mock"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,14 @@
|
||||||
package data
|
package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const envFGNodeShell = "K9S_FEATURE_GATE_NODE_SHELL"
|
const envFGNodeShell = "K9S_FEATURE_GATE_NODE_SHELL"
|
||||||
|
|
@ -48,3 +51,28 @@ func EnsureFullPath(path string, mod os.FileMode) error {
|
||||||
|
|
||||||
return nil
|
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.
|
// Validate validates a namespace is setup correctly.
|
||||||
func (n *Namespace) Validate(c client.Connection) {
|
func (n *Namespace) Validate(conn client.Connection) {
|
||||||
n.mx.RLock()
|
n.mx.RLock()
|
||||||
defer n.mx.RUnlock()
|
defer n.mx.RUnlock()
|
||||||
|
|
||||||
if c == nil || !c.IsValidNamespace(n.Active) {
|
if conn == nil || !conn.IsValidNamespace(n.Active) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, ns := range n.Favorites {
|
for _, ns := range n.Favorites {
|
||||||
if !c.IsValidNamespace(ns) {
|
if !conn.IsValidNamespace(ns) {
|
||||||
slog.Debug("Invalid favorite found",
|
slog.Debug("Invalid favorite found",
|
||||||
slogs.Namespace, ns,
|
slogs.Namespace, ns,
|
||||||
slogs.AllNS, n.isAllNamespaces(),
|
slogs.AllNS, n.isAllNamespaces(),
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/config/data"
|
"github.com/derailed/k9s/internal/config/data"
|
||||||
"github.com/derailed/k9s/internal/config/json"
|
"github.com/derailed/k9s/internal/config/json"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HotKeys represents a collection of plugins.
|
// HotKeys represents a collection of plugins.
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,6 @@ type K9s struct {
|
||||||
Logger Logger `json:"logger" yaml:"logger"`
|
Logger Logger `json:"logger" yaml:"logger"`
|
||||||
Thresholds Threshold `json:"thresholds" yaml:"thresholds"`
|
Thresholds Threshold `json:"thresholds" yaml:"thresholds"`
|
||||||
manualRefreshRate int
|
manualRefreshRate int
|
||||||
manualHeadless *bool
|
|
||||||
manualLogoless *bool
|
|
||||||
manualCrumbsless *bool
|
|
||||||
manualReadOnly *bool
|
manualReadOnly *bool
|
||||||
manualCommand *string
|
manualCommand *string
|
||||||
manualScreenDumpDir *string
|
manualScreenDumpDir *string
|
||||||
|
|
@ -293,9 +290,9 @@ func (k *K9s) Override(k9sFlags *Flags) {
|
||||||
k.manualRefreshRate = *k9sFlags.RefreshRate
|
k.manualRefreshRate = *k9sFlags.RefreshRate
|
||||||
}
|
}
|
||||||
|
|
||||||
k.manualHeadless = k9sFlags.Headless
|
k.UI.manualHeadless = k9sFlags.Headless
|
||||||
k.manualLogoless = k9sFlags.Logoless
|
k.UI.manualLogoless = k9sFlags.Logoless
|
||||||
k.manualCrumbsless = k9sFlags.Crumbsless
|
k.UI.manualCrumbsless = k9sFlags.Crumbsless
|
||||||
if k9sFlags.ReadOnly != nil && *k9sFlags.ReadOnly {
|
if k9sFlags.ReadOnly != nil && *k9sFlags.ReadOnly {
|
||||||
k.manualReadOnly = k9sFlags.ReadOnly
|
k.manualReadOnly = k9sFlags.ReadOnly
|
||||||
}
|
}
|
||||||
|
|
@ -309,7 +306,7 @@ func (k *K9s) Override(k9sFlags *Flags) {
|
||||||
|
|
||||||
// IsHeadless returns headless setting.
|
// IsHeadless returns headless setting.
|
||||||
func (k *K9s) IsHeadless() bool {
|
func (k *K9s) IsHeadless() bool {
|
||||||
if IsBoolSet(k.manualHeadless) {
|
if IsBoolSet(k.UI.manualHeadless) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -318,7 +315,7 @@ func (k *K9s) IsHeadless() bool {
|
||||||
|
|
||||||
// IsLogoless returns logoless setting.
|
// IsLogoless returns logoless setting.
|
||||||
func (k *K9s) IsLogoless() bool {
|
func (k *K9s) IsLogoless() bool {
|
||||||
if IsBoolSet(k.manualLogoless) {
|
if IsBoolSet(k.UI.manualLogoless) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -327,7 +324,7 @@ func (k *K9s) IsLogoless() bool {
|
||||||
|
|
||||||
// IsCrumbsless returns crumbsless setting.
|
// IsCrumbsless returns crumbsless setting.
|
||||||
func (k *K9s) IsCrumbsless() bool {
|
func (k *K9s) IsCrumbsless() bool {
|
||||||
if IsBoolSet(k.manualCrumbsless) {
|
if IsBoolSet(k.UI.manualCrumbsless) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,17 +66,17 @@ func Test_k9sOverrides(t *testing.T) {
|
||||||
ReadOnly: false,
|
ReadOnly: false,
|
||||||
NoExitOnCtrlC: false,
|
NoExitOnCtrlC: false,
|
||||||
UI: UI{
|
UI: UI{
|
||||||
Headless: false,
|
Headless: false,
|
||||||
Logoless: false,
|
Logoless: false,
|
||||||
Crumbsless: false,
|
Crumbsless: false,
|
||||||
|
manualHeadless: &true,
|
||||||
|
manualLogoless: &true,
|
||||||
|
manualCrumbsless: &true,
|
||||||
},
|
},
|
||||||
SkipLatestRevCheck: false,
|
SkipLatestRevCheck: false,
|
||||||
DisablePodCounting: false,
|
DisablePodCounting: false,
|
||||||
manualRefreshRate: 100,
|
manualRefreshRate: 100,
|
||||||
manualReadOnly: &true,
|
manualReadOnly: &true,
|
||||||
manualHeadless: &true,
|
|
||||||
manualLogoless: &true,
|
|
||||||
manualCrumbsless: &true,
|
|
||||||
manualCommand: &cmd,
|
manualCommand: &cmd,
|
||||||
manualScreenDumpDir: &dir,
|
manualScreenDumpDir: &dir,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
|
@ -16,7 +17,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/config/data"
|
"github.com/derailed/k9s/internal/config/data"
|
||||||
"github.com/derailed/k9s/internal/config/json"
|
"github.com/derailed/k9s/internal/config/json"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const k9sPluginsDir = "k9s/plugins"
|
const k9sPluginsDir = "k9s/plugins"
|
||||||
|
|
@ -88,10 +89,14 @@ func (p Plugins) loadPluginDir(dir string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = errors.Join(errs, err)
|
errs = errors.Join(errs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d := yaml.NewDecoder(bytes.NewReader(fileContent))
|
||||||
|
d.KnownFields(true)
|
||||||
|
|
||||||
var plugin Plugin
|
var plugin Plugin
|
||||||
if err = yaml.UnmarshalStrict(fileContent, &plugin); err != nil {
|
if err = d.Decode(&plugin); err != nil {
|
||||||
var plugins Plugins
|
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)
|
return fmt.Errorf("cannot parse %s into either a single plugin nor plugins: %w", fileName, err)
|
||||||
}
|
}
|
||||||
for name, plugin := range plugins.Plugins {
|
for name, plugin := range plugins.Plugins {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/config/json"
|
"github.com/derailed/k9s/internal/config/json"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StyleListener represents a skin's listener.
|
// StyleListener represents a skin's listener.
|
||||||
|
|
|
||||||
|
|
@ -17,3 +17,9 @@ views:
|
||||||
- AGE
|
- AGE
|
||||||
- NAME
|
- NAME
|
||||||
- IP
|
- IP
|
||||||
|
|
||||||
|
bozo:
|
||||||
|
columns:
|
||||||
|
- DUH
|
||||||
|
- BLAH
|
||||||
|
- BLEE
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,8 @@ type UI struct {
|
||||||
|
|
||||||
// DefaultsToFullScreen toggles fullscreen on views like logs, yaml, details.
|
// DefaultsToFullScreen toggles fullscreen on views like logs, yaml, details.
|
||||||
DefaultsToFullScreen bool `json:"defaultsToFullScreen" yaml:"defaultsToFullScreen"`
|
DefaultsToFullScreen bool `json:"defaultsToFullScreen" yaml:"defaultsToFullScreen"`
|
||||||
|
|
||||||
|
manualHeadless *bool
|
||||||
|
manualLogoless *bool
|
||||||
|
manualCrumbsless *bool
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,11 @@ import (
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/config/data"
|
"github.com/derailed/k9s/internal/config/data"
|
||||||
"github.com/derailed/k9s/internal/config/json"
|
"github.com/derailed/k9s/internal/config/json"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ViewConfigListener represents a view config listener.
|
// ViewConfigListener represents a view config listener.
|
||||||
|
|
@ -118,29 +119,65 @@ func (v *CustomView) Load(path string) error {
|
||||||
return nil
|
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.
|
// AddListener registers a new listener.
|
||||||
func (v *CustomView) AddListener(gvr string, l ViewConfigListener) {
|
func (v *CustomView) AddListener(cmd string, l ViewConfigListener) {
|
||||||
v.listeners[gvr] = l
|
v.listeners[cmd] = l
|
||||||
v.fireConfigChanged()
|
v.fireConfigChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveListener unregister a listener.
|
// RemoveListener unregister a listener.
|
||||||
func (v *CustomView) RemoveListener(gvr string) {
|
func (v *CustomView) RemoveListener(l ViewConfigListener) {
|
||||||
delete(v.listeners, gvr)
|
for k, list := range v.listeners {
|
||||||
}
|
if list == l {
|
||||||
|
delete(v.listeners, k)
|
||||||
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) 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 {
|
func (v *CustomView) getVS(gvr, ns string) *ViewSetting {
|
||||||
|
if client.IsAllNamespaces(ns) {
|
||||||
|
ns = client.NamespaceAll
|
||||||
|
}
|
||||||
k := gvr
|
k := gvr
|
||||||
kk := slices.Collect(maps.Keys(v.Views))
|
kk := slices.Collect(maps.Keys(v.Views))
|
||||||
slices.SortFunc(kk, func(s1, s2 string) int {
|
slices.SortFunc(kk, func(s1, s2 string) int {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,10 @@ func TestCustomView_getVS(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
"empty": {},
|
"empty": {},
|
||||||
|
|
||||||
|
"miss": {
|
||||||
|
gvr: "zorg",
|
||||||
|
},
|
||||||
|
|
||||||
"gvr": {
|
"gvr": {
|
||||||
gvr: "v1/pods",
|
gvr: "v1/pods",
|
||||||
e: &ViewSetting{
|
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": {
|
"toast-no-ns": {
|
||||||
gvr: "v1/pods",
|
gvr: "v1/pods",
|
||||||
ns: "zorg",
|
ns: "zorg",
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"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/render/helm"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
"helm.sh/helm/v3/pkg/action"
|
"helm.sh/helm/v3/pkg/action"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
@ -83,7 +83,7 @@ func (h *HelmChart) GetValues(path string, allValues bool) ([]byte, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return yaml.Marshal(resp)
|
return data.WriteYAML(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe returns the chart notes.
|
// Describe returns the chart notes.
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
"helm.sh/helm/v3/pkg/action"
|
"helm.sh/helm/v3/pkg/action"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
|
"github.com/derailed/k9s/internal/config/data"
|
||||||
"github.com/derailed/k9s/internal/render/helm"
|
"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)
|
return nil, fmt.Errorf("expected helm.ReleaseRes, but got %T", rel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var content any
|
||||||
if allValues {
|
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 {
|
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{},
|
Verbs: []string{},
|
||||||
Categories: []string{k9sCat},
|
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{
|
m[client.NewGVR("contexts")] = metav1.APIResource{
|
||||||
Name: "contexts",
|
Name: "contexts",
|
||||||
Kind: "Contexts",
|
Kind: "Contexts",
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/model"
|
"github.com/derailed/k9s/internal/model"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -291,14 +292,15 @@ func makeC(n string) c {
|
||||||
return c{name: n}
|
return c{name: n}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c) InCmdMode() bool { return false }
|
func (c) InCmdMode() bool { return false }
|
||||||
func (c c) Name() string { return c.name }
|
func (c c) Name() string { return c.name }
|
||||||
func (c c) Hints() model.MenuHints { return nil }
|
func (c) SetCommand(*cmd.Interpreter) {}
|
||||||
func (c c) HasFocus() bool { return false }
|
func (c) Hints() model.MenuHints { return nil }
|
||||||
func (c c) ExtraHints() map[string]string { return nil }
|
func (c) HasFocus() bool { return false }
|
||||||
func (c c) Draw(tcell.Screen) {}
|
func (c) ExtraHints() map[string]string { return nil }
|
||||||
func (c c) InputHandler() func(*tcell.EventKey, func(tview.Primitive)) { return nil }
|
func (c) Draw(tcell.Screen) {}
|
||||||
func (c c) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
func (c c) SetRect(int, int, int, int) {}
|
func (c c) SetRect(int, int, int, int) {}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/dao"
|
"github.com/derailed/k9s/internal/dao"
|
||||||
"github.com/derailed/k9s/internal/model1"
|
"github.com/derailed/k9s/internal/model1"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/sahilm/fuzzy"
|
"github.com/sahilm/fuzzy"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
@ -93,6 +94,13 @@ type Component interface {
|
||||||
Hinter
|
Hinter
|
||||||
Commander
|
Commander
|
||||||
Filterer
|
Filterer
|
||||||
|
Viewer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Viewer represents a resource viewer.
|
||||||
|
type Viewer interface {
|
||||||
|
// SetCommand sets the current command.
|
||||||
|
SetCommand(*cmd.Interpreter)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Filterer interface {
|
type Filterer interface {
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ type Renderer interface {
|
||||||
// ColorerFunc returns a row colorer function.
|
// ColorerFunc returns a row colorer function.
|
||||||
ColorerFunc() ColorerFunc
|
ColorerFunc() ColorerFunc
|
||||||
|
|
||||||
|
// SetViewSetting sets custom view settings if any.
|
||||||
SetViewSetting(vs *config.ViewSetting)
|
SetViewSetting(vs *config.ViewSetting)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ func (b *Base) doHeader(dh model1.Header) model1.Header {
|
||||||
return b.specs.Header(dh)
|
return b.specs.Header(dh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetViewSetting sets custom view settings if any.
|
||||||
func (b *Base) SetViewSetting(vs *config.ViewSetting) {
|
func (b *Base) SetViewSetting(vs *config.ViewSetting) {
|
||||||
var cols []string
|
var cols []string
|
||||||
b.vs = vs
|
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 {
|
type Configurator struct {
|
||||||
Config *config.Config
|
Config *config.Config
|
||||||
Styles *config.Styles
|
Styles *config.Styles
|
||||||
CustomView *config.CustomView
|
customView *config.CustomView
|
||||||
BenchFile string
|
BenchFile string
|
||||||
skinFile 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.
|
// HasSkin returns true if a skin file was located.
|
||||||
func (c *Configurator) HasSkin() bool {
|
func (c *Configurator) HasSkin() bool {
|
||||||
return c.skinFile != ""
|
return c.skinFile != ""
|
||||||
|
|
@ -82,13 +90,9 @@ func (c *Configurator) CustomViewsWatcher(ctx context.Context, s synchronizer) e
|
||||||
|
|
||||||
// RefreshCustomViews load view configuration changes.
|
// RefreshCustomViews load view configuration changes.
|
||||||
func (c *Configurator) RefreshCustomViews() error {
|
func (c *Configurator) RefreshCustomViews() error {
|
||||||
if c.CustomView == nil {
|
c.CustomView().Reset()
|
||||||
c.CustomView = config.NewCustomView()
|
|
||||||
} else {
|
|
||||||
c.CustomView.Reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.CustomView.Load(config.AppViewsFile)
|
return c.CustomView().Load(config.AppViewsFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SkinsDirWatcher watches for skin directory file changes.
|
// SkinsDirWatcher watches for skin directory file changes.
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/k9s/internal/model"
|
"github.com/derailed/k9s/internal/model"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -39,23 +40,24 @@ func makeComponent(n string) c {
|
||||||
return c{name: n}
|
return c{name: n}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c) InCmdMode() bool { return false }
|
func (c) SetCommand(*cmd.Interpreter) {}
|
||||||
func (c c) HasFocus() bool { return true }
|
func (c) InCmdMode() bool { return false }
|
||||||
func (c c) Hints() model.MenuHints { return nil }
|
func (c) HasFocus() bool { return true }
|
||||||
func (c c) ExtraHints() map[string]string { return nil }
|
func (c) Hints() model.MenuHints { return nil }
|
||||||
func (c c) Name() string { return c.name }
|
func (c) ExtraHints() map[string]string { return nil }
|
||||||
func (c c) Draw(tcell.Screen) {}
|
func (c c) Name() string { return c.name }
|
||||||
func (c c) InputHandler() func(*tcell.EventKey, func(tview.Primitive)) { return nil }
|
func (c) Draw(tcell.Screen) {}
|
||||||
func (c c) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
func (c c) SetRect(int, int, int, int) {}
|
func (c) SetRect(int, int, int, int) {}
|
||||||
func (c c) GetRect() (int, int, int, int) { return 0, 0, 0, 0 }
|
func (c) GetRect() (int, int, int, int) { return 0, 0, 0, 0 }
|
||||||
func (c c) GetFocusable() tview.Focusable { return c }
|
func (c c) GetFocusable() tview.Focusable { return c }
|
||||||
func (c c) Focus(func(tview.Primitive)) {}
|
func (c) Focus(func(tview.Primitive)) {}
|
||||||
func (c c) Blur() {}
|
func (c) Blur() {}
|
||||||
func (c c) Start() {}
|
func (c) Start() {}
|
||||||
func (c c) Stop() {}
|
func (c) Stop() {}
|
||||||
func (c c) Init(context.Context) error { return nil }
|
func (c) Init(context.Context) error { return nil }
|
||||||
func (c c) SetFilter(string) {}
|
func (c) SetFilter(string) {}
|
||||||
func (c c) SetLabelFilter(map[string]string) {}
|
func (c) SetLabelFilter(map[string]string) {}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ type Table struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
mx sync.RWMutex
|
mx sync.RWMutex
|
||||||
readOnly bool
|
readOnly bool
|
||||||
|
noIcon bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTable returns a new table view.
|
// 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) {
|
func (t *Table) SetReadOnly(ro bool) {
|
||||||
t.mx.Lock()
|
t.mx.Lock()
|
||||||
defer t.mx.Unlock()
|
defer t.mx.Unlock()
|
||||||
|
|
@ -114,7 +124,8 @@ func (t *Table) getMSort() bool {
|
||||||
return t.manualSort
|
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()
|
t.mx.Lock()
|
||||||
defer t.mx.Unlock()
|
defer t.mx.Unlock()
|
||||||
|
|
||||||
|
|
@ -127,7 +138,8 @@ func (t *Table) setViewSetting(vs *config.ViewSetting) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Table) getViewSetting() *config.ViewSetting {
|
// GetViewSetting return current view settings if any.
|
||||||
|
func (t *Table) GetViewSetting() *config.ViewSetting {
|
||||||
t.mx.RLock()
|
t.mx.RLock()
|
||||||
defer t.mx.RUnlock()
|
defer t.mx.RUnlock()
|
||||||
|
|
||||||
|
|
@ -161,7 +173,7 @@ func (t *Table) GVR() client.GVR { return t.gvr }
|
||||||
|
|
||||||
// ViewSettingsChanged notifies listener the view configuration changed.
|
// ViewSettingsChanged notifies listener the view configuration changed.
|
||||||
func (t *Table) ViewSettingsChanged(vs *config.ViewSetting) {
|
func (t *Table) ViewSettingsChanged(vs *config.ViewSetting) {
|
||||||
if t.setViewSetting(vs) {
|
if t.SetViewSetting(vs) {
|
||||||
if vs == nil {
|
if vs == nil {
|
||||||
if !t.getMSort() && !t.sortCol.IsSet() {
|
if !t.getMSort() && !t.sortCol.IsSet() {
|
||||||
t.setSortCol(model1.SortColumn{})
|
t.setSortCol(model1.SortColumn{})
|
||||||
|
|
@ -294,7 +306,7 @@ func (t *Table) doUpdate(data *model1.TableData) *model1.TableData {
|
||||||
t.actions.Delete(KeyShiftP)
|
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
|
return data
|
||||||
}
|
}
|
||||||
|
|
@ -537,9 +549,12 @@ func (t *Table) styleTitle() string {
|
||||||
|
|
||||||
var title string
|
var title string
|
||||||
if ns == client.ClusterScope {
|
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 {
|
} 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()
|
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.
|
// ROIndicator returns an icon showing whether the session is in readonly mode or not.
|
||||||
func ROIndicator(ro bool) string {
|
func ROIndicator(ro, noIC bool) string {
|
||||||
if ro {
|
if noIC {
|
||||||
return LockedIC
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return UnlockedIC
|
if ro {
|
||||||
|
return lockedIC
|
||||||
|
}
|
||||||
|
|
||||||
|
return unlockedIC
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,10 @@ const (
|
||||||
SearchFmt = "<[filter:bg:r]/%s[fg:bg:-]> "
|
SearchFmt = "<[filter:bg:r]/%s[fg:bg:-]> "
|
||||||
|
|
||||||
// NSTitleFmt represents a namespaced view title.
|
// 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 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 = "↓"
|
descIndicator = "↓"
|
||||||
ascIndicator = "↑"
|
ascIndicator = "↑"
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// UnlockedIC represents an unlocked icon.
|
unlockedIC = "🔓"
|
||||||
UnlockedIC = "🔓"
|
lockedIC = "🔒"
|
||||||
|
|
||||||
// LockedIC represents a locked icon.
|
|
||||||
LockedIC = "🔒"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Namespaceable represents a namespaceable model.
|
// Namespaceable represents a namespaceable model.
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/derailed/k9s/internal/ui/dialog"
|
"github.com/derailed/k9s/internal/ui/dialog"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
@ -58,6 +59,12 @@ func (b *Browser) getUpdating() bool {
|
||||||
return b.updating
|
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.
|
// Init watches all running pods in given namespace.
|
||||||
func (b *Browser) Init(ctx context.Context) error {
|
func (b *Browser) Init(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
@ -85,6 +92,7 @@ func (b *Browser) Init(ctx context.Context) error {
|
||||||
b.app.CmdBuff().Reset()
|
b.app.CmdBuff().Reset()
|
||||||
}
|
}
|
||||||
b.Table.SetReadOnly(b.app.Config.IsReadOnly())
|
b.Table.SetReadOnly(b.app.Config.IsReadOnly())
|
||||||
|
b.Table.SetNoIcon(b.app.Config.K9s.UI.NoIcons)
|
||||||
|
|
||||||
b.bindKeys(b.Actions())
|
b.bindKeys(b.Actions())
|
||||||
for _, f := range b.bindKeysFn {
|
for _, f := range b.bindKeysFn {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/derailed/k9s/internal/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Interpreter tracks user prompt input.
|
// Interpreter tracks user prompt input.
|
||||||
|
|
@ -218,7 +220,7 @@ func (c *Interpreter) FuzzyArg() (string, bool) {
|
||||||
func (c *Interpreter) NSArg() (string, bool) {
|
func (c *Interpreter) NSArg() (string, bool) {
|
||||||
ns, ok := c.args[nsKey]
|
ns, ok := c.args[nsKey]
|
||||||
|
|
||||||
return ns, ok && ns != ""
|
return ns, ok && ns != client.BlankNamespace
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasContext returns the current context if any.
|
// 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)
|
slog.Error("Failure detected during command exec", slogs.Error, e)
|
||||||
c.app.Content.Dump()
|
c.app.Content.Dump()
|
||||||
slog.Debug("Dumping history buffer", slogs.CmdHist, c.app.cmdHistory.List())
|
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")
|
p := cmd.NewInterpreter("pod")
|
||||||
cmds := c.app.cmdHistory.List()
|
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 {
|
if comp == nil {
|
||||||
return fmt.Errorf("no component found for %s", gvr)
|
return fmt.Errorf("no component found for %s", gvr)
|
||||||
}
|
}
|
||||||
|
comp.SetCommand(p)
|
||||||
|
|
||||||
c.app.Flash().Infof("Viewing %s...", gvr)
|
c.app.Flash().Infof("Viewing %s...", gvr)
|
||||||
if clearStack {
|
if clearStack {
|
||||||
cmd := contextRX.ReplaceAllString(p.GetLine(), "")
|
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 {
|
if err := c.app.inject(comp, clearStack); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if pushCmd {
|
if pushCmd {
|
||||||
c.app.cmdHistory.Push(p.GetLine())
|
c.app.cmdHistory.Push(p.GetLine())
|
||||||
}
|
}
|
||||||
|
slog.Debug("History", slogs.Stack, c.app.cmdHistory.List())
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/k9s/internal/model"
|
"github.com/derailed/k9s/internal/model"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/sahilm/fuzzy"
|
"github.com/sahilm/fuzzy"
|
||||||
|
|
@ -58,6 +59,7 @@ func NewDetails(app *App, title, subject, contentType string, searchable bool) *
|
||||||
return &d
|
return &d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Details) SetCommand(*cmd.Interpreter) {}
|
||||||
func (d *Details) SetFilter(string) {}
|
func (d *Details) SetFilter(string) {}
|
||||||
func (d *Details) SetLabelFilter(map[string]string) {}
|
func (d *Details) SetLabelFilter(map[string]string) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/model"
|
"github.com/derailed/k9s/internal/model"
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
)
|
)
|
||||||
|
|
@ -43,8 +44,9 @@ func NewHelp(app *App) *Help {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Help) SetFilter(string) {}
|
func (*Help) SetCommand(*cmd.Interpreter) {}
|
||||||
func (h *Help) SetLabelFilter(map[string]string) {}
|
func (*Help) SetFilter(string) {}
|
||||||
|
func (*Help) SetLabelFilter(map[string]string) {}
|
||||||
|
|
||||||
// Init initializes the component.
|
// Init initializes the component.
|
||||||
func (h *Help) Init(ctx context.Context) error {
|
func (h *Help) Init(ctx context.Context) error {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
|
@ -55,8 +56,10 @@ func clipboardWrite(text string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bracketRX = regexp.MustCompile(`\[(.+)\[\]`)
|
||||||
|
|
||||||
func sanitizeEsc(s string) string {
|
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 {
|
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/model"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/sahilm/fuzzy"
|
"github.com/sahilm/fuzzy"
|
||||||
|
|
@ -61,6 +62,7 @@ func NewLiveView(app *App, title string, m model.ResourceViewer) *LiveView {
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *LiveView) SetCommand(*cmd.Interpreter) {}
|
||||||
func (v *LiveView) SetFilter(string) {}
|
func (v *LiveView) SetFilter(string) {}
|
||||||
func (v *LiveView) SetLabelFilter(map[string]string) {}
|
func (v *LiveView) SetLabelFilter(map[string]string) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/model"
|
"github.com/derailed/k9s/internal/model"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
)
|
)
|
||||||
|
|
@ -63,8 +64,9 @@ func NewLog(gvr client.GVR, opts *dao.LogOptions) *Log {
|
||||||
return &l
|
return &l
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) SetFilter(string) {}
|
func (*Log) SetCommand(*cmd.Interpreter) {}
|
||||||
func (l *Log) SetLabelFilter(map[string]string) {}
|
func (*Log) SetFilter(string) {}
|
||||||
|
func (*Log) SetLabelFilter(map[string]string) {}
|
||||||
|
|
||||||
// Init initializes the viewer.
|
// Init initializes the viewer.
|
||||||
func (l *Log) Init(ctx context.Context) (err error) {
|
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/model"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"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) SetFilter(string) {}
|
||||||
func (p *Picker) SetLabelFilter(map[string]string) {}
|
func (p *Picker) SetLabelFilter(map[string]string) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/derailed/k9s/internal/tchart"
|
"github.com/derailed/k9s/internal/tchart"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Graphable represents a graphic component.
|
// 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) SetFilter(string) {}
|
||||||
func (p *Pulse) SetLabelFilter(map[string]string) {}
|
func (p *Pulse) SetLabelFilter(map[string]string) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,9 +90,6 @@ func miscViewers(vv MetaViewers) {
|
||||||
vv[client.NewGVR("pulses")] = MetaViewer{
|
vv[client.NewGVR("pulses")] = MetaViewer{
|
||||||
viewerFn: NewPulse,
|
viewerFn: NewPulse,
|
||||||
}
|
}
|
||||||
vv[client.NewGVR("sanitizer")] = MetaViewer{
|
|
||||||
viewerFn: NewSanitizer,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func appsViewers(vv MetaViewers) {
|
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 (
|
import (
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
|
"github.com/derailed/k9s/internal/config/data"
|
||||||
"github.com/derailed/k9s/internal/dao"
|
"github.com/derailed/k9s/internal/dao"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Secret presents a secret viewer.
|
// Secret presents a secret viewer.
|
||||||
|
|
@ -57,7 +57,7 @@ func (s *Secret) decodeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
raw, err := yaml.Marshal(d)
|
raw, err := data.WriteYAML(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.App().Flash().Errf("Error decoding secret %s", err)
|
s.App().Flash().Errf("Error decoding secret %s", err)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@ import (
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/config"
|
|
||||||
"github.com/derailed/k9s/internal/model"
|
"github.com/derailed/k9s/internal/model"
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -28,6 +28,7 @@ type Table struct {
|
||||||
enterFn EnterFunc
|
enterFn EnterFunc
|
||||||
envFn EnvFunc
|
envFn EnvFunc
|
||||||
bindKeysFn []BindKeysFunc
|
bindKeysFn []BindKeysFunc
|
||||||
|
command *cmd.Interpreter
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTable returns a new viewer.
|
// NewTable returns a new viewer.
|
||||||
|
|
@ -48,11 +49,8 @@ func (t *Table) Init(ctx context.Context) (err error) {
|
||||||
if t.app.Conn() != nil {
|
if t.app.Conn() != nil {
|
||||||
ctx = context.WithValue(ctx, internal.KeyHasMetrics, t.app.Conn().HasMetrics())
|
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.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)
|
t.Table.Init(ctx)
|
||||||
if !t.app.Config.K9s.UI.Reactive {
|
if !t.app.Config.K9s.UI.Reactive {
|
||||||
if err := t.app.RefreshCustomViews(); err != nil {
|
if err := t.app.RefreshCustomViews(); err != nil {
|
||||||
|
|
@ -68,6 +66,11 @@ func (t *Table) Init(ctx context.Context) (err error) {
|
||||||
return nil
|
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.
|
// HeaderIndex returns index of a given column or false if not found.
|
||||||
func (t *Table) HeaderIndex(colName string) (int, bool) {
|
func (t *Table) HeaderIndex(colName string) (int, bool) {
|
||||||
for i := 0; i < t.GetColumnCount(); i++ {
|
for i := 0; i < t.GetColumnCount(); i++ {
|
||||||
|
|
@ -145,14 +148,18 @@ func (t *Table) Start() {
|
||||||
t.Stop()
|
t.Stop()
|
||||||
t.CmdBuff().AddListener(t)
|
t.CmdBuff().AddListener(t)
|
||||||
t.Styles().AddListener(t.Table)
|
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.
|
// Stop terminates the component.
|
||||||
func (t *Table) Stop() {
|
func (t *Table) Stop() {
|
||||||
t.CmdBuff().RemoveListener(t)
|
t.CmdBuff().RemoveListener(t)
|
||||||
t.Styles().RemoveListener(t.Table)
|
t.Styles().RemoveListener(t.Table)
|
||||||
t.App().CustomView.RemoveListener(t.GVR().String())
|
t.App().CustomView().RemoveListener(t.Table)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEnterFn specifies the default enter behavior.
|
// SetEnterFn specifies the default enter behavior.
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/dao"
|
"github.com/derailed/k9s/internal/dao"
|
||||||
"github.com/derailed/k9s/internal/model"
|
"github.com/derailed/k9s/internal/model"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -95,6 +96,9 @@ type ResourceViewer interface {
|
||||||
|
|
||||||
// SetInstance sets a parent FQN
|
// SetInstance sets a parent FQN
|
||||||
SetInstance(string)
|
SetInstance(string)
|
||||||
|
|
||||||
|
// SetCommand sets the current command.
|
||||||
|
SetCommand(*cmd.Interpreter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogViewer represents a log viewer.
|
// LogViewer represents a log viewer.
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/derailed/k9s/internal/ui/dialog"
|
"github.com/derailed/k9s/internal/ui/dialog"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/k9s/internal/xray"
|
"github.com/derailed/k9s/internal/xray"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"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) SetFilter(string) {}
|
||||||
func (x *Xray) SetLabelFilter(map[string]string) {}
|
func (x *Xray) SetLabelFilter(map[string]string) {}
|
||||||
|
|
||||||
|
|
@ -671,9 +673,12 @@ func (x *Xray) styleTitle() string {
|
||||||
|
|
||||||
var title string
|
var title string
|
||||||
if ns == client.ClusterScope {
|
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 {
|
} 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()
|
buff := x.CmdBuff().GetText()
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/derailed/popeye/pkg/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Section represents an xray renderer.
|
// Section represents an xray renderer.
|
||||||
|
|
@ -59,15 +58,15 @@ func (*Section) outcomeRefs(parent *TreeNode, section render.Section) {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
func colorize(s string, l config.Level) string {
|
func colorize(s string, l render.Level) string {
|
||||||
c := "green"
|
c := "green"
|
||||||
// nolint:exhaustive
|
// nolint:exhaustive
|
||||||
switch l {
|
switch l {
|
||||||
case config.ErrorLevel:
|
case render.ErrorLevel:
|
||||||
c = "red"
|
c = "red"
|
||||||
case config.WarnLevel:
|
case render.WarnLevel:
|
||||||
c = "orange"
|
c = "orange"
|
||||||
case config.InfoLevel:
|
case render.InfoLevel:
|
||||||
c = "blue"
|
c = "blue"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("[%s::]%s", c, s)
|
return fmt.Sprintf("[%s::]%s", c, s)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue