Rel v0.40.6 (#3185)

* update deps

* docs

* fix#3161 - add V attr

* add alias sort

* fix#3139 - cpu/mem with rx

* fix#3147 - prompt styles

* fix#3138 - rbac subres

* update deps

* mv pfAddress to main config + relax validation

* fix#3179 - gvr title

* fix#3178 - jobs in wrong ns

* fix#3163 - cust views with ns

* fix#3162 - ctx switch save in wrong context

* switch over to slog logger

* add exit code

* update default timeout

* rel notes

* update docs

* rel notes
mine
Fernand Galiana 2025-03-09 13:58:44 -06:00 committed by GitHub
parent 4867f5361d
commit a8b75ef1e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
159 changed files with 1893 additions and 1031 deletions

View File

@ -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

View File

@ -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}

View File

@ -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.
<br/>
<a href="https://panfactum.com"><img src="assets/sponsors/panfactum.png" alt="panfactum"></a>
<br/>
<br/>
> 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,99 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
# 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)
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2025 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)

View File

@ -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 {

View File

@ -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)

40
go.mod
View File

@ -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

121
go.sum
View File

@ -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=

View File

@ -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()

View File

@ -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

View File

@ -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,
},
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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)
}

View File

@ -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...

View File

@ -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))

View File

@ -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.

View File

@ -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()
}

View File

@ -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)

View File

@ -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

View File

@ -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) {

View File

@ -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"

View File

@ -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]
}
}

View File

@ -9,4 +9,3 @@ k9s:
active: po
featureGates:
nodeShell: false
portForwardAddress: localhost

View File

@ -13,4 +13,3 @@ k9s:
active: dp
featureGates:
nodeShell: true
portForwardAddress: localhost

View File

@ -11,4 +11,3 @@ k9s:
active: po
featureGates:
nodeShell: false
portForwardAddress: localhost

View File

@ -12,4 +12,3 @@ k9s:
active: svc
featureGates:
nodeShell: true
portForwardAddress: fred

View File

@ -9,4 +9,3 @@ k9s:
active: po
featureGates:
nodeShell: false
portForwardAddress: localhost

View File

@ -13,4 +13,3 @@ k9s:
active: dp
featureGates:
nodeShell: true
portForwardAddress: localhost

View File

@ -11,4 +11,3 @@ k9s:
active: po
featureGates:
nodeShell: false
portForwardAddress: localhost

View File

@ -12,4 +12,3 @@ k9s:
active: svc
featureGates:
nodeShell: true
portForwardAddress: fred

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -10,7 +10,6 @@
"cluster": { "type": "string" },
"readOnly": {"type": "boolean"},
"skin": { "type": "string" },
"portForwardAddress": { "type": "string" },
"proxy": {
"oneOf": [
{ "type": "null" },

View File

@ -15,6 +15,7 @@
"noExitOnCtrlC": { "type": "boolean" },
"skipLatestRevCheck": { "type": "boolean" },
"disablePodCounting": { "type": "boolean" },
"portForwardAddress": { "type": "string" },
"ui": {
"type": "object",
"additionalProperties": false,

View File

@ -12,4 +12,3 @@ k9s:
active: pod
featureGates:
nodeShell: false
portForwardAddress: localhost

View File

@ -13,4 +13,3 @@ k9s:
fred: blee
featureGates:
nodeShell: false
portForwardAddress: localhost

View File

@ -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,
)
}
}
}

View File

@ -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)
}
}

View File

@ -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{

View File

@ -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 {

View File

@ -5,6 +5,7 @@ k9s:
maxConnRetry: 5
readOnly: false
noExitOnCtrlC: false
portForwardAddress: localhost
ui:
enableMouse: false
headless: false

View File

@ -5,6 +5,7 @@ k9s:
maxConnRetry: 5
readOnly: true
noExitOnCtrlC: false
portForwardAddress: localhost
ui:
enableMouse: false
headless: false

View File

@ -5,6 +5,7 @@ k9s:
maxConnRetry: 5
readOnly: false
noExitOnCtrlC: false
portForwardAddress: localhost
ui:
enableMouse: false
headless: false

View File

@ -5,3 +5,15 @@ views:
- NAME
- AGE
- IP
v1/pods@default:
columns:
- NAME
- IP
- AGE
v1/pods@ns*:
columns:
- AGE
- NAME
- IP

View File

@ -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
}

View File

@ -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))
})
}
}

View File

@ -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)
})
}
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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",
)
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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) {

View File

@ -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{

View File

@ -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
// }

View File

@ -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
}

View File

@ -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"
// )

View File

@ -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 {

View File

@ -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)

View File

@ -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))

View File

@ -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)...)
}

View File

@ -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)

View File

@ -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
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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
}

View File

@ -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) {

View File

@ -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)

View File

@ -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...))
}

View File

@ -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:

View File

@ -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
}

View File

@ -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
}

View File

@ -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)

View File

@ -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("------------------")
}

View File

@ -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) {

View File

@ -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)

View File

@ -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
}

View File

@ -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)

View File

@ -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)

View File

@ -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))
}
}

View File

@ -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))
}
}

View File

@ -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,
)
}
}
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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
}

View File

@ -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

View File

@ -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)
}

View File

@ -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

View File

@ -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) {

View File

@ -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}},

View File

@ -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
}

View File

@ -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)

View File

@ -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,

Some files were not shown because too many files have changed in this diff Show More