Rel v0.50.4 (#3293)

* fix#3278 honor ns ovewride

* refactor column exclude

* [feat] add configurable api server timeout

* clean

* fix#3285 - add restart option for flux support

* fix#3283 fix dp ready state

* update vulscan deps

* fix#3288 fix cust view filter

* fix#3286 match-exp

* rel notes
mine
Fernand Galiana 2025-04-19 12:07:10 -06:00 committed by GitHub
parent c2694ee3e5
commit e4e3816185
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
60 changed files with 428 additions and 286 deletions

View File

@ -239,77 +239,3 @@ formatters:
- internal/x # extracted from x/tools code
- pkg/goformatters/gci/internal # extracted from gci code
- pkg/goanalysis/runner_checker.go # extracted from x/tools code
# linters:
# default: none
# enable:
# - sloglint
# exclusions:
# generated: lax
# paths:
# - third_party$
# - builtin$
# - examples$
# - \\.(generated\\.deepcopy|pb)\\.go$
# settings:
# gocyclo:
# min-complexity: 35
# govet:
# enable:
# - nilness
# goimports:
# local-prefixes: github.com/derailed/k9s
# unused:
# parameters-are-used: true
# local-variables-are-used: true
# field-writes-are-uses: true
# post-statements-are-reads: true
# exported-fields-are-used: true
# generated-is-used: true
# goheader:
# values:
# regexp:
# PROJECT: 'K9s'
# template: |-
# SPDX-License-Identifier: Apache-2.0
# Copyright Authors of {{ PROJECT }}
# gosec:
# includes:
# - G402
# issues:
# # default is true. Enables skipping of directories:
# # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
# # exclude-dirs-use-default: true
# # Excluding configuration per-path, per-linter, per-text and per-source
# # exclude-rules:
# # - linters: [staticcheck]
# # text: "SA1019" # this is rule for deprecated method
# # - linters: [staticcheck]
# # text: "SA9003: empty branch"
# # - linters: [staticcheck]
# # text: "SA2001: empty critical section"
# # - linters: [err113]
# # text: "do not define dynamic errors, use wrapped static errors instead" # This rule to avoid opinionated check fmt.Errorf("text")
# # # Skip goimports check on generated files
# # - path: \\.(generated\\.deepcopy|pb)\\.go$
# # linters:
# # - goimports
# # # Skip goheader check on files imported and modified from upstream k8s
# # - path: "pkg/ipam/(cidrset|service)/.+\\.go"
# # linters:
# # - goheader

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.50.3
VERSION ?= v0.50.4
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}

View File

@ -409,8 +409,10 @@ You can now override the context portForward default address configuration by se
liveViewAutoRefresh: false
# The path to screen dump. Default: '%temp_dir%/k9s-screens-%username%' (k9s info)
screenDumpDir: /tmp/dumps
# Represents ui poll intervals. Default 2secs
# Represents ui poll intervals in seconds. Default 2secs
refreshRate: 2
# Overrides the default k8s api server requests timeout. Defaults 120s
apiServerTimeout: 15s
# Number of retries once the connection to the api-server is lost. Default 15.
maxConnRetry: 5
# Indicates whether modification commands like delete/kill/edit are disabled. Default is false
@ -431,6 +433,7 @@ You can now override the context portForward default address configuration by se
crumbsless: false
# Set to true to suppress the K9s splash screen on start. Default false. Note that for larger clusters or higher latency connections, there may be no resources visible initially until local caches have finished populating.
splashless: false
# Toggles icons display as not all terminal support these chars. Default: true
noIcons: false
# Toggles reactive UI. This option provide for watching on disk artifacts changes and update the UI live Defaults to false.
reactive: false

View File

@ -0,0 +1,44 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
# Release v0.50.4
## 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/zt-3360a389v-ElLHrb0Dp1kAXqYUItSAFA)
## Maintenance Release!
---
## Resolved Issues
* [#3288](https://github.com/derailed/k9s/issues/3288) Resource search doesn't filter by name in custom view
* [#3286](https://github.com/derailed/k9s/issues/3286) K9S doesn't understand matchExpressions selector in Deployment to Pod navigation
* [#3285](https://github.com/derailed/k9s/issues/3285) Rollout Restart method conflicts with GitOps (Flux, ArgoCD)
* [#3283](https://github.com/derailed/k9s/issues/3283) Deployment status showing wrong ready state
* [#3278](https://github.com/derailed/k9s/issues/3278) k9s doesn't honor the --namespace parameter
---
## 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!!
* [#3292](https://github.com/derailed/k9s/pull/3292) fix: respect insecure flag when switch context
* [#3277](https://github.com/derailed/k9s/pull/3277) feat: add hostPathVolume (docker)
* [#3253](https://github.com/derailed/k9s/pull/3253) fix: set default request timeout to 120 seconds
* [#2866](https://github.com/derailed/k9s/pull/2866) Feature/default_view
---
<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

@ -12,18 +12,17 @@ import (
"strings"
"time"
"github.com/lmittmann/tint"
"github.com/mattn/go-colorable"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/tools/clientcmd/api"
"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/mattn/go-colorable"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/tools/clientcmd/api"
)
const (

9
go.mod
View File

@ -5,7 +5,7 @@ go 1.24.1
require (
github.com/adrg/xdg v0.5.3
github.com/anchore/clio v0.0.0-20250319180342-2cfe4b0cb716
github.com/anchore/grype v0.87.0
github.com/anchore/grype v0.91.0
github.com/anchore/syft v1.22.0
github.com/atotto/clipboard v0.1.4
github.com/cenkalti/backoff/v4 v4.3.0
@ -152,7 +152,7 @@ require (
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gdamore/encoding v1.0.1 // indirect
github.com/github/go-spdx/v2 v2.3.2 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
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
@ -232,8 +232,6 @@ require (
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
@ -262,6 +260,7 @@ require (
github.com/openvex/go-vex v0.2.5 // indirect
github.com/owenrumney/go-sarif v1.1.2-0.20231003122901-1000f5e05554 // indirect
github.com/package-url/packageurl-go v0.1.1 // indirect
github.com/pandatix/go-cvss v0.6.2 // indirect
github.com/pborman/indent v1.2.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
@ -336,7 +335,7 @@ require (
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.31.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/api v0.215.0 // indirect

18
go.sum
View File

@ -712,8 +712,8 @@ github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0v
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ=
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 h1:rmZG77uXgE+o2gozGEBoUMpX27lsku+xrMwlmBZJtbg=
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/grype v0.87.0 h1:cdrNA4rnSMpBxP8NmKNF75VUlP/VWaOG8YJJjiIDwIs=
github.com/anchore/grype v0.87.0/go.mod h1:Umw/9sZHnS+9mPLTnUFd/OzApR6dzE2CPlRsov7kSmQ=
github.com/anchore/grype v0.91.0 h1:x6/jweLDNp+jy6ufyCukBJbFAlVefxSUOqb2eFsJdQY=
github.com/anchore/grype v0.91.0/go.mod h1:O+lJcLV4OWTPwA8Rvr8U1utnqqNA0WgvVgc0q0QbpJw=
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.1.2 h1:0+Jcf7hoImYKfrH2XzN6vvbmbpm68A/woabC43gZ4eU=
@ -1001,8 +1001,8 @@ github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZ
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
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/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
@ -1438,13 +1438,9 @@ github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJ
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
@ -1523,6 +1519,8 @@ github.com/owenrumney/go-sarif v1.1.2-0.20231003122901-1000f5e05554 h1:FvA4bwjKp
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=
github.com/package-url/packageurl-go v0.1.1/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
github.com/pandatix/go-cvss v0.6.2 h1:TFiHlzUkT67s6UkelHmK6s1INKVUG7nlKYiWWDTITGI=
github.com/pandatix/go-cvss v0.6.2/go.mod h1:jDXYlQBZrc8nvrMUVVvTG8PhmuShOnKrxP53nOFkt8Q=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM=
@ -2184,8 +2182,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -6,12 +6,14 @@ package client
import (
"errors"
"fmt"
"log/slog"
"net/http"
"net/url"
"strings"
"sync"
"time"
"github.com/derailed/k9s/internal/slogs"
"k8s.io/cli-runtime/pkg/genericclioptions"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
@ -19,7 +21,8 @@ import (
)
const (
defaultCallTimeoutDuration = 120 * time.Second
// DefaultCallTimeoutDuration is the default api server call timeout duration.
DefaultCallTimeoutDuration time.Duration = 15 * time.Second
// UsePersistentConfig caches client config to avoid reloads.
UsePersistentConfig = true
@ -42,12 +45,13 @@ func NewConfig(f *genericclioptions.ConfigFlags) *Config {
// CallTimeout returns the call timeout if set or the default if not set.
func (c *Config) CallTimeout() time.Duration {
if !isSet(c.flags.Timeout) {
return defaultCallTimeoutDuration
return DefaultCallTimeoutDuration
}
dur, err := time.ParseDuration(*c.flags.Timeout)
if err != nil {
return defaultCallTimeoutDuration
return DefaultCallTimeoutDuration
}
slog.Debug("APIServer timeout", slogs.Duration, dur)
return dur
}
@ -95,6 +99,8 @@ func (c *Config) SwitchContext(name string) error {
flags.ImpersonateGroup = c.flags.ImpersonateGroup
flags.ImpersonateUID = c.flags.ImpersonateUID
flags.Insecure = c.flags.Insecure
flags.BearerToken = c.flags.BearerToken
c.flags = flags
return nil

View File

@ -32,7 +32,7 @@ func TestCallTimeout(t *testing.T) {
e: 1 * time.Minute,
},
"default": {
e: 10 * time.Second,
e: 15 * time.Second,
},
}

View File

@ -17,7 +17,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
)
var NoGVR = &GVR{}
var NoGVR = new(GVR)
// GVR represents a kubernetes resource schema as a string.
// Format is group/version/resources:subresource.

View File

@ -34,7 +34,7 @@ var (
ScGVR = NewGVR("storage.k8s.io/v1/storageclasses")
// Policy...
PdbGVR = NewGVR("policy/v1/PodDisruptionBudgets")
PdbGVR = NewGVR("policy/v1/poddisruptionbudgets")
PspGVR = NewGVR("policy/v1beta1/podsecuritypolicies")
// Metrics...

View File

@ -9,6 +9,7 @@ import (
"io/fs"
"log/slog"
"os"
"time"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config/data"
@ -79,11 +80,24 @@ func (c *Config) ContextPluginsPath() (string, error) {
return AppContextPluginsFile(ct.GetClusterName(), c.K9s.activeContextName), nil
}
func setK8sTimeout(flags *genericclioptions.ConfigFlags, d time.Duration) {
v := d.String()
flags.Timeout = &v
}
// Refine the configuration based on cli args.
func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, cfg *client.Config) error {
if flags == nil {
return nil
}
if !isStringSet(flags.Timeout) {
if d, err := time.ParseDuration(c.K9s.APIServerTimeout); err == nil {
setK8sTimeout(flags, d)
} else {
setK8sTimeout(flags, client.DefaultCallTimeoutDuration)
}
}
if isStringSet(flags.Context) {
if _, err := c.K9s.ActivateContext(*flags.Context); err != nil {
return fmt.Errorf("k8sflags. unable to activate context %q: %w", *flags.Context, err)
@ -107,6 +121,7 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c
c.ResetActiveView()
case isStringSet(flags.Namespace):
ns = *flags.Namespace
c.ResetActiveView()
default:
nss, err := c.K9s.ActiveContextNamespace()
if err != nil {

View File

@ -49,9 +49,8 @@ func TestConfigSave(t *testing.T) {
},
}
for k := range uu {
for k, u := range uu {
xdg.Reload()
u := uu[k]
t.Run(k, func(t *testing.T) {
c := mock.NewMockConfig(t)
_, err := c.K9s.ActivateContext(u.ct)
@ -562,6 +561,7 @@ func TestConfigSaveFile(t *testing.T) {
require.NoError(t, cfg.Load("testdata/configs/k9s.yaml", true))
cfg.K9s.RefreshRate = 100
cfg.K9s.APIServerTimeout = "30s"
cfg.K9s.ReadOnly = true
cfg.K9s.Logger.TailCount = 500
cfg.K9s.Logger.BufferSize = 800

View File

@ -10,6 +10,7 @@
"liveViewAutoRefresh": { "type": "boolean" },
"screenDumpDir": {"type": "string"},
"refreshRate": { "type": "integer" },
"apiServerTimeout": { "type": "string" },
"maxConnRetry": { "type": "integer" },
"readOnly": { "type": "boolean" },
"noExitOnCtrlC": { "type": "boolean" },

View File

@ -24,6 +24,7 @@ type K9s struct {
LiveViewAutoRefresh bool `json:"liveViewAutoRefresh" yaml:"liveViewAutoRefresh"`
ScreenDumpDir string `json:"screenDumpDir" yaml:"screenDumpDir,omitempty"`
RefreshRate int `json:"refreshRate" yaml:"refreshRate"`
APIServerTimeout string `json:"apiServerTimeout" yaml:"apiServerTimeout"`
MaxConnRetry int32 `json:"maxConnRetry" yaml:"maxConnRetry"`
ReadOnly bool `json:"readOnly" yaml:"readOnly"`
NoExitOnCtrlC bool `json:"noExitOnCtrlC" yaml:"noExitOnCtrlC"`
@ -54,6 +55,7 @@ func NewK9s(conn client.Connection, ks data.KubeSettings) *K9s {
return &K9s{
RefreshRate: defaultRefreshRate,
MaxConnRetry: defaultMaxConnRetry,
APIServerTimeout: client.DefaultCallTimeoutDuration.String(),
ScreenDumpDir: AppDumpsDir,
Logger: NewLogger(),
Thresholds: NewThreshold(),
@ -117,9 +119,11 @@ func (k *K9s) Merge(k1 *K9s) {
k.DefaultView = k1.DefaultView
k.ScreenDumpDir = k1.ScreenDumpDir
k.RefreshRate = k1.RefreshRate
k.APIServerTimeout = k1.APIServerTimeout
k.MaxConnRetry = k1.MaxConnRetry
k.ReadOnly = k1.ReadOnly
k.NoExitOnCtrlC = k1.NoExitOnCtrlC
k.PortForwardAddress = k1.PortForwardAddress
k.UI = k1.UI
k.SkipLatestRevCheck = k1.SkipLatestRevCheck
k.DisablePodCounting = k1.DisablePodCounting

View File

@ -8,7 +8,7 @@ const (
DefaultLoggerTailCount = 100
// MaxLogThreshold sets the max value for log size.
MaxLogThreshold = 5000
MaxLogThreshold = 5_000
// DefaultSinceSeconds tracks default log age.
DefaultSinceSeconds = -1 // tail logs by default

View File

@ -2,6 +2,7 @@ k9s:
liveViewAutoRefresh: false
screenDumpDir: /tmp/k9s-test/screen-dumps
refreshRate: 2
apiServerTimeout: 15s
maxConnRetry: 5
readOnly: false
noExitOnCtrlC: false

View File

@ -2,6 +2,7 @@ k9s:
liveViewAutoRefresh: true
screenDumpDir: /tmp/k9s-test/screen-dumps
refreshRate: 100
apiServerTimeout: 30s
maxConnRetry: 5
readOnly: true
noExitOnCtrlC: false

View File

@ -2,6 +2,7 @@ k9s:
liveViewAutoRefresh: true
screenDumpDir: /tmp/k9s-test/screen-dumps
refreshRate: 2
apiServerTimeout: 10s
maxConnRetry: 5
readOnly: false
noExitOnCtrlC: false

View File

@ -56,8 +56,8 @@ func (d *Deployment) Scale(ctx context.Context, path string, replicas int32) err
}
// Restart a Deployment rollout.
func (d *Deployment) Restart(ctx context.Context, path string) error {
return restartRes[*appsv1.Deployment](ctx, d.getFactory(), client.DpGVR, path)
func (d *Deployment) Restart(ctx context.Context, path string, opts *metav1.PatchOptions) error {
return restartRes[*appsv1.Deployment](ctx, d.getFactory(), client.DpGVR, path, opts)
}
// TailLogs tail logs for all pods represented by this Deployment.
@ -395,7 +395,7 @@ func scaleRes(ctx context.Context, f Factory, gvr *client.GVR, path string, repl
}
}
func restartRes[T runtime.Object](ctx context.Context, f Factory, gvr *client.GVR, path string) error {
func restartRes[T runtime.Object](ctx context.Context, f Factory, gvr *client.GVR, path string, opts *metav1.PatchOptions) error {
o, err := f.Get(gvr, path, true, labels.Everything())
if err != nil {
return err
@ -440,7 +440,7 @@ func restartRes[T runtime.Object](ctx context.Context, f Factory, gvr *client.GV
n,
types.StrategicMergePatchType,
diff,
metav1.PatchOptions{},
*opts,
)
case client.DsGVR:
@ -449,7 +449,7 @@ func restartRes[T runtime.Object](ctx context.Context, f Factory, gvr *client.GV
n,
types.StrategicMergePatchType,
diff,
metav1.PatchOptions{},
*opts,
)
case client.StsGVR:
@ -458,7 +458,7 @@ func restartRes[T runtime.Object](ctx context.Context, f Factory, gvr *client.GV
n,
types.StrategicMergePatchType,
diff,
metav1.PatchOptions{},
*opts,
)
}

View File

@ -50,8 +50,8 @@ func (d *DaemonSet) ListImages(_ context.Context, fqn string) ([]string, error)
}
// Restart a DaemonSet rollout.
func (d *DaemonSet) Restart(ctx context.Context, path string) error {
return restartRes[*appsv1.DaemonSet](ctx, d.getFactory(), client.DsGVR, path)
func (d *DaemonSet) Restart(ctx context.Context, path string, opts *metav1.PatchOptions) error {
return restartRes[*appsv1.DaemonSet](ctx, d.getFactory(), client.DsGVR, path, opts)
}
// TailLogs tail logs for all pods represented by this DaemonSet.

View File

@ -13,6 +13,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest"
@ -39,7 +40,11 @@ func (d *Dynamic) List(ctx context.Context, ns string) ([]runtime.Object, error)
}
func (d *Dynamic) toTable(ctx context.Context, fqn string) ([]runtime.Object, error) {
strLabel, _ := ctx.Value(internal.KeyLabels).(string)
sel := labels.Everything()
if s, ok := ctx.Value(internal.KeyLabels).(labels.Selector); ok {
sel = s
}
opts := []string{d.gvr.AsResourceName()}
ns, n := client.Namespaced(fqn)
if n != "" {
@ -51,7 +56,7 @@ func (d *Dynamic) toTable(ctx context.Context, fqn string) ([]runtime.Object, er
b := f.NewBuilder().
Unstructured().
NamespaceParam(ns).DefaultNamespace().AllNamespaces(allNS).
LabelSelectorParam(strLabel).
LabelSelectorParam(sel.String()).
FieldSelectorParam("").
RequestChunksOf(0).
ResourceTypeOrNameArgs(true, opts...).

View File

@ -11,6 +11,7 @@ import (
"github.com/derailed/k9s/internal/client"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic"
)
@ -38,7 +39,10 @@ type Generic struct {
// List returns a collection of resources.
// BOZO!! no auth check??
func (g *Generic) List(ctx context.Context, ns string) ([]runtime.Object, error) {
labelSel, _ := ctx.Value(internal.KeyLabels).(string)
labelSel, ok := ctx.Value(internal.KeyLabels).(labels.Selector)
if !ok {
labelSel = labels.Everything()
}
if client.IsAllNamespace(ns) {
ns = client.BlankNamespace
}
@ -48,11 +52,12 @@ func (g *Generic) List(ctx context.Context, ns string) ([]runtime.Object, error)
return nil, err
}
opts := metav1.ListOptions{LabelSelector: labelSel.String()}
var ll *unstructured.UnstructuredList
if client.IsClusterScoped(ns) {
ll, err = dial.List(ctx, metav1.ListOptions{LabelSelector: labelSel})
ll, err = dial.List(ctx, opts)
} else {
ll, err = dial.Namespace(ns).List(ctx, metav1.ListOptions{LabelSelector: labelSel})
ll, err = dial.Namespace(ns).List(ctx, opts)
}
if err != nil {
return nil, err

View File

@ -25,12 +25,9 @@ type Resource struct {
// List returns a collection of resources.
func (r *Resource) List(ctx context.Context, ns string) ([]runtime.Object, error) {
strLabel, _ := ctx.Value(internal.KeyLabels).(string)
lsel := labels.Everything()
if strLabel != "" {
if sel, err := labels.Parse(strLabel); err == nil {
lsel = sel
}
if sel, ok := ctx.Value(internal.KeyLabels).(labels.Selector); ok {
lsel = sel
}
return r.getFactory().List(r.gvr, ns, false, lsel)

View File

@ -54,8 +54,8 @@ func (s *StatefulSet) Scale(ctx context.Context, path string, replicas int32) er
}
// Restart a StatefulSet rollout.
func (s *StatefulSet) Restart(ctx context.Context, path string) error {
return restartRes[*appsv1.StatefulSet](ctx, s.getFactory(), client.StsGVR, path)
func (s *StatefulSet) Restart(ctx context.Context, path string, opts *metav1.PatchOptions) error {
return restartRes[*appsv1.StatefulSet](ctx, s.getFactory(), client.StsGVR, path, opts)
}
// GetInstance returns a statefulset instance.

View File

@ -12,6 +12,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/rest"
@ -56,7 +57,10 @@ func (t *Table) Get(ctx context.Context, path string) (runtime.Object, error) {
// List all Resources in a given namespace.
func (t *Table) List(ctx context.Context, ns string) ([]runtime.Object, error) {
labelSel, _ := ctx.Value(internal.KeyLabels).(string)
sel := labels.Everything()
if labelSel, ok := ctx.Value(internal.KeyLabels).(labels.Selector); ok {
sel = labelSel
}
fieldSel, _ := ctx.Value(internal.KeyFields).(string)
includeObject := includeMeta
@ -75,7 +79,7 @@ func (t *Table) List(ctx context.Context, ns string) ([]runtime.Object, error) {
Namespace(ns).
Resource(t.gvr.R()).
VersionedParams(&metav1.ListOptions{
LabelSelector: labelSel,
LabelSelector: sel.String(),
FieldSelector: fieldSel,
}, metav1.ParameterCodec).
Do(ctx).Get()

View File

@ -145,7 +145,7 @@ type Switchable interface {
// Restartable represents a restartable resource.
type Restartable interface {
// Restart performs a rollout restart.
Restart(ctx context.Context, path string) error
Restart(context.Context, string, *metav1.PatchOptions) error
}
// Runnable represents a runnable resource.

View File

@ -13,6 +13,7 @@ import (
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/labels"
)
func init() {
@ -313,4 +314,4 @@ func (c) Start() {}
func (c) Stop() {}
func (c) Init(context.Context) error { return nil }
func (c) SetFilter(string) {}
func (c) SetLabelFilter(map[string]string) {}
func (c) SetLabelSelector(labels.Selector) {}

View File

@ -19,6 +19,7 @@ import (
"github.com/derailed/k9s/internal/model1"
"github.com/derailed/k9s/internal/slogs"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
@ -38,15 +39,15 @@ type TableListener interface {
// Table represents a table model.
type Table struct {
gvr *client.GVR
data *model1.TableData
listeners []TableListener
inUpdate int32
refreshRate time.Duration
instance string
labelFilter string
mx sync.RWMutex
vs *config.ViewSetting
gvr *client.GVR
data *model1.TableData
listeners []TableListener
inUpdate int32
refreshRate time.Duration
instance string
labelSelector labels.Selector
mx sync.RWMutex
vs *config.ViewSetting
}
// NewTable returns a new table model.
@ -70,20 +71,20 @@ func (t *Table) SetViewSetting(ctx context.Context, vs *config.ViewSetting) {
}
}
// SetLabelFilter sets the labels filter.
func (t *Table) SetLabelFilter(f string) {
// SetLabelSelector sets the labels selector.
func (t *Table) SetLabelSelector(sel labels.Selector) {
t.mx.Lock()
defer t.mx.Unlock()
t.labelFilter = f
t.labelSelector = sel
}
// GetLabelFilter sets the labels filter.
func (t *Table) GetLabelFilter() string {
// GetLabelSelector sets the labels selector.
func (t *Table) GetLabelSelector() labels.Selector {
t.mx.Lock()
defer t.mx.Unlock()
return t.labelFilter
return t.labelSelector
}
// SetInstance sets a single entry table.
@ -253,7 +254,7 @@ func (t *Table) list(ctx context.Context, a dao.Accessor) ([]runtime.Object, err
a.Init(factory, t.gvr)
t.mx.RLock()
ctx = context.WithValue(ctx, internal.KeyLabels, t.labelFilter)
ctx = context.WithValue(ctx, internal.KeyLabels, t.labelSelector)
t.mx.RUnlock()
ns := client.CleanseNamespace(t.data.GetNamespace())
@ -273,7 +274,7 @@ func (t *Table) reconcile(ctx context.Context) error {
if t.vs != nil {
meta.DAO.SetIncludeObject(true)
}
ctx = context.WithValue(ctx, internal.KeyLabels, t.labelFilter)
ctx = context.WithValue(ctx, internal.KeyLabels, t.labelSelector)
if t.instance == "" {
oo, err = t.list(ctx, meta.DAO)
} else {

View File

@ -13,6 +13,7 @@ import (
"github.com/derailed/k9s/internal/view/cmd"
"github.com/derailed/tview"
"github.com/sahilm/fuzzy"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
@ -105,7 +106,7 @@ type Viewer interface {
type Filterer interface {
SetFilter(string)
SetLabelFilter(map[string]string)
SetLabelSelector(labels.Selector)
}
// Cruder performs crud operations.

View File

@ -8,7 +8,9 @@ import (
"log/slog"
"reflect"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/slogs"
"k8s.io/apimachinery/pkg/util/sets"
)
const ageCol = "AGE"
@ -171,6 +173,24 @@ func (h Header) Diff(header Header) bool {
return !reflect.DeepEqual(h, header)
}
// FilterColIndices return viewable col header indices.
func (h Header) FilterColIndices(ns string, wide bool) sets.Set[int] {
if len(h) == 0 {
return nil
}
nsed := client.IsNamespaced(ns)
cc := sets.New[int]()
for i, c := range h {
if c.Name == "AGE" || !wide && c.Wide || c.Hide || (nsed && c.Name == "NAMESPACE") {
continue
}
cc.Insert(i)
}
return cc
}
// ColumnNames return header col names
func (h Header) ColumnNames(wide bool) []string {
if len(h) == 0 {

View File

@ -164,6 +164,10 @@ func (t *TableData) Filter(f FilterOpts) *TableData {
}
func (t *TableData) rxFilter(q string, inverse bool) (*RowEvents, error) {
if strings.Contains(q, " ") {
return t.rowEvents, nil
}
if inverse {
q = q[1:]
}
@ -172,20 +176,16 @@ func (t *TableData) rxFilter(q string, inverse bool) (*RowEvents, error) {
return nil, fmt.Errorf("invalid rx filter %q: %w", q, err)
}
var startIndex int
if _, ok := t.header.IndexOf("NAMESPACE", true); ok && client.IsNamespaced(t.namespace) {
startIndex = 1
}
vidx := t.header.FilterColIndices(t.namespace, true)
rr := NewRowEvents(t.RowCount() / 2)
ageIndex, _ := t.header.IndexOf("AGE", true)
t.rowEvents.Range(func(_ int, re RowEvent) bool {
ff := make([]string, 0, len(re.Row.Fields))
for _, r := range re.Row.Fields[startIndex:] {
for idx, r := range re.Row.Fields {
if !vidx.Has(idx) {
continue
}
ff = append(ff, r)
}
if ageIndex >= 0 && startIndex != ageIndex && ageIndex+1 <= len(ff) {
ff = append(ff[0:ageIndex], ff[ageIndex+1:]...)
}
match := rx.MatchString(strings.Join(ff, spacer))
if (inverse && !match) || (!inverse && match) {
rr.Add(re)

View File

@ -87,16 +87,20 @@ func (d Deployment) defaultRow(raw *unstructured.Unstructured, r *model1.Row) er
return err
}
var desired int32
if dp.Spec.Replicas != nil {
desired = *dp.Spec.Replicas
}
r.ID = client.MetaFQN(&dp.ObjectMeta)
r.Fields = model1.Fields{
dp.Namespace,
dp.Name,
computeVulScore(dp.Namespace, dp.Labels, &dp.Spec.Template.Spec),
strconv.Itoa(int(dp.Status.AvailableReplicas)) + "/" + strconv.Itoa(int(dp.Status.Replicas)),
strconv.Itoa(int(dp.Status.AvailableReplicas)) + "/" + strconv.Itoa(int(desired)),
strconv.Itoa(int(dp.Status.UpdatedReplicas)),
strconv.Itoa(int(dp.Status.AvailableReplicas)),
mapToStr(dp.Labels),
AsStatus(d.diagnose(dp.Status.Replicas, dp.Status.AvailableReplicas)),
AsStatus(d.diagnose(desired, dp.Status.AvailableReplicas)),
ToAge(dp.GetCreationTimestamp()),
}

View File

@ -215,4 +215,7 @@ const (
// JQExp tracks a jq expression logger key.
JQExp = "jq-exp"
// Duration tracks a duration logger key.
Duration = "duration"
)

View File

@ -15,6 +15,7 @@ import (
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/labels"
)
func init() {
@ -60,4 +61,4 @@ func (c) Start() {}
func (c) Stop() {}
func (c) Init(context.Context) error { return nil }
func (c) SetFilter(string) {}
func (c) SetLabelFilter(map[string]string) {}
func (c) SetLabelSelector(labels.Selector) {}

View File

@ -0,0 +1,70 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s
package dialog
import (
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tview"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type RestartFn func(*metav1.PatchOptions) bool
type RestartDialogOpts struct {
Title, Message string
FieldManager string
Ack RestartFn
Cancel cancelFunc
}
func ShowRestart(styles *config.Dialog, pages *ui.Pages, opts *RestartDialogOpts) {
f := tview.NewForm()
f.SetItemPadding(0)
f.SetButtonsAlign(tview.AlignCenter).
SetButtonBackgroundColor(styles.ButtonBgColor.Color()).
SetButtonTextColor(styles.ButtonFgColor.Color()).
SetLabelColor(styles.LabelFgColor.Color()).
SetFieldTextColor(styles.FieldFgColor.Color())
f.AddButton("Cancel", func() {
dismissConfirm(pages)
opts.Cancel()
})
modal := tview.NewModalForm("<"+opts.Title+">", f)
args := metav1.PatchOptions{
FieldManager: opts.FieldManager,
}
f.AddInputField("FieldManager:", args.FieldManager, 40, nil, func(v string) {
args.FieldManager = v
})
f.AddButton("OK", func() {
if !opts.Ack(&args) {
return
}
dismissConfirm(pages)
opts.Cancel()
})
for i := range 2 {
b := f.GetButton(i)
if b == nil {
continue
}
b.SetBackgroundColorActivated(styles.ButtonFocusBgColor.Color())
b.SetLabelColorActivated(styles.ButtonFocusFgColor.Color())
}
f.SetFocus(0)
message := opts.Message
modal.SetText(message)
modal.SetTextColor(styles.FgColor.Color())
modal.SetDoneFunc(func(int, string) {
dismissConfirm(pages)
opts.Cancel()
})
pages.AddPage(confirmKey, modal, false, false)
pages.ShowPage(confirmKey)
}

View File

@ -315,12 +315,18 @@ func (t *Table) doUpdate(data *model1.TableData) *model1.TableData {
} else {
t.actions.Delete(KeyShiftP)
}
t.setSortCol(data.ComputeSortCol(t.GetViewSetting(), t.getSortCol(), t.getMSort()))
return data
}
func (t *Table) shouldExcludeColumn(h model1.HeaderColumn) bool {
return (h.Hide || (!t.wide && h.Wide)) ||
(h.Name == "NAMESPACE" && !t.GetModel().ClusterWide()) ||
(h.MX && !t.hasMetrics) ||
(h.VS && vul.ImgScanner == nil)
}
func (t *Table) UpdateUI(cdata, data *model1.TableData) {
t.Clear()
fg := t.styles.Table().Header.FgColor.Color()
@ -328,19 +334,9 @@ func (t *Table) UpdateUI(cdata, data *model1.TableData) {
var col int
for _, h := range cdata.Header() {
if h.Hide || (!t.wide && h.Wide) {
if t.shouldExcludeColumn(h) {
continue
}
if h.Name == "NAMESPACE" && !t.GetModel().ClusterWide() {
continue
}
if h.MX && !t.hasMetrics {
continue
}
if h.VS && vul.ImgScanner == nil {
continue
}
t.AddHeaderCell(col, h)
c := t.GetCell(0, col)
c.SetBackgroundColor(bg)
@ -384,17 +380,7 @@ func (t *Table) buildRow(r int, re, ore model1.RowEvent, h model1.Header, pads M
)
continue
}
if h[c].Hide || (!t.wide && h[c].Wide) {
continue
}
if h[c].Name == "NAMESPACE" && !t.GetModel().ClusterWide() {
continue
}
if h[c].MX && !t.hasMetrics {
continue
}
if h[c].VS && vul.ImgScanner == nil {
if t.shouldExcludeColumn(h[c]) {
continue
}
@ -468,7 +454,6 @@ func (t *Table) Refresh() {
if data.HeaderCount() == 0 {
return
}
// BOZO!! Really want to tell model reload now. Refactor!
cdata := t.Update(data, t.hasMetrics)
t.UpdateUI(cdata, data)
}
@ -575,9 +560,14 @@ func (t *Table) styleTitle() string {
buff := t.cmdBuff.GetText()
if internal.IsLabelSelector(buff) {
buff = render.Truncate(TrimLabelSelector(buff), maxTruncate)
} else if l := t.GetModel().GetLabelFilter(); l != "" {
buff = render.Truncate(l, maxTruncate)
sel, err := TrimLabelSelector(buff)
if err != nil {
buff = render.Truncate(sel.String(), maxTruncate)
}
} else if l := t.GetModel().GetLabelSelector(); l != nil && !l.Empty() {
buff = render.Truncate(l.String(), maxTruncate)
} else if buff != "" {
buff = render.Truncate(buff, maxTruncate)
}
if buff == "" {

View File

@ -13,6 +13,7 @@ import (
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/slogs"
"k8s.io/apimachinery/pkg/labels"
)
const (
@ -58,12 +59,13 @@ func TrimCell(tv *SelectTable, row, col int) string {
}
// TrimLabelSelector extracts label query.
func TrimLabelSelector(s string) string {
func TrimLabelSelector(s string) (labels.Selector, error) {
var selStr string
if strings.Index(s, "-l") == 0 {
return strings.TrimSpace(s[2:])
selStr = strings.TrimSpace(s[2:])
}
return s
return labels.Parse(selStr)
}
// SkinTitle decorates a title.

View File

@ -8,6 +8,7 @@ import (
"github.com/derailed/k9s/internal/render"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/labels"
)
func TestTruncate(t *testing.T) {
@ -34,17 +35,29 @@ func TestTruncate(t *testing.T) {
}
func TestTrimLabelSelector(t *testing.T) {
sel, _ := labels.Parse("app=fred,env=blee")
uu := map[string]struct {
sel, e string
sel string
err error
e labels.Selector
}{
"cool": {"-l app=fred,env=blee", "app=fred,env=blee"},
"noSpace": {"-lapp=fred,env=blee", "app=fred,env=blee"},
"cool": {
sel: "-l app=fred,env=blee",
e: sel,
},
"no-space": {
sel: "-lapp=fred,env=blee",
e: sel,
},
}
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
assert.Equal(t, u.e, TrimLabelSelector(u.sel))
sel, err := TrimLabelSelector(u.sel)
assert.Equal(t, u.err, err)
assert.Equal(t, u.e, sel)
})
}
}

View File

@ -17,6 +17,7 @@ import (
"github.com/derailed/k9s/internal/ui"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
@ -42,7 +43,7 @@ func TestTableUpdate(t *testing.T) {
func TestTableSelection(t *testing.T) {
v := ui.NewTable(client.NewGVR("fred"))
v.Init(makeContext())
m := &mockModel{}
m := new(mockModel)
v.SetModel(m)
data := m.Peek()
cdata := v.Update(data, false)
@ -72,8 +73,8 @@ var _ ui.Tabular = &mockModel{}
func (*mockModel) SetViewSetting(context.Context, *config.ViewSetting) {}
func (*mockModel) SetInstance(string) {}
func (*mockModel) SetLabelFilter(string) {}
func (*mockModel) GetLabelFilter() string { return "" }
func (*mockModel) SetLabelSelector(labels.Selector) {}
func (*mockModel) GetLabelSelector() labels.Selector { return nil }
func (*mockModel) Empty() bool { return false }
func (*mockModel) RowCount() int { return 1 }
func (*mockModel) HasMetrics() bool { return true }

View File

@ -12,6 +12,7 @@ import (
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/model1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
@ -49,11 +50,11 @@ type Tabular interface {
// SetInstance sets parent resource path.
SetInstance(string)
// SetLabelFilter sets the label filter.
SetLabelFilter(string)
// SetLabelSelector sets the label selector.
SetLabelSelector(labels.Selector)
// GetLabelFilter fetch the label filter.
GetLabelFilter() string
// GetLabelSelector fetch the label filter.
GetLabelSelector() labels.Selector
// Empty returns true if model has no data.
Empty() bool

View File

@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
@ -35,7 +36,7 @@ func TestAliasNew(t *testing.T) {
func TestAliasSearch(t *testing.T) {
v := view.NewAlias(client.AliGVR)
require.NoError(t, v.Init(makeContext(t)))
v.GetTable().SetModel(&mockModel{})
v.GetTable().SetModel(new(mockModel))
v.GetTable().Refresh()
v.App().Prompt().SetModel(v.GetTable().CmdBuff())
v.App().Prompt().SendStrokes("blee")
@ -93,8 +94,8 @@ func (*mockModel) NextSuggestion() (string, bool) { return
func (*mockModel) PrevSuggestion() (string, bool) { return "", false }
func (*mockModel) ClearSuggestions() {}
func (*mockModel) SetInstance(string) {}
func (*mockModel) SetLabelFilter(string) {}
func (*mockModel) GetLabelFilter() string { return "" }
func (*mockModel) SetLabelSelector(labels.Selector) {}
func (*mockModel) GetLabelSelector() labels.Selector { return nil }
func (*mockModel) Empty() bool { return false }
func (*mockModel) RowCount() int { return 1 }
func (*mockModel) HasMetrics() bool { return true }

View File

@ -26,6 +26,7 @@ import (
"github.com/derailed/k9s/internal/view/cmd"
"github.com/derailed/tcell/v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets"
)
@ -194,9 +195,11 @@ func (b *Browser) SetFilter(s string) {
b.CmdBuff().SetText(s, "")
}
func (b *Browser) SetLabelFilter(labels map[string]string) {
b.CmdBuff().SetText(toLabelsStr(labels), "")
b.GetModel().SetLabelFilter(toLabelsStr(labels))
func (b *Browser) SetLabelSelector(sel labels.Selector) {
if sel != nil {
b.CmdBuff().SetText(sel.String(), "")
}
b.GetModel().SetLabelSelector(sel)
}
// BufferChanged indicates the buffer was changed.
@ -205,9 +208,11 @@ func (*Browser) BufferChanged(_, _ string) {}
// BufferCompleted indicates input was accepted.
func (b *Browser) BufferCompleted(text, _ string) {
if internal.IsLabelSelector(text) {
b.GetModel().SetLabelFilter(ui.TrimLabelSelector(text))
if sel, err := ui.TrimLabelSelector(text); err == nil {
b.GetModel().SetLabelSelector(sel)
}
} else {
b.GetModel().SetLabelFilter("")
b.GetModel().SetLabelSelector(labels.Everything())
}
}
@ -375,7 +380,7 @@ func (b *Browser) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
hasFilter := !b.CmdBuff().Empty()
b.CmdBuff().ClearText(false)
if hasFilter {
b.GetModel().SetLabelFilter("")
b.GetModel().SetLabelSelector(labels.Everything())
b.Refresh()
}
return b.App().PrevCmd(evt)
@ -555,7 +560,9 @@ func (b *Browser) defaultContext() context.Context {
ctx = context.WithValue(ctx, internal.KeyGVR, b.GVR())
ctx = context.WithValue(ctx, internal.KeyPath, b.Path)
if internal.IsLabelSelector(b.CmdBuff().GetText()) {
ctx = context.WithValue(ctx, internal.KeyLabels, ui.TrimLabelSelector(b.CmdBuff().GetText()))
if sel, err := ui.TrimLabelSelector(b.CmdBuff().GetText()); err == nil {
ctx = context.WithValue(ctx, internal.KeyLabels, sel)
}
}
ctx = context.WithValue(ctx, internal.KeyNamespace, client.CleanseNamespace(b.App().Config.ActiveNamespace()))
ctx = context.WithValue(ctx, internal.KeyWithMetrics, b.app.factory.Client().HasMetrics())

View File

@ -16,6 +16,7 @@ import (
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/view/cmd"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets"
)
@ -199,15 +200,15 @@ func (c *Command) run(p *cmd.Interpreter, fqn string, clearStack, pushCmd bool)
co := c.componentFor(gvr, fqn, v)
co.SetFilter("")
co.SetLabelFilter(nil)
co.SetLabelSelector(labels.Everything())
if f, ok := p.FilterArg(); ok {
co.SetFilter(f)
}
if f, ok := p.FuzzyArg(); ok {
co.SetFilter("-f " + f)
}
if ll, ok := p.LabelsArg(); ok {
co.SetLabelFilter(ll)
if ss, ok := p.LabelsArg(); ok {
co.SetLabelSelector(labels.SelectorFromSet(ss))
}
return c.exec(p, gvr, co, clearStack, pushCmd)

View File

@ -16,6 +16,7 @@ import (
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
"github.com/sahilm/fuzzy"
"k8s.io/apimachinery/pkg/labels"
)
const (
@ -61,7 +62,7 @@ func NewDetails(app *App, title, subject, contentType string, searchable bool) *
func (*Details) SetCommand(*cmd.Interpreter) {}
func (*Details) SetFilter(string) {}
func (*Details) SetLabelFilter(map[string]string) {}
func (*Details) SetLabelSelector(labels.Selector) {}
// Init initializes the viewer.
func (d *Details) Init(_ context.Context) error {

View File

@ -90,5 +90,5 @@ func showPodsFromSelector(app *App, path string, sel *metav1.LabelSelector) {
return
}
showPods(app, path, l.String(), "")
showPods(app, path, l, "")
}

View File

@ -17,6 +17,7 @@ import (
"github.com/derailed/k9s/internal/view/cmd"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
"k8s.io/apimachinery/pkg/labels"
)
const (
@ -46,7 +47,7 @@ func NewHelp(app *App) *Help {
func (*Help) SetCommand(*cmd.Interpreter) {}
func (*Help) SetFilter(string) {}
func (*Help) SetLabelFilter(map[string]string) {}
func (*Help) SetLabelSelector(labels.Selector) {}
// Init initializes the component.
func (h *Help) Init(ctx context.Context) error {

View File

@ -23,11 +23,11 @@ import (
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/view/cmd"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
"github.com/sahilm/fuzzy"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets"
)
@ -135,19 +135,10 @@ func describeResource(app *App, _ ui.Tabular, gvr *client.GVR, path string) {
}
}
func toLabelsStr(labels map[string]string) string {
ll := make([]string, 0, len(labels))
for k, v := range labels {
ll = append(ll, fmt.Sprintf("%s=%s", k, v))
}
return strings.Join(ll, ",")
}
func showPods(app *App, path, labelSel, fieldSel string) {
func showPods(app *App, path string, labelSel labels.Selector, fieldSel string) {
v := NewPod(client.PodGVR)
v.SetContextFn(podCtx(app, path, fieldSel))
v.SetLabelFilter(cmd.ToLabels(labelSel))
v.SetLabelSelector(labelSel)
ns, _ := client.Namespaced(path)
if err := app.Config.SetActiveNamespace(ns); err != nil {

View File

@ -19,6 +19,7 @@ import (
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
"github.com/sahilm/fuzzy"
"k8s.io/apimachinery/pkg/labels"
)
const (
@ -64,7 +65,7 @@ func NewLiveView(app *App, title string, m model.ResourceViewer) *LiveView {
func (*LiveView) SetCommand(*cmd.Interpreter) {}
func (*LiveView) SetFilter(string) {}
func (*LiveView) SetLabelFilter(map[string]string) {}
func (*LiveView) SetLabelSelector(labels.Selector) {}
// Init initializes the viewer.
func (v *LiveView) Init(_ context.Context) error {

View File

@ -25,6 +25,7 @@ import (
"github.com/derailed/k9s/internal/view/cmd"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
"k8s.io/apimachinery/pkg/labels"
)
const (
@ -66,7 +67,7 @@ func NewLog(gvr *client.GVR, opts *dao.LogOptions) *Log {
func (*Log) SetCommand(*cmd.Interpreter) {}
func (*Log) SetFilter(string) {}
func (*Log) SetLabelFilter(map[string]string) {}
func (*Log) SetLabelSelector(labels.Selector) {}
// Init initializes the viewer.
func (l *Log) Init(ctx context.Context) (err error) {

View File

@ -92,7 +92,7 @@ func (n *Node) bindKeys(aa *ui.KeyActions) {
}
func (n *Node) showPods(a *App, _ ui.Tabular, _ *client.GVR, path string) {
showPods(a, n.GetTable().GetSelectedItem(), client.BlankNamespace, "spec.nodeName="+path)
showPods(a, n.GetTable().GetSelectedItem(), nil, "spec.nodeName="+path)
}
func (n *Node) drainCmd(evt *tcell.EventKey) *tcell.EventKey {

View File

@ -11,6 +11,7 @@ import (
"github.com/derailed/k9s/internal/view/cmd"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
"k8s.io/apimachinery/pkg/labels"
)
// Picker represents a container picker.
@ -30,7 +31,7 @@ func NewPicker() *Picker {
func (*Picker) SetCommand(*cmd.Interpreter) {}
func (*Picker) SetFilter(string) {}
func (*Picker) SetLabelFilter(map[string]string) {}
func (*Picker) SetLabelSelector(labels.Selector) {}
// Init initializes the view.
func (p *Picker) Init(ctx context.Context) error {

View File

@ -22,6 +22,7 @@ import (
"github.com/derailed/tview"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"k8s.io/apimachinery/pkg/labels"
)
// Graphable represents a graphic component.
@ -80,7 +81,7 @@ func NewPulse(gvr *client.GVR) ResourceViewer {
func (*Pulse) SetCommand(*cmd.Interpreter) {}
func (*Pulse) SetFilter(string) {}
func (*Pulse) SetLabelFilter(map[string]string) {}
func (*Pulse) SetLabelSelector(labels.Selector) {}
// Init initializes the view.
func (p *Pulse) Init(ctx context.Context) error {

View File

@ -13,6 +13,7 @@ import (
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/tcell/v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// RestartExtender represents a restartable resource.
@ -54,22 +55,31 @@ func (r *RestartExtender) restartCmd(*tcell.EventKey) *tcell.EventKey {
msg = fmt.Sprintf("Restart %d %s?", len(paths), r.GVR().R())
}
d := r.App().Styles.Dialog()
dialog.ShowConfirm(&d, r.App().Content.Pages, "Confirm Restart", msg, func() {
ctx, cancel := context.WithTimeout(context.Background(), r.App().Conn().Config().CallTimeout())
defer cancel()
for _, path := range paths {
if err := r.restartRollout(ctx, path); err != nil {
r.App().Flash().Err(err)
} else {
r.App().Flash().Infof("Restart in progress for `%s...", path)
opts := dialog.RestartDialogOpts{
Title: "Confirm Restart",
Message: msg,
FieldManager: "kubectl-rollout",
Ack: func(opts *metav1.PatchOptions) bool {
ctx, cancel := context.WithTimeout(context.Background(), r.App().Conn().Config().CallTimeout())
defer cancel()
for _, path := range paths {
if err := r.restartRollout(ctx, path, opts); err != nil {
r.App().Flash().Err(err)
} else {
r.App().Flash().Infof("Restart in progress for `%s...", path)
}
}
}
}, func() {})
return true
},
Cancel: func() {},
}
dialog.ShowRestart(&d, r.App().Content.Pages, &opts)
return nil
}
func (r *RestartExtender) restartRollout(ctx context.Context, path string) error {
func (r *RestartExtender) restartRollout(ctx context.Context, path string, opts *metav1.PatchOptions) error {
res, err := dao.AccessorFor(r.App().factory, r.GVR())
if err != nil {
return err
@ -79,7 +89,7 @@ func (r *RestartExtender) restartRollout(ctx context.Context, path string) error
return errors.New("resource is not restartable")
}
return s.Restart(ctx, path)
return s.Restart(ctx, path, opts)
}
// Helpers...

View File

@ -74,7 +74,7 @@ func (s *Service) showPods(a *App, _ ui.Tabular, _ *client.GVR, path string) {
return
}
showPods(a, path, toLabelsStr(svc.Spec.Selector), "")
showPods(a, path, labels.SelectorFromSet(svc.Spec.Selector), "")
}
func (*Service) checkSvc(svc *v1.Service) error {

View File

@ -24,6 +24,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
@ -135,8 +136,8 @@ var _ ui.Tabular = (*mockTableModel)(nil)
func (*mockTableModel) SetViewSetting(context.Context, *config.ViewSetting) {}
func (*mockTableModel) SetInstance(string) {}
func (*mockTableModel) SetLabelFilter(string) {}
func (*mockTableModel) GetLabelFilter() string { return "" }
func (*mockTableModel) SetLabelSelector(labels.Selector) {}
func (*mockTableModel) GetLabelSelector() labels.Selector { return nil }
func (*mockTableModel) Empty() bool { return false }
func (*mockTableModel) RowCount() int { return 1 }
func (*mockTableModel) HasMetrics() bool { return true }

View File

@ -112,7 +112,9 @@ func (w *Workload) defaultContext(gvr *client.GVR, fqn string) context.Context {
ctx = context.WithValue(ctx, internal.KeyPath, fqn)
}
if internal.IsLabelSelector(w.GetTable().CmdBuff().GetText()) {
ctx = context.WithValue(ctx, internal.KeyLabels, ui.TrimLabelSelector(w.GetTable().CmdBuff().GetText()))
if sel, err := ui.TrimLabelSelector(w.GetTable().CmdBuff().GetText()); err == nil {
ctx = context.WithValue(ctx, internal.KeyLabels, sel)
}
}
ctx = context.WithValue(ctx, internal.KeyNamespace, client.CleanseNamespace(w.App().Config.ActiveNamespace()))
ctx = context.WithValue(ctx, internal.KeyWithMetrics, w.App().factory.Client().HasMetrics())

View File

@ -28,6 +28,7 @@ import (
"golang.org/x/text/cases"
"golang.org/x/text/language"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets"
)
@ -58,7 +59,7 @@ func NewXray(gvr *client.GVR) ResourceViewer {
func (*Xray) SetCommand(*cmd.Interpreter) {}
func (*Xray) SetFilter(string) {}
func (*Xray) SetLabelFilter(map[string]string) {}
func (*Xray) SetLabelSelector(labels.Selector) {}
// Init initializes the view.
func (x *Xray) Init(ctx context.Context) error {
@ -609,9 +610,11 @@ func (x *Xray) defaultContext() context.Context {
ctx := context.WithValue(context.Background(), internal.KeyFactory, x.app.factory)
ctx = context.WithValue(ctx, internal.KeyFields, "")
if x.CmdBuff().Empty() {
ctx = context.WithValue(ctx, internal.KeyLabels, "")
ctx = context.WithValue(ctx, internal.KeyLabels, labels.Everything())
} else {
ctx = context.WithValue(ctx, internal.KeyLabels, ui.TrimLabelSelector(x.CmdBuff().GetText()))
if sel, err := ui.TrimLabelSelector(x.CmdBuff().GetText()); err == nil {
ctx = context.WithValue(ctx, internal.KeyLabels, sel)
}
}
return ctx
@ -688,7 +691,9 @@ func (x *Xray) styleTitle() string {
return title
}
if internal.IsLabelSelector(buff) {
buff = ui.TrimLabelSelector(buff)
if sel, err := ui.TrimLabelSelector(buff); err == nil {
buff = sel.String()
}
}
return title + ui.SkinTitle(fmt.Sprintf(ui.SearchFmt, buff), &styles)

View File

@ -14,18 +14,18 @@ import (
"github.com/anchore/clio"
"github.com/anchore/grype/cmd/grype/cli/options"
"github.com/anchore/grype/grype"
"github.com/anchore/grype/grype/db/legacy/distribution"
v5 "github.com/anchore/grype/grype/db/v5"
"github.com/anchore/grype/grype/db/v5/matcher"
"github.com/anchore/grype/grype/db/v5/matcher/dotnet"
"github.com/anchore/grype/grype/db/v5/matcher/golang"
"github.com/anchore/grype/grype/db/v5/matcher/java"
"github.com/anchore/grype/grype/db/v5/matcher/javascript"
"github.com/anchore/grype/grype/db/v5/matcher/python"
"github.com/anchore/grype/grype/db/v5/matcher/ruby"
"github.com/anchore/grype/grype/db/v5/matcher/stock"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/matcher"
"github.com/anchore/grype/grype/matcher/dotnet"
"github.com/anchore/grype/grype/matcher/golang"
"github.com/anchore/grype/grype/matcher/java"
"github.com/anchore/grype/grype/matcher/javascript"
"github.com/anchore/grype/grype/matcher/python"
"github.com/anchore/grype/grype/matcher/ruby"
"github.com/anchore/grype/grype/matcher/stock"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/vex"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/syft/syft"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/slogs"
@ -40,8 +40,8 @@ const (
)
type imageScanner struct {
store *v5.ProviderStore
dbStatus *distribution.Status
provider vulnerability.Provider
status *vulnerability.ProviderStatus
opts *options.Grype
scans Scans
mx sync.RWMutex
@ -90,8 +90,9 @@ func (s *imageScanner) Init(name, version string) {
s.opts.GenerateMissingCPEs = true
var err error
s.store, s.dbStatus, err = grype.LoadVulnerabilityDB(
s.opts.DB.ToLegacyCuratorConfig(),
s.provider, s.status, err = grype.LoadVulnerabilityDB(
s.opts.ToClientConfig(),
s.opts.ToCuratorConfig(),
s.opts.DB.AutoUpdate,
)
if err != nil {
@ -99,7 +100,7 @@ func (s *imageScanner) Init(name, version string) {
return
}
if e := validateDBLoad(err, s.dbStatus); e != nil {
if e := validateDBLoad(err, s.status); e != nil {
s.log.Error("VulDb validate failed", slogs.Error, e)
return
}
@ -112,9 +113,9 @@ func (s *imageScanner) Stop() {
s.mx.RLock()
defer s.mx.RUnlock()
if s.store != nil {
_ = s.store.Close()
s.store = nil
if s.provider != nil {
_ = s.provider.Close()
s.provider = nil
}
}
@ -180,11 +181,11 @@ func (s *imageScanner) scan(_ context.Context, img string, sc *Scan) error {
}
v := grype.VulnerabilityMatcher{
Store: *s.store,
IgnoreRules: s.opts.Ignore,
NormalizeByCVE: s.opts.ByCVE,
FailSeverity: s.opts.FailOnSeverity(),
Matchers: getMatchers(s.opts),
VulnerabilityProvider: s.provider,
IgnoreRules: s.opts.Ignore,
NormalizeByCVE: s.opts.ByCVE,
FailSeverity: s.opts.FailOnSeverity(),
Matchers: getMatchers(s.opts),
VexProcessor: vex.NewProcessor(vex.ProcessorOptions{
Documents: s.opts.VexDocuments,
IgnoreRules: s.opts.Ignore,
@ -195,7 +196,7 @@ func (s *imageScanner) scan(_ context.Context, img string, sc *Scan) error {
if err != nil {
errs = errors.Join(errs, err)
}
if err := sc.run(mm, s.store); err != nil {
if err := sc.run(mm, s.provider); err != nil {
errs = errors.Join(errs, err)
}
@ -218,7 +219,7 @@ func getProviderConfig(opts *options.Grype) pkg.ProviderConfig {
}
}
func getMatchers(opts *options.Grype) []matcher.Matcher {
func getMatchers(opts *options.Grype) []match.Matcher {
return matcher.NewDefaultMatchers(
matcher.Config{
Java: java.MatcherConfig{
@ -230,23 +231,23 @@ func getMatchers(opts *options.Grype) []matcher.Matcher {
Dotnet: dotnet.MatcherConfig(opts.Match.Dotnet),
Javascript: javascript.MatcherConfig(opts.Match.Javascript),
Golang: golang.MatcherConfig{
UseCPEs: opts.Match.Golang.UseCPEs,
AlwaysUseCPEForStdlib: opts.Match.Golang.AlwaysUseCPEForStdlib,
UseCPEs: opts.Match.Golang.UseCPEs,
AlwaysUseCPEForStdlib: opts.Match.Golang.AlwaysUseCPEForStdlib,
AllowMainModulePseudoVersionComparison: opts.Match.Golang.AllowMainModulePseudoVersionComparison,
},
Stock: stock.MatcherConfig(opts.Match.Stock),
},
)
}
func validateDBLoad(loadErr error, status *distribution.Status) error {
func validateDBLoad(loadErr error, status *vulnerability.ProviderStatus) error {
if loadErr != nil {
return fmt.Errorf("failed to load vulnerability db: %w", loadErr)
}
if status == nil {
return fmt.Errorf("unable to determine the status of the vulnerability db")
}
if status.Err != nil {
return fmt.Errorf("db could not be loaded: %w", status.Err)
if status.Error != nil {
return fmt.Errorf("db could not be loaded: %w", status.Error)
}
return nil

View File

@ -1,6 +1,6 @@
name: k9s
base: core22
version: 'v0.50.3'
version: 'v0.50.4'
summary: K9s is a CLI to view and manage your Kubernetes clusters.
description: |
K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of your clusters in a single powerful session.