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 notesmine
parent
c2694ee3e5
commit
e4e3816185
|
|
@ -239,77 +239,3 @@ formatters:
|
||||||
- internal/x # extracted from x/tools code
|
- internal/x # extracted from x/tools code
|
||||||
- pkg/goformatters/gci/internal # extracted from gci code
|
- pkg/goformatters/gci/internal # extracted from gci code
|
||||||
- pkg/goanalysis/runner_checker.go # extracted from x/tools 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
|
|
||||||
2
Makefile
2
Makefile
|
|
@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
|
||||||
else
|
else
|
||||||
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
endif
|
endif
|
||||||
VERSION ?= v0.50.3
|
VERSION ?= v0.50.4
|
||||||
IMG_NAME := derailed/k9s
|
IMG_NAME := derailed/k9s
|
||||||
IMAGE := ${IMG_NAME}:${VERSION}
|
IMAGE := ${IMG_NAME}:${VERSION}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -409,8 +409,10 @@ You can now override the context portForward default address configuration by se
|
||||||
liveViewAutoRefresh: false
|
liveViewAutoRefresh: false
|
||||||
# The path to screen dump. Default: '%temp_dir%/k9s-screens-%username%' (k9s info)
|
# The path to screen dump. Default: '%temp_dir%/k9s-screens-%username%' (k9s info)
|
||||||
screenDumpDir: /tmp/dumps
|
screenDumpDir: /tmp/dumps
|
||||||
# Represents ui poll intervals. Default 2secs
|
# Represents ui poll intervals in seconds. Default 2secs
|
||||||
refreshRate: 2
|
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.
|
# Number of retries once the connection to the api-server is lost. Default 15.
|
||||||
maxConnRetry: 5
|
maxConnRetry: 5
|
||||||
# Indicates whether modification commands like delete/kill/edit are disabled. Default is false
|
# 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
|
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.
|
# 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
|
splashless: false
|
||||||
|
# Toggles icons display as not all terminal support these chars. Default: true
|
||||||
noIcons: false
|
noIcons: false
|
||||||
# Toggles reactive UI. This option provide for watching on disk artifacts changes and update the UI live Defaults to false.
|
# Toggles reactive UI. This option provide for watching on disk artifacts changes and update the UI live Defaults to false.
|
||||||
reactive: false
|
reactive: false
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
11
cmd/root.go
11
cmd/root.go
|
|
@ -12,18 +12,17 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/client"
|
||||||
"github.com/derailed/k9s/internal/color"
|
"github.com/derailed/k9s/internal/color"
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/k9s/internal/config/data"
|
"github.com/derailed/k9s/internal/config/data"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"github.com/derailed/k9s/internal/view"
|
"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 (
|
const (
|
||||||
|
|
|
||||||
9
go.mod
9
go.mod
|
|
@ -5,7 +5,7 @@ go 1.24.1
|
||||||
require (
|
require (
|
||||||
github.com/adrg/xdg v0.5.3
|
github.com/adrg/xdg v0.5.3
|
||||||
github.com/anchore/clio v0.0.0-20250319180342-2cfe4b0cb716
|
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/anchore/syft v1.22.0
|
||||||
github.com/atotto/clipboard v0.1.4
|
github.com/atotto/clipboard v0.1.4
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0
|
github.com/cenkalti/backoff/v4 v4.3.0
|
||||||
|
|
@ -152,7 +152,7 @@ require (
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
github.com/gdamore/encoding v1.0.1 // indirect
|
github.com/gdamore/encoding v1.0.1 // indirect
|
||||||
github.com/github/go-spdx/v2 v2.3.2 // 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/glebarez/sqlite v1.11.0 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // 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-billy/v5 v5.6.2 // indirect
|
||||||
|
|
@ -232,8 +232,6 @@ require (
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||||
github.com/mitchellh/go-wordwrap v1.0.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/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||||
github.com/moby/locker v1.0.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/openvex/go-vex v0.2.5 // indirect
|
||||||
github.com/owenrumney/go-sarif v1.1.2-0.20231003122901-1000f5e05554 // 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/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/pborman/indent v1.2.1 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 // 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/sync v0.13.0 // indirect
|
||||||
golang.org/x/sys v0.31.0 // indirect
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
golang.org/x/term v0.30.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/tools v0.31.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||||
google.golang.org/api v0.215.0 // indirect
|
google.golang.org/api v0.215.0 // indirect
|
||||||
|
|
|
||||||
18
go.sum
18
go.sum
|
|
@ -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-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 h1:rmZG77uXgE+o2gozGEBoUMpX27lsku+xrMwlmBZJtbg=
|
||||||
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
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.91.0 h1:x6/jweLDNp+jy6ufyCukBJbFAlVefxSUOqb2eFsJdQY=
|
||||||
github.com/anchore/grype v0.87.0/go.mod h1:Umw/9sZHnS+9mPLTnUFd/OzApR6dzE2CPlRsov7kSmQ=
|
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 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/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=
|
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-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 h1:LFG0ggUKR+KEiiaOvFCmLgJ5NO2zf93AxxddkBn3LdQ=
|
||||||
github.com/gkampitakis/go-snaps v0.5.11/go.mod h1:PcKmy8q5Se7p48ywpogN5Td13reipz1Iivah4wrTIvY=
|
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.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
||||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
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 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
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-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 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
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 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.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
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 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
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/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 h1:KTRE0bK3sKbFKAk3yy63DpeskU7Cvs/x/Da5l+RtzyU=
|
||||||
github.com/package-url/packageurl-go v0.1.1/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
|
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.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM=
|
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.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.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.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
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-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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,14 @@ package client
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
|
@ -19,7 +21,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
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 caches client config to avoid reloads.
|
||||||
UsePersistentConfig = true
|
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.
|
// CallTimeout returns the call timeout if set or the default if not set.
|
||||||
func (c *Config) CallTimeout() time.Duration {
|
func (c *Config) CallTimeout() time.Duration {
|
||||||
if !isSet(c.flags.Timeout) {
|
if !isSet(c.flags.Timeout) {
|
||||||
return defaultCallTimeoutDuration
|
return DefaultCallTimeoutDuration
|
||||||
}
|
}
|
||||||
dur, err := time.ParseDuration(*c.flags.Timeout)
|
dur, err := time.ParseDuration(*c.flags.Timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return defaultCallTimeoutDuration
|
return DefaultCallTimeoutDuration
|
||||||
}
|
}
|
||||||
|
slog.Debug("APIServer timeout", slogs.Duration, dur)
|
||||||
|
|
||||||
return dur
|
return dur
|
||||||
}
|
}
|
||||||
|
|
@ -95,6 +99,8 @@ func (c *Config) SwitchContext(name string) error {
|
||||||
flags.ImpersonateGroup = c.flags.ImpersonateGroup
|
flags.ImpersonateGroup = c.flags.ImpersonateGroup
|
||||||
flags.ImpersonateUID = c.flags.ImpersonateUID
|
flags.ImpersonateUID = c.flags.ImpersonateUID
|
||||||
flags.Insecure = c.flags.Insecure
|
flags.Insecure = c.flags.Insecure
|
||||||
|
flags.BearerToken = c.flags.BearerToken
|
||||||
|
|
||||||
c.flags = flags
|
c.flags = flags
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ func TestCallTimeout(t *testing.T) {
|
||||||
e: 1 * time.Minute,
|
e: 1 * time.Minute,
|
||||||
},
|
},
|
||||||
"default": {
|
"default": {
|
||||||
e: 10 * time.Second,
|
e: 15 * time.Second,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
var NoGVR = &GVR{}
|
var NoGVR = new(GVR)
|
||||||
|
|
||||||
// GVR represents a kubernetes resource schema as a string.
|
// GVR represents a kubernetes resource schema as a string.
|
||||||
// Format is group/version/resources:subresource.
|
// Format is group/version/resources:subresource.
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ var (
|
||||||
ScGVR = NewGVR("storage.k8s.io/v1/storageclasses")
|
ScGVR = NewGVR("storage.k8s.io/v1/storageclasses")
|
||||||
|
|
||||||
// Policy...
|
// Policy...
|
||||||
PdbGVR = NewGVR("policy/v1/PodDisruptionBudgets")
|
PdbGVR = NewGVR("policy/v1/poddisruptionbudgets")
|
||||||
PspGVR = NewGVR("policy/v1beta1/podsecuritypolicies")
|
PspGVR = NewGVR("policy/v1beta1/podsecuritypolicies")
|
||||||
|
|
||||||
// Metrics...
|
// Metrics...
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/config/data"
|
"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
|
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.
|
// Refine the configuration based on cli args.
|
||||||
func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, cfg *client.Config) error {
|
func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, cfg *client.Config) error {
|
||||||
if flags == nil {
|
if flags == nil {
|
||||||
return 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 isStringSet(flags.Context) {
|
||||||
if _, err := c.K9s.ActivateContext(*flags.Context); err != nil {
|
if _, err := c.K9s.ActivateContext(*flags.Context); err != nil {
|
||||||
return fmt.Errorf("k8sflags. unable to activate context %q: %w", *flags.Context, err)
|
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()
|
c.ResetActiveView()
|
||||||
case isStringSet(flags.Namespace):
|
case isStringSet(flags.Namespace):
|
||||||
ns = *flags.Namespace
|
ns = *flags.Namespace
|
||||||
|
c.ResetActiveView()
|
||||||
default:
|
default:
|
||||||
nss, err := c.K9s.ActiveContextNamespace()
|
nss, err := c.K9s.ActiveContextNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -49,9 +49,8 @@ func TestConfigSave(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k := range uu {
|
for k, u := range uu {
|
||||||
xdg.Reload()
|
xdg.Reload()
|
||||||
u := uu[k]
|
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
c := mock.NewMockConfig(t)
|
c := mock.NewMockConfig(t)
|
||||||
_, err := c.K9s.ActivateContext(u.ct)
|
_, 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))
|
require.NoError(t, cfg.Load("testdata/configs/k9s.yaml", true))
|
||||||
|
|
||||||
cfg.K9s.RefreshRate = 100
|
cfg.K9s.RefreshRate = 100
|
||||||
|
cfg.K9s.APIServerTimeout = "30s"
|
||||||
cfg.K9s.ReadOnly = true
|
cfg.K9s.ReadOnly = true
|
||||||
cfg.K9s.Logger.TailCount = 500
|
cfg.K9s.Logger.TailCount = 500
|
||||||
cfg.K9s.Logger.BufferSize = 800
|
cfg.K9s.Logger.BufferSize = 800
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
"liveViewAutoRefresh": { "type": "boolean" },
|
"liveViewAutoRefresh": { "type": "boolean" },
|
||||||
"screenDumpDir": {"type": "string"},
|
"screenDumpDir": {"type": "string"},
|
||||||
"refreshRate": { "type": "integer" },
|
"refreshRate": { "type": "integer" },
|
||||||
|
"apiServerTimeout": { "type": "string" },
|
||||||
"maxConnRetry": { "type": "integer" },
|
"maxConnRetry": { "type": "integer" },
|
||||||
"readOnly": { "type": "boolean" },
|
"readOnly": { "type": "boolean" },
|
||||||
"noExitOnCtrlC": { "type": "boolean" },
|
"noExitOnCtrlC": { "type": "boolean" },
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ type K9s struct {
|
||||||
LiveViewAutoRefresh bool `json:"liveViewAutoRefresh" yaml:"liveViewAutoRefresh"`
|
LiveViewAutoRefresh bool `json:"liveViewAutoRefresh" yaml:"liveViewAutoRefresh"`
|
||||||
ScreenDumpDir string `json:"screenDumpDir" yaml:"screenDumpDir,omitempty"`
|
ScreenDumpDir string `json:"screenDumpDir" yaml:"screenDumpDir,omitempty"`
|
||||||
RefreshRate int `json:"refreshRate" yaml:"refreshRate"`
|
RefreshRate int `json:"refreshRate" yaml:"refreshRate"`
|
||||||
|
APIServerTimeout string `json:"apiServerTimeout" yaml:"apiServerTimeout"`
|
||||||
MaxConnRetry int32 `json:"maxConnRetry" yaml:"maxConnRetry"`
|
MaxConnRetry int32 `json:"maxConnRetry" yaml:"maxConnRetry"`
|
||||||
ReadOnly bool `json:"readOnly" yaml:"readOnly"`
|
ReadOnly bool `json:"readOnly" yaml:"readOnly"`
|
||||||
NoExitOnCtrlC bool `json:"noExitOnCtrlC" yaml:"noExitOnCtrlC"`
|
NoExitOnCtrlC bool `json:"noExitOnCtrlC" yaml:"noExitOnCtrlC"`
|
||||||
|
|
@ -54,6 +55,7 @@ func NewK9s(conn client.Connection, ks data.KubeSettings) *K9s {
|
||||||
return &K9s{
|
return &K9s{
|
||||||
RefreshRate: defaultRefreshRate,
|
RefreshRate: defaultRefreshRate,
|
||||||
MaxConnRetry: defaultMaxConnRetry,
|
MaxConnRetry: defaultMaxConnRetry,
|
||||||
|
APIServerTimeout: client.DefaultCallTimeoutDuration.String(),
|
||||||
ScreenDumpDir: AppDumpsDir,
|
ScreenDumpDir: AppDumpsDir,
|
||||||
Logger: NewLogger(),
|
Logger: NewLogger(),
|
||||||
Thresholds: NewThreshold(),
|
Thresholds: NewThreshold(),
|
||||||
|
|
@ -117,9 +119,11 @@ func (k *K9s) Merge(k1 *K9s) {
|
||||||
k.DefaultView = k1.DefaultView
|
k.DefaultView = k1.DefaultView
|
||||||
k.ScreenDumpDir = k1.ScreenDumpDir
|
k.ScreenDumpDir = k1.ScreenDumpDir
|
||||||
k.RefreshRate = k1.RefreshRate
|
k.RefreshRate = k1.RefreshRate
|
||||||
|
k.APIServerTimeout = k1.APIServerTimeout
|
||||||
k.MaxConnRetry = k1.MaxConnRetry
|
k.MaxConnRetry = k1.MaxConnRetry
|
||||||
k.ReadOnly = k1.ReadOnly
|
k.ReadOnly = k1.ReadOnly
|
||||||
k.NoExitOnCtrlC = k1.NoExitOnCtrlC
|
k.NoExitOnCtrlC = k1.NoExitOnCtrlC
|
||||||
|
k.PortForwardAddress = k1.PortForwardAddress
|
||||||
k.UI = k1.UI
|
k.UI = k1.UI
|
||||||
k.SkipLatestRevCheck = k1.SkipLatestRevCheck
|
k.SkipLatestRevCheck = k1.SkipLatestRevCheck
|
||||||
k.DisablePodCounting = k1.DisablePodCounting
|
k.DisablePodCounting = k1.DisablePodCounting
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ const (
|
||||||
DefaultLoggerTailCount = 100
|
DefaultLoggerTailCount = 100
|
||||||
|
|
||||||
// MaxLogThreshold sets the max value for log size.
|
// MaxLogThreshold sets the max value for log size.
|
||||||
MaxLogThreshold = 5000
|
MaxLogThreshold = 5_000
|
||||||
|
|
||||||
// DefaultSinceSeconds tracks default log age.
|
// DefaultSinceSeconds tracks default log age.
|
||||||
DefaultSinceSeconds = -1 // tail logs by default
|
DefaultSinceSeconds = -1 // tail logs by default
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ k9s:
|
||||||
liveViewAutoRefresh: false
|
liveViewAutoRefresh: false
|
||||||
screenDumpDir: /tmp/k9s-test/screen-dumps
|
screenDumpDir: /tmp/k9s-test/screen-dumps
|
||||||
refreshRate: 2
|
refreshRate: 2
|
||||||
|
apiServerTimeout: 15s
|
||||||
maxConnRetry: 5
|
maxConnRetry: 5
|
||||||
readOnly: false
|
readOnly: false
|
||||||
noExitOnCtrlC: false
|
noExitOnCtrlC: false
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ k9s:
|
||||||
liveViewAutoRefresh: true
|
liveViewAutoRefresh: true
|
||||||
screenDumpDir: /tmp/k9s-test/screen-dumps
|
screenDumpDir: /tmp/k9s-test/screen-dumps
|
||||||
refreshRate: 100
|
refreshRate: 100
|
||||||
|
apiServerTimeout: 30s
|
||||||
maxConnRetry: 5
|
maxConnRetry: 5
|
||||||
readOnly: true
|
readOnly: true
|
||||||
noExitOnCtrlC: false
|
noExitOnCtrlC: false
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ k9s:
|
||||||
liveViewAutoRefresh: true
|
liveViewAutoRefresh: true
|
||||||
screenDumpDir: /tmp/k9s-test/screen-dumps
|
screenDumpDir: /tmp/k9s-test/screen-dumps
|
||||||
refreshRate: 2
|
refreshRate: 2
|
||||||
|
apiServerTimeout: 10s
|
||||||
maxConnRetry: 5
|
maxConnRetry: 5
|
||||||
readOnly: false
|
readOnly: false
|
||||||
noExitOnCtrlC: false
|
noExitOnCtrlC: false
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,8 @@ func (d *Deployment) Scale(ctx context.Context, path string, replicas int32) err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart a Deployment rollout.
|
// Restart a Deployment rollout.
|
||||||
func (d *Deployment) Restart(ctx context.Context, path string) error {
|
func (d *Deployment) Restart(ctx context.Context, path string, opts *metav1.PatchOptions) error {
|
||||||
return restartRes[*appsv1.Deployment](ctx, d.getFactory(), client.DpGVR, path)
|
return restartRes[*appsv1.Deployment](ctx, d.getFactory(), client.DpGVR, path, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TailLogs tail logs for all pods represented by this Deployment.
|
// 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())
|
o, err := f.Get(gvr, path, true, labels.Everything())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -440,7 +440,7 @@ func restartRes[T runtime.Object](ctx context.Context, f Factory, gvr *client.GV
|
||||||
n,
|
n,
|
||||||
types.StrategicMergePatchType,
|
types.StrategicMergePatchType,
|
||||||
diff,
|
diff,
|
||||||
metav1.PatchOptions{},
|
*opts,
|
||||||
)
|
)
|
||||||
|
|
||||||
case client.DsGVR:
|
case client.DsGVR:
|
||||||
|
|
@ -449,7 +449,7 @@ func restartRes[T runtime.Object](ctx context.Context, f Factory, gvr *client.GV
|
||||||
n,
|
n,
|
||||||
types.StrategicMergePatchType,
|
types.StrategicMergePatchType,
|
||||||
diff,
|
diff,
|
||||||
metav1.PatchOptions{},
|
*opts,
|
||||||
)
|
)
|
||||||
|
|
||||||
case client.StsGVR:
|
case client.StsGVR:
|
||||||
|
|
@ -458,7 +458,7 @@ func restartRes[T runtime.Object](ctx context.Context, f Factory, gvr *client.GV
|
||||||
n,
|
n,
|
||||||
types.StrategicMergePatchType,
|
types.StrategicMergePatchType,
|
||||||
diff,
|
diff,
|
||||||
metav1.PatchOptions{},
|
*opts,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ func (d *DaemonSet) ListImages(_ context.Context, fqn string) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart a DaemonSet rollout.
|
// Restart a DaemonSet rollout.
|
||||||
func (d *DaemonSet) Restart(ctx context.Context, path string) error {
|
func (d *DaemonSet) Restart(ctx context.Context, path string, opts *metav1.PatchOptions) error {
|
||||||
return restartRes[*appsv1.DaemonSet](ctx, d.getFactory(), client.DsGVR, path)
|
return restartRes[*appsv1.DaemonSet](ctx, d.getFactory(), client.DsGVR, path, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TailLogs tail logs for all pods represented by this DaemonSet.
|
// TailLogs tail logs for all pods represented by this DaemonSet.
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/client-go/rest"
|
"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) {
|
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()}
|
opts := []string{d.gvr.AsResourceName()}
|
||||||
ns, n := client.Namespaced(fqn)
|
ns, n := client.Namespaced(fqn)
|
||||||
if n != "" {
|
if n != "" {
|
||||||
|
|
@ -51,7 +56,7 @@ func (d *Dynamic) toTable(ctx context.Context, fqn string) ([]runtime.Object, er
|
||||||
b := f.NewBuilder().
|
b := f.NewBuilder().
|
||||||
Unstructured().
|
Unstructured().
|
||||||
NamespaceParam(ns).DefaultNamespace().AllNamespaces(allNS).
|
NamespaceParam(ns).DefaultNamespace().AllNamespaces(allNS).
|
||||||
LabelSelectorParam(strLabel).
|
LabelSelectorParam(sel.String()).
|
||||||
FieldSelectorParam("").
|
FieldSelectorParam("").
|
||||||
RequestChunksOf(0).
|
RequestChunksOf(0).
|
||||||
ResourceTypeOrNameArgs(true, opts...).
|
ResourceTypeOrNameArgs(true, opts...).
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
)
|
)
|
||||||
|
|
@ -38,7 +39,10 @@ type Generic struct {
|
||||||
// List returns a collection of resources.
|
// List returns a collection of resources.
|
||||||
// BOZO!! no auth check??
|
// BOZO!! no auth check??
|
||||||
func (g *Generic) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
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) {
|
if client.IsAllNamespace(ns) {
|
||||||
ns = client.BlankNamespace
|
ns = client.BlankNamespace
|
||||||
}
|
}
|
||||||
|
|
@ -48,11 +52,12 @@ func (g *Generic) List(ctx context.Context, ns string) ([]runtime.Object, error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opts := metav1.ListOptions{LabelSelector: labelSel.String()}
|
||||||
var ll *unstructured.UnstructuredList
|
var ll *unstructured.UnstructuredList
|
||||||
if client.IsClusterScoped(ns) {
|
if client.IsClusterScoped(ns) {
|
||||||
ll, err = dial.List(ctx, metav1.ListOptions{LabelSelector: labelSel})
|
ll, err = dial.List(ctx, opts)
|
||||||
} else {
|
} else {
|
||||||
ll, err = dial.Namespace(ns).List(ctx, metav1.ListOptions{LabelSelector: labelSel})
|
ll, err = dial.Namespace(ns).List(ctx, opts)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,9 @@ type Resource struct {
|
||||||
|
|
||||||
// List returns a collection of resources.
|
// List returns a collection of resources.
|
||||||
func (r *Resource) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
func (r *Resource) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||||
strLabel, _ := ctx.Value(internal.KeyLabels).(string)
|
|
||||||
lsel := labels.Everything()
|
lsel := labels.Everything()
|
||||||
if strLabel != "" {
|
if sel, ok := ctx.Value(internal.KeyLabels).(labels.Selector); ok {
|
||||||
if sel, err := labels.Parse(strLabel); err == nil {
|
lsel = sel
|
||||||
lsel = sel
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.getFactory().List(r.gvr, ns, false, lsel)
|
return r.getFactory().List(r.gvr, ns, false, lsel)
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ func (s *StatefulSet) Scale(ctx context.Context, path string, replicas int32) er
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart a StatefulSet rollout.
|
// Restart a StatefulSet rollout.
|
||||||
func (s *StatefulSet) Restart(ctx context.Context, path string) error {
|
func (s *StatefulSet) Restart(ctx context.Context, path string, opts *metav1.PatchOptions) error {
|
||||||
return restartRes[*appsv1.StatefulSet](ctx, s.getFactory(), client.StsGVR, path)
|
return restartRes[*appsv1.StatefulSet](ctx, s.getFactory(), client.StsGVR, path, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInstance returns a statefulset instance.
|
// GetInstance returns a statefulset instance.
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
"k8s.io/client-go/rest"
|
"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.
|
// List all Resources in a given namespace.
|
||||||
func (t *Table) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
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)
|
fieldSel, _ := ctx.Value(internal.KeyFields).(string)
|
||||||
|
|
||||||
includeObject := includeMeta
|
includeObject := includeMeta
|
||||||
|
|
@ -75,7 +79,7 @@ func (t *Table) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||||
Namespace(ns).
|
Namespace(ns).
|
||||||
Resource(t.gvr.R()).
|
Resource(t.gvr.R()).
|
||||||
VersionedParams(&metav1.ListOptions{
|
VersionedParams(&metav1.ListOptions{
|
||||||
LabelSelector: labelSel,
|
LabelSelector: sel.String(),
|
||||||
FieldSelector: fieldSel,
|
FieldSelector: fieldSel,
|
||||||
}, metav1.ParameterCodec).
|
}, metav1.ParameterCodec).
|
||||||
Do(ctx).Get()
|
Do(ctx).Get()
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ type Switchable interface {
|
||||||
// Restartable represents a restartable resource.
|
// Restartable represents a restartable resource.
|
||||||
type Restartable interface {
|
type Restartable interface {
|
||||||
// Restart performs a rollout restart.
|
// Restart performs a rollout restart.
|
||||||
Restart(ctx context.Context, path string) error
|
Restart(context.Context, string, *metav1.PatchOptions) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runnable represents a runnable resource.
|
// Runnable represents a runnable resource.
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -313,4 +314,4 @@ func (c) Start() {}
|
||||||
func (c) Stop() {}
|
func (c) Stop() {}
|
||||||
func (c) Init(context.Context) error { return nil }
|
func (c) Init(context.Context) error { return nil }
|
||||||
func (c) SetFilter(string) {}
|
func (c) SetFilter(string) {}
|
||||||
func (c) SetLabelFilter(map[string]string) {}
|
func (c) SetLabelSelector(labels.Selector) {}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/model1"
|
"github.com/derailed/k9s/internal/model1"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,15 +39,15 @@ type TableListener interface {
|
||||||
|
|
||||||
// Table represents a table model.
|
// Table represents a table model.
|
||||||
type Table struct {
|
type Table struct {
|
||||||
gvr *client.GVR
|
gvr *client.GVR
|
||||||
data *model1.TableData
|
data *model1.TableData
|
||||||
listeners []TableListener
|
listeners []TableListener
|
||||||
inUpdate int32
|
inUpdate int32
|
||||||
refreshRate time.Duration
|
refreshRate time.Duration
|
||||||
instance string
|
instance string
|
||||||
labelFilter string
|
labelSelector labels.Selector
|
||||||
mx sync.RWMutex
|
mx sync.RWMutex
|
||||||
vs *config.ViewSetting
|
vs *config.ViewSetting
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTable returns a new table model.
|
// 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.
|
// SetLabelSelector sets the labels selector.
|
||||||
func (t *Table) SetLabelFilter(f string) {
|
func (t *Table) SetLabelSelector(sel labels.Selector) {
|
||||||
t.mx.Lock()
|
t.mx.Lock()
|
||||||
defer t.mx.Unlock()
|
defer t.mx.Unlock()
|
||||||
|
|
||||||
t.labelFilter = f
|
t.labelSelector = sel
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelFilter sets the labels filter.
|
// GetLabelSelector sets the labels selector.
|
||||||
func (t *Table) GetLabelFilter() string {
|
func (t *Table) GetLabelSelector() labels.Selector {
|
||||||
t.mx.Lock()
|
t.mx.Lock()
|
||||||
defer t.mx.Unlock()
|
defer t.mx.Unlock()
|
||||||
|
|
||||||
return t.labelFilter
|
return t.labelSelector
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetInstance sets a single entry table.
|
// 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)
|
a.Init(factory, t.gvr)
|
||||||
|
|
||||||
t.mx.RLock()
|
t.mx.RLock()
|
||||||
ctx = context.WithValue(ctx, internal.KeyLabels, t.labelFilter)
|
ctx = context.WithValue(ctx, internal.KeyLabels, t.labelSelector)
|
||||||
t.mx.RUnlock()
|
t.mx.RUnlock()
|
||||||
|
|
||||||
ns := client.CleanseNamespace(t.data.GetNamespace())
|
ns := client.CleanseNamespace(t.data.GetNamespace())
|
||||||
|
|
@ -273,7 +274,7 @@ func (t *Table) reconcile(ctx context.Context) error {
|
||||||
if t.vs != nil {
|
if t.vs != nil {
|
||||||
meta.DAO.SetIncludeObject(true)
|
meta.DAO.SetIncludeObject(true)
|
||||||
}
|
}
|
||||||
ctx = context.WithValue(ctx, internal.KeyLabels, t.labelFilter)
|
ctx = context.WithValue(ctx, internal.KeyLabels, t.labelSelector)
|
||||||
if t.instance == "" {
|
if t.instance == "" {
|
||||||
oo, err = t.list(ctx, meta.DAO)
|
oo, err = t.list(ctx, meta.DAO)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/view/cmd"
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/sahilm/fuzzy"
|
"github.com/sahilm/fuzzy"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -105,7 +106,7 @@ type Viewer interface {
|
||||||
|
|
||||||
type Filterer interface {
|
type Filterer interface {
|
||||||
SetFilter(string)
|
SetFilter(string)
|
||||||
SetLabelFilter(map[string]string)
|
SetLabelSelector(labels.Selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cruder performs crud operations.
|
// Cruder performs crud operations.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ import (
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ageCol = "AGE"
|
const ageCol = "AGE"
|
||||||
|
|
@ -171,6 +173,24 @@ func (h Header) Diff(header Header) bool {
|
||||||
return !reflect.DeepEqual(h, header)
|
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
|
// ColumnNames return header col names
|
||||||
func (h Header) ColumnNames(wide bool) []string {
|
func (h Header) ColumnNames(wide bool) []string {
|
||||||
if len(h) == 0 {
|
if len(h) == 0 {
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,10 @@ func (t *TableData) Filter(f FilterOpts) *TableData {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TableData) rxFilter(q string, inverse bool) (*RowEvents, error) {
|
func (t *TableData) rxFilter(q string, inverse bool) (*RowEvents, error) {
|
||||||
|
if strings.Contains(q, " ") {
|
||||||
|
return t.rowEvents, nil
|
||||||
|
}
|
||||||
|
|
||||||
if inverse {
|
if inverse {
|
||||||
q = q[1:]
|
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)
|
return nil, fmt.Errorf("invalid rx filter %q: %w", q, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var startIndex int
|
vidx := t.header.FilterColIndices(t.namespace, true)
|
||||||
if _, ok := t.header.IndexOf("NAMESPACE", true); ok && client.IsNamespaced(t.namespace) {
|
|
||||||
startIndex = 1
|
|
||||||
}
|
|
||||||
rr := NewRowEvents(t.RowCount() / 2)
|
rr := NewRowEvents(t.RowCount() / 2)
|
||||||
ageIndex, _ := t.header.IndexOf("AGE", true)
|
|
||||||
t.rowEvents.Range(func(_ int, re RowEvent) bool {
|
t.rowEvents.Range(func(_ int, re RowEvent) bool {
|
||||||
ff := make([]string, 0, len(re.Row.Fields))
|
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)
|
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))
|
match := rx.MatchString(strings.Join(ff, spacer))
|
||||||
if (inverse && !match) || (!inverse && match) {
|
if (inverse && !match) || (!inverse && match) {
|
||||||
rr.Add(re)
|
rr.Add(re)
|
||||||
|
|
|
||||||
|
|
@ -87,16 +87,20 @@ func (d Deployment) defaultRow(raw *unstructured.Unstructured, r *model1.Row) er
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var desired int32
|
||||||
|
if dp.Spec.Replicas != nil {
|
||||||
|
desired = *dp.Spec.Replicas
|
||||||
|
}
|
||||||
r.ID = client.MetaFQN(&dp.ObjectMeta)
|
r.ID = client.MetaFQN(&dp.ObjectMeta)
|
||||||
r.Fields = model1.Fields{
|
r.Fields = model1.Fields{
|
||||||
dp.Namespace,
|
dp.Namespace,
|
||||||
dp.Name,
|
dp.Name,
|
||||||
computeVulScore(dp.Namespace, dp.Labels, &dp.Spec.Template.Spec),
|
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.UpdatedReplicas)),
|
||||||
strconv.Itoa(int(dp.Status.AvailableReplicas)),
|
strconv.Itoa(int(dp.Status.AvailableReplicas)),
|
||||||
mapToStr(dp.Labels),
|
mapToStr(dp.Labels),
|
||||||
AsStatus(d.diagnose(dp.Status.Replicas, dp.Status.AvailableReplicas)),
|
AsStatus(d.diagnose(desired, dp.Status.AvailableReplicas)),
|
||||||
ToAge(dp.GetCreationTimestamp()),
|
ToAge(dp.GetCreationTimestamp()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -215,4 +215,7 @@ const (
|
||||||
|
|
||||||
// JQExp tracks a jq expression logger key.
|
// JQExp tracks a jq expression logger key.
|
||||||
JQExp = "jq-exp"
|
JQExp = "jq-exp"
|
||||||
|
|
||||||
|
// Duration tracks a duration logger key.
|
||||||
|
Duration = "duration"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -60,4 +61,4 @@ func (c) Start() {}
|
||||||
func (c) Stop() {}
|
func (c) Stop() {}
|
||||||
func (c) Init(context.Context) error { return nil }
|
func (c) Init(context.Context) error { return nil }
|
||||||
func (c) SetFilter(string) {}
|
func (c) SetFilter(string) {}
|
||||||
func (c) SetLabelFilter(map[string]string) {}
|
func (c) SetLabelSelector(labels.Selector) {}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -315,12 +315,18 @@ func (t *Table) doUpdate(data *model1.TableData) *model1.TableData {
|
||||||
} else {
|
} else {
|
||||||
t.actions.Delete(KeyShiftP)
|
t.actions.Delete(KeyShiftP)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.setSortCol(data.ComputeSortCol(t.GetViewSetting(), t.getSortCol(), t.getMSort()))
|
t.setSortCol(data.ComputeSortCol(t.GetViewSetting(), t.getSortCol(), t.getMSort()))
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
func (t *Table) UpdateUI(cdata, data *model1.TableData) {
|
||||||
t.Clear()
|
t.Clear()
|
||||||
fg := t.styles.Table().Header.FgColor.Color()
|
fg := t.styles.Table().Header.FgColor.Color()
|
||||||
|
|
@ -328,19 +334,9 @@ func (t *Table) UpdateUI(cdata, data *model1.TableData) {
|
||||||
|
|
||||||
var col int
|
var col int
|
||||||
for _, h := range cdata.Header() {
|
for _, h := range cdata.Header() {
|
||||||
if h.Hide || (!t.wide && h.Wide) {
|
if t.shouldExcludeColumn(h) {
|
||||||
continue
|
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)
|
t.AddHeaderCell(col, h)
|
||||||
c := t.GetCell(0, col)
|
c := t.GetCell(0, col)
|
||||||
c.SetBackgroundColor(bg)
|
c.SetBackgroundColor(bg)
|
||||||
|
|
@ -384,17 +380,7 @@ func (t *Table) buildRow(r int, re, ore model1.RowEvent, h model1.Header, pads M
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if h[c].Hide || (!t.wide && h[c].Wide) {
|
if t.shouldExcludeColumn(h[c]) {
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if h[c].Name == "NAMESPACE" && !t.GetModel().ClusterWide() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if h[c].MX && !t.hasMetrics {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if h[c].VS && vul.ImgScanner == nil {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -468,7 +454,6 @@ func (t *Table) Refresh() {
|
||||||
if data.HeaderCount() == 0 {
|
if data.HeaderCount() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// BOZO!! Really want to tell model reload now. Refactor!
|
|
||||||
cdata := t.Update(data, t.hasMetrics)
|
cdata := t.Update(data, t.hasMetrics)
|
||||||
t.UpdateUI(cdata, data)
|
t.UpdateUI(cdata, data)
|
||||||
}
|
}
|
||||||
|
|
@ -575,9 +560,14 @@ func (t *Table) styleTitle() string {
|
||||||
|
|
||||||
buff := t.cmdBuff.GetText()
|
buff := t.cmdBuff.GetText()
|
||||||
if internal.IsLabelSelector(buff) {
|
if internal.IsLabelSelector(buff) {
|
||||||
buff = render.Truncate(TrimLabelSelector(buff), maxTruncate)
|
sel, err := TrimLabelSelector(buff)
|
||||||
} else if l := t.GetModel().GetLabelFilter(); l != "" {
|
if err != nil {
|
||||||
buff = render.Truncate(l, maxTruncate)
|
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 == "" {
|
if buff == "" {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -58,12 +59,13 @@ func TrimCell(tv *SelectTable, row, col int) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrimLabelSelector extracts label query.
|
// 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 {
|
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.
|
// SkinTitle decorates a title.
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTruncate(t *testing.T) {
|
func TestTruncate(t *testing.T) {
|
||||||
|
|
@ -34,17 +35,29 @@ func TestTruncate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTrimLabelSelector(t *testing.T) {
|
func TestTrimLabelSelector(t *testing.T) {
|
||||||
|
sel, _ := labels.Parse("app=fred,env=blee")
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
sel, e string
|
sel string
|
||||||
|
err error
|
||||||
|
e labels.Selector
|
||||||
}{
|
}{
|
||||||
"cool": {"-l app=fred,env=blee", "app=fred,env=blee"},
|
"cool": {
|
||||||
"noSpace": {"-lapp=fred,env=blee", "app=fred,env=blee"},
|
sel: "-l app=fred,env=blee",
|
||||||
|
e: sel,
|
||||||
|
},
|
||||||
|
|
||||||
|
"no-space": {
|
||||||
|
sel: "-lapp=fred,env=blee",
|
||||||
|
e: sel,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -42,7 +43,7 @@ func TestTableUpdate(t *testing.T) {
|
||||||
func TestTableSelection(t *testing.T) {
|
func TestTableSelection(t *testing.T) {
|
||||||
v := ui.NewTable(client.NewGVR("fred"))
|
v := ui.NewTable(client.NewGVR("fred"))
|
||||||
v.Init(makeContext())
|
v.Init(makeContext())
|
||||||
m := &mockModel{}
|
m := new(mockModel)
|
||||||
v.SetModel(m)
|
v.SetModel(m)
|
||||||
data := m.Peek()
|
data := m.Peek()
|
||||||
cdata := v.Update(data, false)
|
cdata := v.Update(data, false)
|
||||||
|
|
@ -72,8 +73,8 @@ var _ ui.Tabular = &mockModel{}
|
||||||
|
|
||||||
func (*mockModel) SetViewSetting(context.Context, *config.ViewSetting) {}
|
func (*mockModel) SetViewSetting(context.Context, *config.ViewSetting) {}
|
||||||
func (*mockModel) SetInstance(string) {}
|
func (*mockModel) SetInstance(string) {}
|
||||||
func (*mockModel) SetLabelFilter(string) {}
|
func (*mockModel) SetLabelSelector(labels.Selector) {}
|
||||||
func (*mockModel) GetLabelFilter() string { return "" }
|
func (*mockModel) GetLabelSelector() labels.Selector { return nil }
|
||||||
func (*mockModel) Empty() bool { return false }
|
func (*mockModel) Empty() bool { return false }
|
||||||
func (*mockModel) RowCount() int { return 1 }
|
func (*mockModel) RowCount() int { return 1 }
|
||||||
func (*mockModel) HasMetrics() bool { return true }
|
func (*mockModel) HasMetrics() bool { return true }
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/model"
|
"github.com/derailed/k9s/internal/model"
|
||||||
"github.com/derailed/k9s/internal/model1"
|
"github.com/derailed/k9s/internal/model1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -49,11 +50,11 @@ type Tabular interface {
|
||||||
// SetInstance sets parent resource path.
|
// SetInstance sets parent resource path.
|
||||||
SetInstance(string)
|
SetInstance(string)
|
||||||
|
|
||||||
// SetLabelFilter sets the label filter.
|
// SetLabelSelector sets the label selector.
|
||||||
SetLabelFilter(string)
|
SetLabelSelector(labels.Selector)
|
||||||
|
|
||||||
// GetLabelFilter fetch the label filter.
|
// GetLabelSelector fetch the label filter.
|
||||||
GetLabelFilter() string
|
GetLabelSelector() labels.Selector
|
||||||
|
|
||||||
// Empty returns true if model has no data.
|
// Empty returns true if model has no data.
|
||||||
Empty() bool
|
Empty() bool
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -35,7 +36,7 @@ func TestAliasNew(t *testing.T) {
|
||||||
func TestAliasSearch(t *testing.T) {
|
func TestAliasSearch(t *testing.T) {
|
||||||
v := view.NewAlias(client.AliGVR)
|
v := view.NewAlias(client.AliGVR)
|
||||||
require.NoError(t, v.Init(makeContext(t)))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
v.GetTable().SetModel(&mockModel{})
|
v.GetTable().SetModel(new(mockModel))
|
||||||
v.GetTable().Refresh()
|
v.GetTable().Refresh()
|
||||||
v.App().Prompt().SetModel(v.GetTable().CmdBuff())
|
v.App().Prompt().SetModel(v.GetTable().CmdBuff())
|
||||||
v.App().Prompt().SendStrokes("blee")
|
v.App().Prompt().SendStrokes("blee")
|
||||||
|
|
@ -93,8 +94,8 @@ func (*mockModel) NextSuggestion() (string, bool) { return
|
||||||
func (*mockModel) PrevSuggestion() (string, bool) { return "", false }
|
func (*mockModel) PrevSuggestion() (string, bool) { return "", false }
|
||||||
func (*mockModel) ClearSuggestions() {}
|
func (*mockModel) ClearSuggestions() {}
|
||||||
func (*mockModel) SetInstance(string) {}
|
func (*mockModel) SetInstance(string) {}
|
||||||
func (*mockModel) SetLabelFilter(string) {}
|
func (*mockModel) SetLabelSelector(labels.Selector) {}
|
||||||
func (*mockModel) GetLabelFilter() string { return "" }
|
func (*mockModel) GetLabelSelector() labels.Selector { return nil }
|
||||||
func (*mockModel) Empty() bool { return false }
|
func (*mockModel) Empty() bool { return false }
|
||||||
func (*mockModel) RowCount() int { return 1 }
|
func (*mockModel) RowCount() int { return 1 }
|
||||||
func (*mockModel) HasMetrics() bool { return true }
|
func (*mockModel) HasMetrics() bool { return true }
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/view/cmd"
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -194,9 +195,11 @@ func (b *Browser) SetFilter(s string) {
|
||||||
b.CmdBuff().SetText(s, "")
|
b.CmdBuff().SetText(s, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Browser) SetLabelFilter(labels map[string]string) {
|
func (b *Browser) SetLabelSelector(sel labels.Selector) {
|
||||||
b.CmdBuff().SetText(toLabelsStr(labels), "")
|
if sel != nil {
|
||||||
b.GetModel().SetLabelFilter(toLabelsStr(labels))
|
b.CmdBuff().SetText(sel.String(), "")
|
||||||
|
}
|
||||||
|
b.GetModel().SetLabelSelector(sel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BufferChanged indicates the buffer was changed.
|
// BufferChanged indicates the buffer was changed.
|
||||||
|
|
@ -205,9 +208,11 @@ func (*Browser) BufferChanged(_, _ string) {}
|
||||||
// BufferCompleted indicates input was accepted.
|
// BufferCompleted indicates input was accepted.
|
||||||
func (b *Browser) BufferCompleted(text, _ string) {
|
func (b *Browser) BufferCompleted(text, _ string) {
|
||||||
if internal.IsLabelSelector(text) {
|
if internal.IsLabelSelector(text) {
|
||||||
b.GetModel().SetLabelFilter(ui.TrimLabelSelector(text))
|
if sel, err := ui.TrimLabelSelector(text); err == nil {
|
||||||
|
b.GetModel().SetLabelSelector(sel)
|
||||||
|
}
|
||||||
} else {
|
} 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()
|
hasFilter := !b.CmdBuff().Empty()
|
||||||
b.CmdBuff().ClearText(false)
|
b.CmdBuff().ClearText(false)
|
||||||
if hasFilter {
|
if hasFilter {
|
||||||
b.GetModel().SetLabelFilter("")
|
b.GetModel().SetLabelSelector(labels.Everything())
|
||||||
b.Refresh()
|
b.Refresh()
|
||||||
}
|
}
|
||||||
return b.App().PrevCmd(evt)
|
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.KeyGVR, b.GVR())
|
||||||
ctx = context.WithValue(ctx, internal.KeyPath, b.Path)
|
ctx = context.WithValue(ctx, internal.KeyPath, b.Path)
|
||||||
if internal.IsLabelSelector(b.CmdBuff().GetText()) {
|
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.KeyNamespace, client.CleanseNamespace(b.App().Config.ActiveNamespace()))
|
||||||
ctx = context.WithValue(ctx, internal.KeyWithMetrics, b.app.factory.Client().HasMetrics())
|
ctx = context.WithValue(ctx, internal.KeyWithMetrics, b.app.factory.Client().HasMetrics())
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/model"
|
"github.com/derailed/k9s/internal/model"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"github.com/derailed/k9s/internal/view/cmd"
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"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 := c.componentFor(gvr, fqn, v)
|
||||||
co.SetFilter("")
|
co.SetFilter("")
|
||||||
co.SetLabelFilter(nil)
|
co.SetLabelSelector(labels.Everything())
|
||||||
if f, ok := p.FilterArg(); ok {
|
if f, ok := p.FilterArg(); ok {
|
||||||
co.SetFilter(f)
|
co.SetFilter(f)
|
||||||
}
|
}
|
||||||
if f, ok := p.FuzzyArg(); ok {
|
if f, ok := p.FuzzyArg(); ok {
|
||||||
co.SetFilter("-f " + f)
|
co.SetFilter("-f " + f)
|
||||||
}
|
}
|
||||||
if ll, ok := p.LabelsArg(); ok {
|
if ss, ok := p.LabelsArg(); ok {
|
||||||
co.SetLabelFilter(ll)
|
co.SetLabelSelector(labels.SelectorFromSet(ss))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.exec(p, gvr, co, clearStack, pushCmd)
|
return c.exec(p, gvr, co, clearStack, pushCmd)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/sahilm/fuzzy"
|
"github.com/sahilm/fuzzy"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -61,7 +62,7 @@ func NewDetails(app *App, title, subject, contentType string, searchable bool) *
|
||||||
|
|
||||||
func (*Details) SetCommand(*cmd.Interpreter) {}
|
func (*Details) SetCommand(*cmd.Interpreter) {}
|
||||||
func (*Details) SetFilter(string) {}
|
func (*Details) SetFilter(string) {}
|
||||||
func (*Details) SetLabelFilter(map[string]string) {}
|
func (*Details) SetLabelSelector(labels.Selector) {}
|
||||||
|
|
||||||
// Init initializes the viewer.
|
// Init initializes the viewer.
|
||||||
func (d *Details) Init(_ context.Context) error {
|
func (d *Details) Init(_ context.Context) error {
|
||||||
|
|
|
||||||
|
|
@ -90,5 +90,5 @@ func showPodsFromSelector(app *App, path string, sel *metav1.LabelSelector) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
showPods(app, path, l.String(), "")
|
showPods(app, path, l, "")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/view/cmd"
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -46,7 +47,7 @@ func NewHelp(app *App) *Help {
|
||||||
|
|
||||||
func (*Help) SetCommand(*cmd.Interpreter) {}
|
func (*Help) SetCommand(*cmd.Interpreter) {}
|
||||||
func (*Help) SetFilter(string) {}
|
func (*Help) SetFilter(string) {}
|
||||||
func (*Help) SetLabelFilter(map[string]string) {}
|
func (*Help) SetLabelSelector(labels.Selector) {}
|
||||||
|
|
||||||
// Init initializes the component.
|
// Init initializes the component.
|
||||||
func (h *Help) Init(ctx context.Context) error {
|
func (h *Help) Init(ctx context.Context) error {
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,11 @@ import (
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/derailed/k9s/internal/view/cmd"
|
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/sahilm/fuzzy"
|
"github.com/sahilm/fuzzy"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"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 {
|
func showPods(app *App, path string, labelSel labels.Selector, fieldSel 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) {
|
|
||||||
v := NewPod(client.PodGVR)
|
v := NewPod(client.PodGVR)
|
||||||
v.SetContextFn(podCtx(app, path, fieldSel))
|
v.SetContextFn(podCtx(app, path, fieldSel))
|
||||||
v.SetLabelFilter(cmd.ToLabels(labelSel))
|
v.SetLabelSelector(labelSel)
|
||||||
|
|
||||||
ns, _ := client.Namespaced(path)
|
ns, _ := client.Namespaced(path)
|
||||||
if err := app.Config.SetActiveNamespace(ns); err != nil {
|
if err := app.Config.SetActiveNamespace(ns); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/sahilm/fuzzy"
|
"github.com/sahilm/fuzzy"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -64,7 +65,7 @@ func NewLiveView(app *App, title string, m model.ResourceViewer) *LiveView {
|
||||||
|
|
||||||
func (*LiveView) SetCommand(*cmd.Interpreter) {}
|
func (*LiveView) SetCommand(*cmd.Interpreter) {}
|
||||||
func (*LiveView) SetFilter(string) {}
|
func (*LiveView) SetFilter(string) {}
|
||||||
func (*LiveView) SetLabelFilter(map[string]string) {}
|
func (*LiveView) SetLabelSelector(labels.Selector) {}
|
||||||
|
|
||||||
// Init initializes the viewer.
|
// Init initializes the viewer.
|
||||||
func (v *LiveView) Init(_ context.Context) error {
|
func (v *LiveView) Init(_ context.Context) error {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/view/cmd"
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -66,7 +67,7 @@ func NewLog(gvr *client.GVR, opts *dao.LogOptions) *Log {
|
||||||
|
|
||||||
func (*Log) SetCommand(*cmd.Interpreter) {}
|
func (*Log) SetCommand(*cmd.Interpreter) {}
|
||||||
func (*Log) SetFilter(string) {}
|
func (*Log) SetFilter(string) {}
|
||||||
func (*Log) SetLabelFilter(map[string]string) {}
|
func (*Log) SetLabelSelector(labels.Selector) {}
|
||||||
|
|
||||||
// Init initializes the viewer.
|
// Init initializes the viewer.
|
||||||
func (l *Log) Init(ctx context.Context) (err error) {
|
func (l *Log) Init(ctx context.Context) (err error) {
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ func (n *Node) bindKeys(aa *ui.KeyActions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) showPods(a *App, _ ui.Tabular, _ *client.GVR, path string) {
|
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 {
|
func (n *Node) drainCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/view/cmd"
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Picker represents a container picker.
|
// Picker represents a container picker.
|
||||||
|
|
@ -30,7 +31,7 @@ func NewPicker() *Picker {
|
||||||
|
|
||||||
func (*Picker) SetCommand(*cmd.Interpreter) {}
|
func (*Picker) SetCommand(*cmd.Interpreter) {}
|
||||||
func (*Picker) SetFilter(string) {}
|
func (*Picker) SetFilter(string) {}
|
||||||
func (*Picker) SetLabelFilter(map[string]string) {}
|
func (*Picker) SetLabelSelector(labels.Selector) {}
|
||||||
|
|
||||||
// Init initializes the view.
|
// Init initializes the view.
|
||||||
func (p *Picker) Init(ctx context.Context) error {
|
func (p *Picker) Init(ctx context.Context) error {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"golang.org/x/text/cases"
|
"golang.org/x/text/cases"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Graphable represents a graphic component.
|
// Graphable represents a graphic component.
|
||||||
|
|
@ -80,7 +81,7 @@ func NewPulse(gvr *client.GVR) ResourceViewer {
|
||||||
|
|
||||||
func (*Pulse) SetCommand(*cmd.Interpreter) {}
|
func (*Pulse) SetCommand(*cmd.Interpreter) {}
|
||||||
func (*Pulse) SetFilter(string) {}
|
func (*Pulse) SetFilter(string) {}
|
||||||
func (*Pulse) SetLabelFilter(map[string]string) {}
|
func (*Pulse) SetLabelSelector(labels.Selector) {}
|
||||||
|
|
||||||
// Init initializes the view.
|
// Init initializes the view.
|
||||||
func (p *Pulse) Init(ctx context.Context) error {
|
func (p *Pulse) Init(ctx context.Context) error {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/derailed/k9s/internal/ui/dialog"
|
"github.com/derailed/k9s/internal/ui/dialog"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RestartExtender represents a restartable resource.
|
// 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())
|
msg = fmt.Sprintf("Restart %d %s?", len(paths), r.GVR().R())
|
||||||
}
|
}
|
||||||
d := r.App().Styles.Dialog()
|
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())
|
opts := dialog.RestartDialogOpts{
|
||||||
defer cancel()
|
Title: "Confirm Restart",
|
||||||
for _, path := range paths {
|
Message: msg,
|
||||||
if err := r.restartRollout(ctx, path); err != nil {
|
FieldManager: "kubectl-rollout",
|
||||||
r.App().Flash().Err(err)
|
Ack: func(opts *metav1.PatchOptions) bool {
|
||||||
} else {
|
ctx, cancel := context.WithTimeout(context.Background(), r.App().Conn().Config().CallTimeout())
|
||||||
r.App().Flash().Infof("Restart in progress for `%s...", path)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return true
|
||||||
}, func() {})
|
},
|
||||||
|
Cancel: func() {},
|
||||||
|
}
|
||||||
|
dialog.ShowRestart(&d, r.App().Content.Pages, &opts)
|
||||||
|
|
||||||
return nil
|
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())
|
res, err := dao.AccessorFor(r.App().factory, r.GVR())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -79,7 +89,7 @@ func (r *RestartExtender) restartRollout(ctx context.Context, path string) error
|
||||||
return errors.New("resource is not restartable")
|
return errors.New("resource is not restartable")
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.Restart(ctx, path)
|
return s.Restart(ctx, path, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ func (s *Service) showPods(a *App, _ ui.Tabular, _ *client.GVR, path string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
showPods(a, path, toLabelsStr(svc.Spec.Selector), "")
|
showPods(a, path, labels.SelectorFromSet(svc.Spec.Selector), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Service) checkSvc(svc *v1.Service) error {
|
func (*Service) checkSvc(svc *v1.Service) error {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -135,8 +136,8 @@ var _ ui.Tabular = (*mockTableModel)(nil)
|
||||||
|
|
||||||
func (*mockTableModel) SetViewSetting(context.Context, *config.ViewSetting) {}
|
func (*mockTableModel) SetViewSetting(context.Context, *config.ViewSetting) {}
|
||||||
func (*mockTableModel) SetInstance(string) {}
|
func (*mockTableModel) SetInstance(string) {}
|
||||||
func (*mockTableModel) SetLabelFilter(string) {}
|
func (*mockTableModel) SetLabelSelector(labels.Selector) {}
|
||||||
func (*mockTableModel) GetLabelFilter() string { return "" }
|
func (*mockTableModel) GetLabelSelector() labels.Selector { return nil }
|
||||||
func (*mockTableModel) Empty() bool { return false }
|
func (*mockTableModel) Empty() bool { return false }
|
||||||
func (*mockTableModel) RowCount() int { return 1 }
|
func (*mockTableModel) RowCount() int { return 1 }
|
||||||
func (*mockTableModel) HasMetrics() bool { return true }
|
func (*mockTableModel) HasMetrics() bool { return true }
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,9 @@ func (w *Workload) defaultContext(gvr *client.GVR, fqn string) context.Context {
|
||||||
ctx = context.WithValue(ctx, internal.KeyPath, fqn)
|
ctx = context.WithValue(ctx, internal.KeyPath, fqn)
|
||||||
}
|
}
|
||||||
if internal.IsLabelSelector(w.GetTable().CmdBuff().GetText()) {
|
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.KeyNamespace, client.CleanseNamespace(w.App().Config.ActiveNamespace()))
|
||||||
ctx = context.WithValue(ctx, internal.KeyWithMetrics, w.App().factory.Client().HasMetrics())
|
ctx = context.WithValue(ctx, internal.KeyWithMetrics, w.App().factory.Client().HasMetrics())
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"golang.org/x/text/cases"
|
"golang.org/x/text/cases"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -58,7 +59,7 @@ func NewXray(gvr *client.GVR) ResourceViewer {
|
||||||
|
|
||||||
func (*Xray) SetCommand(*cmd.Interpreter) {}
|
func (*Xray) SetCommand(*cmd.Interpreter) {}
|
||||||
func (*Xray) SetFilter(string) {}
|
func (*Xray) SetFilter(string) {}
|
||||||
func (*Xray) SetLabelFilter(map[string]string) {}
|
func (*Xray) SetLabelSelector(labels.Selector) {}
|
||||||
|
|
||||||
// Init initializes the view.
|
// Init initializes the view.
|
||||||
func (x *Xray) Init(ctx context.Context) error {
|
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(context.Background(), internal.KeyFactory, x.app.factory)
|
||||||
ctx = context.WithValue(ctx, internal.KeyFields, "")
|
ctx = context.WithValue(ctx, internal.KeyFields, "")
|
||||||
if x.CmdBuff().Empty() {
|
if x.CmdBuff().Empty() {
|
||||||
ctx = context.WithValue(ctx, internal.KeyLabels, "")
|
ctx = context.WithValue(ctx, internal.KeyLabels, labels.Everything())
|
||||||
} else {
|
} 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
|
return ctx
|
||||||
|
|
@ -688,7 +691,9 @@ func (x *Xray) styleTitle() string {
|
||||||
return title
|
return title
|
||||||
}
|
}
|
||||||
if internal.IsLabelSelector(buff) {
|
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)
|
return title + ui.SkinTitle(fmt.Sprintf(ui.SearchFmt, buff), &styles)
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,18 @@ import (
|
||||||
"github.com/anchore/clio"
|
"github.com/anchore/clio"
|
||||||
"github.com/anchore/grype/cmd/grype/cli/options"
|
"github.com/anchore/grype/cmd/grype/cli/options"
|
||||||
"github.com/anchore/grype/grype"
|
"github.com/anchore/grype/grype"
|
||||||
"github.com/anchore/grype/grype/db/legacy/distribution"
|
"github.com/anchore/grype/grype/match"
|
||||||
v5 "github.com/anchore/grype/grype/db/v5"
|
"github.com/anchore/grype/grype/matcher"
|
||||||
"github.com/anchore/grype/grype/db/v5/matcher"
|
"github.com/anchore/grype/grype/matcher/dotnet"
|
||||||
"github.com/anchore/grype/grype/db/v5/matcher/dotnet"
|
"github.com/anchore/grype/grype/matcher/golang"
|
||||||
"github.com/anchore/grype/grype/db/v5/matcher/golang"
|
"github.com/anchore/grype/grype/matcher/java"
|
||||||
"github.com/anchore/grype/grype/db/v5/matcher/java"
|
"github.com/anchore/grype/grype/matcher/javascript"
|
||||||
"github.com/anchore/grype/grype/db/v5/matcher/javascript"
|
"github.com/anchore/grype/grype/matcher/python"
|
||||||
"github.com/anchore/grype/grype/db/v5/matcher/python"
|
"github.com/anchore/grype/grype/matcher/ruby"
|
||||||
"github.com/anchore/grype/grype/db/v5/matcher/ruby"
|
"github.com/anchore/grype/grype/matcher/stock"
|
||||||
"github.com/anchore/grype/grype/db/v5/matcher/stock"
|
|
||||||
"github.com/anchore/grype/grype/pkg"
|
"github.com/anchore/grype/grype/pkg"
|
||||||
"github.com/anchore/grype/grype/vex"
|
"github.com/anchore/grype/grype/vex"
|
||||||
|
"github.com/anchore/grype/grype/vulnerability"
|
||||||
"github.com/anchore/syft/syft"
|
"github.com/anchore/syft/syft"
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
|
|
@ -40,8 +40,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type imageScanner struct {
|
type imageScanner struct {
|
||||||
store *v5.ProviderStore
|
provider vulnerability.Provider
|
||||||
dbStatus *distribution.Status
|
status *vulnerability.ProviderStatus
|
||||||
opts *options.Grype
|
opts *options.Grype
|
||||||
scans Scans
|
scans Scans
|
||||||
mx sync.RWMutex
|
mx sync.RWMutex
|
||||||
|
|
@ -90,8 +90,9 @@ func (s *imageScanner) Init(name, version string) {
|
||||||
s.opts.GenerateMissingCPEs = true
|
s.opts.GenerateMissingCPEs = true
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
s.store, s.dbStatus, err = grype.LoadVulnerabilityDB(
|
s.provider, s.status, err = grype.LoadVulnerabilityDB(
|
||||||
s.opts.DB.ToLegacyCuratorConfig(),
|
s.opts.ToClientConfig(),
|
||||||
|
s.opts.ToCuratorConfig(),
|
||||||
s.opts.DB.AutoUpdate,
|
s.opts.DB.AutoUpdate,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -99,7 +100,7 @@ func (s *imageScanner) Init(name, version string) {
|
||||||
return
|
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)
|
s.log.Error("VulDb validate failed", slogs.Error, e)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -112,9 +113,9 @@ func (s *imageScanner) Stop() {
|
||||||
s.mx.RLock()
|
s.mx.RLock()
|
||||||
defer s.mx.RUnlock()
|
defer s.mx.RUnlock()
|
||||||
|
|
||||||
if s.store != nil {
|
if s.provider != nil {
|
||||||
_ = s.store.Close()
|
_ = s.provider.Close()
|
||||||
s.store = nil
|
s.provider = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,11 +181,11 @@ func (s *imageScanner) scan(_ context.Context, img string, sc *Scan) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
v := grype.VulnerabilityMatcher{
|
v := grype.VulnerabilityMatcher{
|
||||||
Store: *s.store,
|
VulnerabilityProvider: s.provider,
|
||||||
IgnoreRules: s.opts.Ignore,
|
IgnoreRules: s.opts.Ignore,
|
||||||
NormalizeByCVE: s.opts.ByCVE,
|
NormalizeByCVE: s.opts.ByCVE,
|
||||||
FailSeverity: s.opts.FailOnSeverity(),
|
FailSeverity: s.opts.FailOnSeverity(),
|
||||||
Matchers: getMatchers(s.opts),
|
Matchers: getMatchers(s.opts),
|
||||||
VexProcessor: vex.NewProcessor(vex.ProcessorOptions{
|
VexProcessor: vex.NewProcessor(vex.ProcessorOptions{
|
||||||
Documents: s.opts.VexDocuments,
|
Documents: s.opts.VexDocuments,
|
||||||
IgnoreRules: s.opts.Ignore,
|
IgnoreRules: s.opts.Ignore,
|
||||||
|
|
@ -195,7 +196,7 @@ func (s *imageScanner) scan(_ context.Context, img string, sc *Scan) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = errors.Join(errs, err)
|
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)
|
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(
|
return matcher.NewDefaultMatchers(
|
||||||
matcher.Config{
|
matcher.Config{
|
||||||
Java: java.MatcherConfig{
|
Java: java.MatcherConfig{
|
||||||
|
|
@ -230,23 +231,23 @@ func getMatchers(opts *options.Grype) []matcher.Matcher {
|
||||||
Dotnet: dotnet.MatcherConfig(opts.Match.Dotnet),
|
Dotnet: dotnet.MatcherConfig(opts.Match.Dotnet),
|
||||||
Javascript: javascript.MatcherConfig(opts.Match.Javascript),
|
Javascript: javascript.MatcherConfig(opts.Match.Javascript),
|
||||||
Golang: golang.MatcherConfig{
|
Golang: golang.MatcherConfig{
|
||||||
UseCPEs: opts.Match.Golang.UseCPEs,
|
UseCPEs: opts.Match.Golang.UseCPEs,
|
||||||
AlwaysUseCPEForStdlib: opts.Match.Golang.AlwaysUseCPEForStdlib,
|
AlwaysUseCPEForStdlib: opts.Match.Golang.AlwaysUseCPEForStdlib,
|
||||||
|
AllowMainModulePseudoVersionComparison: opts.Match.Golang.AllowMainModulePseudoVersionComparison,
|
||||||
},
|
},
|
||||||
Stock: stock.MatcherConfig(opts.Match.Stock),
|
Stock: stock.MatcherConfig(opts.Match.Stock),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
func validateDBLoad(loadErr error, status *vulnerability.ProviderStatus) error {
|
||||||
func validateDBLoad(loadErr error, status *distribution.Status) error {
|
|
||||||
if loadErr != nil {
|
if loadErr != nil {
|
||||||
return fmt.Errorf("failed to load vulnerability db: %w", loadErr)
|
return fmt.Errorf("failed to load vulnerability db: %w", loadErr)
|
||||||
}
|
}
|
||||||
if status == nil {
|
if status == nil {
|
||||||
return fmt.Errorf("unable to determine the status of the vulnerability db")
|
return fmt.Errorf("unable to determine the status of the vulnerability db")
|
||||||
}
|
}
|
||||||
if status.Err != nil {
|
if status.Error != nil {
|
||||||
return fmt.Errorf("db could not be loaded: %w", status.Err)
|
return fmt.Errorf("db could not be loaded: %w", status.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name: k9s
|
name: k9s
|
||||||
base: core22
|
base: core22
|
||||||
version: 'v0.50.3'
|
version: 'v0.50.4'
|
||||||
summary: K9s is a CLI to view and manage your Kubernetes clusters.
|
summary: K9s is a CLI to view and manage your Kubernetes clusters.
|
||||||
description: |
|
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.
|
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.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue