diff --git a/.golangci.yml b/.golangci.yml
index 052163af..31169efe 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -78,6 +78,62 @@ linters-settings:
- errors
reason: "Go 1.20+ has support for combining multiple errors, see https://go.dev/doc/go1.20#errors"
+ sloglint:
+ # Enforce not mixing key-value pairs and attributes.
+ # https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-mixed-arguments
+ # Default: true
+ no-mixed-args: true
+ # Enforce using key-value pairs only (overrides no-mixed-args, incompatible with attr-only).
+ # https://github.com/go-simpler/sloglint?tab=readme-ov-file#key-value-pairs-only
+ # Default: false
+ kv-only: true
+ # Enforce using attributes only (overrides no-mixed-args, incompatible with kv-only).
+ # https://github.com/go-simpler/sloglint?tab=readme-ov-file#attributes-only
+ # Default: false
+ attr-only: false
+ # Enforce not using global loggers.
+ # Values:
+ # - "": disabled
+ # - "all": report all global loggers
+ # - "default": report only the default slog logger
+ # https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-global
+ # Default: ""
+ no-global: ""
+ # Enforce using methods that accept a context.
+ # Values:
+ # - "": disabled
+ # - "all": report all contextless calls
+ # - "scope": report only if a context exists in the scope of the outermost function
+ # https://github.com/go-simpler/sloglint?tab=readme-ov-file#context-only
+ # Default: ""
+ context: ""
+ # Enforce using static values for log messages.
+ # https://github.com/go-simpler/sloglint?tab=readme-ov-file#static-messages
+ # Default: false
+ static-msg: false
+ # Enforce using constants instead of raw keys.
+ # https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-raw-keys
+ # Default: false
+ no-raw-keys: true
+ # Enforce a single key naming convention.
+ # Values: snake, kebab, camel, pascal
+ # https://github.com/go-simpler/sloglint?tab=readme-ov-file#key-naming-convention
+ # Default: ""
+ key-naming-case: camel
+ # Enforce not using specific keys.
+ # https://github.com/go-simpler/sloglint?tab=readme-ov-file#forbidden-keys
+ # Default: []
+ forbidden-keys:
+ - time
+ - level
+ - msg
+ - source
+ # Enforce putting arguments on separate lines.
+ # https://github.com/go-simpler/sloglint?tab=readme-ov-file#arguments-on-separate-lines
+ # Default: false
+ args-on-sep-lines: false
+
+
issues:
# default is true. Enables skipping of directories:
@@ -124,3 +180,5 @@ linters:
- misspell
- prealloc
- typecheck
+ - sloglint
+
diff --git a/Makefile b/Makefile
index 75659c95..d9b6f5ea 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
else
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
endif
-VERSION ?= v0.40.5
+VERSION ?= v0.40.6
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}
diff --git a/README.md b/README.md
index e1e484f3..f653fc24 100644
--- a/README.md
+++ b/README.md
@@ -66,6 +66,8 @@ Your donations will go a long way in keeping our servers lights on and beers in
Please refer to our [K9s documentation](https://k9scli.io) site for installation, usage, customization and tips.
+---
+
## Slack Channel
Wanna discuss K9s features with your fellow `K9sers` or simply show your support for this tool?
@@ -73,6 +75,21 @@ Wanna discuss K9s features with your fellow `K9sers` or simply show your support
* Channel: [K9sersSlack](https://k9sers.slack.com/)
* Invite: [K9slackers Invite](https://join.slack.com/t/k9sers/shared_invite/enQtOTA5MDEyNzI5MTU0LWQ1ZGI3MzliYzZhZWEyNzYxYzA3NjE0YTk1YmFmNzViZjIyNzhkZGI0MmJjYzhlNjdlMGJhYzE2ZGU1NjkyNTM)
+---
+
+## š„³ A Word From Our Rhodium Sponsors...
+
+Below are organizations that have opted to show their support and sponsor K9s.
+
+
+
+
+
+
+> NOTE! K9s neither vouches for nor endorses these companies or products.
+
+---
+
## Installation
K9s is available on Linux, macOS and Windows platforms.
@@ -627,7 +644,7 @@ The annotation value must specify a container to forward to as well as a local p
---
-## Resource Custom Columns
+## Custom Views
[SneakCast v0.17.0 on The Beach! - Yup! sound is sucking but what a setting!](https://youtu.be/7S33CNLAofk)
@@ -654,6 +671,7 @@ You can have one or more of the following attributes:
* `T` -> time column indicator
* `N` -> number column indicator
* `W` -> turns on wide column aka only shows while in wide mode. Defaults to the standard resource definition when present.
+* `S` -> Ensures a column is visible and not wide. Overrides `wide` std resource definition if present.
* `H` -> Hides the column
* `L` -> Left align (default)
* `R` -> Right align
@@ -674,6 +692,14 @@ views:
- NODE
- STATUS
- READY
+ - MEM/RL|S # => š Overrides std resource default wide attribute via `S` for `Show`
+ - '%MEM/R|' # => NOTE! column names with non alpha names need to be quoted as columns must be strings!
+
+ v1/pods@fred: # => š New v0.40.6! Customize columns for a given resource and namespace!
+ columns:
+ - AGE
+ - NAMESPACE|WR
+
v1/services:
columns:
- AGE
diff --git a/assets/sponsors/panfactum.png b/assets/sponsors/panfactum.png
new file mode 100644
index 00000000..43fc667d
Binary files /dev/null and b/assets/sponsors/panfactum.png differ
diff --git a/change_logs/release_v0.40.6.md b/change_logs/release_v0.40.6.md
new file mode 100644
index 00000000..6f6f2d9c
--- /dev/null
+++ b/change_logs/release_v0.40.6.md
@@ -0,0 +1,99 @@
+
+
+# Release v0.40.6
+
+## Notes
+
+Thank you to all that contributed with flushing out issues and enhancements for K9s!
+I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev
+and see if we're happier with some of the fixes!
+If you've filed an issue please help me verify and close.
+
+Your support, kindness and awesome suggestions to make K9s better are, as ever, very much noted and appreciated!
+Also big thanks to all that have allocated their own time to help others on both slack and on this repo!!
+
+As you may know, K9s is not pimped out by corps with deep pockets, thus if you feel K9s is helping your Kubernetes journey,
+please consider joining our [sponsorship program](https://github.com/sponsors/derailed) and/or make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
+
+On Slack? Please join us [K9slackers](https://join.slack.com/t/k9sers/shared_invite/enQtOTA5MDEyNzI5MTU0LWQ1ZGI3MzliYzZhZWEyNzYxYzA3NjE0YTk1YmFmNzViZjIyNzhkZGI0MmJjYzhlNjdlMGJhYzE2ZGU1NjkyNTM)
+
+## Maintenance Release!
+
+### Breaking change
+
+Moved `portForwardAddress` out of clusterXXX/contextYYY/config.yaml and into the main K9s config file.
+This is a global preference based on your setup vs a cluster/context specific attribute.
+K9s will nag you in the logs if a specific context config still contains this attribute but should not prevent the configuration load.
+
+### Column Blow Reloaded!
+
+We've added another property to the custom view. You can now also specify namespace specific column definition for a given resource.
+For instance, view pods in any namespace using one configuration and view pods in `fred` namespace using an alternate configuration.
+
+```yaml
+# views.yaml
+views:
+ # Using this for all pods...
+ v1/pods:
+ columns:
+ - AGE
+ - NAMESPACE|WR # => š Specifies the NAMESPACE column to be right aligned and only visible while in wide mode
+ - ZORG:.metadata.labels.fred\.io\.kubernetes\.blee # => š extract fred.io.kubernetes.blee label into it's own column
+ - BLEE:.metadata.annotations.blee|R # => š extract annotation blee into it's own column and right align it
+ - NAME
+ - IP
+ - NODE
+ - STATUS
+ - READY
+ - MEM/RL|S # => š Overrides std resource default wide attribute via `S` for `Show`
+ - '%MEM/R|' # => NOTE! column names with non alpha names need to be quoted as columns must be strings!
+
+ # Use this instead for pods in namespace `fred`
+ v1/pods@fred: # => š New v0.40.6! Customize columns for a given resource and namespace!
+ columns:
+ - AGE
+ - NAMESPACE|WR
+```
+
+Additionally, we've added a new column attribute aka `Show` -> `S`. This allows you to now override the default resource column `wide` attribute when set.
+
+
+---
+
+## Videos Are In The Can!
+
+Please dial [K9s Channel](https://www.youtube.com/channel/UC897uwPygni4QIjkPCpgjmw) for up coming content...
+
+* [K9s v0.40.0 -Column Blow- Sneak peek](https://youtu.be/iy6RDozAM4A)
+* [K9s v0.31.0 Configs+Sneak peek](https://youtu.be/X3444KfjguE)
+* [K9s v0.30.0 Sneak peek](https://youtu.be/mVBc1XneRJ4)
+* [Vulnerability Scans](https://youtu.be/ULkl0MsaidU)
+
+---
+
+## Resolved Issues
+
+* [#3179](https://github.com/derailed/k9s/issues/3179) Resource name with full api or group displayed (somewhere and sometimes)
+* [#3178](https://github.com/derailed/k9s/issues/3178) Cronjobs with the same name in different namespaces appear together
+* [#3176](https://github.com/derailed/k9s/issues/3176) Trigger all marked cronjobs
+* [#3162](https://github.com/derailed/k9s/issues/3162) Context configs: context directory created under wrong cluster after context switch
+* [#3161](https://github.com/derailed/k9s/issues/3161) Force wide-only columns to appear outside of wide view
+* [#3147](https://github.com/derailed/k9s/issues/3147) Prompt style is overriden by body
+* [#3139](https://github.com/derailed/k9s/issues/3139) CPU/R:L and MEM/R:L columns invalid in views.yaml
+* [#3138](https://github.com/derailed/k9s/issues/3138) Subresources are not shown correctly in the RBAC view
+
+---
+
+## Contributed PRs
+
+Please be sure to give `Big Thanks!` and `ATTA Girls/Boys!` to all the fine contributors for making K9s better for all of us!!
+
+* [#3182](https://github.com/derailed/k9s/pull/3182) fix: Use the latest version when downloading the Ubuntu deb file
+* [#3168](https://github.com/derailed/k9s/pull/3168) fix(history): handle cases where special commands add their command their command to the history
+* [#3159](https://github.com/derailed/k9s/pull/3159) Added hard contrast gruvbox skins
+* [#3149](https://github.com/derailed/k9s/pull/3149) fix: Pass grv on gotoResource as a String to fix non-default apiGroup list
+* [#3149](https://github.com/derailed/k9s/pull/3149) Add externalsecrets plugin
+* [#3140](https://github.com/derailed/k9s/pull/3140) fix: Avoid false positive matches in enableRegion (#3093)
+
+
+
Ā© 2025 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
\ No newline at end of file
diff --git a/cmd/info.go b/cmd/info.go
index 8fbb01e5..8110bbb1 100644
--- a/cmd/info.go
+++ b/cmd/info.go
@@ -5,12 +5,13 @@ package cmd
import (
"fmt"
+ "log/slog"
"os"
"github.com/derailed/k9s/internal/color"
"github.com/derailed/k9s/internal/config"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
- "github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
)
@@ -60,13 +61,13 @@ func getScreenDumpDirForInfo() string {
f, err := os.ReadFile(config.AppConfigFile)
if err != nil {
- log.Error().Err(err).Msgf("Reads k9s config file %v", err)
+ slog.Error("Unable to reads k9s config file", slogs.Error, err)
return config.AppDumpsDir
}
var cfg config.Config
if err := yaml.Unmarshal(f, &cfg); err != nil {
- log.Error().Err(err).Msgf("Unmarshal k9s config %v", err)
+ slog.Error("Unable to unmarshal k9s config file", slogs.Error, err)
return config.AppDumpsDir
}
if cfg.K9s == nil {
diff --git a/cmd/root.go b/cmd/root.go
index 610c7c7e..9cc30161 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -6,22 +6,25 @@ package cmd
import (
"errors"
"fmt"
+ "log/slog"
"os"
"runtime/debug"
"strings"
-
- "github.com/derailed/k9s/internal/config/data"
- "k8s.io/client-go/tools/clientcmd/api"
+ "time"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/color"
"github.com/derailed/k9s/internal/config"
+ "github.com/derailed/k9s/internal/config/data"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/view"
+
+ "github.com/lmittmann/tint"
+ // "github.com/MatusOllah/slogcolor"
"github.com/mattn/go-colorable"
- "github.com/rs/zerolog"
- "github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
+ "k8s.io/client-go/tools/clientcmd/api"
)
const (
@@ -78,35 +81,37 @@ func run(cmd *cobra.Command, args []string) error {
if err := config.InitLocs(); err != nil {
return err
}
- file, err := os.OpenFile(
+ logFile, err := os.OpenFile(
*k9sFlags.LogFile,
os.O_CREATE|os.O_APPEND|os.O_WRONLY,
data.DefaultFileMod,
)
if err != nil {
- return fmt.Errorf("Log file %q init failed: %w", *k9sFlags.LogFile, err)
+ return fmt.Errorf("log file %q init failed: %w", *k9sFlags.LogFile, err)
}
defer func() {
- if file != nil {
- _ = file.Close()
+ if logFile != nil {
+ _ = logFile.Close()
}
}()
defer func() {
if err := recover(); err != nil {
- log.Error().Msgf("Boom! %v", err)
- log.Error().Msg(string(debug.Stack()))
+ slog.Error("Boom!! k9s init failed", slogs.Error, err)
+ slog.Error("", slogs.Stack, string(debug.Stack()))
printLogo(color.Red)
fmt.Printf("%s", color.Colorize("Boom!! ", color.Red))
fmt.Printf("%v.\n", err)
}
}()
- log.Logger = log.Output(zerolog.ConsoleWriter{Out: file})
- zerolog.SetGlobalLevel(parseLevel(*k9sFlags.LogLevel))
+ slog.SetDefault(slog.New(tint.NewHandler(logFile, &tint.Options{
+ Level: parseLevel(*k9sFlags.LogLevel),
+ TimeFormat: time.Kitchen,
+ })))
cfg, err := loadConfiguration()
if err != nil {
- log.Error().Err(err).Msgf("Fail to load global/context configuration")
+ slog.Warn("Fail to load global/context configuration", slogs.Error, err)
}
app := view.NewApp(cfg)
if err := app.Init(version, *k9sFlags.RefreshRate); err != nil {
@@ -123,7 +128,7 @@ func run(cmd *cobra.Command, args []string) error {
}
func loadConfiguration() (*config.Config, error) {
- log.Info().Msg("š¶ K9s starting up...")
+ slog.Info("š¶ K9s starting up...")
k8sCfg := client.NewConfig(k8sFlags)
k9sCfg := config.NewConfig(k8sCfg)
@@ -134,50 +139,43 @@ func loadConfiguration() (*config.Config, error) {
}
k9sCfg.K9s.Override(k9sFlags)
if err := k9sCfg.Refine(k8sFlags, k9sFlags, k8sCfg); err != nil {
- log.Error().Err(err).Msgf("config refine failed")
+ slog.Error("Fail to refine k9s config", slogs.Error, err)
errs = errors.Join(errs, err)
}
- conn, err := client.InitConnection(k8sCfg)
-
+ conn, err := client.InitConnection(k8sCfg, slog.Default())
if err != nil {
errs = errors.Join(errs, err)
}
-
// Try to access server version if that fail. Connectivity issue?
if !conn.CheckConnectivity() {
errs = errors.Join(errs, fmt.Errorf("cannot connect to context: %s", k9sCfg.K9s.ActiveContextName()))
}
-
if !conn.ConnectionOK() {
+ slog.Warn("š£ Kubernetes connectivity toast!")
errs = errors.Join(errs, fmt.Errorf("k8s connection failed for context: %s", k9sCfg.K9s.ActiveContextName()))
+ } else {
+ slog.Info("ā
Kubernetes connectivity OK")
}
-
k9sCfg.SetConnection(conn)
-
- log.Info().Msg("ā
Kubernetes connectivity")
if err := k9sCfg.Save(false); err != nil {
- log.Error().Err(err).Msg("Config save")
+ slog.Error("K9s config save failed", slogs.Error, err)
errs = errors.Join(errs, err)
}
return k9sCfg, errs
}
-func parseLevel(level string) zerolog.Level {
+func parseLevel(level string) slog.Level {
switch level {
- case "trace":
- return zerolog.TraceLevel
case "debug":
- return zerolog.DebugLevel
+ return slog.LevelDebug
case "warn":
- return zerolog.WarnLevel
+ return slog.LevelWarn
case "error":
- return zerolog.ErrorLevel
- case "fatal":
- return zerolog.FatalLevel
+ return slog.LevelError
default:
- return zerolog.InfoLevel
+ return slog.LevelInfo
}
}
@@ -193,7 +191,7 @@ func initK9sFlags() {
k9sFlags.LogLevel,
"logLevel", "l",
config.DefaultLogLevel,
- "Specify a log level (error, warn, info, debug, trace)",
+ "Specify a log level (error, warn, info, debug)",
)
rootCmd.Flags().StringVarP(
k9sFlags.LogFile,
@@ -265,7 +263,7 @@ func initK8sFlags() {
rootCmd.Flags().StringVar(
k8sFlags.Timeout,
"request-timeout",
- "",
+ "5s",
"The length of time to wait before giving up on a single server request",
)
@@ -376,7 +374,7 @@ func initK8sFlagCompletion() {
_ = rootCmd.RegisterFlagCompletionFunc("namespace", func(cmd *cobra.Command, args []string, s string) ([]string, cobra.ShellCompDirective) {
conn := client.NewConfig(k8sFlags)
- if c, err := client.InitConnection(conn); err == nil {
+ if c, err := client.InitConnection(conn, slog.Default()); err == nil {
if nss, err := c.ValidNamespaceNames(); err == nil {
return filterFlagCompletions(nss, s)
}
@@ -391,7 +389,7 @@ func k8sFlagCompletion[T any](picker k8sPickerFn[T]) completeFn {
conn := client.NewConfig(k8sFlags)
cfg, err := conn.RawConfig()
if err != nil {
- log.Error().Err(err).Msgf("k8s config getter failed")
+ slog.Error("K8s raw config getter failed", slogs.Error, err)
}
return filterFlagCompletions(picker(&cfg), toComplete)
diff --git a/go.mod b/go.mod
index d9cce2c6..e2481840 100644
--- a/go.mod
+++ b/go.mod
@@ -1,12 +1,12 @@
module github.com/derailed/k9s
-go 1.23.2
+go 1.24.0
require (
github.com/adrg/xdg v0.5.3
github.com/anchore/clio v0.0.0-20241115144204-29e89f9fa837
github.com/anchore/grype v0.86.1
- github.com/anchore/syft v1.19.0
+ github.com/anchore/syft v1.20.0
github.com/atotto/clipboard v0.1.4
github.com/cenkalti/backoff/v4 v4.3.0
github.com/derailed/popeye v0.11.3
@@ -16,12 +16,12 @@ require (
github.com/fsnotify/fsnotify v1.8.0
github.com/fvbommel/sortorder v1.1.0
github.com/go-errors/errors v1.5.1
+ github.com/lmittmann/tint v1.0.7
github.com/mattn/go-colorable v0.1.14
github.com/mattn/go-runewidth v0.0.16
github.com/olekukonko/tablewriter v0.0.5
github.com/petergtz/pegomock v2.9.0+incompatible
github.com/rakyll/hey v0.1.4
- github.com/rs/zerolog v1.33.0
github.com/sahilm/fuzzy v0.1.1
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
@@ -69,7 +69,7 @@ require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.11.7 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
- github.com/ProtonMail/go-crypto v1.1.3 // indirect
+ github.com/ProtonMail/go-crypto v1.1.5 // indirect
github.com/acobaugh/osrelease v0.1.0 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 // indirect
@@ -79,7 +79,7 @@ require (
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb // indirect
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 // indirect
- github.com/anchore/packageurl-go v0.1.1-0.20250117185454-edf36a908b10 // indirect
+ github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 // indirect
github.com/anchore/stereoscope v0.0.13 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
@@ -91,9 +91,10 @@ require (
github.com/becheran/wildmatch-go v1.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
+ github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/bmatcuk/doublestar/v2 v2.0.4 // indirect
- github.com/bmatcuk/doublestar/v4 v4.8.0 // indirect
+ github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
@@ -118,7 +119,7 @@ require (
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v27.5.0+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
- github.com/docker/docker v27.5.0+incompatible // indirect
+ github.com/docker/docker v28.0.0+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
@@ -136,7 +137,7 @@ require (
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/facebookincubator/nvdtools v0.1.5 // indirect
github.com/fatih/camelcase v1.0.0 // indirect
- github.com/felixge/fgprof v0.9.3 // indirect
+ github.com/felixge/fgprof v0.9.5 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
@@ -146,7 +147,7 @@ require (
github.com/glebarez/sqlite v1.11.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
- github.com/go-git/go-git/v5 v5.13.1 // indirect
+ github.com/go-git/go-git/v5 v5.13.2 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
@@ -254,7 +255,7 @@ require (
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
- github.com/pjbgf/sha1cd v0.3.0 // indirect
+ github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
@@ -265,6 +266,7 @@ require (
github.com/prometheus/procfs v0.15.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
+ github.com/rs/zerolog v1.33.0 // indirect
github.com/rubenv/sql-migrate v1.7.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/saferwall/pe v1.5.6 // indirect
@@ -314,14 +316,14 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect
go.opentelemetry.io/otel/trace v1.33.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- golang.org/x/crypto v0.32.0 // indirect
+ golang.org/x/crypto v0.33.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
- golang.org/x/mod v0.22.0 // indirect
- golang.org/x/net v0.34.0 // indirect
+ golang.org/x/mod v0.23.0 // indirect
+ golang.org/x/net v0.35.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/sync v0.11.0 // indirect
- golang.org/x/sys v0.29.0 // indirect
- golang.org/x/term v0.28.0 // indirect
+ golang.org/x/sys v0.30.0 // indirect
+ golang.org/x/term v0.29.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.29.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
@@ -340,10 +342,10 @@ require (
k8s.io/component-base v0.32.2 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
- modernc.org/libc v1.55.3 // indirect
- modernc.org/mathutil v1.6.0 // indirect
- modernc.org/memory v1.8.0 // indirect
- modernc.org/sqlite v1.34.5 // indirect
+ modernc.org/libc v1.61.13 // indirect
+ modernc.org/mathutil v1.7.1 // indirect
+ modernc.org/memory v1.8.2 // indirect
+ modernc.org/sqlite v1.35.0 // indirect
oras.land/oras-go v1.2.5 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/kustomize/api v0.18.0 // indirect
diff --git a/go.sum b/go.sum
index 5b880d10..d86ec2a7 100644
--- a/go.sum
+++ b/go.sum
@@ -249,8 +249,8 @@ github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
-github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
-github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
+github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
+github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
@@ -283,12 +283,12 @@ github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 h1:rmZG77uXgE
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/grype v0.86.1 h1:HWpzCOCwjKkwkIEEC5lcKI4yl6GhTF3+Z12tXWYtMoI=
github.com/anchore/grype v0.86.1/go.mod h1:k3VnXfi+e/OGx1mTUL733gy3fyB4W/AdHP8fSyQML9w=
-github.com/anchore/packageurl-go v0.1.1-0.20250117185454-edf36a908b10 h1:zBedM9ZGYbs/61QC4ZOKxtChx5njXKHgHqDeHuUxrTw=
-github.com/anchore/packageurl-go v0.1.1-0.20250117185454-edf36a908b10/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
+github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
+github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
github.com/anchore/stereoscope v0.0.13 h1:9Ivkh7k+vOeG3JHrt44jOg/8UdZrCvMsSjLQ7trHBig=
github.com/anchore/stereoscope v0.0.13/go.mod h1:QfhhFc2pezp5aX/dVJ5qnBFpBUv5+KUTphwaQLxMUig=
-github.com/anchore/syft v1.19.0 h1:cUVVdbOHtYCz+581Aq2hhaEbR1MRowbwXyo+Xw+oW20=
-github.com/anchore/syft v1.19.0/go.mod h1:QyTWjG0LzowbJVNQj5ZX8UVx17eTkU73Xl7jAf6upE8=
+github.com/anchore/syft v1.20.0 h1:4nVM/eiqrb2GJCkW+d1xv8M5mxply8vVblpWOvVCgN8=
+github.com/anchore/syft v1.20.0/go.mod h1:h8U0q+Fk7f1d9ay4oa+gDb//AJYFuQftrBLOuS6llz4=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
@@ -326,12 +326,14 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef h1:TSFnfbbu2oAOuWbeDDTtwXWE6z+PmpgbSsMBeV7l0ww=
+github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef/go.mod h1:9iglf1GG4oNRJ39bZ5AZrjgAFD2RwQbXw6Qf7Cs47wo=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bmatcuk/doublestar/v2 v2.0.4 h1:6I6oUiT/sU27eE2OFcWqBhL1SwjyvQuOssxT4a1yidI=
github.com/bmatcuk/doublestar/v2 v2.0.4/go.mod h1:QMmcs3H2AUQICWhfzLXz+IYln8lRQmTZRptLie8RgRw=
-github.com/bmatcuk/doublestar/v4 v4.8.0 h1:DSXtrypQddoug1459viM9X9D3dp1Z7993fw36I2kNcQ=
-github.com/bmatcuk/doublestar/v4 v4.8.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
+github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
+github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
@@ -366,9 +368,15 @@ github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoC
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
+github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
+github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
+github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@@ -439,8 +447,8 @@ github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvD
github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U=
-github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v28.0.0+incompatible h1:Olh0KS820sJ7nPsBKChVhk5pzqcwDR15fumfAd/p9hM=
+github.com/docker/docker v28.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
@@ -460,8 +468,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
-github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ=
-github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
+github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
+github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY=
github.com/elliotchance/phpserialize v1.4.0/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
@@ -502,8 +510,9 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA=
github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
-github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
+github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
+github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI=
@@ -530,8 +539,8 @@ github.com/gkampitakis/ciinfo v0.3.1 h1:lzjbemlGI4Q+XimPg64ss89x8Mf3xihJqy/0Mgag
github.com/gkampitakis/ciinfo v0.3.1/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
-github.com/gkampitakis/go-snaps v0.5.8 h1:BB4ihcyXgJEVO/Pj/P+4bs7pFzsLcEjsfU2+mFdJh1c=
-github.com/gkampitakis/go-snaps v0.5.8/go.mod h1:PcKmy8q5Se7p48ywpogN5Td13reipz1Iivah4wrTIvY=
+github.com/gkampitakis/go-snaps v0.5.11 h1:LFG0ggUKR+KEiiaOvFCmLgJ5NO2zf93AxxddkBn3LdQ=
+github.com/gkampitakis/go-snaps v0.5.11/go.mod h1:PcKmy8q5Se7p48ywpogN5Td13reipz1Iivah4wrTIvY=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
@@ -546,8 +555,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
-github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
-github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
+github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
+github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -584,6 +593,9 @@ github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
+github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
+github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/goccy/go-yaml v1.15.13 h1:Xd87Yddmr2rC1SLLTm2MNDcTjeO/GYo0JGiww6gSTDg=
github.com/goccy/go-yaml v1.15.13/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -681,6 +693,7 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
+github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
@@ -783,6 +796,7 @@ github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
+github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
@@ -848,12 +862,15 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
+github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
+github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y=
+github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
@@ -993,6 +1010,7 @@ github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaL
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/openvex/go-vex v0.2.5 h1:41utdp2rHgAGCsG+UbjmfMG5CWQxs15nGqir1eRgSrQ=
github.com/openvex/go-vex v0.2.5/go.mod h1:j+oadBxSUELkrKh4NfNb+BPo77U3q7gdKME88IO/0Wo=
+github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/owenrumney/go-sarif v1.1.2-0.20231003122901-1000f5e05554 h1:FvA4bwjKpPqik5WsQ8+4z4DKWgA1tO1RTTtNKr5oYNA=
github.com/owenrumney/go-sarif v1.1.2-0.20231003122901-1000f5e05554/go.mod h1:n73K/hcuJ50MiVznXyN4rde6fZY7naGKWBXOLFTyc94=
github.com/package-url/packageurl-go v0.1.1 h1:KTRE0bK3sKbFKAk3yy63DpeskU7Cvs/x/Da5l+RtzyU=
@@ -1014,8 +1032,8 @@ github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1H
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
-github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
+github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
+github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -1085,8 +1103,8 @@ github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
-github.com/sanity-io/litter v1.5.6 h1:hCFycYzhRnW4niFbbmR7QKdmds69PbVa/sNmEN5euSU=
-github.com/sanity-io/litter v1.5.6/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
+github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rYg=
+github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
@@ -1282,8 +1300,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
-golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
+golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
+golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1324,8 +1342,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
-golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
+golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1384,8 +1402,8 @@ golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfS
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
-golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
+golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
+golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1509,6 +1527,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1525,14 +1544,14 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
-golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
-golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
+golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
+golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1918,28 +1937,28 @@ k8s.io/metrics v0.32.2 h1:7t/rZzTHFrGa9f94XcgLlm3ToAuJtdlHANcJEHlYl9g=
k8s.io/metrics v0.32.2/go.mod h1:VL3nJpzcgB6L5nSljkkzoE0nilZhVgcjCfNRgoylaIQ=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
-modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
-modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
-modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
+modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
+modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
+modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
+modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
-modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
-modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
-modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
-modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
-modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
-modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
-modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
-modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
-modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
-modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
-modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
-modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
-modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g=
-modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE=
-modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
-modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
+modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw=
+modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
+modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8=
+modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E=
+modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
+modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
+modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=
+modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
+modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
+modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
+modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
+modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
+modernc.org/sqlite v1.35.0 h1:yQps4fegMnZFdphtzlfQTCNBWtS0CZv48pRpW3RFHRw=
+modernc.org/sqlite v1.35.0/go.mod h1:9cr2sicr7jIaWTBKQmAxQLfBv9LL0su4ZTEV+utt3ic=
+modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
+modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo=
diff --git a/internal/client/client.go b/internal/client/client.go
index 60ee1892..75184ab2 100644
--- a/internal/client/client.go
+++ b/internal/client/client.go
@@ -7,13 +7,14 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"os"
"path/filepath"
"strings"
"sync"
"time"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
authorizationv1 "k8s.io/api/authorization/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/cache"
@@ -34,7 +35,9 @@ const (
cacheNSKey = "validNamespaces"
)
-var supportedMetricsAPIVersions = []string{"v1beta1"}
+var (
+ supportedMetricsAPIVersions = []string{"v1beta1"}
+)
// NamespaceNames tracks a collection of namespace names.
type NamespaceNames map[string]struct{}
@@ -50,6 +53,7 @@ type APIClient struct {
mx sync.RWMutex
cache *cache.LRUExpireCache
connOK bool
+ log *slog.Logger
}
// NewTestAPIClient for testing ONLY!!
@@ -62,15 +66,16 @@ func NewTestAPIClient() *APIClient {
// InitConnection initialize connection from command line args.
// Checks for connectivity with the api server.
-func InitConnection(config *Config) (*APIClient, error) {
+func InitConnection(config *Config, log *slog.Logger) (*APIClient, error) {
a := APIClient{
config: config,
cache: cache.NewLRUExpireCache(cacheSize),
connOK: true,
+ log: log.With(slogs.Subsys, "client"),
}
err := a.supportsMetricsResources()
if err != nil {
- log.Error().Err(err).Msgf("Fail to locate metrics-server")
+ slog.Warn("Fail to locate metrics-server", slogs.Error, err)
}
if err == nil || errors.Is(err, noMetricServerErr) || errors.Is(err, metricsUnsupportedErr) {
return &a, nil
@@ -113,7 +118,7 @@ func makeCacheKey(ns, gvr, n string, vv []string) string {
func (a *APIClient) ActiveContext() string {
c, err := a.config.CurrentContextName()
if err != nil {
- log.Error().Msgf("Unable to located active cluster")
+ slog.Error("unable to located active cluster", slogs.Error, err)
return ""
}
return c
@@ -158,6 +163,8 @@ func (a *APIClient) CanI(ns, gvr, name string, verbs []string) (auth bool, err e
}
}
+ clog := a.log.With(slogs.Subsys, "can")
+
dial, err := a.Dial()
if err != nil {
return false, err
@@ -169,14 +176,20 @@ func (a *APIClient) CanI(ns, gvr, name string, verbs []string) (auth bool, err e
for _, v := range verbs {
sar.Spec.ResourceAttributes.Verb = v
resp, err := client.Create(ctx, sar, metav1.CreateOptions{})
- log.Trace().Msgf("[CAN] %s(%q/%q) <%v>", gvr, ns, name, verbs)
+ clog.Debug("[CAN] access",
+ slogs.GVR, gvr,
+ slogs.Namespace, ns,
+ slogs.ResName, name,
+ slogs.Verb, verbs,
+ )
if resp != nil {
- log.Trace().Msgf(" Spec: %#v", resp.Spec)
- log.Trace().Msgf(" Auth: %t [%q]", resp.Status.Allowed, resp.Status.Reason)
+ clog.Debug("[CAN] reps",
+ slogs.AuthStatus, resp.Status.Allowed,
+ slogs.AuthReason, resp.Status.Reason,
+ )
}
- log.Trace().Msgf(" <<%v>>", err)
if err != nil {
- log.Warn().Err(err).Msgf(" Dial Failed!")
+ clog.Warn("Auth request failed", slogs.Error, err)
a.cache.Add(key, false, cacheExpiry)
return auth, err
}
@@ -220,7 +233,10 @@ func (a *APIClient) ServerVersion() (*version.Info, error) {
func (a *APIClient) IsValidNamespace(ns string) bool {
ok, err := a.isValidNamespace(ns)
if err != nil {
- log.Warn().Err(err).Msgf("namespace validation failed for: %q", ns)
+ slog.Warn("Namespace validation failed",
+ slogs.Namespace, ns,
+ slogs.Error, err,
+ )
}
return ok
@@ -287,16 +303,15 @@ func (a *APIClient) CheckConnectivity() bool {
}()
cfg, err := a.config.RESTConfig()
-
if err != nil {
- log.Error().Err(err).Msgf("restConfig load failed")
+ slog.Error("RestConfig load failed", slogs.Error, err)
a.connOK = false
return a.connOK
}
cfg.Timeout = a.config.CallTimeout()
client, err := kubernetes.NewForConfig(cfg)
if err != nil {
- log.Error().Err(err).Msgf("Unable to connect to api server")
+ slog.Error("Unable to connect to api server", slogs.Error, err)
a.setConnOK(false)
return a.getConnOK()
}
@@ -307,7 +322,7 @@ func (a *APIClient) CheckConnectivity() bool {
a.reset()
}
} else {
- log.Error().Err(err).Msgf("can't connect to cluster")
+ slog.Error("Unable to fetch server version", slogs.Error, err)
a.setConnOK(false)
}
@@ -541,13 +556,13 @@ func (a *APIClient) invalidateCache() error {
// SwitchContext handles kubeconfig context switches.
func (a *APIClient) SwitchContext(name string) error {
- log.Debug().Msgf("Switching context %q", name)
+ slog.Debug("Switching context", slogs.Context, name)
if err := a.config.SwitchContext(name); err != nil {
return err
}
if !a.CheckConnectivity() {
- log.Debug().Msg("No connectivity, skipping cache invalidation")
+ slog.Debug("No connectivity, skipping cache invalidation")
} else if err := a.invalidateCache(); err != nil {
return err
}
@@ -597,7 +612,7 @@ func (a *APIClient) supportsMetricsResources() error {
dial, err := a.Dial()
if err != nil {
- log.Warn().Err(err).Msgf("Unable to dial discovery API")
+ slog.Warn("Unable to dial API client for metrics", slogs.Error, err)
return err
}
apiGroups, err := dial.Discovery().ServerGroups()
diff --git a/internal/client/config.go b/internal/client/config.go
index e9f01000..e34d0e3c 100644
--- a/internal/client/config.go
+++ b/internal/client/config.go
@@ -19,7 +19,7 @@ import (
)
const (
- defaultCallTimeoutDuration time.Duration = 15 * time.Second
+ defaultCallTimeoutDuration time.Duration = 10 * time.Second
// UsePersistentConfig caches client config to avoid reloads.
UsePersistentConfig = true
diff --git a/internal/client/config_test.go b/internal/client/config_test.go
index c92593af..a9a3386d 100644
--- a/internal/client/config_test.go
+++ b/internal/client/config_test.go
@@ -5,18 +5,18 @@ package client_test
import (
"errors"
+ "log/slog"
"os"
"testing"
"time"
"github.com/derailed/k9s/internal/client"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.FatalLevel)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestCallTimeout(t *testing.T) {
@@ -29,7 +29,7 @@ func TestCallTimeout(t *testing.T) {
e: 1 * time.Minute,
},
"default": {
- e: 15 * time.Second,
+ e: 10 * time.Second,
},
}
diff --git a/internal/client/gvr.go b/internal/client/gvr.go
index ebb7fffd..1d4a4b68 100644
--- a/internal/client/gvr.go
+++ b/internal/client/gvr.go
@@ -5,11 +5,12 @@ package client
import (
"fmt"
+ "log/slog"
"path"
"strings"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/fvbommel/sortorder"
- "github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
@@ -40,7 +41,7 @@ func NewGVR(gvr string) GVR {
case 1:
r = tokens[0]
default:
- log.Error().Err(fmt.Errorf("can't parse GVR %q", gvr)).Msg("GVR init failed!")
+ slog.Error("GVR init failed!", slogs.Error, fmt.Errorf("can't parse GVR %q", gvr))
}
return GVR{raw: gvr, g: g, v: v, r: r, sr: sr}
@@ -186,7 +187,7 @@ func Can(verbs []string, v string) bool {
for _, verb := range verbs {
candidates, err := mapVerb(v)
if err != nil {
- log.Error().Err(err).Msgf("verb mapping failed")
+ slog.Error("Access verb mapping failed", slogs.Error, err)
return false
}
for _, c := range candidates {
diff --git a/internal/client/helpers.go b/internal/client/helpers.go
index afea4b3a..d2dbabd5 100644
--- a/internal/client/helpers.go
+++ b/internal/client/helpers.go
@@ -4,12 +4,14 @@
package client
import (
+ "log/slog"
+ "os"
"os/user"
"path"
"regexp"
"strings"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -81,7 +83,8 @@ func MetaFQN(m metav1.ObjectMeta) string {
func mustHomeDir() string {
usr, err := user.Current()
if err != nil {
- log.Fatal().Err(err).Msg("Die getting user home directory")
+ slog.Error("Die getting user home directory", slogs.Error, err)
+ os.Exit(1)
}
return usr.HomeDir
}
diff --git a/internal/config/alias.go b/internal/config/alias.go
index f189e86e..8172ba97 100644
--- a/internal/config/alias.go
+++ b/internal/config/alias.go
@@ -5,14 +5,14 @@ package config
import (
"errors"
- "fmt"
"io/fs"
+ "log/slog"
"os"
"sync"
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/config/json"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"gopkg.in/yaml.v2"
)
@@ -120,7 +120,7 @@ func (a *Aliases) Load(path string) error {
f, err := EnsureAliasesCfgFile()
if err != nil {
- log.Error().Err(err).Msgf("Unable to gen config aliases")
+ slog.Error("Unable to gen config aliases", slogs.Error, err)
}
// load global alias file
@@ -146,7 +146,7 @@ func (a *Aliases) LoadFile(path string) error {
return err
}
if err := data.JSONValidator.Validate(json.AliasesSchema, bb); err != nil {
- return fmt.Errorf("validation failed for %q: %w", path, err)
+ slog.Warn("Aliases validation failed", slogs.Error, err)
}
var aa Aliases
@@ -193,7 +193,7 @@ func (a *Aliases) loadDefaultAliases() {
// Save alias to disk.
func (a *Aliases) Save() error {
- log.Debug().Msg("[Config] Saving Aliases...")
+ slog.Debug("Saving Aliases...")
return a.SaveAliases(AppAliasesFile)
}
diff --git a/internal/config/config.go b/internal/config/config.go
index a5da1dfa..a0bfa8b1 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -7,13 +7,14 @@ import (
"errors"
"fmt"
"io/fs"
+ "log/slog"
"os"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/config/json"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/view/cmd"
- "github.com/rs/zerolog/log"
"gopkg.in/yaml.v2"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
@@ -33,6 +34,16 @@ func NewConfig(ks data.KubeSettings) *Config {
}
}
+// ActiveClusterName returns the corresponding cluster name.
+func (c *Config) ActiveClusterName(contextName string) (string, error) {
+ ct, err := c.settings.GetContext(contextName)
+ if err != nil {
+ return "", err
+ }
+
+ return ct.Cluster, nil
+}
+
// ContextHotkeysPath returns a context specific hotkeys file spec.
func (c *Config) ContextHotkeysPath() string {
ct, err := c.K9s.ActiveContext()
@@ -82,7 +93,7 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c
return fmt.Errorf("unable to activate context %q: %w", n, err)
}
}
- log.Debug().Msgf("Active Context %q", c.K9s.ActiveContextName())
+ slog.Debug("Using active context", slogs.Context, c.K9s.ActiveContextName())
var ns string
switch {
@@ -113,7 +124,7 @@ func (c *Config) Reset() {
c.K9s.Reset()
}
-func (c *Config) SetCurrentContext(n string) (*data.Context, error) {
+func (c *Config) ActivateContext(n string) (*data.Context, error) {
ct, err := c.K9s.ActivateContext(n)
if err != nil {
return nil, fmt.Errorf("set current context failed. %w", err)
@@ -132,7 +143,7 @@ func (c *Config) CurrentContext() (*data.Context, error) {
func (c *Config) ActiveNamespace() string {
ns, err := c.K9s.ActiveContextNamespace()
if err != nil {
- log.Error().Err(err).Msgf("Unable to assert active namespace. Using default")
+ slog.Error("Unable to assert active namespace. Using default", slogs.Error, err)
ns = client.DefaultNamespace
}
@@ -145,7 +156,7 @@ func (c *Config) FavNamespaces() []string {
if err != nil {
return nil
}
- ct.Validate(c.conn, c.settings)
+ ct.Validate(c.conn, c.K9s.getActiveContextName(), ct.ClusterName)
return ct.Namespace.Favorites
}
@@ -153,7 +164,7 @@ func (c *Config) FavNamespaces() []string {
// SetActiveNamespace set the active namespace in the current context.
func (c *Config) SetActiveNamespace(ns string) error {
if ns == client.NotNamespaced {
- log.Debug().Msgf("[SetNS] No namespace given. skipping!")
+ slog.Debug("No namespace given. skipping!", slogs.Namespace, ns)
return nil
}
ct, err := c.K9s.ActiveContext()
@@ -251,8 +262,13 @@ func (c *Config) Load(path string, force bool) error {
// Save configuration to disk.
func (c *Config) Save(force bool) error {
- c.Validate()
- if err := c.K9s.Save(force); err != nil {
+ contextName := c.K9s.ActiveContextName()
+ clusterName, err := c.ActiveClusterName(contextName)
+ if err != nil {
+ return fmt.Errorf("unable to locate associated cluster for context %q: %w", contextName, err)
+ }
+ c.Validate(contextName, clusterName)
+ if err := c.K9s.Save(contextName, clusterName, force); err != nil {
return err
}
if _, err := os.Stat(AppConfigFile); errors.Is(err, fs.ErrNotExist) {
@@ -267,21 +283,23 @@ func (c *Config) SaveFile(path string) error {
if err := data.EnsureDirPath(path, data.DefaultDirMod); err != nil {
return err
}
+
cfg, err := yaml.Marshal(c)
if err != nil {
- log.Error().Msgf("[Config] Unable to save K9s config file: %v", err)
+ slog.Error("Unable to save K9s config file", slogs.Error, err)
return err
}
+ slog.Info("[CONFIG] Saving K9s config to disk", slogs.Path, path)
return os.WriteFile(path, cfg, data.DefaultFileMod)
}
// Validate the configuration.
-func (c *Config) Validate() {
+func (c *Config) Validate(contextName, clusterName string) {
if c.K9s == nil {
c.K9s = NewK9s(c.conn, c.settings)
}
- c.K9s.Validate(c.conn, c.settings)
+ c.K9s.Validate(c.conn, contextName, clusterName)
}
// Dump for debug...
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index 3910eb5c..38cd3235 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -6,6 +6,7 @@ package config_test
import (
"errors"
"fmt"
+ "log/slog"
"os"
"path/filepath"
"testing"
@@ -16,13 +17,12 @@ import (
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/config/mock"
m "github.com/petergtz/pegomock"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.FatalLevel)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestConfigSave(t *testing.T) {
@@ -324,7 +324,7 @@ Invalid type. Expected: boolean, given: string`,
}
}
-func TestConfigSetCurrentContext(t *testing.T) {
+func TestConfigActivateContext(t *testing.T) {
uu := map[string]struct {
cl, ct string
err string
@@ -344,7 +344,7 @@ func TestConfigSetCurrentContext(t *testing.T) {
u := uu[k]
t.Run(k, func(t *testing.T) {
cfg := mock.NewMockConfig()
- ct, err := cfg.SetCurrentContext(u.ct)
+ ct, err := cfg.ActivateContext(u.ct)
if err != nil {
assert.Equal(t, u.err, err.Error())
return
@@ -529,7 +529,7 @@ func TestConfigValidate(t *testing.T) {
cfg.SetConnection(mock.NewMockConnection())
assert.Nil(t, cfg.Load("testdata/configs/k9s.yaml", true))
- cfg.Validate()
+ cfg.Validate("ct-1-1", "cl-1")
}
func TestConfigLoad(t *testing.T) {
@@ -556,7 +556,7 @@ func TestConfigSaveFile(t *testing.T) {
cfg.K9s.ReadOnly = true
cfg.K9s.Logger.TailCount = 500
cfg.K9s.Logger.BufferSize = 800
- cfg.Validate()
+ cfg.Validate("ct-1-1", "cl-1")
path := filepath.Join("/tmp", "k9s.yaml")
assert.NoError(t, cfg.SaveFile(path))
@@ -571,7 +571,7 @@ func TestConfigReset(t *testing.T) {
cfg := mock.NewMockConfig()
assert.Nil(t, cfg.Load("testdata/configs/k9s.yaml", true))
cfg.Reset()
- cfg.Validate()
+ cfg.Validate("ct-1-1", "cl-1")
path := filepath.Join("/tmp", "k9s.yaml")
assert.NoError(t, cfg.SaveFile(path))
diff --git a/internal/config/data/config.go b/internal/config/data/config.go
index a03020f8..bc894406 100644
--- a/internal/config/data/config.go
+++ b/internal/config/data/config.go
@@ -26,6 +26,7 @@ func NewConfig(ct *api.Context) *Config {
}
}
+// Merge merges configs and updates receiver.
func (c *Config) Merge(c1 *Config) {
if c1 == nil {
return
@@ -36,14 +37,14 @@ func (c *Config) Merge(c1 *Config) {
}
// Validate ensures config is in norms.
-func (c *Config) Validate(conn client.Connection, ks KubeSettings) {
+func (c *Config) Validate(conn client.Connection, contextName, clusterName string) {
c.mx.Lock()
defer c.mx.Unlock()
if c.Context == nil {
c.Context = NewContext()
}
- c.Context.Validate(conn, ks)
+ c.Context.Validate(conn, contextName, clusterName)
}
// Dump used for debugging.
diff --git a/internal/config/data/context.go b/internal/config/data/context.go
index 358a1aae..1e5c29c5 100644
--- a/internal/config/data/context.go
+++ b/internal/config/data/context.go
@@ -13,24 +13,22 @@ import (
// Context tracks K9s context configuration.
type Context struct {
- ClusterName string `yaml:"cluster,omitempty"`
- ReadOnly *bool `yaml:"readOnly,omitempty"`
- Skin string `yaml:"skin,omitempty"`
- Namespace *Namespace `yaml:"namespace"`
- View *View `yaml:"view"`
- FeatureGates FeatureGates `yaml:"featureGates"`
- PortForwardAddress string `yaml:"portForwardAddress"`
- Proxy *Proxy `yaml:"proxy"`
- mx sync.RWMutex
+ ClusterName string `yaml:"cluster,omitempty"`
+ ReadOnly *bool `yaml:"readOnly,omitempty"`
+ Skin string `yaml:"skin,omitempty"`
+ Namespace *Namespace `yaml:"namespace"`
+ View *View `yaml:"view"`
+ FeatureGates FeatureGates `yaml:"featureGates"`
+ Proxy *Proxy `yaml:"proxy"`
+ mx sync.RWMutex
}
// NewContext creates a new cluster configuration.
func NewContext() *Context {
return &Context{
- Namespace: NewNamespace(),
- View: NewView(),
- PortForwardAddress: defaultPFAddress(),
- FeatureGates: NewFeatureGates(),
+ Namespace: NewNamespace(),
+ View: NewView(),
+ FeatureGates: NewFeatureGates(),
}
}
@@ -71,19 +69,11 @@ func (c *Context) GetClusterName() string {
}
// Validate ensures a context config is tip top.
-func (c *Context) Validate(conn client.Connection, ks KubeSettings) {
+func (c *Context) Validate(conn client.Connection, contextName, clusterName string) {
c.mx.Lock()
defer c.mx.Unlock()
- if a := os.Getenv(envPFAddress); a != "" {
- c.PortForwardAddress = a
- }
- if c.PortForwardAddress == "" {
- c.PortForwardAddress = defaultPFAddress()
- }
- if cl, err := ks.CurrentClusterName(); err == nil {
- c.ClusterName = cl
- }
+ c.ClusterName = clusterName
if b := os.Getenv(envFGNodeShell); b != "" {
c.FeatureGates.NodeShell = defaultFGNodeShell()
}
diff --git a/internal/config/data/context_test.go b/internal/config/data/context_test.go
index d66c8d56..30318ead 100644
--- a/internal/config/data/context_test.go
+++ b/internal/config/data/context_test.go
@@ -13,7 +13,7 @@ import (
func TestClusterValidate(t *testing.T) {
c := data.NewContext()
- c.Validate(mock.NewMockConnection(), mock.NewMockKubeSettings(makeFlags("cl-1", "ct-1")))
+ c.Validate(mock.NewMockConnection(), "ct-1", "cl-1")
assert.Equal(t, "po", c.View.Active)
assert.Equal(t, "default", c.Namespace.Active)
@@ -23,7 +23,7 @@ func TestClusterValidate(t *testing.T) {
func TestClusterValidateEmpty(t *testing.T) {
c := data.NewContext()
- c.Validate(mock.NewMockConnection(), mock.NewMockKubeSettings(makeFlags("cl-1", "ct-1")))
+ c.Validate(mock.NewMockConnection(), "ct-1", "cl-1")
assert.Equal(t, "po", c.View.Active)
assert.Equal(t, "default", c.Namespace.Active)
diff --git a/internal/config/data/dir.go b/internal/config/data/dir.go
index 3b045578..6165e5ad 100644
--- a/internal/config/data/dir.go
+++ b/internal/config/data/dir.go
@@ -7,12 +7,13 @@ import (
"errors"
"fmt"
"io/fs"
+ "log/slog"
"os"
"path/filepath"
"sync"
"github.com/derailed/k9s/internal/config/json"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"gopkg.in/yaml.v2"
"k8s.io/client-go/tools/clientcmd/api"
)
@@ -31,18 +32,19 @@ func NewDir(root string) *Dir {
}
// Load loads context configuration.
-func (d *Dir) Load(n string, ct *api.Context) (*Config, error) {
+func (d *Dir) Load(contextName string, ct *api.Context) (*Config, error) {
if ct == nil {
return nil, errors.New("api.Context must not be nil")
}
- var path = filepath.Join(d.root, SanitizeContextSubpath(ct.Cluster, n), MainConfigFile)
+ path := filepath.Join(d.root, SanitizeContextSubpath(ct.Cluster, contextName), MainConfigFile)
+ slog.Debug("[CONFIG] Loading context config from disk", slogs.Path, path, slogs.Cluster, ct.Cluster, slogs.Context, contextName)
f, err := os.Stat(path)
if errors.Is(err, fs.ErrPermission) {
return nil, err
}
if errors.Is(err, fs.ErrNotExist) || (f != nil && f.Size() == 0) {
- log.Debug().Msgf("Context config not found! Generating... %q", path)
+ slog.Debug("Context config not found! Generating..", slogs.Path, path)
return d.genConfig(path, ct)
}
if err != nil {
@@ -89,7 +91,10 @@ func (d *Dir) loadConfig(path string) (*Config, error) {
return nil, err
}
if err := JSONValidator.Validate(json.ContextSchema, bb); err != nil {
- return nil, fmt.Errorf("validation failed for %q: %w", path, err)
+ slog.Warn("Validation failed. Please update your config and restart!",
+ slogs.Path, path,
+ slogs.Error, err,
+ )
}
var cfg Config
diff --git a/internal/config/data/dir_test.go b/internal/config/data/dir_test.go
index b780a585..e4e5118c 100644
--- a/internal/config/data/dir_test.go
+++ b/internal/config/data/dir_test.go
@@ -4,20 +4,20 @@
package data_test
import (
+ "log/slog"
"os"
"strings"
"testing"
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/config/mock"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v2"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.FatalLevel)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestDirLoad(t *testing.T) {
diff --git a/internal/config/data/helpers.go b/internal/config/data/helpers.go
index bff321a0..af086e5b 100644
--- a/internal/config/data/helpers.go
+++ b/internal/config/data/helpers.go
@@ -11,11 +11,7 @@ import (
"regexp"
)
-const (
- envPFAddress = "K9S_DEFAULT_PF_ADDRESS"
- envFGNodeShell = "K9S_FEATURE_GATE_NODE_SHELL"
- defaultPortFwdAddress = "localhost"
-)
+const envFGNodeShell = "K9S_FEATURE_GATE_NODE_SHELL"
var invalidPathCharsRX = regexp.MustCompile(`[:/]+`)
@@ -29,14 +25,6 @@ func SanitizeFileName(name string) string {
return invalidPathCharsRX.ReplaceAllString(name, "-")
}
-func defaultPFAddress() string {
- if a := os.Getenv(envPFAddress); a != "" {
- return a
- }
-
- return defaultPortFwdAddress
-}
-
func defaultFGNodeShell() bool {
if a := os.Getenv(envFGNodeShell); a != "" {
return a == "true"
diff --git a/internal/config/data/ns.go b/internal/config/data/ns.go
index 9b0d5ebf..cf9aba0f 100644
--- a/internal/config/data/ns.go
+++ b/internal/config/data/ns.go
@@ -4,11 +4,12 @@
package data
import (
+ "log/slog"
"slices"
"sync"
"github.com/derailed/k9s/internal/client"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
)
const (
@@ -67,7 +68,10 @@ func (n *Namespace) Validate(c client.Connection) {
}
for _, ns := range n.Favorites {
if !c.IsValidNamespace(ns) {
- log.Debug().Msgf("[Namespace] Invalid favorite found '%s' - %t", ns, n.isAllNamespaces())
+ slog.Debug("Invalid favorite found",
+ slogs.Namespace, ns,
+ slogs.AllNS, n.isAllNamespaces(),
+ )
n.rmFavNS(ns)
}
}
@@ -136,7 +140,7 @@ func (n *Namespace) rmFavNS(ns string) {
func (n *Namespace) trimFavNs() {
if len(n.Favorites) > MaxFavoritesNS {
- log.Debug().Msgf("[Namespace] Number of favorite exceeds hard limit of %v. Trimming.", MaxFavoritesNS)
+ slog.Debug("Number of favorite exceeds hard limit. Trimming.", slogs.Max, MaxFavoritesNS)
n.Favorites = n.Favorites[:MaxFavoritesNS]
}
}
diff --git a/internal/config/data/testdata/configs/aws_ct.yaml b/internal/config/data/testdata/configs/aws_ct.yaml
index 0f81b93b..a0f86f9e 100644
--- a/internal/config/data/testdata/configs/aws_ct.yaml
+++ b/internal/config/data/testdata/configs/aws_ct.yaml
@@ -9,4 +9,3 @@ k9s:
active: po
featureGates:
nodeShell: false
- portForwardAddress: localhost
diff --git a/internal/config/data/testdata/configs/ct-1-1.yaml b/internal/config/data/testdata/configs/ct-1-1.yaml
index 091b9071..e961fdbd 100644
--- a/internal/config/data/testdata/configs/ct-1-1.yaml
+++ b/internal/config/data/testdata/configs/ct-1-1.yaml
@@ -13,4 +13,3 @@ k9s:
active: dp
featureGates:
nodeShell: true
- portForwardAddress: localhost
diff --git a/internal/config/data/testdata/configs/ct-1-2.yaml b/internal/config/data/testdata/configs/ct-1-2.yaml
index e7bd28f5..863ac8ad 100644
--- a/internal/config/data/testdata/configs/ct-1-2.yaml
+++ b/internal/config/data/testdata/configs/ct-1-2.yaml
@@ -11,4 +11,3 @@ k9s:
active: po
featureGates:
nodeShell: false
- portForwardAddress: localhost
diff --git a/internal/config/data/testdata/configs/ct-2-1.yaml b/internal/config/data/testdata/configs/ct-2-1.yaml
index 5a2e25be..230b2a7e 100644
--- a/internal/config/data/testdata/configs/ct-2-1.yaml
+++ b/internal/config/data/testdata/configs/ct-2-1.yaml
@@ -12,4 +12,3 @@ k9s:
active: svc
featureGates:
nodeShell: true
- portForwardAddress: fred
diff --git a/internal/config/data/testdata/configs/def_ct.yaml b/internal/config/data/testdata/configs/def_ct.yaml
index a69eb346..9fcf13a2 100644
--- a/internal/config/data/testdata/configs/def_ct.yaml
+++ b/internal/config/data/testdata/configs/def_ct.yaml
@@ -9,4 +9,3 @@ k9s:
active: po
featureGates:
nodeShell: false
- portForwardAddress: localhost
diff --git a/internal/config/data/testdata/data/k9s/cl-1/ct-1-1/config.yaml b/internal/config/data/testdata/data/k9s/cl-1/ct-1-1/config.yaml
index 091b9071..e961fdbd 100644
--- a/internal/config/data/testdata/data/k9s/cl-1/ct-1-1/config.yaml
+++ b/internal/config/data/testdata/data/k9s/cl-1/ct-1-1/config.yaml
@@ -13,4 +13,3 @@ k9s:
active: dp
featureGates:
nodeShell: true
- portForwardAddress: localhost
diff --git a/internal/config/data/testdata/data/k9s/cl-1/ct-1-2/config.yaml b/internal/config/data/testdata/data/k9s/cl-1/ct-1-2/config.yaml
index e7bd28f5..863ac8ad 100644
--- a/internal/config/data/testdata/data/k9s/cl-1/ct-1-2/config.yaml
+++ b/internal/config/data/testdata/data/k9s/cl-1/ct-1-2/config.yaml
@@ -11,4 +11,3 @@ k9s:
active: po
featureGates:
nodeShell: false
- portForwardAddress: localhost
diff --git a/internal/config/data/testdata/data/k9s/cl-2/ct-2-1/config.yaml b/internal/config/data/testdata/data/k9s/cl-2/ct-2-1/config.yaml
index 5a2e25be..230b2a7e 100644
--- a/internal/config/data/testdata/data/k9s/cl-2/ct-2-1/config.yaml
+++ b/internal/config/data/testdata/data/k9s/cl-2/ct-2-1/config.yaml
@@ -12,4 +12,3 @@ k9s:
active: svc
featureGates:
nodeShell: true
- portForwardAddress: fred
diff --git a/internal/config/files.go b/internal/config/files.go
index 2b246d51..831a5739 100644
--- a/internal/config/files.go
+++ b/internal/config/files.go
@@ -7,13 +7,14 @@ import (
_ "embed"
"errors"
"io/fs"
+ "log/slog"
"os"
"path/filepath"
"github.com/derailed/k9s/internal/config/data"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/adrg/xdg"
- "github.com/rs/zerolog/log"
)
const (
@@ -126,19 +127,31 @@ func initK9sEnvLocs() error {
AppDumpsDir = filepath.Join(AppConfigDir, "screen-dumps")
if err := data.EnsureFullPath(AppDumpsDir, data.DefaultDirMod); err != nil {
- log.Warn().Err(err).Msgf("Unable to create screen-dumps dir: %s", AppDumpsDir)
+ slog.Warn("Unable to create screen-dumps dir",
+ slogs.Dir, AppDumpsDir,
+ slogs.Error, err,
+ )
}
AppBenchmarksDir = filepath.Join(AppConfigDir, "benchmarks")
if err := data.EnsureFullPath(AppBenchmarksDir, data.DefaultDirMod); err != nil {
- log.Warn().Err(err).Msgf("Unable to create benchmarks dir: %s", AppBenchmarksDir)
+ slog.Warn("Unable to create benchmarks dir",
+ slogs.Dir, AppBenchmarksDir,
+ slogs.Error, err,
+ )
}
AppSkinsDir = filepath.Join(AppConfigDir, "skins")
if err := data.EnsureFullPath(AppSkinsDir, data.DefaultDirMod); err != nil {
- log.Warn().Err(err).Msgf("Unable to create skins dir: %s", AppSkinsDir)
+ slog.Warn("Unable to create skins dir",
+ slogs.Dir, AppSkinsDir,
+ slogs.Error, err,
+ )
}
AppContextsDir = filepath.Join(AppConfigDir, "clusters")
if err := data.EnsureFullPath(AppContextsDir, data.DefaultDirMod); err != nil {
- log.Warn().Err(err).Msgf("Unable to create clusters dir: %s", AppContextsDir)
+ slog.Warn("Unable to create clusters dir",
+ slogs.Dir, AppContextsDir,
+ slogs.Error, err,
+ )
}
AppConfigFile = filepath.Join(AppConfigDir, data.MainConfigFile)
@@ -170,7 +183,7 @@ func initXDGLocs() error {
AppSkinsDir = filepath.Join(AppConfigDir, "skins")
if err := data.EnsureFullPath(AppSkinsDir, data.DefaultDirMod); err != nil {
- log.Warn().Err(err).Msgf("No skins dir detected")
+ slog.Warn("No skins dir detected", slogs.Error, err)
}
AppDumpsDir, err = xdg.StateFile(filepath.Join(AppName, "screen-dumps"))
@@ -180,7 +193,10 @@ func initXDGLocs() error {
AppBenchmarksDir, err = xdg.StateFile(filepath.Join(AppName, "benchmarks"))
if err != nil {
- log.Warn().Err(err).Msgf("No benchmarks dir detected")
+ slog.Warn("No benchmarks dir detected",
+ slogs.Dir, AppBenchmarksDir,
+ slogs.Error, err,
+ )
}
dataDir, err := xdg.DataFile(AppName)
@@ -189,7 +205,10 @@ func initXDGLocs() error {
}
AppContextsDir = filepath.Join(dataDir, "clusters")
if err := data.EnsureFullPath(AppContextsDir, data.DefaultDirMod); err != nil {
- log.Warn().Err(err).Msgf("No context dir detected")
+ slog.Warn("No context dir detected",
+ slogs.Dir, AppContextsDir,
+ slogs.Error, err,
+ )
}
return nil
diff --git a/internal/config/helpers.go b/internal/config/helpers.go
index 165885d2..cb0d7f4f 100644
--- a/internal/config/helpers.go
+++ b/internal/config/helpers.go
@@ -4,11 +4,17 @@
package config
import (
+ "log/slog"
"os"
"os/user"
"path/filepath"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
+)
+
+const (
+ envPFAddress = "K9S_DEFAULT_PF_ADDRESS"
+ defaultPortFwdAddress = "localhost"
)
// IsBoolSet checks if a bool ptr is set.
@@ -54,7 +60,16 @@ func MustK9sUser() string {
if envUsr != "" {
return envUsr
}
- log.Fatal().Err(err).Msg("Die on retrieving user info")
+ slog.Error("Die on retrieving user info", slogs.Error, err)
+ os.Exit(1)
}
return usr.Username
}
+
+func defaultPFAddress() string {
+ if a := os.Getenv(envPFAddress); a != "" {
+ return a
+ }
+
+ return defaultPortFwdAddress
+}
diff --git a/internal/config/hotkey.go b/internal/config/hotkey.go
index 65a651e4..b1695085 100644
--- a/internal/config/hotkey.go
+++ b/internal/config/hotkey.go
@@ -5,12 +5,13 @@ package config
import (
"errors"
- "fmt"
"io/fs"
+ "log/slog"
"os"
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/config/json"
+ "github.com/derailed/k9s/internal/slogs"
"gopkg.in/yaml.v2"
)
@@ -57,7 +58,10 @@ func (h HotKeys) LoadHotKeys(path string) error {
return err
}
if err := data.JSONValidator.Validate(json.HotkeysSchema, bb); err != nil {
- return fmt.Errorf("validation failed for %q: %w", path, err)
+ slog.Warn("Validation failed. Please update your config and restart.",
+ slogs.Path, path,
+ slogs.Error, err,
+ )
}
var hh HotKeys
diff --git a/internal/config/json/schemas/context.json b/internal/config/json/schemas/context.json
index b9545b1a..71a6a7dc 100644
--- a/internal/config/json/schemas/context.json
+++ b/internal/config/json/schemas/context.json
@@ -10,7 +10,6 @@
"cluster": { "type": "string" },
"readOnly": {"type": "boolean"},
"skin": { "type": "string" },
- "portForwardAddress": { "type": "string" },
"proxy": {
"oneOf": [
{ "type": "null" },
diff --git a/internal/config/json/schemas/k9s.json b/internal/config/json/schemas/k9s.json
index e6fc4d83..6c842cf7 100644
--- a/internal/config/json/schemas/k9s.json
+++ b/internal/config/json/schemas/k9s.json
@@ -15,6 +15,7 @@
"noExitOnCtrlC": { "type": "boolean" },
"skipLatestRevCheck": { "type": "boolean" },
"disablePodCounting": { "type": "boolean" },
+ "portForwardAddress": { "type": "string" },
"ui": {
"type": "object",
"additionalProperties": false,
diff --git a/internal/config/json/testdata/context/cool.yaml b/internal/config/json/testdata/context/cool.yaml
index fd2c6771..d37bc63f 100644
--- a/internal/config/json/testdata/context/cool.yaml
+++ b/internal/config/json/testdata/context/cool.yaml
@@ -12,4 +12,3 @@ k9s:
active: pod
featureGates:
nodeShell: false
- portForwardAddress: localhost
diff --git a/internal/config/json/testdata/context/toast.yaml b/internal/config/json/testdata/context/toast.yaml
index 997914b4..37d78d35 100644
--- a/internal/config/json/testdata/context/toast.yaml
+++ b/internal/config/json/testdata/context/toast.yaml
@@ -13,4 +13,3 @@ k9s:
fred: blee
featureGates:
nodeShell: false
- portForwardAddress: localhost
diff --git a/internal/config/json/validator.go b/internal/config/json/validator.go
index e9bb0211..5c6e44bd 100644
--- a/internal/config/json/validator.go
+++ b/internal/config/json/validator.go
@@ -8,9 +8,10 @@ import (
_ "embed"
"errors"
"fmt"
+ "log/slog"
"slices"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/xeipuuv/gojsonschema"
"gopkg.in/yaml.v3"
)
@@ -89,9 +90,15 @@ func NewValidator() *Validator {
func (v *Validator) register() {
v.loader = gojsonschema.NewSchemaLoader()
v.loader.Validate = true
+
+ clog := slog.With(slogs.Subsys, "schema")
+
for k, s := range v.schemas {
if err := v.loader.AddSchema(k, s); err != nil {
- log.Error().Err(err).Msgf("schema initialization failed: %q", k)
+ clog.Error("Schema initialization failed",
+ slogs.SchemaFile, k,
+ slogs.Error, err,
+ )
}
}
}
diff --git a/internal/config/k9s.go b/internal/config/k9s.go
index 5cc2ec41..8579b682 100644
--- a/internal/config/k9s.go
+++ b/internal/config/k9s.go
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io/fs"
+ "log/slog"
"net/http"
"net/url"
"os"
@@ -15,7 +16,7 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config/data"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
)
// K9s tracks K9s configuration options.
@@ -26,6 +27,7 @@ type K9s struct {
MaxConnRetry int `json:"maxConnRetry" yaml:"maxConnRetry"`
ReadOnly bool `json:"readOnly" yaml:"readOnly"`
NoExitOnCtrlC bool `json:"noExitOnCtrlC" yaml:"noExitOnCtrlC"`
+ PortForwardAddress string `yaml:"portForwardAddress"`
UI UI `json:"ui" yaml:"ui"`
SkipLatestRevCheck bool `json:"skipLatestRevCheck" yaml:"skipLatestRevCheck"`
DisablePodCounting bool `json:"disablePodCounting" yaml:"disablePodCounting"`
@@ -46,24 +48,40 @@ type K9s struct {
conn client.Connection
ks data.KubeSettings
mx sync.RWMutex
+ contextSwitch bool
}
// NewK9s create a new K9s configuration.
func NewK9s(conn client.Connection, ks data.KubeSettings) *K9s {
return &K9s{
- RefreshRate: defaultRefreshRate,
- MaxConnRetry: defaultMaxConnRetry,
- ScreenDumpDir: AppDumpsDir,
- Logger: NewLogger(),
- Thresholds: NewThreshold(),
- ShellPod: NewShellPod(),
- ImageScans: NewImageScans(),
- dir: data.NewDir(AppContextsDir),
- conn: conn,
- ks: ks,
+ RefreshRate: defaultRefreshRate,
+ MaxConnRetry: defaultMaxConnRetry,
+ ScreenDumpDir: AppDumpsDir,
+ Logger: NewLogger(),
+ Thresholds: NewThreshold(),
+ PortForwardAddress: defaultPFAddress(),
+ ShellPod: NewShellPod(),
+ ImageScans: NewImageScans(),
+ dir: data.NewDir(AppContextsDir),
+ conn: conn,
+ ks: ks,
}
}
+func (k *K9s) ToggleContextSwitch(b bool) {
+ k.mx.Lock()
+ defer k.mx.Unlock()
+
+ k.contextSwitch = b
+}
+
+func (k *K9s) getContextSwitch() bool {
+ k.mx.Lock()
+ defer k.mx.Unlock()
+
+ return k.contextSwitch
+}
+
func (k *K9s) resetConnection(conn client.Connection) {
k.mx.Lock()
defer k.mx.Unlock()
@@ -72,17 +90,15 @@ func (k *K9s) resetConnection(conn client.Connection) {
}
// Save saves the k9s config to disk.
-func (k *K9s) Save(force bool) error {
- if k.getActiveConfig() == nil {
- log.Warn().Msgf("Save failed. no active config detected")
- return nil
- }
+func (k *K9s) Save(contextName, clusterName string, force bool) error {
path := filepath.Join(
AppContextsDir,
- data.SanitizeContextSubpath(k.activeConfig.Context.GetClusterName(), k.getActiveContextName()),
+ data.SanitizeContextSubpath(clusterName, contextName),
data.MainConfigFile,
)
+
if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) || force {
+ slog.Debug("[CONFIG] Saving context config to disk", slogs.Path, path, slogs.Cluster, k.getActiveConfig().Context.GetClusterName(), slogs.Context, k.getActiveContextName())
return k.dir.Save(path, k.getActiveConfig())
}
@@ -168,12 +184,9 @@ func (k *K9s) ActiveContext() (*data.Context, error) {
if cfg := k.getActiveConfig(); cfg != nil && cfg.Context != nil {
return cfg.Context, nil
}
- ct, err := k.ActivateContext(k.getActiveContextName())
- if err != nil {
- return nil, err
- }
+ ct, err := k.ActivateContext(k.ActiveContextName())
- return ct, nil
+ return ct, err
}
func (k *K9s) setActiveConfig(c *data.Config) {
@@ -205,14 +218,14 @@ func (k *K9s) getActiveContextName() string {
}
// ActivateContext initializes the active context if not present.
-func (k *K9s) ActivateContext(n string) (*data.Context, error) {
- k.setActiveContextName(n)
- ct, err := k.ks.GetContext(n)
+func (k *K9s) ActivateContext(contextName string) (*data.Context, error) {
+ k.setActiveContextName(contextName)
+ ct, err := k.ks.GetContext(contextName)
if err != nil {
return nil, err
}
- cfg, err := k.dir.Load(n, ct)
+ cfg, err := k.dir.Load(contextName, ct)
if err != nil {
return nil, err
}
@@ -220,7 +233,7 @@ func (k *K9s) ActivateContext(n string) (*data.Context, error) {
if cfg.Context.Proxy != nil {
k.ks.SetProxy(func(*http.Request) (*url.URL, error) {
- log.Debug().Msgf("[Proxy]: %s", cfg.Context.Proxy.Address)
+ slog.Debug("Using proxy address", slogs.Address, cfg.Context.Proxy.Address)
return url.Parse(cfg.Context.Proxy.Address)
})
@@ -229,25 +242,25 @@ func (k *K9s) ActivateContext(n string) (*data.Context, error) {
// already has an API connection object so we just set the proxy to
// avoid recreation using client.InitConnection
k.conn.Config().SetProxy(func(*http.Request) (*url.URL, error) {
- log.Debug().Msgf("[Proxy]: %s", cfg.Context.Proxy.Address)
+ slog.Debug("Setting proxy address", slogs.Address, cfg.Context.Proxy.Address)
return url.Parse(cfg.Context.Proxy.Address)
})
if !k.conn.CheckConnectivity() {
- return nil, fmt.Errorf("unable to connect to context %q", n)
+ return nil, fmt.Errorf("unable to connect to context %q", contextName)
}
}
}
- k.Validate(k.conn, k.ks)
+ k.Validate(k.conn, contextName, ct.Cluster)
// If the context specifies a namespace, use it!
if ns := ct.Namespace; ns != client.BlankNamespace {
k.getActiveConfig().Context.Namespace.Active = ns
- } else if k.activeConfig.Context.Namespace.Active == "" {
+ } else if k.getActiveConfig().Context.Namespace.Active == "" {
k.getActiveConfig().Context.Namespace.Active = client.DefaultNamespace
}
if k.getActiveConfig().Context == nil {
- return nil, fmt.Errorf("context activation failed for: %s", n)
+ return nil, fmt.Errorf("context activation failed for: %s", contextName)
}
return k.getActiveConfig().Context, nil
@@ -255,6 +268,10 @@ func (k *K9s) ActivateContext(n string) (*data.Context, error) {
// Reload reloads the context config from disk.
func (k *K9s) Reload() error {
+ // Switching context skipping reload...
+ if k.getContextSwitch() {
+ return nil
+ }
ct, err := k.ks.GetContext(k.getActiveContextName())
if err != nil {
return err
@@ -265,7 +282,7 @@ func (k *K9s) Reload() error {
return err
}
k.setActiveConfig(cfg)
- k.getActiveConfig().Validate(k.conn, k.ks)
+ k.getActiveConfig().Validate(k.conn, k.getActiveContextName(), ct.Cluster)
return nil
}
@@ -340,7 +357,7 @@ func (k *K9s) IsReadOnly() bool {
}
// Validate the current configuration.
-func (k *K9s) Validate(c client.Connection, ks data.KubeSettings) {
+func (k *K9s) Validate(c client.Connection, contextName, clusterName string) {
if k.RefreshRate <= 0 {
k.RefreshRate = defaultRefreshRate
}
@@ -348,16 +365,21 @@ func (k *K9s) Validate(c client.Connection, ks data.KubeSettings) {
k.MaxConnRetry = defaultMaxConnRetry
}
+ if a := os.Getenv(envPFAddress); a != "" {
+ k.PortForwardAddress = a
+ }
+ if k.PortForwardAddress == "" {
+ k.PortForwardAddress = defaultPFAddress()
+ }
+
if k.getActiveConfig() == nil {
- if n, err := ks.CurrentContextName(); err == nil {
- _, _ = k.ActivateContext(n)
- }
+ _, _ = k.ActivateContext(contextName)
}
k.ShellPod = k.ShellPod.Validate()
k.Logger = k.Logger.Validate()
k.Thresholds = k.Thresholds.Validate()
if cfg := k.getActiveConfig(); cfg != nil {
- cfg.Validate(c, ks)
+ cfg.Validate(c, contextName, clusterName)
}
}
diff --git a/internal/config/mock/test_helpers.go b/internal/config/mock/test_helpers.go
index c8f954fb..952de7f8 100644
--- a/internal/config/mock/test_helpers.go
+++ b/internal/config/mock/test_helpers.go
@@ -36,6 +36,9 @@ func EnsureDir(d string) error {
}
func NewMockConfig() *config.Config {
+ if _, err := os.Stat("/tmp/test"); errors.Is(err, os.ErrExist) {
+ os.RemoveAll("/tmp/test")
+ }
config.AppContextsDir = "/tmp/test"
cl, ct := "cl-1", "ct-1-1"
flags := genericclioptions.ConfigFlags{
diff --git a/internal/config/plugin.go b/internal/config/plugin.go
index fc14d1fc..2f6773e2 100644
--- a/internal/config/plugin.go
+++ b/internal/config/plugin.go
@@ -7,14 +7,15 @@ import (
"errors"
"fmt"
"io/fs"
+ "log/slog"
"os"
"path/filepath"
"strings"
+ "github.com/adrg/xdg"
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/config/json"
-
- "github.com/adrg/xdg"
+ "github.com/derailed/k9s/internal/slogs"
"gopkg.in/yaml.v2"
)
@@ -113,7 +114,10 @@ func (p *Plugins) load(path string) error {
return err
}
if err := data.JSONValidator.Validate(json.PluginsSchema, bb); err != nil {
- return fmt.Errorf("validation failed for %q: %w", path, err)
+ slog.Warn("Validation failed. Please update your config and restart!",
+ slogs.Path, path,
+ slogs.Error, err,
+ )
}
var pp Plugins
if err := yaml.Unmarshal(bb, &pp); err != nil {
diff --git a/internal/config/testdata/configs/default.yaml b/internal/config/testdata/configs/default.yaml
index 937972f0..bf3a9f91 100644
--- a/internal/config/testdata/configs/default.yaml
+++ b/internal/config/testdata/configs/default.yaml
@@ -5,6 +5,7 @@ k9s:
maxConnRetry: 5
readOnly: false
noExitOnCtrlC: false
+ portForwardAddress: localhost
ui:
enableMouse: false
headless: false
diff --git a/internal/config/testdata/configs/expected.yaml b/internal/config/testdata/configs/expected.yaml
index e7c20911..b83455ad 100644
--- a/internal/config/testdata/configs/expected.yaml
+++ b/internal/config/testdata/configs/expected.yaml
@@ -5,6 +5,7 @@ k9s:
maxConnRetry: 5
readOnly: true
noExitOnCtrlC: false
+ portForwardAddress: localhost
ui:
enableMouse: false
headless: false
diff --git a/internal/config/testdata/configs/k9s.yaml b/internal/config/testdata/configs/k9s.yaml
index c33ed9f6..5af410fd 100644
--- a/internal/config/testdata/configs/k9s.yaml
+++ b/internal/config/testdata/configs/k9s.yaml
@@ -5,6 +5,7 @@ k9s:
maxConnRetry: 5
readOnly: false
noExitOnCtrlC: false
+ portForwardAddress: localhost
ui:
enableMouse: false
headless: false
diff --git a/internal/config/testdata/views/views.yaml b/internal/config/testdata/views/views.yaml
index b6debac3..5ce6a64d 100644
--- a/internal/config/testdata/views/views.yaml
+++ b/internal/config/testdata/views/views.yaml
@@ -5,3 +5,15 @@ views:
- NAME
- AGE
- IP
+
+ v1/pods@default:
+ columns:
+ - NAME
+ - IP
+ - AGE
+
+ v1/pods@ns*:
+ columns:
+ - AGE
+ - NAME
+ - IP
diff --git a/internal/config/views.go b/internal/config/views.go
index 86b1ef3c..11b3ceae 100644
--- a/internal/config/views.go
+++ b/internal/config/views.go
@@ -8,13 +8,16 @@ import (
"errors"
"fmt"
"io/fs"
+ "log/slog"
+ "maps"
"os"
+ "regexp"
"slices"
"strings"
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/config/json"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"gopkg.in/yaml.v2"
)
@@ -22,6 +25,9 @@ import (
type ViewConfigListener interface {
// ViewSettingsChanged notifies listener the view configuration changed.
ViewSettingsChanged(*ViewSetting)
+
+ // GetNamespace return the view namespace
+ GetNamespace() string
}
// ViewSetting represents a view configuration.
@@ -50,13 +56,19 @@ func (v *ViewSetting) SortCol() (string, bool, error) {
return tt[0], tt[1] == "asc", nil
}
+// Equals checks if two view settings are equal.
func (v *ViewSetting) Equals(vs *ViewSetting) bool {
+ if v == nil && vs == nil {
+ return true
+ }
if v == nil || vs == nil {
return false
}
+
if c := slices.Compare(v.Columns, vs.Columns); c != 0 {
return false
}
+
return cmp.Compare(v.SortColumn, vs.SortColumn) == 0
}
@@ -91,7 +103,10 @@ func (v *CustomView) Load(path string) error {
return err
}
if err := data.JSONValidator.Validate(json.ViewsSchema, bb); err != nil {
- return fmt.Errorf("validation failed for %q: %w", path, err)
+ slog.Warn("Validation failed. Please update your config and restart!",
+ slogs.Path, path,
+ slogs.Error, err,
+ )
}
var in CustomView
if err := yaml.Unmarshal(bb, &in); err != nil {
@@ -116,11 +131,41 @@ func (v *CustomView) RemoveListener(gvr string) {
func (v *CustomView) fireConfigChanged() {
for gvr, list := range v.listeners {
- if vs, ok := v.Views[gvr]; ok {
- log.Debug().Msgf("Reloading custom view settings for %s", gvr)
- list.ViewSettingsChanged(&vs)
- } else {
+ 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) getVS(gvr, ns string) *ViewSetting {
+ k := gvr
+ if ns != "" {
+ k += "@" + ns
+ }
+
+ for key := range maps.Keys(v.Views) {
+ if !strings.HasPrefix(key, gvr) {
+ continue
+ }
+
+ switch {
+ case key == k:
+ vs := v.Views[key]
+ return &vs
+ case strings.Contains(key, "@"):
+ tt := strings.Split(key, "@")
+ if len(tt) != 2 {
+ break
+ }
+ if rx, err := regexp.Compile(tt[1]); err == nil && rx.MatchString(k) {
+ vs := v.Views[key]
+ return &vs
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/internal/config/views_int_test.go b/internal/config/views_int_test.go
new file mode 100644
index 00000000..63e532ce
--- /dev/null
+++ b/internal/config/views_int_test.go
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright Authors of K9s
+
+package config
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCustomView_getVS(t *testing.T) {
+ uu := map[string]struct {
+ cv *CustomView
+ gvr, ns string
+ e *ViewSetting
+ }{
+ "empty": {},
+
+ "gvr": {
+ gvr: "v1/pods",
+ e: &ViewSetting{
+ Columns: []string{"NAMESPACE", "NAME", "AGE", "IP"},
+ },
+ },
+
+ "gvr+ns": {
+ gvr: "v1/pods",
+ ns: "default",
+ e: &ViewSetting{
+ Columns: []string{"NAME", "IP", "AGE"},
+ },
+ },
+
+ "rx": {
+ gvr: "v1/pods",
+ ns: "ns-fred",
+ e: &ViewSetting{
+ Columns: []string{"AGE", "NAME", "IP"},
+ },
+ },
+
+ "toast-no-ns": {
+ gvr: "v1/pods",
+ ns: "zorg",
+ },
+
+ "toast-no-res": {
+ gvr: "v1/services",
+ ns: "zorg",
+ },
+ }
+
+ v := NewCustomView()
+ assert.NoError(t, v.Load("testdata/views/views.yaml"))
+ for k, u := range uu {
+ t.Run(k, func(t *testing.T) {
+ assert.Equal(t, u.e, v.getVS(u.gvr, u.ns))
+ })
+ }
+}
diff --git a/internal/config/views_test.go b/internal/config/views_test.go
index 902eda50..b5e22cb0 100644
--- a/internal/config/views_test.go
+++ b/internal/config/views_test.go
@@ -4,37 +4,117 @@
package config_test
import (
+ "log/slog"
"testing"
"github.com/derailed/k9s/internal/config"
"github.com/stretchr/testify/assert"
)
-func TestViewSettingsLoad(t *testing.T) {
- cfg := config.NewCustomView()
-
- assert.Nil(t, cfg.Load("testdata/views/views.yaml"))
- assert.Equal(t, 1, len(cfg.Views))
- assert.Equal(t, 4, len(cfg.Views["v1/pods"].Columns))
+func init() {
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
-func TestViewSetting_Equals(t *testing.T) {
- tests := []struct {
- v1, v2 *config.ViewSetting
- equals bool
+func TestCustomViewLoad(t *testing.T) {
+ uu := map[string]struct {
+ cv *config.CustomView
+ path string
+ key string
+ e []string
}{
- {nil, nil, false},
- {&config.ViewSetting{}, nil, false},
- {nil, &config.ViewSetting{}, false},
- {&config.ViewSetting{}, &config.ViewSetting{}, true},
- {&config.ViewSetting{Columns: []string{"A"}}, &config.ViewSetting{}, false},
- {&config.ViewSetting{Columns: []string{"A"}}, &config.ViewSetting{Columns: []string{"A"}}, true},
- {&config.ViewSetting{Columns: []string{"A"}}, &config.ViewSetting{Columns: []string{"B"}}, false},
- {&config.ViewSetting{SortColumn: "A"}, &config.ViewSetting{SortColumn: "B"}, false},
- {&config.ViewSetting{SortColumn: "A"}, &config.ViewSetting{SortColumn: "A"}, true},
+ "empty": {},
+
+ "gvr": {
+ path: "testdata/views/views.yaml",
+ key: "v1/pods",
+ e: []string{"NAMESPACE", "NAME", "AGE", "IP"},
+ },
+
+ "gvr+ns": {
+ path: "testdata/views/views.yaml",
+ key: "v1/pods@default",
+ e: []string{"NAME", "IP", "AGE"},
+ },
}
- for _, tt := range tests {
- assert.Equalf(t, tt.equals, tt.v1.Equals(tt.v2), "%#v and %#v", tt.v1, tt.v2)
+ for k, u := range uu {
+ t.Run(k, func(t *testing.T) {
+ cfg := config.NewCustomView()
+
+ assert.NoError(t, cfg.Load(u.path))
+ assert.Equal(t, u.e, cfg.Views[u.key].Columns)
+ })
+ }
+}
+
+func TestViewSettingEquals(t *testing.T) {
+ uu := map[string]struct {
+ v1, v2 *config.ViewSetting
+ e bool
+ }{
+ "v1-nil-v2-nil": {
+ e: true,
+ },
+
+ "v1-v2-empty": {
+ v1: new(config.ViewSetting),
+ v2: new(config.ViewSetting),
+ e: true,
+ },
+
+ "v1-nil": {
+ v1: new(config.ViewSetting),
+ },
+
+ "nil-v2": {
+ v2: new(config.ViewSetting),
+ },
+
+ "v1-v2-blank": {
+ v1: &config.ViewSetting{
+ Columns: []string{"A"},
+ },
+ v2: new(config.ViewSetting),
+ },
+
+ "v1-v2-nil": {
+ v1: &config.ViewSetting{
+ Columns: []string{"A"},
+ },
+ },
+
+ "same": {
+ v1: &config.ViewSetting{
+ Columns: []string{"A", "B", "C"},
+ },
+ v2: &config.ViewSetting{
+ Columns: []string{"A", "B", "C"},
+ },
+ e: true,
+ },
+
+ "order": {
+ v1: &config.ViewSetting{
+ Columns: []string{"C", "A", "B"},
+ },
+ v2: &config.ViewSetting{
+ Columns: []string{"A", "B", "C"},
+ },
+ },
+
+ "delta": {
+ v1: &config.ViewSetting{
+ Columns: []string{"A", "B", "C"},
+ },
+ v2: &config.ViewSetting{
+ Columns: []string{"B"},
+ },
+ },
+ }
+
+ for k, u := range uu {
+ t.Run(k, func(t *testing.T) {
+ assert.Equalf(t, u.e, u.v1.Equals(u.v2), "%#v and %#v", u.v1, u.v2)
+ })
}
}
diff --git a/internal/dao/cluster.go b/internal/dao/cluster.go
index dab4fbf1..8b0b1b7c 100644
--- a/internal/dao/cluster.go
+++ b/internal/dao/cluster.go
@@ -6,12 +6,14 @@ package dao
import (
"context"
"errors"
+ "fmt"
+ "log/slog"
"sync"
"time"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
)
// RefScanner represents a resource reference scanner.
@@ -55,7 +57,7 @@ func scanners() map[string]RefScanner {
// ScanForRefs scans cluster resources for resource references.
func ScanForRefs(ctx context.Context, f Factory) (Refs, error) {
defer func(t time.Time) {
- log.Debug().Msgf("Cluster Scan %v", time.Since(t))
+ slog.Debug("Cluster Scan", slogs.Elapsed, time.Since(t))
}(time.Now())
gvr, ok := ctx.Value(internal.KeyGVR).(client.GVR)
@@ -68,7 +70,7 @@ func ScanForRefs(ctx context.Context, f Factory) (Refs, error) {
}
wait, ok := ctx.Value(internal.KeyWait).(bool)
if !ok {
- log.Error().Msgf("expecting Context Wait Key")
+ slog.Warn("Expecting context Wait key. Using default")
}
ss := scanners()
@@ -81,7 +83,10 @@ func ScanForRefs(ctx context.Context, f Factory) (Refs, error) {
s.Init(f, client.NewGVR(kind))
refs, err := s.Scan(ctx, gvr, fqn, wait)
if err != nil {
- log.Error().Err(err).Msgf("scan failed for %T", s)
+ slog.Error("Reference scan failed for",
+ slogs.RefType, fmt.Sprintf("%T", s),
+ slogs.Error, err,
+ )
return
}
select {
@@ -108,7 +113,7 @@ func ScanForRefs(ctx context.Context, f Factory) (Refs, error) {
// ScanForSARefs scans cluster resources for serviceaccount refs.
func ScanForSARefs(ctx context.Context, f Factory) (Refs, error) {
defer func(t time.Time) {
- log.Debug().Msgf("SA Cluster Scan %v", time.Since(t))
+ slog.Debug("Time to scan Cluster SA", slogs.Elapsed, time.Since(t))
}(time.Now())
fqn, ok := ctx.Value(internal.KeyPath).(string)
@@ -130,7 +135,10 @@ func ScanForSARefs(ctx context.Context, f Factory) (Refs, error) {
s.Init(f, client.NewGVR(kind))
refs, err := s.ScanSA(ctx, fqn, wait)
if err != nil {
- log.Error().Err(err).Msgf("scan failed for %T", s)
+ slog.Error("ServiceAccount scan failed",
+ slogs.RefType, fmt.Sprintf("%T", s),
+ slogs.Error, err,
+ )
return
}
select {
diff --git a/internal/dao/context.go b/internal/dao/context.go
index 671ed34f..de655be2 100644
--- a/internal/dao/context.go
+++ b/internal/dao/context.go
@@ -5,10 +5,11 @@ package dao
import (
"context"
+ "log/slog"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"k8s.io/apimachinery/pkg/runtime"
)
@@ -53,7 +54,7 @@ func (c *Context) List(_ context.Context, _ string) ([]runtime.Object, error) {
func (c *Context) MustCurrentContextName() string {
cl, err := c.config().CurrentContextName()
if err != nil {
- log.Fatal().Err(err).Msg("Fetching current context")
+ slog.Error("Fetching current context", slogs.Error, err)
}
return cl
}
diff --git a/internal/dao/cronjob.go b/internal/dao/cronjob.go
index 31533495..ba5514c3 100644
--- a/internal/dao/cronjob.go
+++ b/internal/dao/cronjob.go
@@ -7,10 +7,11 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
batchv1 "k8s.io/api/batch/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -200,7 +201,10 @@ func (c *CronJob) Scan(ctx context.Context, gvr client.GVR, fqn string, wait boo
case SecGVR:
found, err := hasSecret(c.Factory, &cj.Spec.JobTemplate.Spec.Template.Spec, cj.Namespace, n, wait)
if err != nil {
- log.Warn().Err(err).Msgf("locate secret %q", fqn)
+ slog.Warn("Failed to locate secret",
+ slogs.FQN, fqn,
+ slogs.Error, err,
+ )
continue
}
if !found {
diff --git a/internal/dao/describe.go b/internal/dao/describe.go
index d990dfd4..541f453c 100644
--- a/internal/dao/describe.go
+++ b/internal/dao/describe.go
@@ -4,8 +4,10 @@
package dao
import (
+ "log/slog"
+
"github.com/derailed/k9s/internal/client"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"k8s.io/kubectl/pkg/describe"
)
@@ -14,13 +16,19 @@ func Describe(c client.Connection, gvr client.GVR, path string) (string, error)
mapper := RestMapper{Connection: c}
m, err := mapper.ToRESTMapper()
if err != nil {
- log.Error().Err(err).Msgf("No REST mapper for resource %s", gvr)
+ slog.Error("No REST mapper for resource",
+ slogs.GVR, gvr,
+ slogs.Error, err,
+ )
return "", err
}
gvk, err := m.KindFor(gvr.GVR())
if err != nil {
- log.Error().Err(err).Msgf("No GVK for resource %s", gvr)
+ slog.Error("No GVK for resource %s",
+ slogs.GVR, gvr,
+ slogs.Error, err,
+ )
return "", err
}
@@ -30,12 +38,19 @@ func Describe(c client.Connection, gvr client.GVR, path string) (string, error)
}
mapping, err := mapper.ResourceFor(gvr.AsResourceName(), gvk.Kind)
if err != nil {
- log.Error().Err(err).Msgf("Unable to find mapper for %s %s", gvr, n)
+ slog.Error("Unable to find mapper",
+ slogs.GVR, gvr,
+ slogs.ResName, n,
+ slogs.Error, err,
+ )
return "", err
}
d, err := describe.Describer(c.Config().Flags(), mapping)
if err != nil {
- log.Error().Err(err).Msgf("Unable to find describer for %#v", mapping)
+ slog.Error("Unable to find describer",
+ slogs.GVR, gvr.AsResourceName(),
+ slogs.Error, err,
+ )
return "", err
}
diff --git a/internal/dao/dp.go b/internal/dao/dp.go
index 01f9513d..c4d432a3 100644
--- a/internal/dao/dp.go
+++ b/internal/dao/dp.go
@@ -7,10 +7,11 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -215,7 +216,10 @@ func (d *Deployment) Scan(ctx context.Context, gvr client.GVR, fqn string, wait
case SecGVR:
found, err := hasSecret(d.Factory, &dp.Spec.Template.Spec, dp.Namespace, n, wait)
if err != nil {
- log.Warn().Err(err).Msgf("scanning secret %q", fqn)
+ slog.Warn("Fail to locate secret",
+ slogs.FQN, fqn,
+ slogs.Error, err,
+ )
continue
}
if !found {
diff --git a/internal/dao/ds.go b/internal/dao/ds.go
index 3bbd7827..dcbadaed 100644
--- a/internal/dao/ds.go
+++ b/internal/dao/ds.go
@@ -7,13 +7,14 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"strings"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/watch"
- "github.com/rs/zerolog/log"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -235,7 +236,10 @@ func (d *DaemonSet) Scan(ctx context.Context, gvr client.GVR, fqn string, wait b
case SecGVR:
found, err := hasSecret(d.Factory, &ds.Spec.Template.Spec, ds.Namespace, n, wait)
if err != nil {
- log.Warn().Err(err).Msgf("locate secret %q", fqn)
+ slog.Warn("Unable to locate secret",
+ slogs.FQN, fqn,
+ slogs.Error, err,
+ )
continue
}
if !found {
diff --git a/internal/dao/helm_chart.go b/internal/dao/helm_chart.go
index 819a9c63..3a20d918 100644
--- a/internal/dao/helm_chart.go
+++ b/internal/dao/helm_chart.go
@@ -6,11 +6,12 @@ package dao
import (
"context"
"fmt"
+ "log/slog"
"os"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render/helm"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"gopkg.in/yaml.v2"
"helm.sh/helm/v3/pkg/action"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -163,6 +164,9 @@ func ensureHelmConfig(flags *genericclioptions.ConfigFlags, ns string) (*action.
return cfg, err
}
-func helmLogger(fmt string, args ...interface{}) {
- log.Debug().Msgf("[Helm] "+fmt, args...)
+func helmLogger(fmat string, args ...interface{}) {
+ slog.Debug("Log",
+ slogs.Log, fmt.Sprintf(fmat, args...),
+ slogs.Subsys, "helm",
+ )
}
diff --git a/internal/dao/helpers.go b/internal/dao/helpers.go
index 552d3137..734e4757 100644
--- a/internal/dao/helpers.go
+++ b/internal/dao/helpers.go
@@ -7,10 +7,11 @@ import (
"bytes"
"errors"
"fmt"
+ "log/slog"
"math"
"github.com/derailed/k9s/internal/client"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -35,7 +36,10 @@ func GetDefaultContainer(m metav1.ObjectMeta, spec v1.PodSpec) (string, bool) {
return defaultContainer, true
}
}
- log.Warn().Msg(defaultContainer + " container not found. " + defaultContainerAnnotation + " annotation will be ignored")
+ slog.Warn("Container not found. Annotation ignored",
+ slogs.CO, defaultContainer,
+ slogs.Annotation, defaultContainerAnnotation,
+ )
return "", false
}
@@ -43,7 +47,7 @@ func GetDefaultContainer(m metav1.ObjectMeta, spec v1.PodSpec) (string, bool) {
func extractFQN(o runtime.Object) string {
u, ok := o.(*unstructured.Unstructured)
if !ok {
- log.Error().Err(fmt.Errorf("expecting unstructured but got %T", o))
+ slog.Error("Expecting unstructured", slogs.ResType, fmt.Sprintf("%T", o))
return client.NA
}
@@ -93,7 +97,7 @@ func ToYAML(o runtime.Object, showManaged bool) (string, error) {
}
err := p.PrintObj(o, &buff)
if err != nil {
- log.Error().Msgf("Marshal Error %v", err)
+ slog.Error("Marshal failed", slogs.Error, err)
return "", err
}
diff --git a/internal/dao/job.go b/internal/dao/job.go
index 0c3ba1a1..5afb933c 100644
--- a/internal/dao/job.go
+++ b/internal/dao/job.go
@@ -7,11 +7,12 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
batchv1 "k8s.io/api/batch/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
@@ -159,7 +160,10 @@ func (j *Job) Scan(ctx context.Context, gvr client.GVR, fqn string, wait bool) (
case SecGVR:
found, err := hasSecret(j.Factory, &job.Spec.Template.Spec, job.Namespace, n, wait)
if err != nil {
- log.Warn().Err(err).Msgf("locate secret %q", fqn)
+ slog.Warn("Locate secret failed",
+ slogs.FQN, fqn,
+ slogs.Error, err,
+ )
continue
}
if !found {
diff --git a/internal/dao/log_items_test.go b/internal/dao/log_items_test.go
index 787fddfe..a4f08214 100644
--- a/internal/dao/log_items_test.go
+++ b/internal/dao/log_items_test.go
@@ -5,16 +5,16 @@ package dao_test
import (
"fmt"
+ "log/slog"
"testing"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.FatalLevel)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestLogItemsFilter(t *testing.T) {
diff --git a/internal/dao/node.go b/internal/dao/node.go
index d348255b..09795372 100644
--- a/internal/dao/node.go
+++ b/internal/dao/node.go
@@ -8,11 +8,12 @@ import (
"errors"
"fmt"
"io"
+ "log/slog"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
@@ -37,16 +38,23 @@ type Node struct {
}
// ToggleCordon toggles cordon/uncordon a node.
-func (n *Node) ToggleCordon(path string, cordon bool) error {
- log.Debug().Msgf("CORDON %q::%t -- %q", path, cordon, n.gvr.GVK())
- o, err := FetchNode(context.Background(), n.Factory, path)
+func (n *Node) ToggleCordon(fqn string, cordon bool) error {
+ slog.Debug("Toggle cordon on node",
+ slogs.GVR, n.GVR(),
+ slogs.FQN, fqn,
+ slogs.Bool, cordon,
+ )
+ o, err := FetchNode(context.Background(), n.Factory, fqn)
if err != nil {
return err
}
h, err := drain.NewCordonHelperFromRuntimeObject(o, scheme.Scheme, n.gvr.GVK())
if err != nil {
- log.Debug().Msgf("BOOM %v", err)
+ slog.Debug("Fail to toggle cordon on node",
+ slogs.FQN, fqn,
+ slogs.Error, err,
+ )
return err
}
@@ -174,7 +182,10 @@ func (n *Node) List(ctx context.Context, ns string) ([]runtime.Object, error) {
if shouldCountPods {
podCount, err = n.CountPods(name)
if err != nil {
- log.Error().Err(err).Msgf("unable to get pods count for %s", name)
+ slog.Error("Unable to get pods count",
+ slogs.ResName, name,
+ slogs.Error, err,
+ )
}
}
res = append(res, &render.NodeWithMetrics{
diff --git a/internal/dao/ofaas.go b/internal/dao/ofaas.go
deleted file mode 100644
index 0030aa4e..00000000
--- a/internal/dao/ofaas.go
+++ /dev/null
@@ -1,221 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-// Copyright Authors of K9s
-
-package dao
-
-// BOZO!! Revamp with latest
-
-// import (
-// "bytes"
-// "context"
-// "encoding/json"
-// "errors"
-// "fmt"
-// "io"
-// "net/http"
-// "net/url"
-// "os"
-// "path"
-// "strings"
-// "time"
-
-// "github.com/derailed/k9s/internal/client"
-// "github.com/derailed/k9s/internal/render"
-// "github.com/openfaas/faas-cli/proxy"
-// "github.com/openfaas/faas/gateway/requests"
-// "github.com/rs/zerolog/log"
-// "k8s.io/apimachinery/pkg/runtime"
-// "sigs.k8s.io/yaml"
-// )
-
-// const (
-// oFaasGatewayEnv = "OPENFAAS_GATEWAY"
-// oFaasJWTTokenEnv = "OPENFAAS_JWT_TOKEN"
-// oFaasTLSInsecure = "OPENFAAS_TLS_INSECURE"
-// )
-
-// var (
-// _ Accessor = (*OpenFaas)(nil)
-// _ Nuker = (*OpenFaas)(nil)
-// _ Describer = (*OpenFaas)(nil)
-// )
-
-// // OpenFaas represents a faas gateway connection.
-// type OpenFaas struct {
-// NonResource
-// }
-
-// // IsOpenFaasEnabled returns true if a gateway url is set in the environment.
-// func IsOpenFaasEnabled() bool {
-// return os.Getenv(oFaasGatewayEnv) != ""
-// }
-
-// func getOpenFAASFlags() (string, string, bool) {
-// gw, token := os.Getenv(oFaasGatewayEnv), os.Getenv(oFaasJWTTokenEnv)
-// tlsInsecure := false
-// if os.Getenv(oFaasTLSInsecure) == "true" {
-// tlsInsecure = true
-// }
-
-// return gw, token, tlsInsecure
-// }
-
-// // Get returns a function by name.
-// func (f *OpenFaas) Get(ctx context.Context, path string) (runtime.Object, error) {
-// ns, n := client.Namespaced(path)
-
-// oo, err := f.List(ctx, ns)
-// if err != nil {
-// return nil, err
-// }
-
-// var found runtime.Object
-// for _, o := range oo {
-// r, ok := o.(render.OpenFaasRes)
-// if !ok {
-// continue
-// }
-// if r.Function.Name == n {
-// found = o
-// break
-// }
-// }
-
-// if found == nil {
-// return nil, fmt.Errorf("unable to locate function %q", path)
-// }
-
-// return found, nil
-// }
-
-// // List returns a collection of functions.
-// func (f *OpenFaas) List(_ context.Context, ns string) ([]runtime.Object, error) {
-// if !IsOpenFaasEnabled() {
-// return nil, errors.New("OpenFAAS is not enabled on this cluster")
-// }
-
-// gw, token, tls := getOpenFAASFlags()
-// ff, err := proxy.ListFunctionsToken(gw, tls, token, ns)
-// if err != nil {
-// return nil, err
-// }
-
-// oo := make([]runtime.Object, 0, len(ff))
-// for _, f := range ff {
-// oo = append(oo, render.OpenFaasRes{Function: f})
-// }
-
-// return oo, nil
-// }
-
-// // Delete removes a function.
-// func (f *OpenFaas) Delete(path string, _, _ bool) error {
-// gw, token, tls := getOpenFAASFlags()
-// ns, n := client.Namespaced(path)
-
-// // BOZO!! openfaas spews to stdout. Not good for us...
-// return deleteFunctionToken(gw, n, tls, token, ns)
-// }
-
-// // ToYAML dumps a function to yaml.
-// func (f *OpenFaas) ToYAML(path string, _ bool) (string, error) {
-// return f.Describe(path)
-// }
-
-// // Describe describes a function.
-// func (f *OpenFaas) Describe(path string) (string, error) {
-// o, err := f.Get(context.Background(), path)
-// if err != nil {
-// return "", err
-// }
-
-// fn, ok := o.(render.OpenFaasRes)
-// if !ok {
-// return "", fmt.Errorf("expecting OpenFaasRes but got %T", o)
-// }
-
-// raw, err := json.Marshal(fn)
-// if err != nil {
-// return "", err
-// }
-
-// bytes, err := yaml.JSONToYAML(raw)
-// if err != nil {
-// return "", err
-// }
-
-// return string(bytes), nil
-// }
-
-// // BOZO!! Meow! openfaas fn prints to stdout have to dup ;(.
-// func deleteFunctionToken(gateway string, functionName string, tlsInsecure bool, token string, namespace string) error {
-// defaultCommandTimeout := 60 * time.Second
-
-// gateway = strings.TrimRight(gateway, "/")
-// delReq := requests.DeleteFunctionRequest{FunctionName: functionName}
-// reqBytes, _ := json.Marshal(&delReq)
-// reader := bytes.NewReader(reqBytes)
-
-// c := proxy.MakeHTTPClient(&defaultCommandTimeout, tlsInsecure)
-
-// deleteEndpoint, err := createSystemEndpoint(gateway, namespace)
-// if err != nil {
-// return err
-// }
-
-// req, err := http.NewRequestWithContext(context.Background(), "DELETE", deleteEndpoint, reader)
-// if err != nil {
-// return err
-// }
-// req.Header.Set("Content-Type", "application/json")
-
-// if len(token) > 0 {
-// proxy.SetToken(req, token)
-// } else {
-// proxy.SetAuth(req, gateway)
-// }
-
-// delRes, delErr := c.Do(req)
-// if delErr != nil {
-// return delErr
-// }
-
-// if delRes.Body != nil {
-// defer func() {
-// if err := delRes.Body.Close(); err != nil {
-// log.Error().Err(err).Msgf("closing delete-gtw body")
-// }
-// }()
-// }
-
-// switch delRes.StatusCode {
-// case http.StatusOK, http.StatusCreated, http.StatusAccepted:
-// return nil
-// case http.StatusNotFound:
-// return fmt.Errorf("no function named %s found", functionName)
-// case http.StatusUnauthorized:
-// return fmt.Errorf("unauthorized access, run \"faas-cli login\" to setup authentication for this server")
-// default:
-// bytesOut, err := io.ReadAll(delRes.Body)
-// if err != nil {
-// return err
-// }
-// return fmt.Errorf("server returned unexpected status code %d %s", delRes.StatusCode, string(bytesOut))
-// }
-// }
-
-// func createSystemEndpoint(gateway, namespace string) (string, error) {
-// const systemPath = "/system/functions"
-
-// gatewayURL, err := url.Parse(gateway)
-// if err != nil {
-// return "", fmt.Errorf("invalid gateway URL: %w", err)
-// }
-// gatewayURL.Path = path.Join(gatewayURL.Path, systemPath)
-// if len(namespace) > 0 {
-// q := gatewayURL.Query()
-// q.Set("namespace", namespace)
-// gatewayURL.RawQuery = q.Encode()
-// }
-// return gatewayURL.String(), nil
-// }
diff --git a/internal/dao/pod.go b/internal/dao/pod.go
index f4ee453d..2ed3f6bd 100644
--- a/internal/dao/pod.go
+++ b/internal/dao/pod.go
@@ -9,15 +9,16 @@ import (
"errors"
"fmt"
"io"
+ "log/slog"
"sync"
"time"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/watch"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -287,7 +288,10 @@ func (p *Pod) Scan(ctx context.Context, gvr client.GVR, fqn string, wait bool) (
case SecGVR:
found, err := hasSecret(p.Factory, &pod.Spec, pod.Namespace, n, wait)
if err != nil {
- log.Warn().Err(err).Msgf("locate secret %q", fqn)
+ slog.Warn("Locate secret failed",
+ slogs.FQN, fqn,
+ slogs.Error, err,
+ )
continue
}
if !found {
@@ -332,30 +336,33 @@ func tailLogs(ctx context.Context, logger Logger, opts *LogOptions) LogChan {
go func() {
defer wg.Done()
podOpts := opts.ToPodLogOptions()
- var stream io.ReadCloser
for r := 0; r < logRetryCount; r++ {
- var e error
req, err := logger.Logs(opts.Path, podOpts)
if err == nil {
// This call will block if nothing is in the stream!!
- if stream, err = req.Stream(ctx); err == nil {
+ if stream, e := req.Stream(ctx); e == nil {
wg.Add(1)
go readLogs(ctx, &wg, stream, out, opts)
return
+ } else {
+ slog.Error("Stream logs failed",
+ slogs.Error, e,
+ slogs.Container, opts.Info(),
+ )
}
- e = fmt.Errorf("stream logs failed %w for %s", err, opts.Info())
- log.Error().Err(e).Msg("logs-stream")
} else {
- e = fmt.Errorf("stream logs failed %w for %s", err, opts.Info())
- log.Error().Err(e).Msg("log-request")
+ slog.Error("Log request failed",
+ slogs.Container, opts.Info(),
+ slogs.Error, err,
+ )
}
select {
case <-ctx.Done():
return
default:
- if e != nil {
- out <- opts.ToErrLogItem(e)
+ if err != nil {
+ out <- opts.ToErrLogItem(err)
}
time.Sleep(logRetryWait)
}
@@ -372,12 +379,15 @@ func tailLogs(ctx context.Context, logger Logger, opts *LogOptions) LogChan {
func readLogs(ctx context.Context, wg *sync.WaitGroup, stream io.ReadCloser, out chan<- *LogItem, opts *LogOptions) {
defer func() {
if err := stream.Close(); err != nil {
- log.Error().Err(err).Msgf("Fail to close stream %s", opts.Info())
+ slog.Error("Fail to close stream",
+ slogs.Container, opts.Info(),
+ slogs.Error, err,
+ )
}
wg.Done()
}()
- log.Debug().Msgf(">>> LOG-READER PROCESSING %#v", opts)
+ slog.Debug("Processing logs", slogs.Options, opts.Info())
r := bufio.NewReader(stream)
for {
var item *LogItem
@@ -387,11 +397,14 @@ func readLogs(ctx context.Context, wg *sync.WaitGroup, stream io.ReadCloser, out
if errors.Is(err, io.EOF) {
e := fmt.Errorf("stream closed %w for %s", err, opts.Info())
item = opts.ToErrLogItem(e)
- log.Warn().Err(e).Msg("log-reader EOF")
+ slog.Warn("Log reader EOF",
+ slogs.Container, opts.Info(),
+ slogs.Error, e,
+ )
} else {
e := fmt.Errorf("stream canceled %w for %s", err, opts.Info())
item = opts.ToErrLogItem(e)
- log.Warn().Err(e).Msg("log-reader canceled")
+ slog.Warn("Log stream canceled")
}
}
select {
@@ -488,7 +501,6 @@ func (p *Pod) Sanitize(ctx context.Context, ns string) (int, error) {
if err != nil {
continue
}
- log.Debug().Msgf("Pod status: %q", render.PodStatus(&pod))
switch render.PodStatus(&pod) {
case render.PhaseCompleted:
fallthrough
@@ -506,16 +518,20 @@ func (p *Pod) Sanitize(ctx context.Context, ns string) (int, error) {
fallthrough
case render.PhaseOOMKilled:
// !!BOZO!! Might need to bump timeout otherwise rev limit if too many??
- log.Debug().Msgf("Sanitizing %s:%s", pod.Namespace, pod.Name)
fqn := client.FQN(pod.Namespace, pod.Name)
+ slog.Debug("Sanitizing resource", slogs.FQN, fqn)
if err := p.Delete(ctx, fqn, nil, 0); err != nil {
- log.Debug().Msgf("Aborted! Sanitizer deleted %d pods", count)
+ slog.Debug("Aborted! Sanitizer delete failed",
+ slogs.FQN, fqn,
+ slogs.Count, count,
+ slogs.Error, err,
+ )
return count, err
}
count++
}
}
- log.Debug().Msgf("Sanitizer deleted %d pods", count)
+ slog.Debug("Sanitizer deleted pods", slogs.Count, count)
return count, nil
}
diff --git a/internal/dao/popeye.go b/internal/dao/popeye.go
index 7cb08e9a..f23476d0 100644
--- a/internal/dao/popeye.go
+++ b/internal/dao/popeye.go
@@ -22,7 +22,6 @@ package dao
// "github.com/derailed/popeye/pkg"
// "github.com/derailed/popeye/pkg/config"
// "github.com/derailed/popeye/types"
-// "github.com/rs/zerolog/log"
// "k8s.io/apimachinery/pkg/runtime"
// )
diff --git a/internal/dao/port_forward.go b/internal/dao/port_forward.go
index 3a0b6280..8d9cc424 100644
--- a/internal/dao/port_forward.go
+++ b/internal/dao/port_forward.go
@@ -6,6 +6,7 @@ package dao
import (
"context"
"fmt"
+ "log/slog"
"regexp"
"strings"
@@ -13,7 +14,7 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/render"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
@@ -45,7 +46,7 @@ func (p *PortForward) List(ctx context.Context, _ string) ([]runtime.Object, err
config, err := config.NewBench(benchFile)
if err != nil {
- log.Debug().Msgf("No custom benchmark config file found: %q", benchFile)
+ slog.Debug("No custom benchmark config file found", slogs.FileName, benchFile)
}
ff, cc := p.getFactory().Forwarders(), config.Benchmarks.Containers
@@ -92,7 +93,10 @@ func BenchConfigFor(benchFile, path string) config.BenchConfig {
def := config.DefaultBenchSpec()
cust, err := config.NewBench(benchFile)
if err != nil {
- log.Debug().Msgf("No custom benchmark config file found. Using default: %q", benchFile)
+ slog.Debug("No custom benchmark config file found. Using default",
+ slogs.FileName, benchFile,
+ slogs.Error, err,
+ )
return def
}
if b, ok := cust.Benchmarks.Containers[PodToKey(path)]; ok {
diff --git a/internal/dao/port_forwarder.go b/internal/dao/port_forwarder.go
index 54aed487..c42bbb69 100644
--- a/internal/dao/port_forwarder.go
+++ b/internal/dao/port_forwarder.go
@@ -12,7 +12,6 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/port"
- "github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -101,7 +100,6 @@ func (p *PortForwarder) Container() string {
// Stop terminates a port forward.
func (p *PortForwarder) Stop() {
- log.Debug().Msgf("<<< Stopping PortForward %s", p.ID())
p.active = false
if p.stopChan != nil {
close(p.stopChan)
diff --git a/internal/dao/rbac.go b/internal/dao/rbac.go
index 7e688aee..afcd4ce6 100644
--- a/internal/dao/rbac.go
+++ b/internal/dao/rbac.go
@@ -6,11 +6,12 @@ package dao
import (
"context"
"fmt"
+ "log/slog"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
@@ -121,9 +122,9 @@ func (r *Rbac) loadRoleBinding(path string) ([]runtime.Object, error) {
return asRuntimeObjects(parseRules(client.ClusterScope, "-", role.Rules)), nil
}
-func (r *Rbac) loadClusterRole(path string) ([]runtime.Object, error) {
- log.Debug().Msgf("LOAD-CR %q", path)
- o, err := r.getFactory().Get(crGVR, path, true, labels.Everything())
+func (r *Rbac) loadClusterRole(fqn string) ([]runtime.Object, error) {
+ slog.Debug("LOAD-CR", slogs.FQN, fqn)
+ o, err := r.getFactory().Get(crGVR, fqn, true, labels.Everything())
if err != nil {
return nil, err
}
@@ -165,6 +166,9 @@ func parseRules(ns, binding string, rules []rbacv1.PolicyRule) render.Policies {
pp := make(render.Policies, 0, len(rules))
for _, rule := range rules {
for _, grp := range rule.APIGroups {
+ if grp == "" {
+ grp = "core"
+ }
for _, res := range rule.Resources {
for _, na := range rule.ResourceNames {
pp = pp.Upsert(render.NewPolicyRes(ns, binding, FQN(res, na), grp, rule.Verbs))
diff --git a/internal/dao/rbac_policy.go b/internal/dao/rbac_policy.go
index 3b9fe7d8..067b45b3 100644
--- a/internal/dao/rbac_policy.go
+++ b/internal/dao/rbac_policy.go
@@ -6,11 +6,12 @@ package dao
import (
"context"
"fmt"
+ "log/slog"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
@@ -102,7 +103,10 @@ func (p *Policy) loadRoleBinding(kind, name string) (render.Policies, error) {
rows := make(render.Policies, 0, len(crs))
for _, cr := range crs {
if rbNs, ok := rbsMap["ClusterRole:"+cr.Name]; ok {
- log.Debug().Msgf("Loading rules for clusterrole %q:%q", rbNs, cr.Name)
+ slog.Debug("Loading rules for clusterrole",
+ slogs.Namespace, rbNs,
+ slogs.ResName, cr.Name,
+ )
rows = append(rows, parseRules(rbNs, "CR:"+cr.Name, cr.Rules)...)
}
}
@@ -115,7 +119,10 @@ func (p *Policy) loadRoleBinding(kind, name string) (render.Policies, error) {
if _, ok := rbsMap["Role:"+ro.Name]; !ok {
continue
}
- log.Debug().Msgf("Loading rules for role %q:%q", ro.Namespace, ro.Name)
+ slog.Debug("Loading rules for role",
+ slogs.Namespace, ro.Namespace,
+ slogs.ResName, ro.Name,
+ )
rows = append(rows, parseRules(ro.Namespace, "RO:"+ro.Name, ro.Rules)...)
}
diff --git a/internal/dao/registry.go b/internal/dao/registry.go
index 74cf9446..ef04efb3 100644
--- a/internal/dao/registry.go
+++ b/internal/dao/registry.go
@@ -5,13 +5,14 @@ package dao
import (
"fmt"
+ "log/slog"
"slices"
"sort"
"strings"
"sync"
"github.com/derailed/k9s/internal/client"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -97,7 +98,7 @@ func AccessorFor(f Factory, gvr client.GVR) (Accessor, error) {
r, ok := m[gvr]
if !ok {
r = new(Scaler)
- log.Debug().Msgf("No DAO registry entry for %q. Using generics!", gvr)
+ slog.Debug("No DAO registry entry. Using generics!", slogs.GVR, gvr)
}
r.Init(f, gvr)
@@ -348,7 +349,7 @@ func loadRBAC(m ResourceMetas) {
func loadPreferred(f Factory, m ResourceMetas) error {
if f.Client() == nil || !f.Client().ConnectionOK() {
- log.Error().Msgf("Load cluster resources - No API server connection")
+ slog.Error("Load cluster resources - No API server connection")
return nil
}
@@ -358,7 +359,7 @@ func loadPreferred(f Factory, m ResourceMetas) error {
}
rr, err := dial.ServerPreferredResources()
if err != nil {
- log.Debug().Err(err).Msgf("Failed to load preferred resources")
+ slog.Debug("Failed to load preferred resources", slogs.Error, err)
}
for _, r := range rr {
for _, res := range r.APIResources {
@@ -400,7 +401,7 @@ func loadCRDs(f Factory, m ResourceMetas) {
oo, err := f.List(crdGVR, client.ClusterScope, true, labels.Everything())
if err != nil {
- log.Warn().Err(err).Msgf("Fail CRDs load")
+ slog.Warn("CRDs load Fail", slogs.Error, err)
return
}
@@ -408,7 +409,7 @@ func loadCRDs(f Factory, m ResourceMetas) {
var crd apiext.CustomResourceDefinition
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &crd)
if err != nil {
- log.Err(err).Msg("boom")
+ slog.Error("CRD conversion failed", slogs.Error, err)
continue
}
gvr, version, ok := newGVRFromCRD(&crd)
diff --git a/internal/dao/scalable.go b/internal/dao/scalable.go
index 7664b535..80c0fac5 100644
--- a/internal/dao/scalable.go
+++ b/internal/dao/scalable.go
@@ -5,14 +5,14 @@ package dao
import (
"context"
+ "log/slog"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/client"
+ "github.com/derailed/k9s/internal/slogs"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/restmapper"
"k8s.io/client-go/scale"
-
- "github.com/derailed/k9s/internal/client"
)
var _ Scalable = (*Scaler)(nil)
@@ -59,7 +59,10 @@ func (s *Scaler) Scale(ctx context.Context, path string, replicas int32) error {
return err
}
- log.Debug().Msgf("%s scaled to %d", path, updatedScale.Spec.Replicas)
+ slog.Debug("Scaled resource",
+ slogs.FQN, path,
+ slogs.Replicas, updatedScale.Spec.Replicas,
+ )
return nil
}
diff --git a/internal/dao/sts.go b/internal/dao/sts.go
index 4e352f16..153d8296 100644
--- a/internal/dao/sts.go
+++ b/internal/dao/sts.go
@@ -7,11 +7,12 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"strings"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -236,7 +237,10 @@ func (s *StatefulSet) Scan(ctx context.Context, gvr client.GVR, fqn string, wait
case SecGVR:
found, err := hasSecret(s.Factory, &sts.Spec.Template.Spec, sts.Namespace, n, wait)
if err != nil {
- log.Warn().Err(err).Msgf("locate secret %q", fqn)
+ slog.Warn("Locate secret failed",
+ slogs.FQN, fqn,
+ slogs.Error, err,
+ )
continue
}
if !found {
diff --git a/internal/dao/workload.go b/internal/dao/workload.go
index 604c6ca9..5b1b8d6e 100644
--- a/internal/dao/workload.go
+++ b/internal/dao/workload.go
@@ -8,13 +8,14 @@ import (
"encoding/json"
"errors"
"fmt"
+ "log/slog"
"strconv"
"strings"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -206,12 +207,18 @@ func isReady(s string) bool {
}
r, err := strconv.Atoi(tt[0])
if err != nil {
- log.Error().Msgf("invalid ready count: %q", tt[0])
+ slog.Error("Invalid ready count",
+ slogs.Error, err,
+ slogs.Count, tt[0],
+ )
return false
}
c, err := strconv.Atoi(tt[1])
if err != nil {
- log.Error().Msgf("invalid expected count: %q", tt[1])
+ slog.Error("invalid expected count: %q",
+ slogs.Error, err,
+ slogs.Count, tt[1],
+ )
return false
}
diff --git a/internal/model/cluster_info.go b/internal/model/cluster_info.go
index 62f6e7f1..d3aed24e 100644
--- a/internal/model/cluster_info.go
+++ b/internal/model/cluster_info.go
@@ -8,15 +8,16 @@ import (
"encoding/json"
"errors"
"io"
+ "log/slog"
"net/http"
"sync"
"time"
"github.com/derailed/k9s/internal/config"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
- "github.com/rs/zerolog/log"
"k8s.io/apimachinery/pkg/util/cache"
)
@@ -107,7 +108,7 @@ func (c *ClusterInfo) fetchK9sLatestRev() string {
latestRev, err := fetchLatestRev()
if err != nil {
- log.Warn().Msgf("k9s latest rev fetch failed %s", err)
+ slog.Warn("k9s latest rev fetch failed", slogs.Error, err)
} else {
c.cache.Add(k9sLatestRevKey, latestRev, cacheExpiry)
}
@@ -206,7 +207,7 @@ func (c *ClusterInfo) fireNoMetaChanged(data ClusterMeta) {
// Helpers...
func fetchLatestRev() (string, error) {
- log.Debug().Msgf("Fetching latest k9s rev...")
+ slog.Debug("Fetching latest k9s rev...")
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
@@ -234,7 +235,7 @@ func fetchLatestRev() (string, error) {
}
if v, ok := m["name"]; ok {
- log.Debug().Msgf("K9s latest rev: %q", v.(string))
+ slog.Debug("K9s latest rev", slogs.Revision, v.(string))
return v.(string), nil
}
diff --git a/internal/model/cluster_info_test.go b/internal/model/cluster_info_test.go
index 48426ba5..ca0c637e 100644
--- a/internal/model/cluster_info_test.go
+++ b/internal/model/cluster_info_test.go
@@ -4,15 +4,15 @@
package model_test
import (
+ "log/slog"
"testing"
"github.com/derailed/k9s/internal/model"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.FatalLevel)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestClusterMetaDelta(t *testing.T) {
diff --git a/internal/model/describe.go b/internal/model/describe.go
index 2d5d845a..501426b4 100644
--- a/internal/model/describe.go
+++ b/internal/model/describe.go
@@ -6,6 +6,7 @@ package model
import (
"context"
"fmt"
+ "log/slog"
"reflect"
"strings"
"sync/atomic"
@@ -15,7 +16,7 @@ import (
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/sahilm/fuzzy"
)
@@ -113,7 +114,7 @@ func (d *Describe) Watch(ctx context.Context) error {
}
func (d *Describe) updater(ctx context.Context) {
- defer log.Debug().Msgf("Describe canceled -- %q", d.gvr)
+ defer slog.Debug("Describe canceled", slogs.GVR, d.gvr)
backOff := NewExpBackOff(ctx, defaultReaderRefreshRate, maxReaderRetryInterval)
delay := defaultReaderRefreshRate
@@ -125,7 +126,7 @@ func (d *Describe) updater(ctx context.Context) {
if err := d.refresh(ctx); err != nil {
d.fireResourceFailed(err)
if delay = backOff.NextBackOff(); delay == backoff.Stop {
- log.Error().Err(err).Msgf("Describe gave up!")
+ slog.Error("Describe gave up!", slogs.Error, err)
return
}
} else {
@@ -138,13 +139,16 @@ func (d *Describe) updater(ctx context.Context) {
func (d *Describe) refresh(ctx context.Context) error {
if !atomic.CompareAndSwapInt32(&d.inUpdate, 0, 1) {
- log.Debug().Msgf("Dropping update...")
+ slog.Debug("Dropping update...")
return nil
}
defer atomic.StoreInt32(&d.inUpdate, 0)
if err := d.reconcile(ctx); err != nil {
- log.Error().Err(err).Msgf("reconcile failed %q", d.gvr)
+ slog.Error("reconcile failed",
+ slogs.GVR, d.gvr,
+ slogs.Error, err,
+ )
d.fireResourceFailed(err)
return err
}
@@ -170,7 +174,7 @@ func (d *Describe) reconcile(ctx context.Context) error {
// Describe describes a given resource.
func (d *Describe) describe(ctx context.Context, gvr client.GVR, path string) (string, error) {
defer func(t time.Time) {
- log.Debug().Msgf("Describe model elapsed: %v", time.Since(t))
+ slog.Debug("Describe model elapsed", slogs.Elapsed, time.Since(t))
}(time.Now())
meta, err := getMeta(ctx, gvr)
diff --git a/internal/model/flash.go b/internal/model/flash.go
index d4bb7c8d..727af7f2 100644
--- a/internal/model/flash.go
+++ b/internal/model/flash.go
@@ -6,9 +6,10 @@ package model
import (
"context"
"fmt"
+ "log/slog"
"time"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
)
const (
@@ -86,7 +87,7 @@ func (f *Flash) Infof(fmat string, args ...interface{}) {
// Warn displays a warning flash message.
func (f *Flash) Warn(msg string) {
- log.Warn().Msg(msg)
+ slog.Warn(msg)
f.SetMessage(FlashWarn, msg)
}
@@ -97,7 +98,7 @@ func (f *Flash) Warnf(fmat string, args ...interface{}) {
// Err displays an error flash message.
func (f *Flash) Err(err error) {
- log.Error().Msg(err.Error())
+ slog.Error("Flash failed", slogs.Error, err)
f.SetMessage(FlashErr, err.Error())
}
@@ -110,7 +111,10 @@ func (f *Flash) Errf(fmat string, args ...interface{}) {
err = e
}
}
- log.Error().Err(err).Msgf(fmat, args...)
+ slog.Error("Flashing error",
+ slogs.Error, err,
+ slogs.Message, fmt.Sprintf(fmat, args...),
+ )
f.SetMessage(FlashErr, fmt.Sprintf(fmat, args...))
}
diff --git a/internal/model/log.go b/internal/model/log.go
index f78e5a27..19495847 100644
--- a/internal/model/log.go
+++ b/internal/model/log.go
@@ -6,6 +6,7 @@ package model
import (
"context"
"fmt"
+ "log/slog"
"sync"
"time"
@@ -14,7 +15,7 @@ import (
"github.com/derailed/k9s/internal/color"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/dao"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
)
// LogsListener represents a log model listener.
@@ -164,7 +165,7 @@ func (l *Log) Restart(ctx context.Context) {
// Start starts logging.
func (l *Log) Start(ctx context.Context) {
if err := l.load(ctx); err != nil {
- log.Error().Err(err).Msgf("Tail logs failed!")
+ slog.Error("Tail logs failed!", slogs.Error, err)
l.fireLogError(err)
}
}
@@ -219,7 +220,6 @@ func (l *Log) cancel() {
defer l.mx.Unlock()
if l.cancelFn != nil {
l.cancelFn()
- log.Debug().Msgf("!!! LOG-MODEL CANCELED !!!")
l.cancelFn = nil
}
}
@@ -231,7 +231,7 @@ func (l *Log) load(ctx context.Context) error {
}
loggable, ok := accessor.(dao.Loggable)
if !ok {
- return fmt.Errorf("Resource %s is not Loggable", l.gvr)
+ return fmt.Errorf("resource %s is not Loggable", l.gvr)
}
l.cancel()
@@ -240,7 +240,7 @@ func (l *Log) load(ctx context.Context) error {
cc, err := loggable.TailLogs(ctx, l.logOptions)
if err != nil {
- log.Error().Err(err).Msgf("Tail logs failed")
+ slog.Error("Tail logs failed", slogs.Error, err)
l.cancel()
l.fireLogError(err)
}
@@ -288,8 +288,6 @@ func (l *Log) ToggleAllContainers(ctx context.Context) {
}
func (l *Log) updateLogs(ctx context.Context, c dao.LogChan) {
- defer log.Debug().Msgf("<<< LOG-MODEL UPDATER DONE %s!!!!", l.logOptions.Info())
- log.Debug().Msgf(">>> START LOG-MODEL UPDATER %s", l.logOptions.Info())
for {
select {
case item, ok := <-c:
diff --git a/internal/model/pulse.go b/internal/model/pulse.go
index 0b62a1a7..dd974c9a 100644
--- a/internal/model/pulse.go
+++ b/internal/model/pulse.go
@@ -6,13 +6,14 @@ package model
import (
"context"
"fmt"
+ "log/slog"
"sync/atomic"
"time"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/health"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"k8s.io/apimachinery/pkg/runtime"
)
@@ -53,7 +54,7 @@ func (p *Pulse) Watch(ctx context.Context) {
}
func (p *Pulse) updater(ctx context.Context) {
- defer log.Debug().Msgf("Pulse canceled -- %q", p.gvr)
+ defer slog.Debug("Pulse canceled", slogs.GVR, p.gvr)
rate := initRefreshRate
for {
@@ -77,13 +78,13 @@ func (p *Pulse) Refresh(ctx context.Context) {
func (p *Pulse) refresh(ctx context.Context) {
if !atomic.CompareAndSwapInt32(&p.inUpdate, 0, 1) {
- log.Debug().Msgf("Dropping update...")
+ slog.Debug("Dropping update...")
return
}
defer atomic.StoreInt32(&p.inUpdate, 0)
if err := p.reconcile(ctx); err != nil {
- log.Error().Err(err).Msg("Reconcile failed")
+ slog.Error("Reconcile failed", slogs.Error, err)
p.firePulseFailed(err)
return
}
diff --git a/internal/model/pulse_health.go b/internal/model/pulse_health.go
index d2be69c9..8e2027e8 100644
--- a/internal/model/pulse_health.go
+++ b/internal/model/pulse_health.go
@@ -6,13 +6,14 @@ package model
import (
"context"
"fmt"
+ "log/slog"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/health"
"github.com/derailed/k9s/internal/model1"
"github.com/derailed/k9s/internal/render"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
@@ -72,7 +73,7 @@ func (h *PulseHealth) checkMetrics(ctx context.Context) (health.Checks, error) {
nmx, err := dial.FetchNodesMetrics(ctx)
if err != nil {
- log.Error().Err(err).Msgf("Fetching metrics")
+ slog.Error("Fetching metrics", slogs.Error, err)
return nil, err
}
diff --git a/internal/model/rev_values.go b/internal/model/rev_values.go
index a0b8cb98..059fa0ef 100644
--- a/internal/model/rev_values.go
+++ b/internal/model/rev_values.go
@@ -5,6 +5,7 @@ package model
import (
"context"
+ "log/slog"
"strings"
"sync/atomic"
"time"
@@ -13,7 +14,7 @@ import (
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/sahilm/fuzzy"
)
@@ -48,7 +49,7 @@ func getHelmHistDao() *dao.HelmHistory {
func getRevValues(path, rev string) []string {
vals, err := getHelmHistDao().GetValues(path, true)
if err != nil {
- log.Error().Err(err).Msgf("Failed to get Helm values")
+ slog.Error("Failed to get Helm values", slogs.Error, err)
}
return strings.Split(string(vals), "\n")
}
@@ -133,7 +134,7 @@ func (v *RevValues) Watch(ctx context.Context) error {
}
func (v *RevValues) updater(ctx context.Context) {
- defer log.Debug().Msgf("YAML canceled -- %q", v.gvr)
+ defer slog.Debug("YAML canceled", slogs.GVR, v.gvr)
backOff := NewExpBackOff(ctx, defaultReaderRefreshRate, maxReaderRetryInterval)
delay := defaultReaderRefreshRate
@@ -145,7 +146,7 @@ func (v *RevValues) updater(ctx context.Context) {
if err := v.refresh(ctx); err != nil {
v.fireResourceFailed(err)
if delay = backOff.NextBackOff(); delay == backoff.Stop {
- log.Error().Err(err).Msgf("giving up retrieving chart values")
+ slog.Error("Giving up retrieving chart values", slogs.Error, err)
return
}
} else {
@@ -158,7 +159,7 @@ func (v *RevValues) updater(ctx context.Context) {
func (v *RevValues) refresh(ctx context.Context) error {
if !atomic.CompareAndSwapInt32(&v.inUpdate, 0, 1) {
- log.Debug().Msgf("Dropping update...")
+ slog.Debug("Dropping update...")
return nil
}
defer atomic.StoreInt32(&v.inUpdate, 0)
diff --git a/internal/model/stack.go b/internal/model/stack.go
index 55221b85..d3d8446c 100644
--- a/internal/model/stack.go
+++ b/internal/model/stack.go
@@ -4,9 +4,11 @@
package model
import (
+ "fmt"
+ "log/slog"
"sync"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
)
const (
@@ -188,9 +190,9 @@ func (s *Stack) notify(a StackAction, c Component) {
// Dump prints out the stack.
func (s *Stack) Dump() {
- log.Debug().Msgf("--- Stack Dump %p---", s)
+ slog.Debug("Stack Dump", slogs.Stack, fmt.Sprintf("%p", s))
for i, c := range s.components {
- log.Debug().Msgf("%d -- %s -- %#v", i, c.Name(), c)
+ slog.Debug(fmt.Sprintf("%d -- %s -- %#v", i, c.Name(), c))
}
- log.Debug().Msg("------------------")
+ slog.Debug("------------------")
}
diff --git a/internal/model/stack_test.go b/internal/model/stack_test.go
index 5a3eca62..6d8c756e 100644
--- a/internal/model/stack_test.go
+++ b/internal/model/stack_test.go
@@ -5,17 +5,17 @@ package model_test
import (
"context"
+ "log/slog"
"testing"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.FatalLevel)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestStackClear(t *testing.T) {
diff --git a/internal/model/table.go b/internal/model/table.go
index ab135c24..2c0c8dc3 100644
--- a/internal/model/table.go
+++ b/internal/model/table.go
@@ -6,6 +6,7 @@ package model
import (
"context"
"fmt"
+ "log/slog"
"sync"
"sync/atomic"
"time"
@@ -16,7 +17,7 @@ import (
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/model1"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
@@ -63,7 +64,7 @@ func (t *Table) SetViewSetting(ctx context.Context, vs *config.ViewSetting) {
if ctx != context.Background() {
if err := t.reconcile(ctx); err != nil {
- log.Err(err).Msgf("refresh failed for gvr: %s", t.gvr)
+ slog.Error("Refresh failed", slogs.GVR, t.gvr)
}
}
}
@@ -209,13 +210,13 @@ func (t *Table) updater(ctx context.Context) {
rate = t.refreshRate
err := backoff.Retry(func() error {
if err := t.refresh(ctx); err != nil {
- log.Err(err).Msgf("refresh failed for gvr: %s", t.gvr)
+ slog.Error("Refresh failed", slogs.GVR, t.gvr)
return err
}
return nil
}, backoff.WithContext(bf, ctx))
if err != nil {
- log.Warn().Err(err).Msgf("reconciler exited")
+ slog.Warn("Reconciler exited", slogs.Error, err)
t.fireTableLoadFailed(err)
return
}
@@ -224,12 +225,8 @@ func (t *Table) updater(ctx context.Context) {
}
func (t *Table) refresh(ctx context.Context) error {
- defer func(ti time.Time) {
- log.Trace().Msgf("Refresh [%s](%d) %s ", t.gvr, t.data.RowCount(), time.Since(ti))
- }(time.Now())
-
if !atomic.CompareAndSwapInt32(&t.inUpdate, 0, 1) {
- log.Debug().Msgf("Dropping update...")
+ slog.Debug("Dropping update...")
return nil
}
defer atomic.StoreInt32(&t.inUpdate, 0)
diff --git a/internal/model/tree.go b/internal/model/tree.go
index 6f32d13c..f97b5852 100644
--- a/internal/model/tree.go
+++ b/internal/model/tree.go
@@ -6,6 +6,7 @@ package model
import (
"context"
"fmt"
+ "log/slog"
"regexp"
"strings"
"sync/atomic"
@@ -15,8 +16,8 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/xray"
- "github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
@@ -162,7 +163,7 @@ func (t *Tree) ToYAML(ctx context.Context, gvr, path string) (string, error) {
}
func (t *Tree) updater(ctx context.Context) {
- defer log.Debug().Msgf("Tree-model canceled -- %q", t.gvr)
+ defer slog.Debug("Tree-model canceled", slogs.GVR, t.gvr)
rate := initTreeRefreshRate
for {
@@ -179,13 +180,13 @@ func (t *Tree) updater(ctx context.Context) {
func (t *Tree) refresh(ctx context.Context) {
if !atomic.CompareAndSwapInt32(&t.inUpdate, 0, 1) {
- log.Debug().Msgf("Dropping update...")
+ slog.Debug("Dropping update...")
return
}
defer atomic.StoreInt32(&t.inUpdate, 0)
if err := t.reconcile(ctx); err != nil {
- log.Error().Err(err).Msg("Reconcile failed")
+ slog.Error("Reconcile failed", slogs.Error, err)
t.fireTreeLoadFailed(err)
return
}
diff --git a/internal/model/values.go b/internal/model/values.go
index eafe098d..41515f65 100644
--- a/internal/model/values.go
+++ b/internal/model/values.go
@@ -6,6 +6,7 @@ package model
import (
"context"
"fmt"
+ "log/slog"
"strings"
"sync/atomic"
"time"
@@ -14,7 +15,7 @@ import (
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/sahilm/fuzzy"
)
@@ -162,7 +163,7 @@ func (v *Values) Watch(ctx context.Context) error {
}
func (v *Values) updater(ctx context.Context) {
- defer log.Debug().Msgf("YAML canceled -- %q", v.gvr)
+ defer slog.Debug("YAML canceled", slogs.GVR, v.gvr)
backOff := NewExpBackOff(ctx, defaultReaderRefreshRate, maxReaderRetryInterval)
delay := defaultReaderRefreshRate
@@ -174,7 +175,7 @@ func (v *Values) updater(ctx context.Context) {
if err := v.refresh(ctx); err != nil {
v.fireResourceFailed(err)
if delay = backOff.NextBackOff(); delay == backoff.Stop {
- log.Error().Err(err).Msgf("giving up retrieving chart values")
+ slog.Error("Giving up retrieving chart values", slogs.Error, err)
return
}
} else {
@@ -187,7 +188,7 @@ func (v *Values) updater(ctx context.Context) {
func (v *Values) refresh(ctx context.Context) error {
if !atomic.CompareAndSwapInt32(&v.inUpdate, 0, 1) {
- log.Debug().Msgf("Dropping update...")
+ slog.Debug("Dropping update...")
return nil
}
defer atomic.StoreInt32(&v.inUpdate, 0)
diff --git a/internal/model/yaml.go b/internal/model/yaml.go
index 6ba1ff86..b6ecc2b6 100644
--- a/internal/model/yaml.go
+++ b/internal/model/yaml.go
@@ -6,6 +6,7 @@ package model
import (
"context"
"fmt"
+ "log/slog"
"reflect"
"strings"
"sync/atomic"
@@ -15,7 +16,7 @@ import (
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/sahilm/fuzzy"
)
@@ -121,7 +122,7 @@ func (y *YAML) Watch(ctx context.Context) error {
}
func (y *YAML) updater(ctx context.Context) {
- defer log.Debug().Msgf("YAML canceled -- %q", y.gvr)
+ defer slog.Debug("YAML canceled", slogs.GVR, y.gvr)
backOff := NewExpBackOff(ctx, defaultReaderRefreshRate, maxReaderRetryInterval)
delay := defaultReaderRefreshRate
@@ -133,7 +134,7 @@ func (y *YAML) updater(ctx context.Context) {
if err := y.refresh(ctx); err != nil {
y.fireResourceFailed(err)
if delay = backOff.NextBackOff(); delay == backoff.Stop {
- log.Error().Err(err).Msgf("YAML gave up!")
+ slog.Error("YAML gave up!", slogs.Error, err)
return
}
} else {
@@ -146,7 +147,7 @@ func (y *YAML) updater(ctx context.Context) {
func (y *YAML) refresh(ctx context.Context) error {
if !atomic.CompareAndSwapInt32(&y.inUpdate, 0, 1) {
- log.Debug().Msgf("Dropping update...")
+ slog.Debug("Dropping update...", slogs.GVR, y.gvr)
return nil
}
defer atomic.StoreInt32(&y.inUpdate, 0)
diff --git a/internal/model1/header.go b/internal/model1/header.go
index 9e1ab6f2..4355d2b5 100644
--- a/internal/model1/header.go
+++ b/internal/model1/header.go
@@ -5,9 +5,10 @@ package model1
import (
"fmt"
+ "log/slog"
"reflect"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
)
const ageCol = "AGE"
@@ -16,6 +17,7 @@ type Attrs struct {
Align int
Decorator DecoratorFunc
Wide bool
+ Show bool
MX bool
MXC, MXM bool
Time bool
@@ -34,18 +36,20 @@ func (a Attrs) Merge(b Attrs) Attrs {
if a.Align == 0 {
a.Align = b.Align
}
- if !a.Wide {
+
+ if !a.Hide {
+ a.Hide = b.Hide
+ }
+ if !a.Show && !a.Wide {
a.Wide = b.Wide
}
+
if !a.Time {
a.Time = b.Time
}
if !a.Capacity {
a.Capacity = b.Capacity
}
- if !a.Hide {
- a.Hide = b.Hide
- }
return a
}
@@ -107,7 +111,7 @@ func (h Header) MapIndices(cols []string, wide bool) []int {
for _, col := range cols {
idx, ok := h.IndexOf(col, true)
if !ok {
- log.Warn().Msgf("Column %q not found on resource", col)
+ slog.Warn("Column not found on resource", slogs.ColName, col)
}
ii, cc[idx] = append(ii, idx), struct{}{}
}
@@ -134,7 +138,7 @@ func (h Header) Customize(cols []string, wide bool) Header {
for _, c := range cols {
idx, ok := h.IndexOf(c, true)
if !ok {
- log.Warn().Msgf("Column %s is not available on this resource", c)
+ slog.Warn("Column is not available on this resource", slogs.ColName, c)
cc = append(cc, HeaderColumn{Name: c})
continue
}
@@ -232,8 +236,8 @@ func (h Header) IndexOf(colName string, includeWide bool) (int, bool) {
// Dump for debugging.
func (h Header) Dump() {
- log.Debug().Msgf("HEADER")
+ slog.Debug("HEADER")
for i, c := range h {
- log.Debug().Msgf("%d %q -- %t", i, c.Name, c.Wide)
+ slog.Debug(fmt.Sprintf("%d %q -- %t", i, c.Name, c.Wide))
}
}
diff --git a/internal/model1/row_event.go b/internal/model1/row_event.go
index 1c5e065b..b4921b74 100644
--- a/internal/model1/row_event.go
+++ b/internal/model1/row_event.go
@@ -5,9 +5,8 @@ package model1
import (
"fmt"
+ "log/slog"
"sort"
-
- "github.com/rs/zerolog/log"
)
type ReRangeFn func(int, RowEvent) bool
@@ -274,11 +273,11 @@ func (r *RowEvents) Sort(ns string, sortCol int, isDuration, numCol, isCapacity,
r.reindex()
}
-// For debguging...
+// For debugging...
func (re RowEvents) Dump(msg string) {
- log.Debug().Msg(msg)
+ slog.Debug("[DEBUG] RowEvents" + msg)
for _, r := range re.events {
- log.Debug().Msgf("!!YO!! %#v", r)
+ slog.Debug(fmt.Sprintf(" %#v", r))
}
}
diff --git a/internal/model1/table_data.go b/internal/model1/table_data.go
index 3c70a8d5..dc25afb2 100644
--- a/internal/model1/table_data.go
+++ b/internal/model1/table_data.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"regexp"
"strings"
"sync"
@@ -14,7 +15,7 @@ import (
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/sahilm/fuzzy"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -155,7 +156,7 @@ func (t *TableData) Filter(f FilterOpts) *TableData {
if err == nil {
td.rowEvents = rr
} else {
- log.Error().Err(err).Msg("rx filter failed")
+ slog.Error("RX filter failed", slogs.Error, err)
}
return td
@@ -206,12 +207,11 @@ func (t *TableData) fuzzyFilter(q string) *RowEvents {
mm := fuzzy.Find(q, ss)
rr := NewRowEvents(t.RowCount() / 2)
for _, m := range mm {
- re, ok := t.rowEvents.At(m.Index)
- if !ok {
- log.Error().Msgf("unable to find event for index in fuzzfilter: %d", m.Index)
- continue
+ if re, ok := t.rowEvents.At(m.Index); !ok {
+ slog.Error("Unable to find event for index in fuzzfilter", slogs.Index, m.Index)
+ } else {
+ rr.Add(re)
}
- rr.Add(re)
}
return rr
@@ -474,7 +474,10 @@ func (t *TableData) Delete(newKeys map[string]struct{}) {
})
for _, id := range victims {
if err := t.rowEvents.Delete(id); err != nil {
- log.Error().Err(err).Msgf("table delete failed: %q", id)
+ slog.Error("Table delete failed",
+ slogs.Error, err,
+ slogs.Message, id,
+ )
}
}
}
diff --git a/internal/model1/table_data_test.go b/internal/model1/table_data_test.go
index dddbdf36..185f2cac 100644
--- a/internal/model1/table_data_test.go
+++ b/internal/model1/table_data_test.go
@@ -4,16 +4,16 @@
package model1
import (
+ "log/slog"
"testing"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.FatalLevel)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestTableDataComputeSortCol(t *testing.T) {
diff --git a/internal/perf/benchmark.go b/internal/perf/benchmark.go
index 5440e53a..80544b11 100644
--- a/internal/perf/benchmark.go
+++ b/internal/perf/benchmark.go
@@ -8,6 +8,7 @@ import (
"context"
"fmt"
"io"
+ "log/slog"
"net/http"
"os"
"path/filepath"
@@ -16,11 +17,11 @@ import (
"time"
"github.com/derailed/k9s/internal/config/data"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/rakyll/hey/requester"
- "github.com/rs/zerolog/log"
)
const (
@@ -59,7 +60,7 @@ func (b *Benchmark) init(base, version string) error {
req.SetBasicAuth(b.config.Auth.User, b.config.Auth.Password)
}
req.Header = b.config.HTTP.Headers
- log.Debug().Msgf("Benchmarking Request %s", req.URL.String())
+ slog.Debug("Benchmarking Request", slogs.URL, req.URL.String())
ua := req.UserAgent()
if ua == "" {
@@ -73,8 +74,7 @@ func (b *Benchmark) init(base, version string) error {
}
req.Header.Set("User-Agent", ua)
- log.Debug().Msgf("Using bench config N:%d--C:%d", b.config.N, b.config.C)
-
+ slog.Debug(fmt.Sprintf("Using bench config N:%d--C:%d", b.config.N, b.config.C))
b.worker = &requester.Work{
Request: req,
RequestBody: []byte(b.config.HTTP.Body),
@@ -108,7 +108,10 @@ func (b *Benchmark) Canceled() bool {
// Run starts a benchmark.
func (b *Benchmark) Run(cluster, context string, done func()) {
- log.Debug().Msgf("Running benchmark on context %s", cluster)
+ slog.Debug("Running benchmark",
+ slogs.Cluster, cluster,
+ slogs.Context, context,
+ )
buff := new(bytes.Buffer)
b.worker.Writer = buff
// this call will block until the benchmark is complete or times out.
@@ -116,7 +119,7 @@ func (b *Benchmark) Run(cluster, context string, done func()) {
b.worker.Stop()
if buff.Len() > 0 {
if err := b.save(cluster, context, buff); err != nil {
- log.Error().Err(err).Msg("Saving Benchmark")
+ slog.Error("Saving Benchmark", slogs.Error, err)
}
}
done()
@@ -141,7 +144,10 @@ func (b *Benchmark) save(cluster, context string, r io.Reader) error {
}
defer func() {
if e := f.Close(); e != nil {
- log.Error().Err(e).Msgf("Benchmark file close failed: %q", bf)
+ slog.Error("Benchmark file close failed",
+ slogs.Error, e,
+ slogs.Path, bf,
+ )
}
}()
if _, err = io.Copy(f, r); err != nil {
diff --git a/internal/port/tunnel.go b/internal/port/tunnel.go
index 758675ad..585228a6 100644
--- a/internal/port/tunnel.go
+++ b/internal/port/tunnel.go
@@ -5,12 +5,16 @@ package port
import (
"fmt"
+ "log/slog"
"net"
+
+ "github.com/derailed/k9s/internal/slogs"
)
// PortTunnels represents a collection of tunnels.
type PortTunnels []PortTunnel
+// CheckAvailable checks if all port tunnels are available.
func (t PortTunnels) CheckAvailable() error {
for _, pt := range t {
if !IsPortFree(pt) {
@@ -26,6 +30,7 @@ type PortTunnel struct {
Address, Container, LocalPort, ContainerPort string
}
+// NewPortTunnel returns a new instance.
func NewPortTunnel(a, co, lp, cp string) PortTunnel {
return PortTunnel{
Address: a,
@@ -45,13 +50,17 @@ func (t PortTunnel) PortMap() string {
if t.LocalPort == "" {
t.LocalPort = t.ContainerPort
}
+
return t.LocalPort + ":" + t.ContainerPort
}
+// IsPortFree checks if a address/port pair is available on host.
func IsPortFree(t PortTunnel) bool {
s, err := net.Listen("tcp", fmt.Sprintf("%s:%s", t.Address, t.LocalPort))
if err != nil {
+ slog.Warn("Port is not available", slogs.Port, t.LocalPort, slogs.Address, t.Address)
return false
}
+
return s.Close() == nil
}
diff --git a/internal/render/alias.go b/internal/render/alias.go
index b7faa76e..6b7f841e 100644
--- a/internal/render/alias.go
+++ b/internal/render/alias.go
@@ -5,6 +5,7 @@ package render
import (
"fmt"
+ "slices"
"strings"
"github.com/derailed/k9s/internal/client"
@@ -36,13 +37,14 @@ func (Alias) Render(o interface{}, ns string, r *model1.Row) error {
return fmt.Errorf("expected AliasRes, but got %T", o)
}
+ slices.Sort(a.Aliases)
gvr := client.NewGVR(a.GVR)
r.ID = gvr.String()
r.Fields = append(r.Fields,
gvr.R(),
gvr.G(),
gvr.V(),
- strings.Join(a.Aliases, ","),
+ strings.Join(a.Aliases, " "),
)
return nil
diff --git a/internal/render/alias_test.go b/internal/render/alias_test.go
index a71b39e7..b29bca0f 100644
--- a/internal/render/alias_test.go
+++ b/internal/render/alias_test.go
@@ -76,7 +76,7 @@ func TestAliasRender(t *testing.T) {
assert.Nil(t, a.Render(o, "fred/v1/blee", &r))
assert.Equal(t, model1.Row{
ID: "fred/v1/blee",
- Fields: model1.Fields{"blee", "fred", "v1", "a,b,c"},
+ Fields: model1.Fields{"blee", "fred", "v1", "a b c"},
}, r)
}
diff --git a/internal/render/base.go b/internal/render/base.go
index 34bb5e31..091db7ba 100644
--- a/internal/render/base.go
+++ b/internal/render/base.go
@@ -4,9 +4,11 @@
package render
import (
+ "log/slog"
+
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model1"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
)
// DecoratorFunc decorates a string.
@@ -48,7 +50,7 @@ func (b *Base) SetViewSetting(vs *config.ViewSetting) {
}
specs, err := NewColsSpecs(cols...).parseSpecs()
if err != nil {
- log.Error().Err(err).Msg("unable to grok custom columns")
+ slog.Error("Unable to grok custom columns", slogs.Error, err)
return
}
b.specs = specs
diff --git a/internal/render/benchmark_int_test.go b/internal/render/benchmark_int_test.go
index a7d4a387..bb6c42ed 100644
--- a/internal/render/benchmark_int_test.go
+++ b/internal/render/benchmark_int_test.go
@@ -4,16 +4,16 @@
package render
import (
+ "log/slog"
"os"
"testing"
"github.com/derailed/k9s/internal/model1"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.FatalLevel)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestAugmentRow(t *testing.T) {
diff --git a/internal/render/container.go b/internal/render/container.go
index 37cd38cc..c05242a0 100644
--- a/internal/render/container.go
+++ b/internal/render/container.go
@@ -87,8 +87,8 @@ func (Container) defaultHeader() model1.Header {
model1.HeaderColumn{Name: "PROBES(L:R:S)"},
model1.HeaderColumn{Name: "CPU", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},
model1.HeaderColumn{Name: "MEM", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},
- model1.HeaderColumn{Name: "CPU/R:L", Attrs: model1.Attrs{Align: tview.AlignRight}},
- model1.HeaderColumn{Name: "MEM/R:L", Attrs: model1.Attrs{Align: tview.AlignRight}},
+ model1.HeaderColumn{Name: "CPU/RL", Attrs: model1.Attrs{Align: tview.AlignRight}},
+ model1.HeaderColumn{Name: "MEM/RL", Attrs: model1.Attrs{Align: tview.AlignRight}},
model1.HeaderColumn{Name: "%CPU/R", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},
model1.HeaderColumn{Name: "%CPU/L", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},
model1.HeaderColumn{Name: "%MEM/R", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},
diff --git a/internal/render/context.go b/internal/render/context.go
index 06a622ad..2aa17368 100644
--- a/internal/render/context.go
+++ b/internal/render/context.go
@@ -5,11 +5,12 @@ package render
import (
"fmt"
+ "log/slog"
+ "os"
"strings"
"github.com/derailed/k9s/internal/model1"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/tools/clientcmd/api"
@@ -88,8 +89,8 @@ func NewNamedContext(c ContextNamer, n string, ctx *api.Context) *NamedContext {
func (c *NamedContext) IsCurrentContext(n string) bool {
cl, err := c.Config.CurrentContextName()
if err != nil {
- log.Fatal().Err(err).Msg("Fetching current context")
- return false
+ slog.Error("Fail to retrieve current context. Exiting!")
+ os.Exit(1)
}
return cl == n
}
diff --git a/internal/render/crd.go b/internal/render/crd.go
index efcb6af9..a5bb5af7 100644
--- a/internal/render/crd.go
+++ b/internal/render/crd.go
@@ -6,11 +6,12 @@ package render
import (
"errors"
"fmt"
+ "log/slog"
"strings"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/model1"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
@@ -84,7 +85,7 @@ func (c CustomResourceDefinition) defaultRow(raw *unstructured.Unstructured, r *
}
}
if len(versions) == 0 {
- log.Warn().Msgf("unable to assert CRD versions for %s", crd.Name)
+ slog.Warn("Unable to assert CRD versions", slogs.FQN, crd.Name)
}
r.ID = client.MetaFQN(crd.ObjectMeta)
diff --git a/internal/render/cust_col.go b/internal/render/cust_col.go
index b7ceefc3..ae26a267 100644
--- a/internal/render/cust_col.go
+++ b/internal/render/cust_col.go
@@ -5,14 +5,16 @@ package render
import (
"fmt"
+ "log/slog"
"regexp"
"github.com/derailed/k9s/internal/model1"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/tview"
"k8s.io/kubectl/pkg/cmd/get"
)
-var fullRX = regexp.MustCompile(`^([\w\s%\/-]+)\:?([\w\d\S\W]*?)\|?([N|T|W|R|L|H]{0,3})$`)
+var fullRX = regexp.MustCompile(`^([\w\s%\/-]+)\:?([\w\d\S\W]*?)\|?([N|T|W|V|R|L|H]{0,3})$`)
type colAttr byte
@@ -20,6 +22,7 @@ const (
number colAttr = 'N'
age colAttr = 'T'
wide colAttr = 'W'
+ show colAttr = 'V'
alignLeft colAttr = 'L'
alignRight colAttr = 'R'
hide colAttr = 'H'
@@ -32,6 +35,7 @@ type colAttrs struct {
mxm bool
time bool
wide bool
+ show bool
hide bool
capacity bool
}
@@ -46,7 +50,9 @@ func newColFlags(flags string) colAttrs {
case hide:
c.hide = true
case wide:
- c.wide = true
+ c.wide, c.show = true, false
+ case show:
+ c.show, c.wide = true, false
case alignLeft:
c.align = tview.AlignLeft
case alignRight:
@@ -55,6 +61,8 @@ func newColFlags(flags string) colAttrs {
c.time = true
case number:
c.capacity, c.align = true, tview.AlignRight
+ default:
+ slog.Warn("Unknown column attribute", slogs.Attr, b)
}
}
@@ -69,8 +77,6 @@ type colDef struct {
spec string
}
-// TAG:.spec.containers[0].image|split(":")|.[-1]|TW
-
func parse(s string) (colDef, error) {
mm := fullRX.FindStringSubmatch(s)
if len(mm) == 4 {
@@ -95,6 +101,7 @@ func (c colDef) toHeaderCol() model1.HeaderColumn {
Attrs: model1.Attrs{
Align: c.align,
Wide: c.wide,
+ Show: c.show,
Time: c.time,
MX: c.mx,
MXC: c.mxc,
diff --git a/internal/render/cust_cols.go b/internal/render/cust_cols.go
index 7fde0e72..b3026ffc 100644
--- a/internal/render/cust_cols.go
+++ b/internal/render/cust_cols.go
@@ -5,12 +5,13 @@ package render
import (
"fmt"
+ "log/slog"
"reflect"
"strings"
"time"
"github.com/derailed/k9s/internal/model1"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -150,7 +151,7 @@ func hydrate(o runtime.Object, cc ColumnSpecs, parsers []*jsonpath.JSONPath, rh
Header: cc[idx].Header,
Value: NAValue,
}
- log.Warn().Msgf("unable to find column %s", cc[idx].Header.Name)
+ slog.Warn("Unable to find column %s", slogs.Name, cc[idx].Header.Name)
continue
}
var v string
diff --git a/internal/render/helpers.go b/internal/render/helpers.go
index 92dce48a..5ce26018 100644
--- a/internal/render/helpers.go
+++ b/internal/render/helpers.go
@@ -5,16 +5,17 @@ package render
import (
"context"
+ "log/slog"
"sort"
"strconv"
"strings"
"time"
"github.com/derailed/k9s/internal/client"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/vul"
"github.com/derailed/tview"
"github.com/mattn/go-runewidth"
- "github.com/rs/zerolog/log"
"golang.org/x/text/language"
"golang.org/x/text/message"
v1 "k8s.io/api/core/v1"
@@ -72,7 +73,7 @@ func AsStatus(err error) string {
func asSelector(s *metav1.LabelSelector) string {
sel, err := metav1.LabelSelectorAsSelector(s)
if err != nil {
- log.Error().Err(err).Msg("Selector conversion failed")
+ slog.Error("Selector conversion failed", slogs.Error, err)
return NAValue
}
diff --git a/internal/render/pod.go b/internal/render/pod.go
index bff3a716..6e93b1d2 100644
--- a/internal/render/pod.go
+++ b/internal/render/pod.go
@@ -106,8 +106,8 @@ func (Pod) defaultHeader() model1.Header {
model1.HeaderColumn{Name: "LAST RESTART", Attrs: model1.Attrs{Align: tview.AlignRight, Time: true, Wide: true}},
model1.HeaderColumn{Name: "CPU", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},
model1.HeaderColumn{Name: "MEM", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},
- model1.HeaderColumn{Name: "CPU/R:L", Attrs: model1.Attrs{Align: tview.AlignRight, Wide: true}},
- model1.HeaderColumn{Name: "MEM/R:L", Attrs: model1.Attrs{Align: tview.AlignRight, Wide: true}},
+ model1.HeaderColumn{Name: "CPU/RL", Attrs: model1.Attrs{Align: tview.AlignRight, Wide: true}},
+ model1.HeaderColumn{Name: "MEM/RL", Attrs: model1.Attrs{Align: tview.AlignRight, Wide: true}},
model1.HeaderColumn{Name: "%CPU/R", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},
model1.HeaderColumn{Name: "%CPU/L", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},
model1.HeaderColumn{Name: "%MEM/R", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},
diff --git a/internal/render/policy.go b/internal/render/policy.go
index 2bcd82a5..2e8297a6 100644
--- a/internal/render/policy.go
+++ b/internal/render/policy.go
@@ -5,11 +5,13 @@ package render
import (
"fmt"
+ "log/slog"
+ "strings"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/model1"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
@@ -78,14 +80,16 @@ func (Policy) Render(o interface{}, gvr string, r *model1.Row) error {
// Helpers...
func cleanseResource(r string) string {
- if r == "" {
+ if r == "" || r[0] == '/' {
return r
}
- if r[0] == '/' {
+ tt := strings.Split(r, "/")
+ switch len(tt) {
+ case 2, 3:
+ return strings.TrimPrefix(r, tt[0]+"/")
+ default:
return r
}
- _, n := client.Namespaced(r)
- return n
}
// PolicyRes represents a rbac policy rule.
@@ -158,7 +162,7 @@ func (pp Policies) Upsert(p PolicyRes) Policies {
}
p, err := pp[idx].Merge(p)
if err != nil {
- log.Error().Err(err).Msg("policy upsert failed")
+ slog.Error("Policy upsert failed", slogs.Error, err)
return pp
}
pp[idx] = p
diff --git a/internal/render/policy_int_test.go b/internal/render/policy_int_test.go
new file mode 100644
index 00000000..b16d5264
--- /dev/null
+++ b/internal/render/policy_int_test.go
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright Authors of K9s
+
+package render
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_cleanseResource(t *testing.T) {
+ uu := map[string]struct {
+ r, e string
+ }{
+ "empty": {},
+ "single": {
+ r: "fred",
+ e: "fred",
+ },
+ "grp/res": {
+ r: "fred/blee",
+ e: "blee",
+ },
+ "grp/res/sub": {
+ r: "fred/blee/bob",
+ e: "blee/bob",
+ },
+ }
+
+ for k, u := range uu {
+ t.Run(k, func(t *testing.T) {
+ assert.Equal(t, u.e, cleanseResource(u.r))
+ })
+ }
+}
diff --git a/internal/render/table.go b/internal/render/table.go
index 0bc07e92..21c268be 100644
--- a/internal/render/table.go
+++ b/internal/render/table.go
@@ -7,11 +7,11 @@ import (
"encoding/json"
"errors"
"fmt"
+ "log/slog"
"strings"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/model1"
- "github.com/rs/zerolog/log"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -132,7 +132,7 @@ func (t *Table) defaultRow(row *metav1.TableRow, ns string, r *model1.Row) error
if d, ok := age.(string); ok {
r.Fields = append(r.Fields, d)
} else if t.ageIndex > 0 {
- log.Warn().Msgf("No Duration detected on age field")
+ slog.Warn("No Duration detected on age field")
r.Fields = append(r.Fields, NAValue)
}
diff --git a/internal/slogs/child.go b/internal/slogs/child.go
new file mode 100644
index 00000000..e252ae0a
--- /dev/null
+++ b/internal/slogs/child.go
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright Authors of K9s
+
+package slogs
+
+import "log/slog"
+
+// CLog returns a child logger.
+func CLog(subsys string) *slog.Logger {
+ return slog.With(Subsys, subsys)
+}
diff --git a/internal/slogs/keys.go b/internal/slogs/keys.go
new file mode 100644
index 00000000..abdd01c4
--- /dev/null
+++ b/internal/slogs/keys.go
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright Authors of K9s
+
+package slogs
+
+const (
+ // Error tracks an error logger key.
+ Error = "error"
+
+ // Stack tracks a stack logger key.
+ Stack = "stack"
+
+ // Subsys tracks a subsystem logger key.
+ Subsys = "subsys"
+
+ // SchemaFile tracks a schema file logger key.
+ SchemaFile = "schema-file"
+
+ // RefType tracks a reference type.
+ RefType = "ref-type"
+
+ // GVR tracks a group version resource logger key.
+ GVR = "gvr"
+
+ // AuthorSpec tracks an author spec logger key.
+ AuthSpec = "auth-spec"
+
+ // AuthStatus tracks an auth status logger key.
+ AuthStatus = "auth-status"
+
+ // AuthReason tracks an auth reason logger key.
+ AuthReason = "auth-reason"
+
+ // Ports tracks a ports logger key.
+ Port = "port"
+
+ // Address tracks an address logger key.
+ Address = "address"
+
+ // ResName tracks a resource name logger key.
+ ResName = "res-name"
+
+ // Verb tracks a verb logger key.
+ Verb = "verb"
+
+ // ResType tracks a resource type logger key.
+ ResType = "res-type"
+
+ // View tracks a view logger key.
+ View = "view"
+
+ // GOR tracks a gor logger key.
+ GOR = "gor"
+
+ // Action tracks an action logger key.
+ Action = "action"
+
+ // Page tracks a page logger key.
+ Page = "page"
+
+ // Skin tracks a skin logger key.
+ Skin = "skin"
+
+ // CmdHist tracks a command history logger key.
+ CmdHist = "cmd-hist"
+
+ // Image tracks an image logger key.
+ Image = "image"
+
+ // FQN tracks a fully qualified name logger key.
+ FQN = "fqn"
+
+ // ConfigName tracks a config name logger key.
+ ConfigName = "config-name"
+
+ // CompName tracks a component name logger key.
+ CompName = "comp-name"
+
+ // Command tracks a command logger key.
+ Command = "cmd"
+
+ // Context tracks a context logger key.
+ Context = "context"
+ // Cluster tracks a cluster logger key.
+ Cluster = "cluster"
+
+ // Container tracks a container logger key.
+ Container = "container"
+
+ // Options tracks an options logger key.
+ Options = "options"
+
+ // Count tracks a count logger key.
+ Count = "count"
+
+ // MaxRetries tracks a max retries logger key.
+ MaxRetries = "max-retries"
+
+ // Retry tracks a retry logger key.
+ Retry = "retry"
+
+ // Message tracks a message logger key.
+ Message = "message"
+
+ // Index tracks an index logger key.
+ Index = "index"
+
+ // Path tracks a path logger key.
+ Path = "path"
+
+ // Dir tracks a directory logger key.
+ Dir = "dir"
+
+ // FileName tracks a file name logger key.
+ FileName = "file-name"
+
+ // Key tracks a key logger key.
+ Key = "key"
+
+ // Component tracks a component logger key.
+ Component = "component"
+
+ // RowID tracks a row id logger key.
+ RowID = "row-id"
+
+ // Cell tracks a cell logger key.
+ Cell = "cell"
+
+ // HeaderSize tracks a header size logger key.
+ HeaderSize = "row-size"
+
+ // Namespace tracks a namespace logger key.
+ Namespace = "ns"
+
+ // AllNS tracks all namespaces logger key.
+ AllNS = "all-ns"
+
+ // Max tracks a max logger key.
+ Max = "max"
+
+ // Elapsed tracks an elapsed logger key.
+ Elapsed = "elapsed"
+
+ // Log tracks a log logger key.
+ Log = "log"
+
+ // CO tracks a container logger key.
+ CO = "container"
+
+ // Annotation tracks an annotation logger key.
+ Annotation = "annotation"
+
+ // Bool tracks a boolean logger key.
+ Bool = "bool"
+
+ // Replicas tracks a replicas logger key.
+ Replicas = "replicas"
+
+ // Revision tracks a revision logger key.
+ Revision = "revision"
+
+ // ColName tracks a column name logger key.
+ ColName = "col-name"
+
+ // URL tracks a URL logger key.
+ URL = "url"
+
+ // Attr tracks an attribute logger key.
+ Attr = "attr"
+
+ // Name tracks a name logger key.
+ Name = "name"
+
+ // Matches tracks a matches logger key.
+ Matches = "matches"
+
+ // Line tracks a line logger key.
+ Line = "line"
+
+ // Sig tracks a signal logger key.
+ Sig = "signal"
+
+ // Bin tracks a binary logger key.
+ Bin = "binary"
+
+ // Args tracks an arguments logger key.
+ Args = "args"
+
+ // PodPhase tracks a pod phase logger key.
+ PodPhase = "pod-phase"
+
+ // ShellPodCfg tracks a shell pod config logger key.
+ ShellPodCfg = "shell-pod-cfg"
+
+ // PFID tracks a port forward id logger key.
+ PFID = "port-fwd-id"
+
+ // PFTunnel tracks a port forward tunnel logger key.
+ PFTunnel = "port-fwd-tunnel"
+
+ // Config tracks a config logger key.
+ Config = "config"
+
+ // ResKind tracks a resource kind logger key.
+ ResKind = "res-kind"
+
+ // ResGrpVersion tracks a resource group version logger key.
+ ResGrpVersion = "res-grp-version"
+
+ // ID tracks an id logger key.
+ ID = "id"
+)
diff --git a/internal/ui/action.go b/internal/ui/action.go
index f270e67b..305d3624 100644
--- a/internal/ui/action.go
+++ b/internal/ui/action.go
@@ -4,12 +4,13 @@
package ui
import (
+ "log/slog"
"sort"
"sync"
"github.com/derailed/k9s/internal/model"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
)
type (
@@ -213,7 +214,7 @@ func (a *KeyActions) Hints() model.MenuHints {
},
)
} else {
- log.Error().Msgf("Unable to locate KeyName for %#v", k)
+ slog.Error("Unable to locate key name", slogs.Key, k)
}
}
diff --git a/internal/ui/app.go b/internal/ui/app.go
index acefef97..cbfb1a64 100644
--- a/internal/ui/app.go
+++ b/internal/ui/app.go
@@ -4,15 +4,16 @@
package ui
import (
+ "log/slog"
"os"
"sync"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
)
// App represents an application.
@@ -127,11 +128,11 @@ func (a *App) StylesChanged(s *config.Styles) {
if h, ok := f.ItemAt(0).(*tview.Flex); ok {
h.SetBackgroundColor(s.BgColor())
} else {
- log.Error().Msgf("Header not found")
+ slog.Warn("Header not found", slogs.Subsys, "styles", slogs.Component, "app")
}
}
} else {
- log.Error().Msgf("Main not found")
+ slog.Error("Main panel not found", slogs.Subsys, "styles", slogs.Component, "app")
}
}
@@ -151,13 +152,13 @@ func (a *App) bindKeys() {
}
// BailOut exits the application.
-func (a *App) BailOut() {
+func (a *App) BailOut(exitCode int) {
if err := a.Config.Save(true); err != nil {
- log.Error().Err(err).Msg("config save failed!")
+ slog.Error("Config save failed!", slogs.Error, err)
}
a.Stop()
- os.Exit(0)
+ os.Exit(exitCode)
}
// ResetPrompt reset the prompt model and marks buffer as active.
diff --git a/internal/ui/config.go b/internal/ui/config.go
index 487a1cc9..98019b19 100644
--- a/internal/ui/config.go
+++ b/internal/ui/config.go
@@ -7,15 +7,16 @@ import (
"context"
"errors"
"io/fs"
+ "log/slog"
"os"
"path/filepath"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/model1"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/config"
"github.com/fsnotify/fsnotify"
- "github.com/rs/zerolog/log"
)
// Synchronizer manages ui event queue.
@@ -55,17 +56,17 @@ func (c *Configurator) CustomViewsWatcher(ctx context.Context, s synchronizer) e
if evt.Name == config.AppViewsFile && evt.Op != fsnotify.Chmod {
s.QueueUpdateDraw(func() {
if err := c.RefreshCustomViews(); err != nil {
- log.Warn().Err(err).Msgf("Custom views refresh failed")
+ slog.Warn("Custom views refresh failed", slogs.Error, err)
}
})
}
case err := <-w.Errors:
- log.Warn().Err(err).Msg("CustomView watcher failed")
+ slog.Warn("CustomView watcher failed", slogs.Error, err)
return
case <-ctx.Done():
- log.Debug().Msgf("CustomViewWatcher CANCELED `%s!!", config.AppViewsFile)
+ slog.Debug("CustomViewWatcher canceled", slogs.FileName, config.AppViewsFile)
if err := w.Close(); err != nil {
- log.Error().Err(err).Msg("Closing CustomView watcher")
+ slog.Error("Closing CustomView watcher", slogs.Error, err)
}
return
}
@@ -104,29 +105,29 @@ func (c *Configurator) SkinsDirWatcher(ctx context.Context, s synchronizer) erro
select {
case evt := <-w.Events:
if evt.Op != fsnotify.Chmod && filepath.Base(evt.Name) == filepath.Base(c.skinFile) {
- log.Debug().Msgf("Skin changed: %s", c.skinFile)
+ slog.Debug("Skin file changed detected", slogs.FileName, c.skinFile)
s.QueueUpdateDraw(func() {
c.RefreshStyles(s)
})
}
case err := <-w.Errors:
- log.Info().Err(err).Msg("Skin watcher failed")
+ slog.Warn("Skin watcher failed", slogs.Error, err)
return
case <-ctx.Done():
- log.Debug().Msgf("SkinWatcher CANCELED `%s!!", c.skinFile)
+ slog.Debug("SkinWatcher canceled", slogs.FileName, c.skinFile)
if err := w.Close(); err != nil {
- log.Error().Err(err).Msg("Closing Skin watcher")
+ slog.Error("Closing Skin watcher", slogs.Error, err)
}
return
}
}
}()
- log.Debug().Msgf("SkinWatcher watching %q", config.AppSkinsDir)
+ slog.Debug("SkinWatcher initialized", slogs.Dir, config.AppSkinsDir)
return w.Add(config.AppSkinsDir)
}
-// ConfigWatcher watches for skin settings changes.
+// ConfigWatcher watches for config settings changes.
func (c *Configurator) ConfigWatcher(ctx context.Context, s synchronizer) error {
w, err := fsnotify.NewWatcher()
if err != nil {
@@ -138,16 +139,16 @@ func (c *Configurator) ConfigWatcher(ctx context.Context, s synchronizer) error
select {
case evt := <-w.Events:
if evt.Has(fsnotify.Create) || evt.Has(fsnotify.Write) {
- log.Debug().Msgf("ConfigWatcher file changed: %s", evt.Name)
+ slog.Debug("ConfigWatcher file changed", slogs.FileName, evt.Name)
if evt.Name == config.AppConfigFile {
if err := c.Config.Load(evt.Name, false); err != nil {
- log.Error().Err(err).Msgf("k9s config reload failed")
+ slog.Error("K9s config reload failed", slogs.Error, err)
s.Flash().Warn("k9s config reload failed. Check k9s logs!")
s.Logo().Warn("K9s config reload failed!")
}
} else {
if err := c.Config.K9s.Reload(); err != nil {
- log.Error().Err(err).Msgf("k9s context config reload failed")
+ slog.Error("K9s context config reload failed", slogs.Error, err)
s.Flash().Warn("Context config reload failed. Check k9s logs!")
s.Logo().Warn("Context config reload failed!")
}
@@ -157,19 +158,19 @@ func (c *Configurator) ConfigWatcher(ctx context.Context, s synchronizer) error
})
}
case err := <-w.Errors:
- log.Info().Err(err).Msg("ConfigWatcher failed")
+ slog.Warn("ConfigWatcher failed", slogs.Error, err)
return
case <-ctx.Done():
- log.Debug().Msg("ConfigWatcher CANCELED")
+ slog.Debug("ConfigWatcher canceled")
if err := w.Close(); err != nil {
- log.Error().Err(err).Msg("Canceling ConfigWatcher")
+ slog.Error("Canceling ConfigWatcher", slogs.Error, err)
}
return
}
}
}()
- log.Debug().Msgf("ConfigWatcher watching: %q", config.AppConfigFile)
+ slog.Debug("ConfigWatcher watching", slogs.FileName, config.AppConfigFile)
if err := w.Add(config.AppConfigFile); err != nil {
return err
}
@@ -179,7 +180,7 @@ func (c *Configurator) ConfigWatcher(ctx context.Context, s synchronizer) error
return nil
}
ctConfigFile := filepath.Join(config.AppContextConfig(cl, ct))
- log.Debug().Msgf("ConfigWatcher watching: %q", ctConfigFile)
+ slog.Debug("ConfigWatcher watching", slogs.FileName, ctConfigFile)
return w.Add(ctConfigFile)
}
@@ -193,14 +194,17 @@ func (c *Configurator) activeSkin() (string, bool) {
if ct, err := c.Config.K9s.ActiveContext(); err == nil && ct.Skin != "" {
if _, err := os.Stat(config.SkinFileFromName(ct.Skin)); err == nil {
skin = ct.Skin
- log.Debug().Msgf("[Skin] Loading context skin (%q) from %q", skin, c.Config.K9s.ActiveContextName())
+ slog.Debug("Loading context skin",
+ slogs.Skin, skin,
+ slogs.Context, c.Config.K9s.ActiveContextName(),
+ )
}
}
if sk := c.Config.K9s.UI.Skin; skin == "" && sk != "" {
if _, err := os.Stat(config.SkinFileFromName(sk)); err == nil {
skin = sk
- log.Debug().Msgf("[Skin] Loading global skin (%q)", skin)
+ slog.Debug("Loading global skin", slogs.Skin, skin)
}
}
@@ -237,7 +241,11 @@ func (c *Configurator) RefreshStyles(s synchronizer) {
}
// !!BOZO!! Lame move out!
if bc, err := config.EnsureBenchmarksCfgFile(cl, ct); err != nil {
- log.Warn().Err(err).Msgf("No benchmark config file found: %q@%q", cl, ct)
+ slog.Warn("No benchmark config file found",
+ slogs.Cluster, cl,
+ slogs.Context, ct,
+ slogs.Error, err,
+ )
} else {
c.BenchFile = bc
}
@@ -246,19 +254,26 @@ func (c *Configurator) RefreshStyles(s synchronizer) {
func (c *Configurator) loadSkinFile(s synchronizer) {
skin, ok := c.activeSkin()
if !ok {
- log.Debug().Msgf("No custom skin found. Using stock skin")
+ slog.Debug("No custom skin found. Using stock skin")
c.updateStyles("")
return
}
skinFile := config.SkinFileFromName(skin)
- log.Debug().Msgf("Loading skin file: %q", skinFile)
+ slog.Debug("Loading skin file", slogs.Skin, skinFile)
if err := c.Styles.Load(skinFile); err != nil {
if errors.Is(err, os.ErrNotExist) {
- log.Warn().Msgf("Skin file %q not found in skins dir: %s", filepath.Base(skinFile), config.AppSkinsDir)
+ slog.Warn("Skin file not found in skins dir",
+ slogs.Skin, filepath.Base(skinFile),
+ slogs.Dir, config.AppSkinsDir,
+ slogs.Error, err,
+ )
c.updateStyles("")
} else {
- log.Error().Msgf("Failed to parse skin file -- %s: %s.", filepath.Base(skinFile), err)
+ slog.Error("Failed to parse skin file",
+ slogs.Path, filepath.Base(skinFile),
+ slogs.Error, err,
+ )
c.updateStyles(skinFile)
}
} else {
diff --git a/internal/ui/crumbs_test.go b/internal/ui/crumbs_test.go
index 644f2745..dc43b5ba 100644
--- a/internal/ui/crumbs_test.go
+++ b/internal/ui/crumbs_test.go
@@ -5,6 +5,7 @@ package ui_test
import (
"context"
+ "log/slog"
"testing"
"github.com/derailed/k9s/internal/config"
@@ -12,12 +13,11 @@ import (
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.FatalLevel)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestNewCrumbs(t *testing.T) {
diff --git a/internal/ui/flash.go b/internal/ui/flash.go
index f1884641..0ba37d5b 100644
--- a/internal/ui/flash.go
+++ b/internal/ui/flash.go
@@ -5,12 +5,12 @@ package ui
import (
"context"
+ "log/slog"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
)
const (
@@ -55,7 +55,7 @@ func (f *Flash) StylesChanged(s *config.Styles) {
// Watch watches for flash changes.
func (f *Flash) Watch(ctx context.Context, c model.FlashChan) {
- defer log.Debug().Msgf("Flash Watch Canceled!")
+ defer slog.Debug("Flash Watch Canceled!")
for {
select {
case <-ctx.Done():
diff --git a/internal/ui/pages.go b/internal/ui/pages.go
index eed87e5d..af18fd3e 100644
--- a/internal/ui/pages.go
+++ b/internal/ui/pages.go
@@ -5,10 +5,11 @@ package ui
import (
"fmt"
+ "log/slog"
"github.com/derailed/k9s/internal/model"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
)
// Pages represents a stack of view pages.
@@ -72,9 +73,9 @@ func (p *Pages) delete(c model.Component) {
// Dump for debug.
func (p *Pages) Dump() {
- log.Debug().Msgf("Dumping Pages %p", p)
+ slog.Debug("Dumping Pages", slogs.Page, p)
for i, c := range p.Stack.Peek() {
- log.Debug().Msgf("%d -- %s -- %#v", i, componentID(c), p.GetPrimitive(componentID(c)))
+ slog.Debug(fmt.Sprintf("%d -- %s -- %#v", i, componentID(c), p.GetPrimitive(componentID(c))))
}
}
@@ -102,7 +103,7 @@ func (p *Pages) StackTop(top model.Component) {
func componentID(c model.Component) string {
if c.Name() == "" {
- log.Error().Msg("Component has no name")
+ slog.Error("Component has no name", slogs.Component, fmt.Sprintf("%T", c))
}
return fmt.Sprintf("%s-%p", c.Name(), c)
}
diff --git a/internal/ui/prompt.go b/internal/ui/prompt.go
index 9e8b7ea6..3f43fd28 100644
--- a/internal/ui/prompt.go
+++ b/internal/ui/prompt.go
@@ -104,8 +104,6 @@ func NewPrompt(app *App, noIcons bool, styles *config.Styles) *Prompt {
p.SetDynamicColors(true)
p.SetBorder(true)
p.SetBorderPadding(0, 0, 1, 1)
- p.SetBackgroundColor(styles.K9s.Prompt.BgColor.Color())
- p.SetTextColor(styles.K9s.Prompt.FgColor.Color())
styles.AddListener(&p)
p.SetInputCapture(p.keyboard)
@@ -236,6 +234,7 @@ func (p *Prompt) write(text, suggest string) {
if suggest != "" {
txt += fmt.Sprintf("[%s::-]%s", p.styles.Prompt().SuggestColor, suggest)
}
+ p.StylesChanged(p.styles)
fmt.Fprintf(p, defaultPrompt, p.icon, txt)
}
diff --git a/internal/ui/table.go b/internal/ui/table.go
index fdf68b7d..de4c5151 100644
--- a/internal/ui/table.go
+++ b/internal/ui/table.go
@@ -6,6 +6,7 @@ package ui
import (
"context"
"fmt"
+ "log/slog"
"sync"
"github.com/derailed/k9s/internal"
@@ -15,12 +16,10 @@ import (
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/model1"
"github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/vul"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
- "golang.org/x/text/cases"
- "golang.org/x/text/language"
)
const maxTruncate = 50
@@ -270,6 +269,14 @@ func (t *Table) Update(data *model1.TableData, hasMetrics bool) *model1.TableDat
return t.doUpdate(t.filtered(data))
}
+func (t *Table) GetNamespace() string {
+ if t.GetModel() != nil {
+ return t.GetModel().GetNamespace()
+ }
+
+ return client.NamespaceAll
+}
+
func (t *Table) doUpdate(data *model1.TableData) *model1.TableData {
if client.IsAllNamespaces(data.GetNamespace()) {
t.actions.Add(
@@ -323,7 +330,7 @@ func (t *Table) UpdateUI(cdata, data *model1.TableData) {
cdata.RowsRange(func(row int, re model1.RowEvent) bool {
ore, ok := data.FindRow(re.Row.ID)
if !ok {
- log.Error().Msgf("unable to find original re: %q", re.Row.ID)
+ slog.Error("Unable to find original row event", slogs.RowID, re.Row.ID)
return true
}
t.buildRow(row+1, re, ore, cdata.Header(), pads, isNamespaced)
@@ -346,7 +353,11 @@ func (t *Table) buildRow(r int, re, ore model1.RowEvent, h model1.Header, pads M
ns := t.GetModel().GetNamespace()
for c, field := range re.Row.Fields {
if c >= len(h) {
- log.Error().Msgf("field/header overflow detected for %q -- %d::%d. Check your mappings!", t.GVR(), c, len(h))
+ slog.Error("Field/header overflow detected. Check your mappings!",
+ slogs.GVR, t.GVR(),
+ slogs.Cell, c,
+ slogs.HeaderSize, len(h),
+ )
continue
}
if h[c].Hide || (!t.wide && h[c].Wide) {
@@ -505,7 +516,6 @@ func (t *Table) styleTitle() string {
rc--
}
- base := cases.Title(language.Und, cases.NoLower).String(t.gvr.R())
ns := t.GetModel().GetNamespace()
if client.IsClusterWide(ns) || ns == client.NotNamespaced {
ns = client.NamespaceAll
@@ -524,9 +534,9 @@ func (t *Table) styleTitle() string {
}
var title string
if ns == client.ClusterScope {
- title = SkinTitle(fmt.Sprintf(TitleFmt, base, render.AsThousands(rc)), t.styles.Frame())
+ title = SkinTitle(fmt.Sprintf(TitleFmt, t.gvr, render.AsThousands(rc)), t.styles.Frame())
} else {
- title = SkinTitle(fmt.Sprintf(NSTitleFmt, base, ns, render.AsThousands(rc)), t.styles.Frame())
+ title = SkinTitle(fmt.Sprintf(NSTitleFmt, t.gvr, ns, render.AsThousands(rc)), t.styles.Frame())
}
buff := t.cmdBuff.GetText()
diff --git a/internal/ui/table_helper.go b/internal/ui/table_helper.go
index 479ed2d0..30c4359f 100644
--- a/internal/ui/table_helper.go
+++ b/internal/ui/table_helper.go
@@ -6,11 +6,13 @@ package ui
import (
"context"
"fmt"
+ "log/slog"
+ "os"
"strings"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/config"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
)
const (
@@ -39,7 +41,8 @@ const (
func mustExtractStyles(ctx context.Context) *config.Styles {
styles, ok := ctx.Value(internal.KeyStyles).(*config.Styles)
if !ok {
- log.Fatal().Msg("Expecting valid styles")
+ slog.Error("Expecting valid styles. Exiting!")
+ os.Exit(1)
}
return styles
}
@@ -48,7 +51,7 @@ func mustExtractStyles(ctx context.Context) *config.Styles {
func TrimCell(tv *SelectTable, row, col int) string {
c := tv.GetCell(row, col)
if c == nil {
- log.Error().Err(fmt.Errorf("No cell at location [%d:%d]", row, col)).Msg("Trim cell failed!")
+ slog.Error("Trim cell failed", slogs.Error, fmt.Errorf("no cell at [%d:%d]", row, col))
return ""
}
return strings.TrimSpace(c.Text)
diff --git a/internal/view/actions.go b/internal/view/actions.go
index fa4cf6ad..0b3cf767 100644
--- a/internal/view/actions.go
+++ b/internal/view/actions.go
@@ -6,13 +6,14 @@ package view
import (
"errors"
"fmt"
+ "log/slog"
"strings"
"github.com/derailed/k9s/internal/config"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
)
// AllScopes represents actions available for all views.
@@ -80,12 +81,15 @@ func hotKeyActions(r Runner, aa *ui.KeyActions) error {
errs = errors.Join(errs, fmt.Errorf("duplicate hotkey found for %q in %q", hk.ShortCut, k))
continue
}
- log.Debug().Msgf("Action %q has been overridden by hotkey in %q", hk.ShortCut, k)
+ slog.Debug("Action has been overridden by hotkey",
+ slogs.Action, hk.ShortCut,
+ slogs.Key, k,
+ )
}
command, err := r.EnvFn()().Substitute(hk.Command)
if err != nil {
- log.Warn().Err(err).Msg("Invalid shortcut command")
+ slog.Warn("Invalid shortcut command", slogs.Error, err)
continue
}
@@ -144,7 +148,10 @@ func pluginActions(r Runner, aa *ui.KeyActions) error {
errs = errors.Join(errs, fmt.Errorf("duplicate plugin key found for %q in %q", plugin.ShortCut, k))
continue
}
- log.Debug().Msgf("Action %q has been overridden by plugin in %q", plugin.ShortCut, k)
+ slog.Debug("Action has been overridden by plugin action",
+ slogs.Action, plugin.ShortCut,
+ slogs.Key, k,
+ )
}
if plugin.Dangerous && ro {
@@ -178,7 +185,7 @@ func pluginAction(r Runner, p config.Plugin) ui.ActionHandler {
for i, a := range p.Args {
arg, err := r.EnvFn()().Substitute(a)
if err != nil {
- log.Error().Err(err).Msg("Plugin Args match failed")
+ slog.Error("Plugin Args match failed", slogs.Error, err)
return nil
}
args[i] = arg
diff --git a/internal/view/actions_test.go b/internal/view/actions_test.go
index 76f8abaf..051927fc 100644
--- a/internal/view/actions_test.go
+++ b/internal/view/actions_test.go
@@ -4,14 +4,14 @@
package view
import (
+ "log/slog"
"testing"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.Disabled)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestHasAll(t *testing.T) {
diff --git a/internal/view/app.go b/internal/view/app.go
index 8128c9bc..3ddac265 100644
--- a/internal/view/app.go
+++ b/internal/view/app.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"os"
"os/signal"
"runtime"
@@ -21,6 +22,7 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/k9s/internal/view/cmd"
@@ -28,7 +30,6 @@ import (
"github.com/derailed/k9s/internal/watch"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
)
// ExitStatus indicates UI exit conditions.
@@ -146,10 +147,10 @@ func (a *App) stopImgScanner() {
func (a *App) initImgScanner(version string) {
defer func(t time.Time) {
- log.Debug().Msgf("Scanner init time %s", time.Since(t))
+ slog.Debug("Scanner init time", slogs.Elapsed, time.Since(t))
}(time.Now())
- vul.ImgScanner = vul.NewImageScanner(a.Config.K9s.ImageScans)
+ vul.ImgScanner = vul.NewImageScanner(a.Config.K9s.ImageScans, slog.Default())
go vul.ImgScanner.Init("k9s", version)
}
@@ -183,7 +184,7 @@ func (a *App) initSignals() {
func (a *App) suggestCommand() model.SuggestionFunc {
contextNames, err := a.contextNames()
if err != nil {
- log.Error().Err(err).Msg("failed to list contexts")
+ slog.Error("Failed to list contexts", slogs.Error, err)
}
return func(s string) (entries sort.StringSlice) {
@@ -203,7 +204,7 @@ func (a *App) suggestCommand() model.SuggestionFunc {
namespaceNames, err := a.factory.Client().ValidNamespaceNames()
if err != nil {
- log.Error().Err(err).Msg("failed to list namespaces")
+ slog.Error("Failed to obtain list of namespaces", slogs.Error, err)
}
entries = append(entries, cmd.SuggestSubCommand(s, namespaceNames, contextNames)...)
if len(entries) == 0 {
@@ -215,9 +216,6 @@ func (a *App) suggestCommand() model.SuggestionFunc {
}
func (a *App) contextNames() ([]string, error) {
- if !a.Conn().ConnectionOK() {
- return nil, errors.New("no connection")
- }
contexts, err := a.factory.Client().Config().Contexts()
if err != nil {
return nil, err
@@ -254,10 +252,11 @@ func (a *App) bindKeys() {
}
func (a *App) dumpGOR(evt *tcell.EventKey) *tcell.EventKey {
- log.Debug().Msgf("GOR %d", runtime.NumGoroutine())
- // bb := make([]byte, 5_000_000)
- // runtime.Stack(bb, true)
- // log.Debug().Msgf("GOR\n%s", string(bb))
+ slog.Debug("GOR", slogs.GOR, runtime.NumGoroutine())
+ bb := make([]byte, 5_000_000)
+ runtime.Stack(bb, true)
+ slog.Debug("GOR stack", slogs.Stack, string(bb))
+
return evt
}
@@ -270,7 +269,8 @@ func (a *App) toggleHeader(header, logo bool) {
a.showHeader, a.showLogo = header, logo
flex, ok := a.Main.GetPrimitive("main").(*tview.Flex)
if !ok {
- log.Fatal().Msg("Expecting valid flex view")
+ slog.Error("Expecting flex view main panel. Exiting!")
+ os.Exit(1)
}
if a.showHeader {
flex.RemoveItemAtIndex(0)
@@ -285,7 +285,8 @@ func (a *App) toggleCrumbs(flag bool) {
a.showCrumbs = flag
flex, ok := a.Main.GetPrimitive("main").(*tview.Flex)
if !ok {
- log.Fatal().Msg("Expecting valid flex view")
+ slog.Error("Expecting valid flex view main panel. Exiting!")
+ os.Exit(1)
}
if a.showCrumbs {
if _, ok := flex.ItemAt(2).(*ui.Crumbs); !ok {
@@ -341,20 +342,20 @@ func (a *App) Resume() {
if a.Config.K9s.UI.Reactive {
if err := a.ConfigWatcher(ctx, a); err != nil {
- log.Warn().Err(err).Msgf("ConfigWatcher failed")
+ slog.Warn("ConfigWatcher failed", slogs.Error, err)
}
if err := a.SkinsDirWatcher(ctx, a); err != nil {
- log.Warn().Err(err).Msgf("SkinsWatcher failed")
+ slog.Warn("SkinsWatcher failed", slogs.Error, err)
}
if err := a.CustomViewsWatcher(ctx, a); err != nil {
- log.Warn().Err(err).Msgf("CustomView watcher failed")
+ slog.Warn("CustomView watcher failed", slogs.Error, err)
}
}
}
func (a *App) clusterUpdater(ctx context.Context) {
if err := a.refreshCluster(ctx); err != nil {
- log.Error().Err(err).Msgf("Cluster updater failed!")
+ slog.Error("Cluster updater failed!", slogs.Error, err)
return
}
@@ -363,13 +364,13 @@ func (a *App) clusterUpdater(ctx context.Context) {
for {
select {
case <-ctx.Done():
- log.Debug().Msg("ClusterInfo updater canceled!")
+ slog.Debug("ClusterInfo updater canceled!")
return
case <-time.After(delay):
if err := a.refreshCluster(ctx); err != nil {
- log.Error().Err(err).Msgf("ClusterUpdater failed")
+ slog.Error("Cluster updates failed. Giving up ;(", slogs.Error, err)
if delay = bf.NextBackOff(); delay == backoff.Stop {
- a.BailOut()
+ a.BailOut(1)
return
}
} else {
@@ -400,9 +401,12 @@ func (a *App) refreshCluster(context.Context) error {
count, maxConnRetry := atomic.LoadInt32(&a.conRetry), int32(a.Config.K9s.MaxConnRetry)
if count >= maxConnRetry {
- log.Error().Msgf("Conn check failed (%d/%d). Bailing out!", count, maxConnRetry)
+ slog.Error("Conn check failed. Bailing out!",
+ slogs.Retry, count,
+ slogs.MaxRetries, maxConnRetry,
+ )
ExitStatus = fmt.Sprintf("Lost K8s connection (%d). Bailing out!", count)
- a.BailOut()
+ a.BailOut(1)
}
if count > 0 {
a.Status(model.FlashWarn, fmt.Sprintf("Dial K8s Toast [%d/%d]", count, maxConnRetry))
@@ -412,7 +416,7 @@ func (a *App) refreshCluster(context.Context) error {
// Reload alias
go func() {
if err := a.command.Reset(a.Config.ContextAliasesPath(), false); err != nil {
- log.Warn().Err(err).Msgf("Command reset failed")
+ slog.Warn("Command reset failed", slogs.Error, err)
a.QueueUpdateDraw(func() {
a.Logo().Warn("Aliases load failed!")
})
@@ -439,18 +443,16 @@ func (a *App) switchNS(ns string) error {
}
func (a *App) switchContext(ci *cmd.Interpreter, force bool) error {
- name, ok := ci.HasContext()
- if !ok || a.Config.ActiveContextName() == name {
- if !force {
- return nil
- }
+ contextName, ok := ci.HasContext()
+ if (!ok || a.Config.ActiveContextName() == contextName) && !force {
+ return nil
}
a.Halt()
defer a.Resume()
{
a.Config.Reset()
- ct, err := a.Config.K9s.ActivateContext(name)
+ ct, err := a.Config.ActivateContext(contextName)
if err != nil {
return err
}
@@ -465,7 +467,7 @@ func (a *App) switchContext(ci *cmd.Interpreter, force bool) error {
}
ns := a.Config.ActiveNamespace()
if !a.Conn().IsValidNamespace(ns) {
- log.Warn().Msgf("Unable to validate namespace: %q. Using %q as active namespace", ns, ns)
+ slog.Warn("Unable to validate namespace", slogs.Namespace, ns)
if err := a.Config.SetActiveNamespace(ns); err != nil {
return err
}
@@ -473,17 +475,19 @@ func (a *App) switchContext(ci *cmd.Interpreter, force bool) error {
a.Flash().Infof("Using %q namespace", ns)
if err := a.Config.Save(true); err != nil {
- log.Error().Err(err).Msg("config save failed!")
- } else {
- log.Debug().Msgf("Saved context config for: %q", name)
+ slog.Error("Fail to save config to disk", slogs.Subsys, "config", slogs.Error, err)
}
a.initFactory(ns)
if err := a.command.Reset(a.Config.ContextAliasesPath(), true); err != nil {
return err
}
- log.Debug().Msgf("--> Switching Context %q -- %q -- %q", name, ns, a.Config.ActiveView())
- a.Flash().Infof("Switching context to %q::%q", name, ns)
+ slog.Debug("Switching Context",
+ slogs.Context, contextName,
+ slogs.Namespace, ns,
+ slogs.View, a.Config.ActiveView(),
+ )
+ a.Flash().Infof("Switching context to %q::%q", contextName, ns)
a.ReloadStyles()
a.gotoResource(a.Config.ActiveView(), "", true, true)
a.clusterModel.Reset(a.factory)
@@ -498,24 +502,20 @@ func (a *App) initFactory(ns string) {
}
// BailOut exists the application.
-func (a *App) BailOut() {
+func (a *App) BailOut(exitCode int) {
defer func() {
if err := recover(); err != nil {
- log.Error().Msgf("Bailing out %v", err)
+ slog.Error("Bailout failed", slogs.Error, err)
}
}()
- if err := a.Config.Save(true); err != nil {
- log.Error().Err(err).Msg("config save failed!")
- }
-
if err := nukeK9sShell(a); err != nil {
- log.Error().Err(err).Msgf("nuking k9s shell pod")
+ slog.Error("Unable to nuke k9s shell pod", slogs.Error, err)
}
a.stopImgScanner()
a.factory.Terminate()
- a.App.BailOut()
+ a.App.BailOut(exitCode)
}
// Run starts the application loop.
@@ -646,7 +646,7 @@ func (a *App) cowCmd(msg string) {
}
func (a *App) dirCmd(path string, pushCmd bool) error {
- log.Debug().Msgf("DIR PATH %q", path)
+ slog.Debug("Exec Dir command", slogs.Path, path)
_, err := os.Stat(path)
if err != nil {
return err
@@ -670,7 +670,7 @@ func (a *App) quitCmd(evt *tcell.EventKey) *tcell.EventKey {
}
if !a.Config.K9s.NoExitOnCtrlC {
- a.BailOut()
+ a.BailOut(0)
}
// overwrite the default ctrl-c behavior of tview
@@ -765,7 +765,10 @@ func (a *App) gotoResource(c, path string, clearStack bool, pushCmd bool) {
func (a *App) inject(c model.Component, clearStack bool) error {
ctx := context.WithValue(context.Background(), internal.KeyApp, a)
if err := c.Init(ctx); err != nil {
- log.Error().Err(err).Msgf("Component init failed for %q", c.Name())
+ slog.Error("Component init failed",
+ slogs.Error, err,
+ slogs.CompName, c.Name(),
+ )
return err
}
if clearStack {
diff --git a/internal/view/benchmark.go b/internal/view/benchmark.go
index 535b39f8..386241e7 100644
--- a/internal/view/benchmark.go
+++ b/internal/view/benchmark.go
@@ -5,6 +5,7 @@ package view
import (
"context"
+ "log/slog"
"os"
"path/filepath"
"strings"
@@ -13,9 +14,10 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/config/data"
+ "github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
)
// Benchmark represents a service benchmark results view.
@@ -71,8 +73,10 @@ func fileToSubject(path string) string {
func benchDir(cfg *config.Config) string {
ct, err := cfg.K9s.ActiveContext()
if err != nil {
- log.Error().Err(err).Msgf("no active context located")
+ slog.Error("No active context located", slogs.Error, err)
+ return render.MissingValue
}
+
return filepath.Join(
config.AppBenchmarksDir,
data.SanitizeFileName(ct.ClusterName),
diff --git a/internal/view/browser.go b/internal/view/browser.go
index 52b6a2cb..e691b8d0 100644
--- a/internal/view/browser.go
+++ b/internal/view/browser.go
@@ -6,6 +6,7 @@ package view
import (
"context"
"fmt"
+ "log/slog"
"sort"
"strconv"
"strings"
@@ -18,10 +19,10 @@ import (
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/model1"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -60,6 +61,7 @@ func (b *Browser) getUpdating() bool {
// Init watches all running pods in given namespace.
func (b *Browser) Init(ctx context.Context) error {
var err error
+
b.meta, err = dao.MetaAccess.MetaFor(b.GVR())
if err != nil {
return err
@@ -151,7 +153,7 @@ func (b *Browser) Start() {
ns = n
}
if err := b.app.switchNS(ns); err != nil {
- log.Error().Err(err).Msgf("ns switch failed")
+ slog.Error("Unable to switch namespace", slogs.Error, err)
}
b.Stop()
@@ -205,7 +207,10 @@ func (b *Browser) BufferActive(state bool, k model.BufferKind) {
return
}
if err := b.GetModel().Refresh(b.GetContext()); err != nil {
- log.Error().Err(err).Msgf("Refresh failed for %s", b.GVR())
+ slog.Error("Model refresh failed",
+ slogs.GVR, b.GVR(),
+ slogs.Error, err,
+ )
}
data := b.GetModel().Peek()
cdata := b.Update(data, b.App().Conn().HasMetrics())
@@ -462,7 +467,7 @@ func editRes(app *App, gvr client.GVR, path string) error {
func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
i, err := strconv.Atoi(string(evt.Rune()))
if err != nil {
- log.Error().Err(err).Msgf("Fail to switch namespace")
+ slog.Error("Unable to convert keystroke", slogs.Error, err)
return nil
}
ns := b.namespaces[i]
@@ -487,7 +492,7 @@ func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
b.SelectRow(1, 0, true)
b.app.CmdBuff().Reset()
if err := b.app.Config.SetActiveNamespace(b.GetModel().GetNamespace()); err != nil {
- log.Error().Err(err).Msg("Config save NS failed!")
+ slog.Error("Unable to set active namespace during ns switch", slogs.Error, err)
}
return nil
@@ -561,11 +566,11 @@ func (b *Browser) refreshActions() {
b.Actions().Merge(aa)
if err := pluginActions(b, b.Actions()); err != nil {
- log.Warn().Msgf("Plugins load failed: %s", err)
+ slog.Warn("Plugins load failed", slogs.Error, err)
b.app.Logo().Warn("Plugins load failed!")
}
if err := hotKeyActions(b, b.Actions()); err != nil {
- log.Warn().Msgf("Hotkeys load failed: %s", err)
+ slog.Warn("Hotkeys load failed", slogs.Error, err)
b.app.Logo().Warn("HotKeys load failed!")
}
b.app.Menu().HydrateMenu(b.Hints())
@@ -591,7 +596,11 @@ func (b *Browser) namespaceActions(aa *ui.KeyActions) {
b.namespaces[index] = ns
index++
} else {
- log.Warn().Msgf("No number key available for favorite namespace %s (%d of %d). Skipping...", ns, index, len(favNamespaces))
+ slog.Warn("No number key available for favorite namespace. Skipping...",
+ slogs.Namespace, ns,
+ slogs.Index, index,
+ slogs.Max, len(favNamespaces),
+ )
break
}
}
diff --git a/internal/view/cluster_info.go b/internal/view/cluster_info.go
index 3ff7cd83..f11c541e 100644
--- a/internal/view/cluster_info.go
+++ b/internal/view/cluster_info.go
@@ -5,15 +5,16 @@ package view
import (
"fmt"
+ "log/slog"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
)
var _ model.ClusterInfoListener = (*ClusterInfo)(nil)
@@ -55,7 +56,7 @@ func (c *ClusterInfo) hasMetrics() bool {
if mx {
auth, err := c.app.Conn().CanI("", "metrics.k8s.io/v1beta1/nodes", "", client.ListAccess)
if err != nil {
- log.Warn().Err(err).Msgf("No nodes metrics access")
+ slog.Warn("No nodes metrics access", slogs.Error, err)
}
mx = auth
}
diff --git a/internal/view/cmd/helpers_test.go b/internal/view/cmd/helpers_test.go
index da4f821d..204bbe67 100644
--- a/internal/view/cmd/helpers_test.go
+++ b/internal/view/cmd/helpers_test.go
@@ -4,14 +4,14 @@
package cmd
import (
+ "log/slog"
"testing"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.FatalLevel)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func Test_toLabels(t *testing.T) {
diff --git a/internal/view/command.go b/internal/view/command.go
index 3a6fb670..e3695da7 100644
--- a/internal/view/command.go
+++ b/internal/view/command.go
@@ -6,16 +6,16 @@ package view
import (
"errors"
"fmt"
+ "log/slog"
"regexp"
"runtime/debug"
"strings"
"sync"
- "github.com/rs/zerolog/log"
-
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/model"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/view/cmd"
)
@@ -47,7 +47,7 @@ func (c *Command) AliasesFor(s string) []string {
func (c *Command) Init(path string) error {
c.alias = dao.NewAlias(c.app.factory)
if _, err := c.alias.Ensure(path); err != nil {
- log.Error().Err(err).Msgf("Alias ensure failed!")
+ slog.Error("Ensure aliases failed", slogs.Error, err)
return err
}
customViewers = loadCustomViewers()
@@ -164,9 +164,9 @@ func (c *Command) run(p *cmd.Interpreter, fqn string, clearStack bool, pushCmd b
if context, ok := p.HasContext(); ok {
if context != c.app.Config.ActiveContextName() {
if err := c.app.Config.Save(true); err != nil {
- log.Error().Err(err).Msg("config save failed!")
+ slog.Error("Config save failed during command exec", slogs.Error, err)
} else {
- log.Debug().Msgf("Saved context config for: %q", context)
+ slog.Debug("Successfully saved config", slogs.Context, context)
}
}
res, err := dao.AccessorFor(c.app.factory, client.NewGVR("contexts"))
@@ -178,7 +178,7 @@ func (c *Command) run(p *cmd.Interpreter, fqn string, clearStack bool, pushCmd b
return errors.New("expecting a switchable resource")
}
if err := switcher.Switch(context); err != nil {
- log.Error().Err(err).Msgf("Context switch failed")
+ slog.Error("Unable to switch context", slogs.Error, err)
return err
}
if err := c.app.switchContext(p, false); err != nil {
@@ -226,7 +226,10 @@ func (c *Command) defaultCmd(isRoot bool) error {
if err := c.run(p, "", true, true); err != nil {
p = p.Reset(defCmd)
- log.Error().Err(fmt.Errorf("Command failed. Using default command: %s", p.GetLine()))
+ slog.Error("Command exec failed. Using default command",
+ slogs.Command, p.GetLine(),
+ slogs.Error, err,
+ )
return c.run(p, "", true, true)
}
@@ -242,7 +245,7 @@ func (c *Command) specialCmd(p *cmd.Interpreter, pushCmd bool) bool {
c.app.cowCmd(msg)
}
case p.IsBailCmd():
- c.app.BailOut()
+ c.app.BailOut(0)
case p.IsHelpCmd():
_ = c.app.helpCmd(nil)
case p.IsAliasCmd():
@@ -323,10 +326,10 @@ func (c *Command) componentFor(gvr client.GVR, fqn string, v *MetaViewer) Resour
func (c *Command) exec(p *cmd.Interpreter, gvr client.GVR, comp model.Component, clearStack bool, pushCmd bool) (err error) {
defer func() {
if e := recover(); e != nil {
- log.Error().Msgf("Something bad happened! %#v", e)
+ slog.Error("Failure detected during command exec", slogs.Error, e)
c.app.Content.Dump()
- log.Debug().Msgf("History %v", c.app.cmdHistory.List())
- log.Error().Msg(string(debug.Stack()))
+ slog.Debug("Dumping history buffer", slogs.CmdHist, c.app.cmdHistory.List())
+ slog.Error("Dumping stack", slogs.Stack, debug.Stack())
p := cmd.NewInterpreter("pod")
cmds := c.app.cmdHistory.List()
diff --git a/internal/view/context.go b/internal/view/context.go
index e4788229..cd339091 100644
--- a/internal/view/context.go
+++ b/internal/view/context.go
@@ -6,14 +6,15 @@ package view
import (
"errors"
"fmt"
+ "log/slog"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/view/cmd"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
)
const (
@@ -103,7 +104,10 @@ func (c *Context) showRenameModal(name string, ok func(form *tview.Form, context
}
func (c *Context) useCtx(app *App, model ui.Tabular, gvr client.GVR, path string) {
- log.Debug().Msgf("SWITCH CTX %q--%q", gvr, path)
+ slog.Debug("Using context",
+ slogs.GVR, gvr,
+ slogs.FQN, path,
+ )
if err := useContext(app, path); err != nil {
app.Flash().Err(err)
return
@@ -124,8 +128,17 @@ func useContext(app *App, name string) error {
if !ok {
return errors.New("expecting a switchable resource")
}
+
+ app.Config.K9s.ToggleContextSwitch(true)
+ defer app.Config.K9s.ToggleContextSwitch(false)
+
+ // Save config prior to context switch...
+ if err := app.Config.Save(true); err != nil {
+ slog.Error("Fail to save config to disk", slogs.Subsys, "config", slogs.Error, err)
+ }
+
if err := switcher.Switch(name); err != nil {
- log.Error().Err(err).Msgf("Context switch failed")
+ slog.Error("Context switch failed during use command", slogs.Error, err)
return err
}
diff --git a/internal/view/cronjob.go b/internal/view/cronjob.go
index 05a78fe3..0862e041 100644
--- a/internal/view/cronjob.go
+++ b/internal/view/cronjob.go
@@ -6,16 +6,17 @@ 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/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
batchv1 "k8s.io/api/batch/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
@@ -44,9 +45,9 @@ func NewCronJob(gvr client.GVR) ResourceViewer {
return &c
}
-func (c *CronJob) showJobs(app *App, model ui.Tabular, gvr client.GVR, path string) {
- log.Debug().Msgf("Showing Jobs %q:%q -- %q", model.GetNamespace(), gvr, path)
- o, err := app.factory.Get(gvr.String(), path, true, labels.Everything())
+func (c *CronJob) showJobs(app *App, _ ui.Tabular, gvr client.GVR, fqn string) {
+ slog.Debug("Showing Jobs", slogs.GVR, gvr, slogs.FQN, fqn)
+ o, err := app.factory.Get(gvr.String(), fqn, true, labels.Everything())
if err != nil {
app.Flash().Err(err)
return
@@ -59,16 +60,20 @@ func (c *CronJob) showJobs(app *App, model ui.Tabular, gvr client.GVR, path stri
return
}
+ ns, _ := client.Namespaced(fqn)
+ if err := app.Config.SetActiveNamespace(ns); err != nil {
+ slog.Error("Unable to set active namespace during show pods", slogs.Error, err)
+ }
v := NewJob(client.NewGVR("batch/v1/jobs"))
- v.SetContextFn(jobCtx(path, string(cj.UID)))
+ v.SetContextFn(jobCtx(fqn, string(cj.UID)))
if err := app.inject(v, false); err != nil {
app.Flash().Err(err)
}
}
-func jobCtx(path, uid string) ContextFunc {
+func jobCtx(fqn, uid string) ContextFunc {
return func(ctx context.Context) context.Context {
- ctx = context.WithValue(ctx, internal.KeyPath, path)
+ ctx = context.WithValue(ctx, internal.KeyPath, fqn)
return context.WithValue(ctx, internal.KeyUID, uid)
}
}
diff --git a/internal/view/env.go b/internal/view/env.go
index 8ac71558..ebcc214a 100644
--- a/internal/view/env.go
+++ b/internal/view/env.go
@@ -5,12 +5,13 @@ package view
import (
"fmt"
+ "log/slog"
"regexp"
"sort"
"strconv"
"strings"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
)
// Env represent K9s and K8s available environment variables.
@@ -50,7 +51,10 @@ func (e Env) Substitute(arg string) (string, error) {
key, inverse := keyFromSubmatch(m)
v, ok := e[strings.ToUpper(key)]
if !ok {
- log.Warn().Msgf("no k9s environment matching key %q:%q", m[0], key)
+ slog.Warn("No k9s environment matching key",
+ slogs.Matches, matches,
+ slogs.Key, key,
+ )
continue
}
if b, err := strconv.ParseBool(v); err == nil {
diff --git a/internal/view/exec.go b/internal/view/exec.go
index d7d0fa04..6c8e9a26 100644
--- a/internal/view/exec.go
+++ b/internal/view/exec.go
@@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"io"
+ "log/slog"
"os"
"os/exec"
"os/signal"
@@ -17,13 +18,13 @@ import (
"time"
"github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/fatih/color"
- "github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
@@ -85,7 +86,7 @@ func runK(a *App, opts shellOpts) error {
return fmt.Errorf("unable to run command")
}
for v := range stChan {
- log.Debug().Msgf(" - %s", v)
+ slog.Debug("stdout", slogs.Line, v)
}
var errs error
for e := range errChan {
@@ -183,19 +184,19 @@ func execute(opts shellOpts, statusChan chan<- string) error {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
go func(cancel context.CancelFunc) {
- defer log.Debug().Msgf("SIGNAL_GOR - BAILED!!")
+ defer slog.Debug("Got signal canceled")
select {
case sig := <-sigChan:
- log.Debug().Msgf("Command canceled with signal! %#v", sig)
+ slog.Debug("Command canceled with signal", slogs.Sig, sig)
cancel()
case <-ctx.Done():
- log.Debug().Msgf("SIGNAL Context CANCELED!")
+ slog.Debug("Signal context canceled!")
}
}(cancel)
cmds := make([]*exec.Cmd, 0, 1)
cmd := exec.CommandContext(ctx, opts.binary, opts.args...)
- log.Debug().Msgf("RUNNING> %s", opts)
+ slog.Debug("Exec command", slogs.Command, opts)
if env := os.Getenv("K9S_EDITOR"); env != "" {
// There may be situations where the user sets the editor as the binary
@@ -218,14 +219,17 @@ func execute(opts shellOpts, statusChan chan<- string) error {
continue
}
cmd := exec.CommandContext(ctx, tokens[0], tokens[1:]...)
- log.Debug().Msgf("\t| %s", cmd)
+ slog.Debug("Exec command", slogs.Command, cmd)
cmds = append(cmds, cmd)
}
var o, e bytes.Buffer
err := pipe(ctx, opts, statusChan, &o, &e, cmds...)
if err != nil {
- log.Err(err).Msgf("Command failed")
+ slog.Error("Exec failed",
+ slogs.Error, err,
+ slogs.Command, cmds,
+ )
return errors.Join(err, fmt.Errorf("%s", e.String()))
}
@@ -235,11 +239,11 @@ func execute(opts shellOpts, statusChan chan<- string) error {
func runKu(a *App, opts shellOpts) (string, error) {
bin, err := exec.LookPath("kubectl")
if errors.Is(err, exec.ErrDot) {
- log.Error().Err(err).Msgf("kubectl command must not be in the current working directory")
+ slog.Error("Kubectl exec can not reside in current working directory", slogs.Error, err)
return "", err
}
if err != nil {
- log.Error().Err(err).Msgf("kubectl command is not in your path")
+ slog.Error("Kubectl exec not found", slogs.Error, err)
return "", err
}
var args []string
@@ -266,7 +270,10 @@ func oneShoot(opts shellOpts) (string, error) {
clearScreen()
}
- log.Debug().Msgf("Running command> %s %s", opts.binary, strings.Join(opts.args, " "))
+ slog.Debug("Executing command",
+ slogs.Bin, opts.binary,
+ slogs.Args, strings.Join(opts.args, " "),
+ )
cmd := exec.Command(opts.binary, opts.args...)
var err error
@@ -348,7 +355,7 @@ func sshIn(a *App, fqn, co string) error {
}
args = append(args, "sh", "-c", shellCheck)
}
- log.Debug().Msgf("ARGS %#v", args)
+ slog.Debug("Running command with args", slogs.Args, args)
c := color.New(color.BgGreen).Add(color.FgBlack).Add(color.Bold)
err = runK(a, shellOpts{clear: true, banner: c.Sprintf(bannerFmt, fqn, co), args: args})
@@ -416,7 +423,10 @@ func launchShellPod(ctx context.Context, a *App, node string) error {
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod); err != nil {
return err
}
- log.Debug().Msgf("Checking shell pod [%d] %v", i, pod.Status.Phase)
+ slog.Debug("Checking k9s shell pod retries",
+ slogs.Retry, i,
+ slogs.PodPhase, pod.Status.Phase,
+ )
if pod.Status.Phase == v1.PodRunning {
return nil
}
@@ -439,7 +449,7 @@ func k9sShellPod(node string, cfg config.ShellPod) *v1.Pod {
var grace int64
var priv bool = true
- log.Debug().Msgf("Shell Config %#v", cfg)
+ slog.Debug("Shell pod config", slogs.ShellPodCfg, cfg)
c := v1.Container{
Name: k9sShell,
Image: cfg.Image,
@@ -518,7 +528,7 @@ func pipe(_ context.Context, opts shellOpts, statusChan chan<- string, w, e *byt
go func() {
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, w, e
if err := cmd.Run(); err != nil {
- log.Error().Err(err).Msgf("Command failed: %s", err)
+ slog.Error("Command exec failed", slogs.Error, err)
} else {
for _, l := range strings.Split(w.String(), "\n") {
if l != "" {
@@ -526,7 +536,7 @@ func pipe(_ context.Context, opts shellOpts, statusChan chan<- string, w, e *byt
}
}
statusChan <- fmt.Sprintf("Command completed successfully: %q", render.Truncate(cmd.String(), 20))
- log.Info().Msgf("Command completed successfully: %q", cmd.String())
+ slog.Info("Command ran successfully", slogs.Command, cmd.String())
}
close(statusChan)
}()
@@ -535,9 +545,9 @@ func pipe(_ context.Context, opts shellOpts, statusChan chan<- string, w, e *byt
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
_, _ = cmd.Stdout.Write([]byte(opts.banner))
- log.Debug().Msgf("Running Start")
+ slog.Debug("Exec started")
err := cmd.Run()
- log.Debug().Msgf("Running Done: %v", err)
+ slog.Debug("Running exec done", slogs.Error, err)
if err == nil {
statusChan <- fmt.Sprintf("Command completed successfully: %q", cmd.String())
}
@@ -557,7 +567,7 @@ func pipe(_ context.Context, opts shellOpts, statusChan chan<- string, w, e *byt
cmds[last].Stdout = os.Stdout
for _, cmd := range cmds {
- log.Debug().Msgf("Starting CMD %s", cmd)
+ slog.Debug("Starting command", slogs.Command, cmd)
if err := cmd.Start(); err != nil {
return err
}
diff --git a/internal/view/helpers.go b/internal/view/helpers.go
index ad8fb4c5..e533c9ee 100644
--- a/internal/view/helpers.go
+++ b/internal/view/helpers.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"os"
"strconv"
"strings"
@@ -19,11 +20,11 @@ import (
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/model1"
"github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/view/cmd"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
"github.com/sahilm/fuzzy"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -47,7 +48,11 @@ func aliasesFor(m v1.APIResource, aa []string) map[string]struct{} {
}
func clipboardWrite(text string) error {
- return clipboard.WriteAll(text)
+ if text != "" {
+ return clipboard.WriteAll(text)
+ }
+
+ return nil
}
func sanitizeEsc(s string) string {
@@ -149,7 +154,7 @@ func showPods(app *App, path, labelSel, fieldSel string) {
ns, _ := client.Namespaced(path)
if err := app.Config.SetActiveNamespace(ns); err != nil {
- log.Error().Err(err).Msg("Config NS set failed!")
+ slog.Error("Unable to set active namespace during show pods", slogs.Error, err)
}
if err := app.inject(v, false); err != nil {
app.Flash().Err(err)
diff --git a/internal/view/helpers_test.go b/internal/view/helpers_test.go
index 5c2ddbe2..f7eefcdb 100644
--- a/internal/view/helpers_test.go
+++ b/internal/view/helpers_test.go
@@ -6,6 +6,7 @@ package view
import (
"context"
"errors"
+ "log/slog"
"testing"
"github.com/derailed/k9s/internal"
@@ -15,14 +16,13 @@ import (
"github.com/derailed/k9s/internal/model1"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog"
"github.com/sahilm/fuzzy"
"github.com/stretchr/testify/assert"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.Disabled)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestParsePFAnn(t *testing.T) {
diff --git a/internal/view/image_extender.go b/internal/view/image_extender.go
index 10d99dda..96d364c9 100644
--- a/internal/view/image_extender.go
+++ b/internal/view/image_extender.go
@@ -6,13 +6,14 @@ package view
import (
"context"
"fmt"
+ "log/slog"
"strings"
"github.com/derailed/k9s/internal/dao"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
corev1 "k8s.io/api/core/v1"
)
@@ -94,8 +95,8 @@ func (s *ImageExtender) showImageDialog(path string) error {
return nil
}
-func (s *ImageExtender) makeSetImageForm(sel string) (*tview.Form, error) {
- podSpec, err := s.getPodSpec(sel)
+func (s *ImageExtender) makeSetImageForm(fqn string) (*tview.Form, error) {
+ podSpec, err := s.getPodSpec(fqn)
if err != nil {
return nil, err
}
@@ -126,12 +127,15 @@ func (s *ImageExtender) makeSetImageForm(sel string) (*tview.Form, error) {
}
ctx, cancel := context.WithTimeout(context.Background(), s.App().Conn().Config().CallTimeout())
defer cancel()
- if err := s.setImages(ctx, sel, imageSpecsModified); err != nil {
- log.Error().Err(err).Msgf("PodSpec %s image update failed", sel)
+ if err := s.setImages(ctx, fqn, imageSpecsModified); err != nil {
+ slog.Error("Unable to set image name",
+ slogs.FQN, fqn,
+ slogs.Error, err,
+ )
s.App().Flash().Err(err)
return
}
- s.App().Flash().Infof("Resource %s:%s image updated successfully", s.GVR(), sel)
+ s.App().Flash().Infof("Resource %s:%s image updated successfully", s.GVR(), fqn)
}).
AddButton("Cancel", func() {
s.dismissDialog()
diff --git a/internal/view/live_view.go b/internal/view/live_view.go
index bc24d4d2..e63d88e0 100644
--- a/internal/view/live_view.go
+++ b/internal/view/live_view.go
@@ -6,16 +6,17 @@ package view
import (
"context"
"fmt"
+ "log/slog"
"strconv"
"strings"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
"github.com/sahilm/fuzzy"
)
@@ -233,12 +234,12 @@ func (v *LiveView) Start() {
ctx, v.cancel = context.WithCancel(v.defaultCtx())
if err := v.model.Watch(ctx); err != nil {
- log.Error().Err(err).Msgf("LiveView watcher failed")
+ slog.Error("LiveView watcher failed", slogs.Error, err)
}
return
}
if err := v.model.Refresh(v.defaultCtx()); err != nil {
- log.Error().Err(err).Msgf("refresh failed")
+ slog.Error("LiveView refresh failed", slogs.Error, err)
}
}
diff --git a/internal/view/log.go b/internal/view/log.go
index d5682e78..e56d94ef 100644
--- a/internal/view/log.go
+++ b/internal/view/log.go
@@ -7,6 +7,7 @@ import (
"context"
"fmt"
"io"
+ "log/slog"
"os"
"path/filepath"
"strings"
@@ -19,10 +20,10 @@ import (
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/model"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
)
const (
@@ -113,13 +114,13 @@ func (l *Log) InCmdMode() bool {
// LogCanceled indicates no more logs are coming.
func (l *Log) LogCanceled() {
- log.Debug().Msgf("LOGS_CANCELED!!!")
+ slog.Debug("Logs watcher canceled!")
l.Flush([][]byte{[]byte("\nš [red::b]Stream exited! No more logs...")})
}
// LogStop disables log flushes.
func (l *Log) LogStop() {
- log.Debug().Msgf("LOG_STOP!!!")
+ slog.Debug("Logs watcher stopped!")
l.mx.Lock()
defer l.mx.Unlock()
@@ -149,7 +150,7 @@ func (l *Log) LogFailed(err error) {
l.logs.Clear()
}
if _, err = l.ansiWriter.Write([]byte(tview.Escape(color.Colorize(err.Error(), color.Red)))); err != nil {
- log.Error().Err(err).Msgf("Writing log error")
+ slog.Error("Log line write failed", slogs.Error, err)
}
})
}
@@ -204,7 +205,6 @@ func (l *Log) cancel() {
l.mx.Lock()
defer l.mx.Unlock()
if l.cancelFn != nil {
- log.Debug().Msgf("!!! LOG-VIEWER CANCELED !!!")
l.cancelFn()
l.cancelFn = nil
}
@@ -430,12 +430,18 @@ func saveData(dir, fqn, logs string) (string, error) {
mod := os.O_CREATE | os.O_WRONLY
file, err := os.OpenFile(path, mod, 0600)
if err != nil {
- log.Error().Err(err).Msgf("Log file save failed: %q", path)
+ slog.Error("Unable to save log file",
+ slogs.Path, path,
+ slogs.Error, err,
+ )
return "", nil
}
defer func() {
if err := file.Close(); err != nil {
- log.Error().Err(err).Msg("Closing Log file")
+ slog.Error("Closing Log file failed",
+ slogs.Path, path,
+ slogs.Error, err,
+ )
}
}()
if _, err := file.WriteString(logs); err != nil {
diff --git a/internal/view/node.go b/internal/view/node.go
index 31a20015..165c7f17 100644
--- a/internal/view/node.go
+++ b/internal/view/node.go
@@ -6,15 +6,16 @@ package view
import (
"context"
"fmt"
+ "log/slog"
"time"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -68,7 +69,7 @@ func (n *Node) bindDangerousKeys(aa *ui.KeyActions) {
})
ct, err := n.App().Config.K9s.ActiveContext()
if err != nil {
- log.Error().Err(err).Msgf("No active context located")
+ slog.Error("No active context located", slogs.Error, err)
return
}
if ct.FeatureGates.NodeShell {
diff --git a/internal/view/owner_extender.go b/internal/view/owner_extender.go
index 3ed99b3e..1425e88c 100644
--- a/internal/view/owner_extender.go
+++ b/internal/view/owner_extender.go
@@ -6,16 +6,17 @@ package view
import (
"context"
"fmt"
+ "log/slog"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/tcell/v2"
"github.com/go-errors/errors"
- "github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
@@ -46,7 +47,10 @@ func (v *OwnerExtender) ownerCmd(evt *tcell.EventKey) *tcell.EventKey {
}
if err := v.findOwnerFor(path); err != nil {
- log.Warn().Msgf("Unable to jump to the owner of resource %q: %s", path, err)
+ slog.Warn("Unable to jump to owner of resource",
+ slogs.FQN, path,
+ slogs.Error, err,
+ )
v.App().Flash().Warnf("Unable to jump owner: %s", err)
}
return nil
diff --git a/internal/view/pf.go b/internal/view/pf.go
index 252ea533..eaa7c86a 100644
--- a/internal/view/pf.go
+++ b/internal/view/pf.go
@@ -6,6 +6,7 @@ package view
import (
"context"
"fmt"
+ "log/slog"
"regexp"
"time"
@@ -14,10 +15,10 @@ import (
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/perf"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
)
// PortForward presents active portforward viewer.
@@ -95,7 +96,7 @@ func (p *PortForward) toggleBenchCmd(evt *tcell.EventKey) *tcell.EventKey {
cfg.Name = path
r, _ := p.GetTable().GetSelection()
- log.Debug().Msgf("PF NS %q", p.GetTable().GetModel().GetNamespace())
+ slog.Debug("Port forward namespace", slogs.Namespace, p.GetTable().GetModel().GetNamespace())
col := 3
if client.IsAllNamespaces(p.GetTable().GetModel().GetNamespace()) {
col = 4
@@ -112,7 +113,7 @@ func (p *PortForward) toggleBenchCmd(evt *tcell.EventKey) *tcell.EventKey {
p.App().Status(model.FlashWarn, "Benchmark in progress...")
go func() {
if err := p.runBenchmark(); err != nil {
- log.Error().Err(err).Msgf("Benchmark run failed")
+ slog.Error("Benchmark run failed", slogs.Error, err)
}
}()
@@ -120,7 +121,7 @@ func (p *PortForward) toggleBenchCmd(evt *tcell.EventKey) *tcell.EventKey {
}
func (p *PortForward) runBenchmark() error {
- log.Debug().Msg("Bench starting...")
+ slog.Debug("Bench starting...")
ct, err := p.App().Config.K9s.ActiveContext()
if err != nil {
@@ -128,7 +129,7 @@ func (p *PortForward) runBenchmark() error {
}
name := p.App().Config.K9s.ActiveContextName()
p.bench.Run(ct.ClusterName, name, func() {
- log.Debug().Msgf("Benchmark %q Completed!", name)
+ slog.Debug("Benchmark Completed!", slogs.Name, name)
p.App().QueueUpdate(func() {
if p.bench.Canceled() {
p.App().Status(model.FlashInfo, "Benchmark canceled")
diff --git a/internal/view/pf_dialog.go b/internal/view/pf_dialog.go
index 27f0d29b..5fdbd81b 100644
--- a/internal/view/pf_dialog.go
+++ b/internal/view/pf_dialog.go
@@ -5,14 +5,15 @@ package view
import (
"fmt"
+ "log/slog"
"math"
"strconv"
"strings"
"github.com/derailed/k9s/internal/port"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
)
const portForwardKey = "portforward"
@@ -33,15 +34,12 @@ func ShowPortForwards(v ResourceViewer, path string, ports port.ContainerPortSpe
SetFieldTextColor(styles.FieldFgColor.Color()).
SetFieldBackgroundColor(styles.BgColor.Color())
- ct, err := v.App().Config.K9s.ActiveContext()
- if err != nil {
- log.Error().Err(err).Msgf("No active context detected")
- return
- }
-
pf, err := aa.PreferredPorts(ports)
if err != nil {
- log.Warn().Err(err).Msgf("unable to resolve ports on %s", path)
+ slog.Warn("Unable to resolve preferred ports",
+ slogs.FQN, path,
+ slogs.Error, err,
+ )
}
p1, p2 := pf.ToPortSpec(ports)
@@ -61,7 +59,7 @@ func ShowPortForwards(v ResourceViewer, path string, ports port.ContainerPortSpe
if loField.GetText() == "" {
loField.SetPlaceholder("Enter a local port")
}
- address := ct.PortForwardAddress
+ address := v.App().Config.K9s.PortForwardAddress
f.AddInputField("Address:", address, fieldLen, nil, func(h string) {
address = h
})
diff --git a/internal/view/pf_extender.go b/internal/view/pf_extender.go
index 2e19cc26..6a0072a7 100644
--- a/internal/view/pf_extender.go
+++ b/internal/view/pf_extender.go
@@ -6,16 +6,17 @@ 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/port"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/watch"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
@@ -159,7 +160,10 @@ func startFwdCB(v ResourceViewer, path string, pts port.PortTunnels) error {
if err != nil {
return err
}
- log.Debug().Msgf(">>> Starting port forward %q -- %#v", pf.ID(), pt)
+ slog.Debug(">>> Starting port forward",
+ slogs.PFID, pf.ID(),
+ slogs.PFTunnel, pt,
+ )
go runForward(v, pf, fwd)
tt = append(tt, pt.LocalPort)
}
@@ -173,10 +177,6 @@ func startFwdCB(v ResourceViewer, path string, pts port.PortTunnels) error {
}
func showFwdDialog(v ResourceViewer, path string, cb PortForwardCB) error {
- ct, err := v.App().Config.CurrentContext()
- if err != nil {
- return err
- }
mm, anns, err := fetchPodPorts(v.App().factory, path)
if err != nil {
return err
@@ -196,7 +196,7 @@ func showFwdDialog(v ResourceViewer, path string, cb PortForwardCB) error {
return err
}
- pts, err := pfs.ToTunnels(ct.PortForwardAddress, ports, port.IsPortFree)
+ pts, err := pfs.ToTunnels(v.App().Config.K9s.PortForwardAddress, ports, port.IsPortFree)
if err != nil {
return err
}
@@ -210,7 +210,7 @@ func showFwdDialog(v ResourceViewer, path string, cb PortForwardCB) error {
}
func fetchPodPorts(f *watch.Factory, path string) (map[string][]v1.ContainerPort, map[string]string, error) {
- log.Debug().Msgf("Fetching ports on pod %q", path)
+ slog.Debug("Fetching ports on pod", slogs.FQN, path)
o, err := f.Get("v1/pods", path, true, labels.Everything())
if err != nil {
return nil, nil, err
diff --git a/internal/view/pod.go b/internal/view/pod.go
index 879bd6b4..df219d8f 100644
--- a/internal/view/pod.go
+++ b/internal/view/pod.go
@@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"io/fs"
+ "log/slog"
"os"
"strings"
@@ -17,11 +18,11 @@ import (
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/model1"
"github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/tcell/v2"
"github.com/fatih/color"
- "github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -392,7 +393,7 @@ func resumeShellIn(a *App, c model.Component, path, co string) {
func shellIn(a *App, fqn, co string) {
os, err := getPodOS(a.factory, fqn)
if err != nil {
- log.Warn().Err(err).Msgf("os detect failed")
+ slog.Warn("OS detect failed", slogs.Error, err)
}
args := computeShellArgs(fqn, co, a.Conn().Config().Flags().KubeConfig, os)
@@ -513,10 +514,13 @@ func fetchPod(f dao.Factory, path string) (*v1.Pod, error) {
return &pod, nil
}
-func podIsRunning(f dao.Factory, path string) bool {
- po, err := fetchPod(f, path)
+func podIsRunning(f dao.Factory, fqn string) bool {
+ po, err := fetchPod(f, fqn)
if err != nil {
- log.Error().Err(err).Msg("unable to fetch pod")
+ slog.Error("Unable to fetch pod",
+ slogs.FQN, fqn,
+ slogs.Error, err,
+ )
return false
}
diff --git a/internal/view/sanitizer.go b/internal/view/sanitizer.go
index 100e3f82..4497e8d5 100644
--- a/internal/view/sanitizer.go
+++ b/internal/view/sanitizer.go
@@ -6,6 +6,7 @@ package view
import (
"context"
"fmt"
+ "log/slog"
"strings"
"github.com/derailed/k9s/internal"
@@ -13,11 +14,11 @@ import (
"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"
- "github.com/rs/zerolog/log"
"golang.org/x/text/cases"
"golang.org/x/text/language"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -82,7 +83,7 @@ func (s *Sanitizer) Init(ctx context.Context) error {
s.SetChangedFunc(func(n *tview.TreeNode) {
spec, ok := n.GetReference().(xray.NodeSpec)
if !ok {
- log.Error().Msgf("No ref found on node %s", n.GetText())
+ slog.Error("No ref field found on node", slogs.FQN, n.GetText())
return
}
s.SetSelectedItem(spec.AsPath())
@@ -141,7 +142,7 @@ func (s *Sanitizer) selectedSpec() *xray.NodeSpec {
ref, ok := node.GetReference().(xray.NodeSpec)
if !ok {
- log.Error().Msgf("Expecting a NodeSpec!")
+ slog.Error("Expecting a NodeSpec", slogs.RefType, fmt.Sprintf("%T", node.GetReference()))
return nil
}
@@ -290,7 +291,7 @@ func (s *Sanitizer) update(node *xray.TreeNode) {
root.Walk(func(node, parent *tview.TreeNode) bool {
spec, ok := node.GetReference().(xray.NodeSpec)
if !ok {
- log.Error().Msgf("Expecting a NodeSpec but got %T", node.GetReference())
+ slog.Error("Expecting a NodeSpec", slogs.RefType, fmt.Sprintf("%T", node.GetReference()))
return false
}
// BOZO!! Figure this out expand/collapse but the root
diff --git a/internal/view/scale_extender.go b/internal/view/scale_extender.go
index fd4b7d3a..86ae6f06 100644
--- a/internal/view/scale_extender.go
+++ b/internal/view/scale_extender.go
@@ -6,14 +6,15 @@ package view
import (
"context"
"fmt"
+ "log/slog"
"strconv"
"strings"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
"github.com/derailed/k9s/internal/dao"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
)
@@ -37,7 +38,10 @@ func (s *ScaleExtender) bindKeys(aa *ui.KeyActions) {
meta, err := dao.MetaAccess.MetaFor(s.GVR())
if err != nil {
- log.Error().Err(err).Msgf("Unable to retrieve meta information for %s", s.GVR())
+ slog.Error("No meta information found",
+ slogs.GVR, s.GVR(),
+ slogs.Error, err,
+ )
return
}
@@ -127,13 +131,13 @@ func (s *ScaleExtender) replicasFromScaleSubresource(sel string) (string, error)
return strconv.Itoa(int(replicas)), nil
}
-func (s *ScaleExtender) makeScaleForm(sels []string) (*tview.Form, error) {
+func (s *ScaleExtender) makeScaleForm(fqns []string) (*tview.Form, error) {
factor := "0"
- if len(sels) == 1 {
+ if len(fqns) == 1 {
// If the CRD resource supports scaling, then first try to
// read the replicas directly from the CRD.
if meta, _ := dao.MetaAccess.MetaFor(s.GVR()); dao.IsScalable(meta) {
- replicas, err := s.replicasFromScaleSubresource(sels[0])
+ replicas, err := s.replicasFromScaleSubresource(fqns[0])
if err == nil && len(replicas) != 0 {
factor = replicas
}
@@ -142,7 +146,7 @@ func (s *ScaleExtender) makeScaleForm(sels []string) (*tview.Form, error) {
// For built-in resources or cases where we can't get the replicas from the CRD, we can
// only try to get the number of copies from the READY field.
if factor == "0" {
- replicas, err := s.replicasFromReady(sels[0])
+ replicas, err := s.replicasFromReady(fqns[0])
if err != nil {
return nil, err
}
@@ -176,17 +180,17 @@ func (s *ScaleExtender) makeScaleForm(sels []string) (*tview.Form, error) {
}
ctx, cancel := context.WithTimeout(context.Background(), s.App().Conn().Config().CallTimeout())
defer cancel()
- for _, sel := range sels {
- if err := s.scale(ctx, sel, count); err != nil {
- log.Error().Err(err).Msgf("DP %s scaling failed", sel)
+ for _, fqn := range fqns {
+ if err := s.scale(ctx, fqn, count); err != nil {
+ slog.Error("Unable to scale resource", slogs.FQN, fqn)
s.App().Flash().Err(err)
return
}
}
- if len(sels) != 1 {
- s.App().Flash().Infof("[%d] %s scaled successfully", len(sels), singularize(s.GVR().R()))
+ if len(fqns) != 1 {
+ s.App().Flash().Infof("[%d] %s scaled successfully", len(fqns), singularize(s.GVR().R()))
} else {
- s.App().Flash().Infof("%s %s scaled successfully", s.GVR().R(), sels[0])
+ s.App().Flash().Infof("%s %s scaled successfully", s.GVR().R(), fqns[0])
}
})
f.AddButton("Cancel", func() {
diff --git a/internal/view/screen_dump.go b/internal/view/screen_dump.go
index 83bbca09..c36b54a7 100644
--- a/internal/view/screen_dump.go
+++ b/internal/view/screen_dump.go
@@ -5,13 +5,14 @@ package view
import (
"context"
+ "log/slog"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config/data"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
)
// ScreenDump presents a directory listing viewer.
@@ -45,7 +46,7 @@ func (s *ScreenDump) dirContext(ctx context.Context) context.Context {
}
func (s *ScreenDump) edit(app *App, _ ui.Tabular, _ client.GVR, path string) {
- log.Debug().Msgf("ScreenDump selection is %q", path)
+ slog.Debug("ScreenDump selection", slogs.FQN, path)
s.Stop()
defer s.Start()
diff --git a/internal/view/svc.go b/internal/view/svc.go
index 9c92a8ef..c2f2459e 100644
--- a/internal/view/svc.go
+++ b/internal/view/svc.go
@@ -6,6 +6,7 @@ package view
import (
"errors"
"fmt"
+ "log/slog"
"strings"
"time"
@@ -15,9 +16,9 @@ import (
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/perf"
"github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
@@ -100,7 +101,7 @@ func (s *Service) getExternalPort(svc *v1.Service) (string, error) {
func (s *Service) toggleBenchCmd(evt *tcell.EventKey) *tcell.EventKey {
if s.bench != nil {
- log.Debug().Msg(">>> Benchmark canceled!!")
+ slog.Debug(">>> Benchmark canceled!!")
s.App().Status(model.FlashErr, "Benchmark Canceled!")
s.bench.Cancel()
s.App().ClearStatus(true)
@@ -114,7 +115,7 @@ func (s *Service) toggleBenchCmd(evt *tcell.EventKey) *tcell.EventKey {
cust, err := config.NewBench(s.App().BenchFile)
if err != nil {
- log.Debug().Msgf("No bench config file found %s", s.App().BenchFile)
+ slog.Debug("No bench config file found", slogs.FileName, s.App().BenchFile)
}
cfg, ok := cust.Benchmarks.Services[path]
@@ -123,7 +124,7 @@ func (s *Service) toggleBenchCmd(evt *tcell.EventKey) *tcell.EventKey {
return nil
}
cfg.Name = path
- log.Debug().Msgf("Benchmark config %#v", cfg)
+ slog.Debug("Benchmark config", slogs.Config, cfg)
svc, err := fetchService(s.App().factory, path)
if err != nil {
@@ -170,7 +171,7 @@ func (s *Service) runBenchmark(port string, cfg config.BenchConfig) error {
}
s.App().Status(model.FlashWarn, "Benchmark in progress...")
- log.Debug().Msg("Benchmark starting...")
+ slog.Debug("Benchmark starting...")
ct, err := s.App().Config.K9s.ActiveContext()
if err != nil {
@@ -184,7 +185,7 @@ func (s *Service) runBenchmark(port string, cfg config.BenchConfig) error {
}
func (s *Service) benchDone() {
- log.Debug().Msg("Bench Completed!")
+ slog.Debug("Bench Completed!")
s.App().QueueUpdate(func() {
if s.bench.Canceled() {
s.App().Status(model.FlashInfo, "Benchmark canceled")
diff --git a/internal/view/table.go b/internal/view/table.go
index 0956f625..9900df8d 100644
--- a/internal/view/table.go
+++ b/internal/view/table.go
@@ -5,6 +5,7 @@ package view
import (
"context"
+ "log/slog"
"path/filepath"
"strings"
"time"
@@ -14,9 +15,9 @@ import (
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
)
// Table represents a table viewer.
@@ -55,7 +56,7 @@ func (t *Table) Init(ctx context.Context) (err error) {
t.Table.Init(ctx)
if !t.app.Config.K9s.UI.Reactive {
if err := t.app.RefreshCustomViews(); err != nil {
- log.Warn().Err(err).Msg("CustomViews load failed")
+ slog.Warn("CustomViews load failed", slogs.Error, err)
t.app.Logo().Warn("Views load failed!")
}
}
diff --git a/internal/view/table_helper.go b/internal/view/table_helper.go
index 34dfc81e..ca4154a8 100644
--- a/internal/view/table_helper.go
+++ b/internal/view/table_helper.go
@@ -6,6 +6,7 @@ package view
import (
"encoding/csv"
"fmt"
+ "log/slog"
"os"
"path/filepath"
"strings"
@@ -14,8 +15,8 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/model1"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
- "github.com/rs/zerolog/log"
)
func computeFilename(dumpPath, ns, title, path string) (string, error) {
@@ -51,7 +52,7 @@ func saveTable(dir, title, path string, data *model1.TableData) (string, error)
if err != nil {
return "", err
}
- log.Debug().Msgf("Saving Table to %s", fPath)
+ slog.Debug("Saving table to disk", slogs.FileName, fPath)
mod := os.O_CREATE | os.O_WRONLY
out, err := os.OpenFile(fPath, mod, 0600)
@@ -60,7 +61,10 @@ func saveTable(dir, title, path string, data *model1.TableData) (string, error)
}
defer func() {
if err := out.Close(); err != nil {
- log.Error().Err(err).Msg("Closing file")
+ slog.Error("Closing file failed",
+ slogs.Path, fPath,
+ slogs.Error, err,
+ )
}
}()
diff --git a/internal/view/value_extender.go b/internal/view/value_extender.go
index 8d3a244c..d772454b 100644
--- a/internal/view/value_extender.go
+++ b/internal/view/value_extender.go
@@ -5,13 +5,14 @@ package view
import (
"context"
+ "log/slog"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/model"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
)
// ValueExtender adds values actions to a given viewer.
@@ -61,7 +62,7 @@ func showValues(ctx context.Context, app *App, path string, gvr client.GVR) {
}
if err := vm.Refresh(ctx); err != nil {
- log.Error().Err(err).Msgf("values refresh failed")
+ slog.Error("Values viewer refresh failed", slogs.Error, err)
return nil
}
diff --git a/internal/view/workload.go b/internal/view/workload.go
index 2b7acabf..fea30e19 100644
--- a/internal/view/workload.go
+++ b/internal/view/workload.go
@@ -6,16 +6,17 @@ 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/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/tcell/v2"
- "github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -69,7 +70,7 @@ func (w *Workload) bindKeys(aa *ui.KeyActions) {
func parsePath(path string) (client.GVR, string, bool) {
tt := strings.Split(path, "|")
if len(tt) != 3 {
- log.Error().Msgf("unable to parse path: %q", path)
+ slog.Error("Unable to parse workload path", slogs.Path, path)
return client.NewGVR(""), client.FQN("", ""), false
}
diff --git a/internal/view/xray.go b/internal/view/xray.go
index 2fff2783..c797e071 100644
--- a/internal/view/xray.go
+++ b/internal/view/xray.go
@@ -6,6 +6,7 @@ package view
import (
"context"
"fmt"
+ "log/slog"
"regexp"
"strings"
"time"
@@ -16,12 +17,12 @@ import (
"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/ui/dialog"
"github.com/derailed/k9s/internal/xray"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
"github.com/sahilm/fuzzy"
"golang.org/x/text/cases"
"golang.org/x/text/language"
@@ -89,7 +90,7 @@ func (x *Xray) Init(ctx context.Context) error {
x.SetChangedFunc(func(n *tview.TreeNode) {
spec, ok := n.GetReference().(xray.NodeSpec)
if !ok {
- log.Error().Msgf("No ref found on node %s", n.GetText())
+ slog.Error("No ref found on node", slogs.FQN, n.GetText())
return
}
x.SetSelectedItem(spec.AsPath())
@@ -134,10 +135,10 @@ func (x *Xray) refreshActions() {
defer func() {
if err := pluginActions(x, aa); err != nil {
- log.Warn().Err(err).Msg("Plugins load failed")
+ slog.Warn("Plugins load failed", slogs.Error, err)
}
if err := hotKeyActions(x, aa); err != nil {
- log.Warn().Err(err).Msg("HotKeys load failed")
+ slog.Warn("HotKeys load failed", slogs.Error, err)
}
x.Actions().Merge(aa)
@@ -157,7 +158,10 @@ func (x *Xray) refreshActions() {
var err error
x.meta, err = dao.MetaAccess.MetaFor(client.NewGVR(gvr))
if err != nil {
- log.Warn().Msgf("NO meta for %q -- %s", gvr, err)
+ slog.Warn("No meta found!",
+ slogs.GVR, gvr,
+ slogs.Error, err,
+ )
return
}
@@ -212,7 +216,10 @@ func (x *Xray) selectedSpec() *xray.NodeSpec {
ref, ok := node.GetReference().(xray.NodeSpec)
if !ok {
- log.Error().Msgf("Expecting a NodeSpec!")
+ slog.Error("Expecting a NodeSpec",
+ slogs.Path, node.GetText(),
+ slogs.RefType, fmt.Sprintf("%T", node.GetReference()),
+ )
return nil
}
@@ -375,7 +382,10 @@ func (x *Xray) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
gvr := client.NewGVR(spec.GVR())
meta, err := dao.MetaAccess.MetaFor(gvr)
if err != nil {
- log.Warn().Msgf("NO meta for %q -- %s", spec.GVR(), err)
+ slog.Warn("No meta found!",
+ slogs.GVR, spec.GVR(),
+ slogs.Error, err,
+ )
return nil
}
x.resourceDelete(gvr, spec, fmt.Sprintf("Delete %s %s?", meta.SingularName, spec.Path()))
@@ -535,7 +545,10 @@ func (x *Xray) update(node *xray.TreeNode) {
root.Walk(func(node, parent *tview.TreeNode) bool {
spec, ok := node.GetReference().(xray.NodeSpec)
if !ok {
- log.Error().Msgf("Expecting a NodeSpec but got %T", node.GetReference())
+ slog.Error("Expecting a NodeSpec",
+ slogs.FQN, node.GetText(),
+ slogs.RefType, fmt.Sprintf("%T", node.GetReference()),
+ )
return false
}
// BOZO!! Figure this out expand/collapse but the root
@@ -679,7 +692,10 @@ func (x *Xray) resourceDelete(gvr client.GVR, spec *xray.NodeSpec, msg string) {
x.app.Flash().Infof("Delete resource %s %s", spec.GVR(), spec.Path())
accessor, err := dao.AccessorFor(x.app.factory, gvr)
if err != nil {
- log.Error().Err(err).Msgf("No accessor")
+ slog.Error("No accessor found",
+ slogs.GVR, gvr,
+ slogs.Error, err,
+ )
return
}
diff --git a/internal/view/yaml.go b/internal/view/yaml.go
index a7364e4c..3c6114c9 100644
--- a/internal/view/yaml.go
+++ b/internal/view/yaml.go
@@ -5,6 +5,7 @@ package view
import (
"fmt"
+ "log/slog"
"os"
"path/filepath"
"regexp"
@@ -13,8 +14,8 @@ import (
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/config/data"
+ "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/tview"
- "github.com/rs/zerolog/log"
)
var (
@@ -74,12 +75,18 @@ func saveYAML(dir, name, raw string) (string, error) {
mod := os.O_CREATE | os.O_WRONLY
file, err := os.OpenFile(fpath, mod, 0600)
if err != nil {
- log.Error().Err(err).Msgf("YAML create %s", fpath)
+ slog.Error("Unable to open YAML file",
+ slogs.Path, fpath,
+ slogs.Error, err,
+ )
return "", nil
}
defer func() {
if err := file.Close(); err != nil {
- log.Error().Err(err).Msg("Closing yaml file")
+ slog.Error("Closing yaml file failed",
+ slogs.Path, fpath,
+ slogs.Error, err,
+ )
}
}()
if _, err := file.Write([]byte(raw)); err != nil {
diff --git a/internal/vul/scanner.go b/internal/vul/scanner.go
index 4cac816c..7628de55 100644
--- a/internal/vul/scanner.go
+++ b/internal/vul/scanner.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"sync"
"time"
@@ -27,7 +28,7 @@ import (
"github.com/anchore/grype/grype/vex"
"github.com/anchore/syft/syft"
"github.com/derailed/k9s/internal/config"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -47,13 +48,15 @@ type imageScanner struct {
mx sync.RWMutex
initialized bool
config config.ImageScans
+ log *slog.Logger
}
// NewImageScanner returns a new instance.
-func NewImageScanner(cfg config.ImageScans) *imageScanner {
+func NewImageScanner(cfg config.ImageScans, l *slog.Logger) *imageScanner {
return &imageScanner{
scans: make(Scans),
config: cfg,
+ log: l.With(slogs.Subsys, "vul"),
}
}
@@ -93,12 +96,12 @@ func (s *imageScanner) Init(name, version string) {
s.opts.DB.AutoUpdate,
)
if err != nil {
- log.Error().Err(err).Msgf("VulDb load failed")
+ s.log.Error("VulDb load failed", slogs.Error, err)
return
}
if err := validateDBLoad(err, s.dbStatus); err != nil {
- log.Error().Err(err).Msgf("VulDb validate failed")
+ s.log.Error("VulDb validate failed", slogs.Error, err)
return
}
@@ -150,19 +153,25 @@ func (s *imageScanner) Enqueue(ctx context.Context, images ...string) {
}
func (s *imageScanner) scanWorker(ctx context.Context, img string) {
- defer log.Debug().Msgf("ScanWorker bailing out!")
+ defer s.log.Debug("ScanWorker bailing out!")
- log.Debug().Msgf("ScanWorker processing: %q", img)
+ s.log.Debug("ScanWorker processing image", slogs.Image, img)
sc := newScan(img)
s.setScan(img, sc)
if err := s.scan(ctx, img, sc); err != nil {
- log.Warn().Err(err).Msgf("Scan failed for img %s --", img)
+ s.log.Warn("Scan failed for image",
+ slogs.Image, img,
+ slogs.Error, err,
+ )
}
}
func (s *imageScanner) scan(_ context.Context, img string, sc *Scan) error {
defer func(t time.Time) {
- log.Debug().Msgf("ScanTime %q: %v", img, time.Since(t))
+ s.log.Debug("Time to run vulscan",
+ slogs.Image, img,
+ slogs.Elapsed, time.Since(t),
+ )
}(time.Now())
var errs error
diff --git a/internal/watch/factory.go b/internal/watch/factory.go
index 014ecfb1..75a5ea86 100644
--- a/internal/watch/factory.go
+++ b/internal/watch/factory.go
@@ -5,11 +5,11 @@ package watch
import (
"fmt"
+ "log/slog"
"strings"
"sync"
"time"
- "github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
@@ -18,6 +18,7 @@ import (
"k8s.io/client-go/informers"
"github.com/derailed/k9s/internal/client"
+ "github.com/derailed/k9s/internal/slogs"
)
const (
@@ -48,10 +49,10 @@ func (f *Factory) Start(ns string) {
f.mx.Lock()
defer f.mx.Unlock()
- log.Debug().Msgf("Factory START with ns %q", ns)
+ slog.Debug("Factory started", slogs.Namespace, ns)
f.stopChan = make(chan struct{})
for ns, fac := range f.factories {
- log.Debug().Msgf("Starting factory in ns %q", ns)
+ slog.Debug("Starting factory for ns", slogs.Namespace, ns)
fac.Start(f.stopChan)
}
}
@@ -163,7 +164,11 @@ func (f *Factory) WaitForCacheSync() {
for ns, fac := range f.factories {
m := fac.WaitForCacheSync(f.stopChan)
for k, v := range m {
- log.Debug().Msgf("CACHE `%q Loaded %t:%s", ns, v, k)
+ slog.Debug("CACHE `%q Loaded %t:%s",
+ slogs.Namespace, ns,
+ slogs.ResGrpVersion, v,
+ slogs.ResKind, k,
+ )
}
}
}
@@ -216,7 +221,10 @@ func (f *Factory) ForResource(ns, gvr string) (informers.GenericInformer, error)
}
inf := fact.ForResource(toGVR(gvr))
if inf == nil {
- log.Error().Err(fmt.Errorf("MEOW! No informer for %q:%q", ns, gvr))
+ slog.Error("No informer found",
+ slogs.GVR, gvr,
+ slogs.Namespace, ns,
+ )
return inf, nil
}
@@ -262,7 +270,10 @@ func (f *Factory) AddForwarder(pf Forwarder) {
// DeleteForwarder deletes portforward for a given container.
func (f *Factory) DeleteForwarder(path string) {
count := f.forwarders.Kill(path)
- log.Warn().Msgf("Deleted (%d) portforward for %q", count, path)
+ slog.Warn("Deleted portforward",
+ slogs.Count, count,
+ slogs.GVR, path,
+ )
}
// Forwarders returns all portforwards.
@@ -289,12 +300,12 @@ func (f *Factory) ValidatePortForwards() {
for k, fwd := range f.forwarders {
tokens := strings.Split(k, ":")
if len(tokens) != 2 {
- log.Error().Msgf("Invalid fwd keys %q", k)
+ slog.Error("Invalid port-forward key", slogs.Key, k)
return
}
paths := strings.Split(tokens[0], "|")
if len(paths) < 1 {
- log.Error().Msgf("Invalid path %q", tokens[0])
+ slog.Error("Invalid port-forward path", slogs.Path, tokens[0])
}
o, err := f.Get("v1/pods", paths[0], false, labels.Everything())
if err != nil {
diff --git a/internal/watch/forwarders.go b/internal/watch/forwarders.go
index 0c16f38d..0133c99e 100644
--- a/internal/watch/forwarders.go
+++ b/internal/watch/forwarders.go
@@ -4,11 +4,13 @@
package watch
import (
+ "fmt"
+ "log/slog"
"strings"
"time"
"github.com/derailed/k9s/internal/port"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
"k8s.io/client-go/tools/portforward"
)
@@ -83,7 +85,7 @@ func (ff Forwarders) IsContainerForwarded(fqn, co string) bool {
// DeleteAll stops and delete all port-forwards.
func (ff Forwarders) DeleteAll() {
for k, f := range ff {
- log.Debug().Msgf("Deleting forwarder %s", f.ID())
+ slog.Debug("Deleting forwarder", slogs.ID, f.ID())
f.Stop()
delete(ff, k)
}
@@ -101,7 +103,7 @@ func (ff Forwarders) Kill(path string) int {
for k, f := range ff {
if k == path || strings.HasPrefix(k, prefix) {
stats++
- log.Debug().Msgf("Stop + Delete port-forward %s", k)
+ slog.Debug("Stop and delete port-forward", slogs.Name, k)
f.Stop()
delete(ff, k)
}
@@ -112,8 +114,8 @@ func (ff Forwarders) Kill(path string) int {
// Dump for debug!
func (ff Forwarders) Dump() {
- log.Debug().Msgf("----------- PORT-FORWARDS --------------")
+ slog.Debug("----------- PORT-FORWARDS --------------")
for k, f := range ff {
- log.Debug().Msgf(" %s -- %s", k, f)
+ slog.Debug(fmt.Sprintf(" %s -- %s", k, f))
}
}
diff --git a/internal/watch/forwarders_test.go b/internal/watch/forwarders_test.go
index eae8ae63..4ebbff7d 100644
--- a/internal/watch/forwarders_test.go
+++ b/internal/watch/forwarders_test.go
@@ -4,18 +4,18 @@
package watch_test
import (
+ "log/slog"
"testing"
"time"
"github.com/derailed/k9s/internal/port"
"github.com/derailed/k9s/internal/watch"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"k8s.io/client-go/tools/portforward"
)
func init() {
- zerolog.SetGlobalLevel(zerolog.FatalLevel)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestIsPodForwarded(t *testing.T) {
diff --git a/internal/watch/helper.go b/internal/watch/helper.go
index 2381f4b4..67e79455 100644
--- a/internal/watch/helper.go
+++ b/internal/watch/helper.go
@@ -4,10 +4,11 @@
package watch
import (
+ "fmt"
+ "log/slog"
"path"
"strings"
- "github.com/rs/zerolog/log"
"k8s.io/apimachinery/pkg/runtime/schema"
)
@@ -32,22 +33,22 @@ func namespaced(n string) (string, string) {
// DumpFactory for debug.
func DumpFactory(f *Factory) {
- log.Debug().Msgf("----------- FACTORIES -------------")
+ slog.Debug("----------- FACTORIES -------------")
for ns := range f.factories {
- log.Debug().Msgf(" Factory for NS %q", ns)
+ slog.Debug(fmt.Sprintf(" Factory for NS %q", ns))
}
- log.Debug().Msgf("-----------------------------------")
+ slog.Debug("-----------------------------------")
}
// DebugFactory for debug.
func DebugFactory(f *Factory, ns string, gvr string) {
- log.Debug().Msgf("----------- DEBUG FACTORY (%s) -------------", gvr)
+ slog.Debug(fmt.Sprintf("----------- DEBUG FACTORY (%s) -------------", gvr))
fac, ok := f.factories[ns]
if !ok {
return
}
inf := fac.ForResource(toGVR(gvr))
for i, k := range inf.Informer().GetStore().ListKeys() {
- log.Debug().Msgf("%d -- %s", i, k)
+ slog.Debug(fmt.Sprintf("%d -- %s", i, k))
}
}
diff --git a/internal/xray/container.go b/internal/xray/container.go
index 50a60860..9818c204 100644
--- a/internal/xray/container.go
+++ b/internal/xray/container.go
@@ -6,12 +6,13 @@ package xray
import (
"context"
"fmt"
+ "log/slog"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/render"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal/slogs"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
@@ -95,7 +96,10 @@ func validate(f dao.Factory, n *TreeNode, optional *bool) {
res, err := f.Get(n.GVR, n.ID, true, labels.Everything())
if err != nil || res == nil {
if optional == nil || !*optional {
- log.Warn().Err(err).Msgf("Missing ref %q::%q", n.GVR, n.ID)
+ slog.Warn("Missing ref",
+ slogs.GVR, n.GVR,
+ slogs.ID, n.ID,
+ )
n.Extras[StatusKey] = MissingRefStatus
}
return
diff --git a/internal/xray/container_test.go b/internal/xray/container_test.go
index 09d51b73..0c78100b 100644
--- a/internal/xray/container_test.go
+++ b/internal/xray/container_test.go
@@ -7,6 +7,7 @@ import (
"context"
"encoding/json"
"fmt"
+ "log/slog"
"os"
"testing"
@@ -16,7 +17,6 @@ import (
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/watch"
"github.com/derailed/k9s/internal/xray"
- "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -26,7 +26,7 @@ import (
)
func init() {
- zerolog.SetGlobalLevel(zerolog.FatalLevel)
+ slog.SetDefault(slog.New(slog.DiscardHandler))
}
func TestCOConfigMapRefs(t *testing.T) {
diff --git a/internal/xray/tree_node.go b/internal/xray/tree_node.go
index 7a62e48e..44b86970 100644
--- a/internal/xray/tree_node.go
+++ b/internal/xray/tree_node.go
@@ -5,6 +5,7 @@ package xray
import (
"fmt"
+ "log/slog"
"reflect"
"sort"
"strings"
@@ -12,7 +13,6 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/fvbommel/sortorder"
- "github.com/rs/zerolog/log"
)
const (
@@ -352,10 +352,10 @@ func (t *TreeNode) Dump() {
func dump(n *TreeNode, level int) {
if n == nil {
- log.Debug().Msgf("NO DATA!!")
+ slog.Debug("NO DATA!!")
return
}
- log.Debug().Msgf("%s%s::%s\n", strings.Repeat(" ", level), n.GVR, n.ID)
+ slog.Debug(fmt.Sprintf("%s%s::%s\n", strings.Repeat(" ", level), n.GVR, n.ID))
for _, c := range n.Children {
dump(c, level+1)
}
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 0cf21c95..c662218b 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,6 +1,6 @@
name: k9s
base: core22
-version: 'v0.40.5'
+version: 'v0.40.6'
summary: K9s is a CLI to view and manage your Kubernetes clusters.
description: |
K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of your clusters in a single powerful session.