Rel v0.40.11 (#3239)

* Fix #3222 - add gvr title config option

* Fix #3224 - pod shell use default co annotation

* Fix #3226 - yaml search busted

* Fix #3210 - description line is buggy -> update ro icons

* deps-update

* spring cleaning

* update deps + add stale issues
* cleanup secrets
mine
Fernand Galiana 2025-03-30 11:42:45 -06:00 committed by GitHub
parent 88e04217a2
commit 3a03b67af0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
122 changed files with 1250 additions and 674 deletions

View File

@ -18,7 +18,7 @@ jobs:
cache-dependency-path: go.sum
- name: Lint
uses: golangci/golangci-lint-action@v6.5.2
uses: golangci/golangci-lint-action@v7.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-pr-check

25
.github/workflows/stales.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: Closeout Stale Issues
on:
schedule:
- cron: "0 2 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
days-before-issue-stale: 30
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: 30
days-before-pr-close: 14
stale-pr-label: "stale"
stale-pr-message: "This PR is stale because it has been open for 30 days with no activity."
close-pr-message: "This PR was closed because it has been inactive for 14 days since being marked as stale."
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,6 +1,5 @@
# options for analysis running
version: "2"
run:
# default concurrency is an available CPU number
concurrency: 8
# timeout for analysis, e.g. 30s, 5m, default is 1m
@ -8,118 +7,113 @@ run:
# exit code when at least one issue was found, default is 1
issues-exit-code: 1
# include test files or not, default is true
tests: true
# which dirs to skip: they won't be analyzed;
# can use regexp here: generated.*, regexp is applied on full path;
# default value is empty list, but next dirs are always skipped independently
# from this option's value:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
# skip-dirs:
# - ^test.*
formatters:
enable:
- gci
- gofmt
# - gofumpt
- goimports
# - golines
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
# If invoked with -mod=readonly, the go command is disallowed from the implicit
# automatic updating of go.mod described above. Instead, it fails when any changes
# to go.mod are needed. This setting is most useful to check that go.mod does
# not need updates, such as in a continuous integration and testing system.
# If invoked with -mod=vendor, the go command assumes that the vendor
# directory holds the correct copies of dependencies and ignores
# the dependency descriptions in go.mod.
modules-download-mode: readonly
linters:
disable-all: true
enable:
- sloglint
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
- \\.(generated\\.deepcopy|pb)\\.go$
# which files to skip: they will be analyzed, but issues from them
# won't be reported. Default value is empty list, but there is
# no need to include all autogenerated files, we confidently recognize
# autogenerated files. If it's not please let us know.
# skip-files:
# - ".*\\.my\\.go$"
# - lib/bad.go
# all available settings of specific linters
linters-settings:
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
settings:
gocyclo:
min-complexity: 35
govet:
enable:
- nilness
goimports:
local-prefixes: github.com/cilium/cilium
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
sloglint:
# Enforce not mixing key-value pairs and attributes.
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-mixed-arguments
# Default: true
no-mixed-args: true
# Enforce using key-value pairs only (overrides no-mixed-args, incompatible with attr-only).
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#key-value-pairs-only
# Default: false
kv-only: true
# Enforce using attributes only (overrides no-mixed-args, incompatible with kv-only).
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#attributes-only
# Default: false
attr-only: false
# Enforce not using global loggers.
# Values:
# - "": disabled
# - "all": report all global loggers
# - "default": report only the default slog logger
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-global
# Default: ""
no-global: ""
# Enforce using methods that accept a context.
# Values:
# - "": disabled
# - "all": report all contextless calls
# - "scope": report only if a context exists in the scope of the outermost function
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#context-only
# Default: ""
context: ""
# Enforce using static values for log messages.
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#static-messages
# Default: false
static-msg: false
# Enforce using constants instead of raw keys.
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-raw-keys
# Default: false
no-raw-keys: true
# Enforce a single key naming convention.
# Values: snake, kebab, camel, pascal
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#key-naming-convention
# Default: ""
key-naming-case: camel
# Enforce not using specific keys.
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#forbidden-keys
# Default: []
forbidden-keys:
- time
- level
- msg
- source
# Enforce putting arguments on separate lines.
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#arguments-on-separate-lines
# Default: false
args-on-sep-lines: false
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
sloglint:
# Enforce not mixing key-value pairs and attributes.
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-mixed-arguments
# Default: true
no-mixed-args: true
# Enforce using key-value pairs only (overrides no-mixed-args, incompatible with attr-only).
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#key-value-pairs-only
# Default: false
kv-only: true
# Enforce using attributes only (overrides no-mixed-args, incompatible with kv-only).
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#attributes-only
# Default: false
attr-only: false
# Enforce not using global loggers.
# Values:
# - "": disabled
# - "all": report all global loggers
# - "default": report only the default slog logger
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-global
# Default: ""
no-global: ""
# Enforce using methods that accept a context.
# Values:
# - "": disabled
# - "all": report all contextless calls
# - "scope": report only if a context exists in the scope of the outermost function
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#context-only
# Default: ""
context: ""
# Enforce using static values for log messages.
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#static-messages
# Default: false
static-msg: false
# Enforce using constants instead of raw keys.
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-raw-keys
# Default: false
no-raw-keys: true
# Enforce a single key naming convention.
# Values: snake, kebab, camel, pascal
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#key-naming-convention
# Default: ""
key-naming-case: camel
# Enforce not using specific keys.
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#forbidden-keys
# Default: []
forbidden-keys:
- time
- level
- msg
- source
# Enforce putting arguments on separate lines.
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#arguments-on-separate-lines
# Default: false
args-on-sep-lines: false
issues:
@ -132,10 +126,13 @@ issues:
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
@ -146,27 +143,3 @@ issues:
- path: "pkg/ipam/(cidrset|service)/.+\\.go"
linters:
- goheader
linters:
disable-all: true
enable:
- err113
- gofmt
- goimports
- govet
- ineffassign
- misspell
- unused
- goheader
- gosec
- gomodguard
- gosimple
- errcheck
- gocyclo
- gosec
- gosimple
- misspell
- prealloc
- typecheck
- sloglint

View File

@ -436,6 +436,8 @@ You can now override the context portForward default address configuration by se
skin: dracula # => assumes the file skins/dracula.yaml is present in the $XDG_DATA_HOME/k9s/skins directory
# Allows to set certain views default fullscreen mode. (yaml, helm history, describe, value_extender, details, logs) Default false
defaultsToFullScreen: false
# Show full resource GVR (Group/Version/Resource) vs just R. Default: false.
useGVRTitleFormat: false
# Toggles icons display as not all terminal support these chars.
noIcons: false
# Toggles whether k9s should check for the latest revision from the GitHub repository releases. Default is false.

View File

@ -0,0 +1,52 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
# Release v0.40.11
## Notes
Thank you to all that contributed with flushing out issues and enhancements for K9s!
I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev
and see if we're happier with some of the fixes!
If you've filed an issue please help me verify and close.
Your support, kindness and awesome suggestions to make K9s better are, as ever, very much noted and appreciated!
Also big thanks to all that have allocated their own time to help others on both slack and on this repo!!
As you may know, K9s is not pimped out by corps with deep pockets, thus if you feel K9s is helping your Kubernetes journey,
please consider joining our [sponsorship program](https://github.com/sponsors/derailed) and/or make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
On Slack? Please join us [K9slackers](https://join.slack.com/t/k9sers/shared_invite/enQtOTA5MDEyNzI5MTU0LWQ1ZGI3MzliYzZhZWEyNzYxYzA3NjE0YTk1YmFmNzViZjIyNzhkZGI0MmJjYzhlNjdlMGJhYzE2ZGU1NjkyNTM)
## Maintenance Release!
## Videos Are In The Can!
Please dial [K9s Channel](https://www.youtube.com/channel/UC897uwPygni4QIjkPCpgjmw) for up coming content...
* [K9s v0.40.0 -Column Blow- Sneak peek](https://youtu.be/iy6RDozAM4A)
* [K9s v0.31.0 Configs+Sneak peek](https://youtu.be/X3444KfjguE)
* [K9s v0.30.0 Sneak peek](https://youtu.be/mVBc1XneRJ4)
* [Vulnerability Scans](https://youtu.be/ULkl0MsaidU)
---
## Resolved Issues
* [#3226](https://github.com/derailed/k9s/issues/3226) Filter view will show mess when filtering some string
* [#3224](https://github.com/derailed/k9s/issues/3224) Respect kubectl.kubernetes.io/default-container annotation
* [#3222](https://github.com/derailed/k9s/issues/3222) Option to Display Resource Names Without API Version Prefix
* [#3210](https://github.com/derailed/k9s/issues/3210) Description line is buggy
---
## 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!!
* [#3237](https://github.com/derailed/k9s/pull/3237) fix: List CRDs which has k8s.io in their names
* [#3223](https://github.com/derailed/k9s/pull/3223) Fixed skin config ref of in_the_navy to in-the-navy
* [#3110](https://github.com/derailed/k9s/pull/3110) feat: add splashless option to suppress splash screen on start
---
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2025 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)

View File

@ -48,9 +48,9 @@ func printInfo(cmd *cobra.Command, args []string) error {
func printLogo(c color.Paint) {
for _, l := range ui.LogoSmall {
fmt.Fprintln(out, color.Colorize(l, c))
_, _ = fmt.Fprintln(out, color.Colorize(l, c))
}
fmt.Fprintln(out)
_, _ = fmt.Fprintln(out)
}
// getScreenDumpDirForInfo get default screen dump config dir or from config.K9sConfigFile configuration.

View File

@ -18,7 +18,6 @@ import (
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/view"
"github.com/lmittmann/tint"
"github.com/mattn/go-colorable"
"github.com/spf13/cobra"

View File

@ -44,8 +44,8 @@ func printVersion(short bool) {
func printTuple(fmat, section, value string, outputColor color.Paint) {
if outputColor != -1 {
fmt.Fprintf(out, fmat, color.Colorize(section+":", outputColor), value)
_, _ = fmt.Fprintf(out, fmat, color.Colorize(section+":", outputColor), value)
return
}
fmt.Fprintf(out, fmat, section, value)
_, _ = fmt.Fprintf(out, fmat, section, value)
}

55
go.mod
View File

@ -1,15 +1,15 @@
module github.com/derailed/k9s
go 1.24.0
go 1.24.1
require (
github.com/adrg/xdg v0.5.3
github.com/anchore/clio v0.0.0-20241115144204-29e89f9fa837
github.com/anchore/grype v0.86.1
github.com/anchore/syft v1.20.0
github.com/anchore/grype v0.87.0
github.com/anchore/syft v1.21.0
github.com/atotto/clipboard v0.1.4
github.com/cenkalti/backoff/v4 v4.3.0
github.com/derailed/tcell/v2 v2.3.1-rc.3
github.com/derailed/tcell/v2 v2.3.1-rc.4
github.com/derailed/tview v0.8.5
github.com/fatih/color v1.18.0
github.com/fsnotify/fsnotify v1.8.0
@ -78,11 +78,12 @@ require (
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 // indirect
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 // indirect
github.com/anchore/stereoscope v0.0.13 // indirect
github.com/anchore/stereoscope v0.1.0 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/aquasecurity/go-pep440-version v0.0.1 // indirect
github.com/aquasecurity/go-version v0.0.1 // indirect
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go v1.44.288 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
@ -96,9 +97,12 @@ require (
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/charmbracelet/lipgloss v1.0.0 // indirect
github.com/charmbracelet/x/ansi v0.4.5 // indirect
github.com/cloudflare/circl v1.3.8 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/lipgloss v1.1.0 // indirect
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/cloudflare/circl v1.6.0 // indirect
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/containerd v1.7.27 // indirect
@ -111,13 +115,13 @@ require (
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.1.1 // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v27.5.0+incompatible // indirect
github.com/docker/cli v28.0.1+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v28.0.0+incompatible // indirect
github.com/docker/docker v28.0.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
@ -145,7 +149,7 @@ require (
github.com/glebarez/sqlite v1.11.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-git/go-git/v5 v5.13.2 // indirect
github.com/go-git/go-git/v5 v5.14.0 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
@ -156,12 +160,12 @@ require (
github.com/go-test/deep v1.1.1 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-containerregistry v0.20.3 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/licensecheck v0.3.1 // indirect
@ -179,7 +183,7 @@ require (
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-getter v1.7.6 // indirect
github.com/hashicorp/go-getter v1.7.8 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
@ -198,7 +202,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f // indirect
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d // indirect
@ -214,7 +218,6 @@ require (
github.com/masahiro331/go-mvn-version v0.0.0-20210429150710-d3157d602a08 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
@ -234,7 +237,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
@ -242,7 +245,7 @@ require (
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.35.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runtime-spec v1.1.0 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
github.com/openvex/go-vex v0.2.5 // indirect
@ -252,7 +255,7 @@ require (
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect
@ -266,6 +269,7 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rubenv/sql-migrate v1.7.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c // indirect
github.com/saferwall/pe v1.5.6 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
@ -276,16 +280,17 @@ require (
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb // indirect
github.com/spdx/tools-golang v0.5.5 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/afero v1.14.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/sylabs/sif/v2 v2.20.2 // indirect
github.com/sylabs/squashfs v1.0.4 // indirect
github.com/sylabs/squashfs v1.0.5 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/vbatts/go-mtree v0.5.4 // indirect
@ -315,7 +320,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/sync v0.12.0 // indirect
@ -342,7 +347,7 @@ require (
modernc.org/libc v1.61.13 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.2 // indirect
modernc.org/sqlite v1.35.0 // indirect
modernc.org/sqlite v1.36.1 // indirect
oras.land/oras-go v1.2.5 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/kustomize/api v0.18.0 // indirect

813
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -35,9 +35,7 @@ const (
cacheNSKey = "validNamespaces"
)
var (
supportedMetricsAPIVersions = []string{"v1beta1"}
)
var supportedMetricsAPIVersions = []string{"v1beta1"}
// NamespaceNames tracks a collection of namespace names.
type NamespaceNames map[string]struct{}

View File

@ -139,7 +139,6 @@ func (c *Config) CurrentClusterName() (string, error) {
}
return ct.Cluster, nil
}
// CurrentContextName returns the currently active config context.

View File

@ -45,7 +45,7 @@ func TestCallTimeout(t *testing.T) {
}
func TestConfigCurrentContext(t *testing.T) {
var kubeConfig = "./testdata/config"
kubeConfig := "./testdata/config"
uu := map[string]struct {
context string

View File

@ -240,7 +240,6 @@ func TestClusterLoad(t *testing.T) {
eSize: 0,
},
"ok": {
nodes: &v1.NodeList{
Items: []v1.Node{
makeNode("n1", "100m", "4Mi", "50m", "2Mi"),

View File

@ -556,6 +556,7 @@ func TestConfigSaveFile(t *testing.T) {
cfg.K9s.ReadOnly = true
cfg.K9s.Logger.TailCount = 500
cfg.K9s.Logger.BufferSize = 800
cfg.K9s.UI.UseFullGVRTitle = true
cfg.Validate("ct-1-1", "cl-1")
path := filepath.Join("/tmp", "k9s.yaml")

View File

@ -51,5 +51,5 @@ func (c *Config) Validate(conn client.Connection, contextName, clusterName strin
func (c *Config) Dump(w io.Writer) {
bb, _ := yaml.Marshal(&c)
fmt.Fprintf(w, "%s\n", string(bb))
_, _ = fmt.Fprintf(w, "%s\n", string(bb))
}

View File

@ -38,7 +38,6 @@ func NewContextFromConfig(cfg *api.Context) *Context {
ct.Namespace, ct.ClusterName = NewActiveNamespace(cfg.Namespace), cfg.Cluster
return ct
}
// NewContextFromKubeConfig returns a new instance based on kubesettings or an error.

View File

@ -63,9 +63,10 @@ func TestHelperInList(t *testing.T) {
}
func TestEnsureDirPathNone(t *testing.T) {
var mod os.FileMode = 0744
const mod = 0744
dir := filepath.Join("/tmp", "k9s-test")
os.Remove(dir)
_ = os.Remove(dir)
path := filepath.Join(dir, "duh.yaml")
assert.NoError(t, data.EnsureDirPath(path, mod))

View File

@ -11,10 +11,9 @@ import (
"os"
"path/filepath"
"github.com/adrg/xdg"
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/slogs"
"github.com/adrg/xdg"
)
const (
@ -127,10 +126,7 @@ func initK9sEnvLocs() error {
AppDumpsDir = filepath.Join(AppConfigDir, "screen-dumps")
if err := data.EnsureFullPath(AppDumpsDir, data.DefaultDirMod); err != nil {
slog.Warn("Unable to create screen-dumps dir",
slogs.Dir, AppDumpsDir,
slogs.Error, err,
)
slog.Warn("Unable to create screen-dumps dir", slogs.Dir, AppDumpsDir, slogs.Error, err)
}
AppBenchmarksDir = filepath.Join(AppConfigDir, "benchmarks")
if err := data.EnsureFullPath(AppBenchmarksDir, data.DefaultDirMod); err != nil {

View File

@ -17,15 +17,15 @@ func Test_initXDGLocs(t *testing.T) {
tmp, err := UserTmpDir()
assert.NoError(t, err)
os.Unsetenv("XDG_CONFIG_HOME")
os.Unsetenv("XDG_CACHE_HOME")
os.Unsetenv("XDG_STATE_HOME")
os.Unsetenv("XDG_DATA_HOME")
assert.NoError(t, os.Unsetenv("XDG_CONFIG_HOME"))
assert.NoError(t, os.Unsetenv("XDG_CACHE_HOME"))
assert.NoError(t, os.Unsetenv("XDG_STATE_HOME"))
assert.NoError(t, os.Unsetenv("XDG_DATA_HOME"))
os.Setenv("XDG_CONFIG_HOME", filepath.Join(tmp, "k9s-xdg", "config"))
os.Setenv("XDG_CACHE_HOME", filepath.Join(tmp, "k9s-xdg", "cache"))
os.Setenv("XDG_STATE_HOME", filepath.Join(tmp, "k9s-xdg", "state"))
os.Setenv("XDG_DATA_HOME", filepath.Join(tmp, "k9s-xdg", "data"))
assert.NoError(t, os.Setenv("XDG_CONFIG_HOME", filepath.Join(tmp, "k9s-xdg", "config")))
assert.NoError(t, os.Setenv("XDG_CACHE_HOME", filepath.Join(tmp, "k9s-xdg", "cache")))
assert.NoError(t, os.Setenv("XDG_STATE_HOME", filepath.Join(tmp, "k9s-xdg", "state")))
assert.NoError(t, os.Setenv("XDG_DATA_HOME", filepath.Join(tmp, "k9s-xdg", "data")))
xdg.Reload()
uu := map[string]struct {

View File

@ -39,17 +39,17 @@ func TestInitLogLoc(t *testing.T) {
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
os.Unsetenv(config.K9sEnvLogsDir)
os.Unsetenv("XDG_STATE_HOME")
os.Unsetenv(config.K9sEnvConfigDir)
assert.NoError(t, os.Unsetenv(config.K9sEnvLogsDir))
assert.NoError(t, os.Unsetenv("XDG_STATE_HOME"))
assert.NoError(t, os.Unsetenv(config.K9sEnvConfigDir))
switch k {
case "log-env":
os.Setenv(config.K9sEnvLogsDir, u.dir)
assert.NoError(t, os.Setenv(config.K9sEnvLogsDir, u.dir))
case "xdg-env":
os.Setenv("XDG_STATE_HOME", u.dir)
assert.NoError(t, os.Setenv("XDG_STATE_HOME", u.dir))
xdg.Reload()
case "cfg-env":
os.Setenv(config.K9sEnvConfigDir, u.dir)
assert.NoError(t, os.Setenv(config.K9sEnvConfigDir, u.dir))
}
err := config.InitLogLoc()
assert.NoError(t, err)
@ -60,7 +60,7 @@ func TestInitLogLoc(t *testing.T) {
}
func TestEnsureBenchmarkCfg(t *testing.T) {
os.Setenv(config.K9sEnvConfigDir, "/tmp/test-config")
assert.NoError(t, os.Setenv(config.K9sEnvConfigDir, "/tmp/test-config"))
assert.NoError(t, config.InitLocs())
defer assert.NoError(t, os.RemoveAll("/tmp/test-config"))

View File

@ -28,7 +28,8 @@
"noIcons": {"type": "boolean"},
"reactive": {"type": "boolean"},
"skin": {"type": "string"},
"defaultsToFullScreen": {"type": "boolean"}
"defaultsToFullScreen": {"type": "boolean"},
"useFullGVRTitle": {"type": "boolean"}
}
},
"shellPod": {

View File

@ -101,7 +101,6 @@ func Test_k9sOverrides(t *testing.T) {
assert.Equal(t, u.sl, u.k.IsSplashless())
assert.Equal(t, u.hl, u.k.IsHeadless())
assert.Equal(t, u.ll, u.k.IsLogoless())
})
}
}

View File

@ -37,7 +37,7 @@ func EnsureDir(d string) error {
func NewMockConfig() *config.Config {
if _, err := os.Stat("/tmp/test"); errors.Is(err, os.ErrExist) {
os.RemoveAll("/tmp/test")
_ = os.RemoveAll("/tmp/test")
}
config.AppContextsDir = "/tmp/test"
cl, ct := "cl-1", "ct-1-1"

View File

@ -15,6 +15,7 @@ k9s:
reactive: false
noIcons: false
defaultsToFullScreen: false
useFullGVRTitle: false
skipLatestRevCheck: false
disablePodCounting: false
shellPod:

View File

@ -15,6 +15,7 @@ k9s:
reactive: false
noIcons: false
defaultsToFullScreen: false
useFullGVRTitle: true
skipLatestRevCheck: false
disablePodCounting: false
shellPod:

View File

@ -15,6 +15,7 @@ k9s:
reactive: false
noIcons: false
defaultsToFullScreen: false
useFullGVRTitle: false
skipLatestRevCheck: false
disablePodCounting: false
shellPod:

View File

@ -38,6 +38,9 @@ type UI struct {
// DefaultsToFullScreen toggles fullscreen on views like logs, yaml, details.
DefaultsToFullScreen bool `json:"defaultsToFullScreen" yaml:"defaultsToFullScreen"`
// UseFullGVRTitle toggles the display of full GVR (group/version/resource) vs R in views title.
UseFullGVRTitle bool `json:"useFullGVRTitle" yaml:"useFullGVRTitle"`
manualHeadless *bool
manualLogoless *bool
manualCrumbsless *bool

View File

@ -17,8 +17,8 @@ import (
func TestAsGVR(t *testing.T) {
a := dao.NewAlias(makeFactory())
a.Aliases.Define("v1/pods", "po", "pod", "pods")
a.Aliases.Define("workloads", "workloads", "workload", "wkl")
a.Define("v1/pods", "po", "pod", "pods")
a.Define("workloads", "workloads", "workload", "wkl")
uu := map[string]struct {
cmd string

View File

@ -3,9 +3,7 @@
package dao
var (
_ Accessor = (*ConfigMap)(nil)
)
var _ Accessor = (*ConfigMap)(nil)
// ConfigMap represents a configmap resource.
type ConfigMap struct {

View File

@ -321,8 +321,8 @@ func hasConfigMap(spec *v1.PodSpec, name string) bool {
}
for _, v := range spec.Volumes {
if cm := v.VolumeSource.ConfigMap; cm != nil {
if cm.LocalObjectReference.Name == name {
if cm := v.ConfigMap; cm != nil {
if cm.Name == name {
return true
}
}
@ -375,7 +375,7 @@ func hasSecret(f Factory, spec *v1.PodSpec, ns, name string, wait bool) (bool, e
}
for _, v := range spec.Volumes {
if sec := v.VolumeSource.Secret; sec != nil {
if sec := v.Secret; sec != nil {
if sec.SecretName == name {
return true, nil
}

View File

@ -9,14 +9,13 @@ import (
"strconv"
"strings"
"helm.sh/helm/v3/pkg/action"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/render/helm"
"helm.sh/helm/v3/pkg/action"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
var (

View File

@ -20,13 +20,15 @@ import (
)
const (
defaultServiceAccount = "default"
defaultContainerAnnotation = "kubectl.kubernetes.io/default-container"
defaultServiceAccount = "default"
// DefaultContainerAnnotation represents the annotation key for the default container.
DefaultContainerAnnotation = "kubectl.kubernetes.io/default-container"
)
// GetDefaultContainer returns a container name if specified in an annotation.
func GetDefaultContainer(m metav1.ObjectMeta, spec v1.PodSpec) (string, bool) {
defaultContainer, ok := m.Annotations[defaultContainerAnnotation]
defaultContainer, ok := m.Annotations[DefaultContainerAnnotation]
if !ok {
return "", false
}
@ -38,7 +40,7 @@ func GetDefaultContainer(m metav1.ObjectMeta, spec v1.PodSpec) (string, bool) {
}
slog.Warn("Container not found. Annotation ignored",
slogs.CO, defaultContainer,
slogs.Annotation, defaultContainerAnnotation,
slogs.Annotation, DefaultContainerAnnotation,
)
return "", false

View File

@ -14,9 +14,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
)
var (
_ Accessor = (*ImageScan)(nil)
)
var _ Accessor = (*ImageScan)(nil)
// ImageScan represents vulnerability scans.
type ImageScan struct {

View File

@ -62,7 +62,7 @@ func (j *Job) List(ctx context.Context, ns string) ([]runtime.Object, error) {
continue
}
for _, r := range j.ObjectMeta.OwnerReferences {
for _, r := range j.OwnerReferences {
if r.Name == n {
ll = append(ll, o)
}

View File

@ -115,7 +115,7 @@ func (n *Node) Drain(path string, opts DrainOptions, w io.Writer) error {
dd, errs := h.GetPodsForDeletion(path)
if len(errs) != 0 {
for _, e := range errs {
if _, err := h.ErrOut.Write([]byte(fmt.Sprintf("[%s] %s\n", path, e.Error()))); err != nil {
if _, err := fmt.Fprintf(h.ErrOut, "[%s] %s\n", path, e.Error()); err != nil {
return err
}
}
@ -125,7 +125,7 @@ func (n *Node) Drain(path string, opts DrainOptions, w io.Writer) error {
if err := h.DeleteOrEvictPods(dd.Pods()); err != nil {
return err
}
fmt.Fprintf(h.Out, "Node %s drained!", path)
_, _ = fmt.Fprintf(h.Out, "Node %s drained!", path)
return nil
}

View File

@ -3,9 +3,7 @@
package dao
var (
_ Accessor = (*Namespace)(nil)
)
var _ Accessor = (*Namespace)(nil)
// Namespace represents a namespace resource.
type Namespace struct {

View File

@ -243,7 +243,7 @@ func (p *Pod) ScanSA(ctx context.Context, fqn string, wait bool) (Refs, error) {
return nil, errors.New("expecting Deployment resource")
}
// Just pick controller less pods...
if len(pod.ObjectMeta.OwnerReferences) > 0 {
if len(pod.OwnerReferences) > 0 {
continue
}
if serviceAccountMatches(pod.Spec.ServiceAccountName, n) {
@ -273,7 +273,7 @@ func (p *Pod) Scan(ctx context.Context, gvr client.GVR, fqn string, wait bool) (
return nil, errors.New("expecting Pod resource")
}
// Just pick controller less pods...
if len(pod.ObjectMeta.OwnerReferences) > 0 {
if len(pod.OwnerReferences) > 0 {
continue
}
switch gvr {

View File

@ -29,7 +29,7 @@ func TestGetDefaultContainer(t *testing.T) {
"container_not_present": {
po: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"kubectl.kubernetes.io/default-container": "container1"},
Annotations: map[string]string{DefaultContainerAnnotation: "container1"},
},
},
wantContainer: "",
@ -38,7 +38,7 @@ func TestGetDefaultContainer(t *testing.T) {
"container_found": {
po: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"kubectl.kubernetes.io/default-container": "container1"},
Annotations: map[string]string{DefaultContainerAnnotation: "container1"},
},
Spec: v1.PodSpec{
Containers: []v1.Container{{Name: "container1"}},

View File

@ -57,7 +57,7 @@ func (r *ReplicaSet) Load(f Factory, path string) (*appsv1.ReplicaSet, error) {
}
func getRSRevision(rs *appsv1.ReplicaSet) (int64, error) {
revision := rs.ObjectMeta.Annotations["deployment.kubernetes.io/revision"]
revision := rs.Annotations["deployment.kubernetes.io/revision"]
if rs.Status.Replicas != 0 {
return 0, errors.New("can not rollback current replica")
}
@ -70,7 +70,7 @@ func getRSRevision(rs *appsv1.ReplicaSet) (int64, error) {
}
func controllerInfo(rs *appsv1.ReplicaSet) (string, string, string, error) {
for _, ref := range rs.ObjectMeta.OwnerReferences {
for _, ref := range rs.OwnerReferences {
if ref.Controller == nil {
continue
}
@ -80,7 +80,7 @@ func controllerInfo(rs *appsv1.ReplicaSet) (string, string, string, error) {
}
return ref.Name, ref.Kind, group, nil
}
return "", "", "", fmt.Errorf("unable to find controller for replicaset: %s", rs.ObjectMeta.Name)
return "", "", "", fmt.Errorf("unable to find controller for replicaset: %s", rs.Name)
}
// Rollback reverses the last deployment.

View File

@ -15,8 +15,10 @@ import (
"k8s.io/client-go/scale"
)
var _ Scalable = (*Scaler)(nil)
var _ ReplicasGetter = (*Scaler)(nil)
var (
_ Scalable = (*Scaler)(nil)
_ ReplicasGetter = (*Scaler)(nil)
)
// Scaler represents a generic resource with scaling.
type Scaler struct {

View File

@ -16,46 +16,41 @@ import (
// Secret represents a secret K8s resource.
type Secret struct {
Resource
decode bool
decodeData bool
}
// Describe describes a secret that can be encoded or decoded.
func (s *Secret) Describe(path string) (string, error) {
encodedDescription, err := s.Generic.Describe(path)
if err != nil {
return "", err
}
if !s.decode {
if !s.decodeData {
return encodedDescription, nil
}
return s.Decode(encodedDescription, path)
}
// SetDecode sets the decode flag.
func (s *Secret) SetDecode(flag bool) {
s.decode = flag
// SetDecodeData toggles decode mode.
func (s *Secret) SetDecodeData(b bool) {
s.decodeData = b
}
// Decode removes the encoded part from the secret's description and appends the
// secret's decoded data.
func (s *Secret) Decode(encodedDescription, path string) (string, error) {
o, err := s.getFactory().Get(s.GVR(), path, true, labels.Everything())
if err != nil {
return "", err
}
dataEndIndex := strings.Index(encodedDescription, "====")
if dataEndIndex == -1 {
return "", fmt.Errorf("unable to find data section in secret description")
}
dataEndIndex += 4
if dataEndIndex >= len(encodedDescription) {
return "", fmt.Errorf("data section in secret description is invalid")
}
@ -64,19 +59,19 @@ func (s *Secret) Decode(encodedDescription, path string) (string, error) {
// More details about the reasoning of index: https://github.com/kubernetes/kubectl/blob/v0.29.0/pkg/describe/describe.go#L2542
body := encodedDescription[0:dataEndIndex]
d, err := ExtractSecrets(o.(*unstructured.Unstructured))
data, err := ExtractSecrets(o)
if err != nil {
return "", err
}
decodedSecrets := []string{}
for k, v := range d {
decodedSecrets = append(decodedSecrets, "\n", k, ":\t", v)
decodedSecrets := make([]string, 0, len(data))
for k, v := range data {
line := fmt.Sprintf("%s: %s", k, v)
decodedSecrets = append(decodedSecrets, strings.TrimSpace(line))
}
return body + strings.Join(decodedSecrets, ""), nil
return body + "\n" + strings.Join(decodedSecrets, "\n"), nil
}
// ExtractSecrets takes an unstructured object and attempts to convert it into a
@ -84,16 +79,18 @@ func (s *Secret) Decode(encodedDescription, path string) (string, error) {
// It returns a map where the keys are the secret data keys and the values are
// the corresponding secret data values.
// If the conversion fails, it returns an error.
func ExtractSecrets(o *unstructured.Unstructured) (map[string]string, error) {
func ExtractSecrets(o runtime.Object) (map[string]string, error) {
u, ok := o.(*unstructured.Unstructured)
if !ok {
return nil, fmt.Errorf("expecting *unstructured.Unstructured but got %T", o)
}
var secret v1.Secret
err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.Object, &secret)
err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &secret)
if err != nil {
return nil, err
}
secretData := make(map[string]string, len(secret.Data))
for k, val := range secret.Data {
secretData[k] = string(val)
}

View File

@ -12,11 +12,10 @@ import (
)
func TestEncodedSecretDescribe(t *testing.T) {
s := dao.Secret{}
var s dao.Secret
s.Init(makeFactory(), client.NewGVR("v1/secrets"))
encodedString :=
`
encodedString := `
Name: bootstrap-token-abcdef
Namespace: kube-system
Labels: <none>
@ -37,8 +36,9 @@ token-secret: 24 bytes`
"\n" +
"Data\n" +
"====\n" +
"token-secret:\t0123456789abcdef"
"token-secret: 0123456789abcdef"
decodedDescription, _ := s.Decode(encodedString, "kube-system/bootstrap-token-abcdef")
decodedDescription, err := s.Decode(encodedString, "kube-system/bootstrap-token-abcdef")
assert.NoError(t, err)
assert.Equal(t, expected, decodedDescription)
}

View File

@ -8,15 +8,14 @@ import (
"io"
"time"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/watch"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers"
restclient "k8s.io/client-go/rest"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/watch"
)
// ResourceMetas represents a collection of resource metadata.

View File

@ -82,7 +82,7 @@ func (w *Workload) Delete(ctx context.Context, path string, propagation *metav1.
}
func (a *Workload) fetch(ctx context.Context, gvr client.GVR, ns string) (*metav1.Table, error) {
a.Table.gvr = gvr
a.gvr = gvr
oo, err := a.Table.List(ctx, ns)
if err != nil {
return nil, err
@ -113,14 +113,12 @@ func (a *Workload) List(ctx context.Context, ns string) ([]runtime.Object, error
for _, r := range table.Rows {
if obj := r.Object.Object; obj != nil {
if m, err := meta.Accessor(obj); err == nil {
ns = m.GetNamespace()
ts = m.GetCreationTimestamp()
ns, ts = m.GetNamespace(), m.GetCreationTimestamp()
}
} else {
var m metav1.PartialObjectMetadata
if err := json.Unmarshal(r.Object.Raw, &m); err == nil {
ns = m.GetNamespace()
ts = m.CreationTimestamp
ns, ts = m.GetNamespace(), m.CreationTimestamp
}
}
stat := status(gvr, r, table.ColumnDefinitions)

View File

@ -13,11 +13,10 @@ import (
"sync"
"time"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/slogs"
"k8s.io/apimachinery/pkg/util/cache"
)
@ -239,5 +238,5 @@ func fetchLatestRev() (string, error) {
return v.(string), nil
}
return "", errors.New("No version found")
return "", errors.New("no version found")
}

View File

@ -187,7 +187,7 @@ func (d *Describe) describe(ctx context.Context, gvr client.GVR, path string) (s
}
if desc, ok := meta.DAO.(*dao.Secret); ok {
desc.SetDecode(d.decode)
desc.SetDecodeData(d.decode)
}
return desc.Describe(path)

View File

@ -262,14 +262,17 @@ func newTestView() *testView {
func (t *testView) LogCanceled() {}
func (t *testView) LogStop() {}
func (t *testView) LogResume() {}
func (t *testView) LogChanged(ll [][]byte) {
t.data = ll
t.dataCalled++
}
func (t *testView) LogCleared() {
t.clearCalled++
t.data = nil
}
func (t *testView) LogFailed(err error) {
fmt.Println("LogErr", err)
t.errCalled++

View File

@ -113,7 +113,7 @@ func (p *Pulse) reconcile(ctx context.Context) error {
for _, o := range oo {
c, ok := o.(*health.Check)
if !ok {
return fmt.Errorf("Expecting health check but got %T", o)
return fmt.Errorf("expecting health check but got %T", o)
}
p.data = append(p.data, c)
p.firePulseChanged(c)

View File

@ -113,10 +113,6 @@ var Registry = map[string]ResourceMeta{
Renderer: &render.Service{},
TreeRenderer: &xray.Service{},
},
"v1/events": {
DAO: &dao.Table{},
Renderer: &render.Event{},
},
"v1/serviceaccounts": {
Renderer: &render.ServiceAccount{},
},
@ -193,14 +189,4 @@ var Registry = map[string]ResourceMeta{
"rbac.authorization.k8s.io/v1/rolebindings": {
Renderer: &render.RoleBinding{},
},
// Autoscaling...
"autoscaling/v1/horizontalpodautoscalers": {
Renderer: &render.HorizontalPodAutoscaler{},
DAO: &dao.Table{},
},
"autoscaling/v2/horizontalpodautoscalers": {
Renderer: &render.HorizontalPodAutoscaler{},
DAO: &dao.Table{},
},
}

View File

@ -81,11 +81,6 @@ func TestTableMeta(t *testing.T) {
accessor: &dao.Node{},
renderer: &render.Node{},
},
"table": {
gvr: "v1/events",
accessor: &dao.Table{},
renderer: &render.Event{},
},
}
for k := range uu {

View File

@ -82,7 +82,6 @@ func (t *Text) RemoveListener(listener TextListener) {
break
}
}
if victim >= 0 {
t.listeners = append(t.listeners[:victim], t.listeners[victim+1:]...)
}

View File

@ -59,7 +59,7 @@ func (v *Values) getValues() ([]string, error) {
valuer, ok := accessor.(dao.Valuer)
if !ok {
return nil, fmt.Errorf("Resource %s is not Valuer", v.gvr)
return nil, fmt.Errorf("resource %s is not Valuer", v.gvr)
}
values, err := valuer.GetValues(v.path, v.allValues)

View File

@ -160,7 +160,7 @@ func lessCapacity(s1, s2 string) bool {
}
func lessNumber(s1, s2 string) bool {
v1, v2 := strings.Replace(s1, ",", "", -1), strings.Replace(s2, ",", "", -1)
v1, v2 := strings.ReplaceAll(s1, ",", ""), strings.ReplaceAll(s2, ",", "")
return sortorder.NaturalLess(v1, v2)
}

View File

@ -54,7 +54,6 @@ func TestLabelize(t *testing.T) {
}
func TestIsValid(t *testing.T) {
uu := map[string]struct {
ns string
h Header

View File

@ -299,7 +299,6 @@ func (r RowEventSorter) Len() int {
}
func (r RowEventSorter) Swap(i, j int) {
r.Events.events[i], r.Events.events[j] = r.Events.events[j], r.Events.events[i]
}

View File

@ -16,11 +16,10 @@ import (
"sync"
"time"
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/slogs"
"github.com/rakyll/hey/requester"
)
@ -127,8 +126,8 @@ func (b *Benchmark) Run(cluster, context string, done func()) {
func (b *Benchmark) save(cluster, context string, r io.Reader) error {
ns, n := client.Namespaced(b.config.Name)
n = strings.Replace(n, "|", "_", -1)
n = strings.Replace(n, ":", "_", -1)
n = strings.ReplaceAll(n, "|", "_")
n = strings.ReplaceAll(n, ":", "_")
dir, err := config.EnsureBenchmarksDir(cluster, context)
if err != nil {
return err

View File

@ -41,7 +41,7 @@ func ParsePlainPF(ann string) (*PFAnn, error) {
var pf PFAnn
mm := pfPlainRX.FindStringSubmatch(strings.TrimSpace(ann))
if len(mm) < 3 {
return nil, fmt.Errorf("Invalid plain port-forward %s", ann)
return nil, fmt.Errorf("invalid plain port-forward %s", ann)
}
if len(mm[2]) == 0 {
pf.ContainerPort = intstr.Parse(mm[1])

View File

@ -24,7 +24,6 @@ func (m ConfigMap) Header(_ string) model1.Header {
return m.doHeader(m.defaultHeader())
}
// Header returns a header rbw.
func (ConfigMap) defaultHeader() model1.Header {
return model1.Header{
model1.HeaderColumn{Name: "NAMESPACE"},

View File

@ -62,7 +62,7 @@ func (ClusterRole) defaultRow(raw *unstructured.Unstructured, r *model1.Row) err
return err
}
r.ID = client.FQN("-", cr.ObjectMeta.Name)
r.ID = client.FQN("-", cr.Name)
r.Fields = model1.Fields{
cr.Name,
mapToStr(cr.Labels),

View File

@ -67,7 +67,7 @@ func (ClusterRoleBinding) defaultRow(raw *unstructured.Unstructured, r *model1.R
kind, ss := renderSubjects(crb.Subjects)
r.ID = client.FQN("-", crb.ObjectMeta.Name)
r.ID = client.FQN("-", crb.Name)
r.Fields = model1.Fields{
crb.Name,
crb.RoleRef.Name,

View File

@ -98,7 +98,7 @@ func (cc ColumnSpecs) Header(rh model1.Header) model1.Header {
for _, h := range rh {
if idx, ok := hh.IndexOf(h.Name, true); ok {
hh[idx].Attrs = hh[idx].Attrs.Merge(h.Attrs)
hh[idx].Attrs = hh[idx].Merge(h.Attrs)
continue
}
hh = append(hh, h)

View File

@ -1,103 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s
package render
import (
"fmt"
"strings"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/model1"
"github.com/derailed/tcell/v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Event renders a K8s Event to screen.
type Event struct {
Table
}
func (*Event) IsGeneric() bool {
return true
}
// ColorerFunc colors a resource row.
func (e *Event) ColorerFunc() model1.ColorerFunc {
return func(ns string, h model1.Header, re *model1.RowEvent) tcell.Color {
idx, ok := h.IndexOf("REASON", true)
if ok && strings.TrimSpace(re.Row.Fields[idx]) == "Killing" {
return model1.KillColor
}
return model1.DefaultColorer(ns, h, re)
}
}
var ageCols = map[string]struct{}{
"FIRST SEEN": {},
"LAST SEEN": {},
}
var wideCols = map[string]struct{}{
"SUBOBJECT": {},
"COUNT": {},
"SOURCE": {},
"FIRST SEEN": {},
"NAME": {},
"MESSAGE": {},
}
// Header returns a header row.
func (e *Event) Header(_ string) model1.Header {
if e.table == nil {
return model1.Header{}
}
hh := make(model1.Header, 0, len(e.table.ColumnDefinitions))
hh = append(hh, model1.HeaderColumn{Name: "NAMESPACE"})
for _, h := range e.table.ColumnDefinitions {
header := model1.HeaderColumn{Name: strings.ToUpper(h.Name)}
if _, ok := ageCols[header.Name]; ok {
header.Time = true
}
if _, ok := wideCols[header.Name]; ok {
header.Wide = true
}
hh = append(hh, header)
}
return hh
}
// Render renders a K8s resource to screen.
func (e *Event) Render(o interface{}, ns string, r *model1.Row) error {
row, ok := o.(metav1.TableRow)
if !ok {
return fmt.Errorf("expecting a TableRow but got %T", o)
}
nns, name, err := resourceNS(row.Object.Raw)
if err != nil {
return err
}
r.ID = client.FQN(nns, name)
r.Fields = make(model1.Fields, 0, len(e.Header(ns)))
r.Fields = append(r.Fields, nns)
for _, o := range row.Cells {
if o == nil {
r.Fields = append(r.Fields, Blank)
continue
}
if s, ok := o.(fmt.Stringer); ok {
r.Fields = append(r.Fields, s.String())
continue
}
if s, ok := o.(string); ok {
r.Fields = append(r.Fields, s)
continue
}
r.Fields = append(r.Fields, fmt.Sprintf("%v", o))
}
return nil
}

View File

@ -1,26 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s
package render_test
// BOZO!!
// func TestEventRender(t *testing.T) {
// c := render.Event{}
// r := model1.NewRow(7)
// c.Render(load(t, "ev"), "", &r)
// assert.Equal(t, "default/hello-1567197780-mn4mv.15bfce150bd764dd", r.ID)
// assert.Equal(t, model1.Fields{"default", "pod:hello-1567197780-mn4mv", "Normal", "Pulled", "kubelet", "1", `Successfully pulled image "blang/busybox-bash"`}, r.Fields[:7])
// }
// func BenchmarkEventRender(b *testing.B) {
// ev := load(b, "ev")
// var re render.Event
// r := model1.NewRow(7)
// b.ResetTimer()
// b.ReportAllocs()
// for i := 0; i < b.N; i++ {
// _ = re.Render(&ev, "", &r)
// }
// }

View File

@ -51,7 +51,6 @@ func (c ImageScan) ColorerFunc() model1.ColorerFunc {
return c
}
}
// Header returns a header row.

View File

@ -100,7 +100,7 @@ func (p PersistentVolume) defaultRow(raw *unstructured.Unstructured, r *model1.R
}
phase := pv.Status.Phase
if pv.ObjectMeta.DeletionTimestamp != nil {
if pv.DeletionTimestamp != nil {
phase = terminatingPhase
}
var claim string

View File

@ -69,7 +69,7 @@ func (p PersistentVolumeClaim) defaultRow(raw *unstructured.Unstructured, r *mod
}
phase := pvc.Status.Phase
if pvc.ObjectMeta.DeletionTimestamp != nil {
if pvc.DeletionTimestamp != nil {
phase = "Terminating"
}

View File

@ -68,7 +68,7 @@ func (s StorageClass) defaultRow(raw *unstructured.Unstructured, r *model1.Row)
return err
}
r.ID = client.FQN(client.ClusterScope, sc.ObjectMeta.Name)
r.ID = client.FQN(client.ClusterScope, sc.Name)
r.Fields = model1.Fields{
s.nameWithDefault(sc.ObjectMeta),
sc.Provisioner,

View File

@ -38,7 +38,10 @@ var _ os.FileInfo = fileInfo{}
func (f fileInfo) Name() string { return "bob" }
func (f fileInfo) Size() int64 { return 100 }
func (f fileInfo) Mode() os.FileMode { return os.FileMode(0644) }
func (f fileInfo) ModTime() time.Time { return testTime() }
func (f fileInfo) IsDir() bool { return false }
func (f fileInfo) Sys() interface{} { return nil }
func (f fileInfo) Mode() os.FileMode {
return os.FileMode(0644)
}

View File

@ -7,12 +7,11 @@ import (
"fmt"
"strconv"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/model1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)
// Secret renders a K8s Secret to screen.

View File

@ -75,7 +75,7 @@ func (s Service) defaultRow(raw *unstructured.Unstructured, r *model1.Row) error
r.ID = client.MetaFQN(svc.ObjectMeta)
r.Fields = model1.Fields{
svc.Namespace,
svc.ObjectMeta.Name,
svc.Name,
string(svc.Spec.Type),
toIP(svc.Spec.ClusterIP),
toIPs(svc.Spec.Type, getSvcExtIPS(&svc)),

View File

@ -5,7 +5,6 @@ package render
import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
@ -138,39 +137,3 @@ func (t *Table) defaultRow(row *metav1.TableRow, ns string, r *model1.Row) error
return nil
}
// ----------------------------------------------------------------------------
// Helpers...
func resourceNS(raw []byte) (string, string, error) {
var obj map[string]interface{}
var ns, name string
err := json.Unmarshal(raw, &obj)
if err != nil {
return ns, name, err
}
meta, ok := obj["metadata"].(map[string]interface{})
if !ok {
return ns, name, errors.New("no metadata found on generic resource")
}
ina, ok := meta["name"]
if !ok {
return ns, name, errors.New("unable to extract resource name")
}
name, ok = ina.(string)
if !ok {
return ns, name, fmt.Errorf("expecting name string type but got %T", ns)
}
ins, ok := meta["namespace"]
if !ok {
return client.ClusterScope, name, nil
}
ns, ok = ins.(string)
if !ok {
return ns, name, fmt.Errorf("expecting namespace string type but got %T", ns)
}
return ns, name, nil
}

View File

@ -11,11 +11,10 @@ import (
"os"
"path/filepath"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/model1"
"github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/config"
"github.com/fsnotify/fsnotify"
)

View File

@ -14,7 +14,7 @@ import (
)
func Test_activeConfig(t *testing.T) {
os.Setenv(config.K9sEnvConfigDir, "/tmp/test-config")
assert.NoError(t, os.Setenv(config.K9sEnvConfigDir, "/tmp/test-config"))
assert.NoError(t, config.InitLocs())
cl, ct := "cl-1", "ct-1-1"

View File

@ -21,7 +21,7 @@ import (
)
func TestSkinnedContext(t *testing.T) {
os.Setenv(config.K9sEnvConfigDir, "/tmp/k9s-test")
assert.NoError(t, os.Setenv(config.K9sEnvConfigDir, "/tmp/k9s-test"))
assert.NoError(t, config.InitLocs())
defer assert.NoError(t, os.RemoveAll(config.K9sEnvConfigDir))
@ -52,7 +52,7 @@ func TestSkinnedContext(t *testing.T) {
}
func TestBenchConfig(t *testing.T) {
os.Setenv(config.K9sEnvConfigDir, "/tmp/test-config")
assert.NoError(t, os.Setenv(config.K9sEnvConfigDir, "/tmp/test-config"))
assert.NoError(t, config.InitLocs())
defer assert.NoError(t, os.RemoveAll(config.K9sEnvConfigDir))

View File

@ -66,9 +66,9 @@ func (c *Crumbs) refresh(crumbs []string) {
if i == last {
bgColor = c.styles.Frame().Crumb.ActiveColor
}
fmt.Fprintf(c, "[%s:%s:b] <%s> [-:%s:-] ",
_, _ = fmt.Fprintf(c, "[%s:%s:b] <%s> [-:%s:-] ",
c.styles.Frame().Crumb.FgColor,
bgColor, strings.Replace(strings.ToLower(crumb), " ", "", -1),
bgColor, strings.ReplaceAll(strings.ToLower(crumb), " ", ""),
c.styles.Body().BgColor)
}
}

View File

@ -80,7 +80,7 @@ const (
KeySlash = 47
KeyColon = 58
KeySpace = 32
KeyDash = 45 //or minus for those searching in the code
KeyDash = 45
KeyLeftBracket = 91
KeyRightBracket = 93
)

View File

@ -105,9 +105,9 @@ func (l *Logo) refreshLogo(c config.Color) {
defer l.mx.Unlock()
l.logo.Clear()
for i, s := range LogoSmall {
fmt.Fprintf(l.logo, "[%s::b]%s", c, s)
_, _ = fmt.Fprintf(l.logo, "[%s::b]%s", c, s)
if i+1 < len(LogoSmall) {
fmt.Fprintf(l.logo, "\n")
_, _ = fmt.Fprintf(l.logo, "\n")
}
}
}

View File

@ -197,7 +197,7 @@ func ToMnemonic(s string) string {
func formatNSMenu(i int, name string, styles config.Frame) string {
fmat := strings.Replace(menuIndexFmt, "[key", "["+styles.Menu.NumKeyColor.String(), 1)
fmat = strings.Replace(fmat, ":bg:", ":"+styles.Title.BgColor.String()+":", -1)
fmat = strings.ReplaceAll(fmat, ":bg:", ":"+styles.Title.BgColor.String()+":")
fmat = strings.Replace(fmat, "[fg", "["+styles.Menu.FgColor.String(), 1)
fmat = strings.Replace(fmat, "fgstyle]", styles.Menu.FgStyle.ToShortString()+"]", 1)
@ -208,7 +208,7 @@ func formatPlainMenu(h model.MenuHint, size int, styles config.Frame) string {
menuFmt := " [key:-:b]%-" + strconv.Itoa(size+2) + "s [fg:-:fgstyle]%s "
fmat := strings.Replace(menuFmt, "[key", "["+styles.Menu.KeyColor.String(), 1)
fmat = strings.Replace(fmat, "[fg", "["+styles.Menu.FgColor.String(), 1)
fmat = strings.Replace(fmat, ":bg:", ":"+styles.Title.BgColor.String()+":", -1)
fmat = strings.ReplaceAll(fmat, ":bg:", ":"+styles.Title.BgColor.String()+":")
fmat = strings.Replace(fmat, "fgstyle]", styles.Menu.FgStyle.ToShortString()+"]", 1)
return fmt.Sprintf(fmat, ToMnemonic(h.Mnemonic), h.Description)

View File

@ -24,7 +24,7 @@ func NewPages() *Pages {
Pages: tview.NewPages(),
Stack: model.NewStack(),
}
p.Stack.AddListener(&p)
p.AddListener(&p)
return &p
}
@ -74,7 +74,7 @@ func (p *Pages) delete(c model.Component) {
// Dump for debug.
func (p *Pages) Dump() {
slog.Debug("Dumping Pages", slogs.Page, p)
for i, c := range p.Stack.Peek() {
for i, c := range p.Peek() {
slog.Debug(fmt.Sprintf("%d -- %s -- %#v", i, componentID(c), p.GetPrimitive(componentID(c))))
}
}

View File

@ -235,7 +235,7 @@ func (p *Prompt) write(text, suggest string) {
txt += fmt.Sprintf("[%s::-]%s", p.styles.Prompt().SuggestColor, suggest)
}
p.StylesChanged(p.styles)
fmt.Fprintf(p, defaultPrompt, p.icon, txt)
_, _ = fmt.Fprintf(p, defaultPrompt, p.icon, txt)
}
// ----------------------------------------------------------------------------

View File

@ -6,11 +6,10 @@ package ui_test
import (
"testing"
"github.com/derailed/tcell/v2"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
"github.com/stretchr/testify/assert"
)

View File

@ -60,12 +60,12 @@ func NewSplash(styles *config.Styles, version string) *Splash {
func (s *Splash) layoutLogo(t *tview.TextView, styles *config.Styles) {
logo := strings.Join(LogoBig, fmt.Sprintf("\n[%s::b]", styles.Body().LogoColor))
fmt.Fprintf(t, "%s[%s::b]%s\n",
_, _ = fmt.Fprintf(t, "%s[%s::b]%s\n",
strings.Repeat("\n", 2),
styles.Body().LogoColor,
logo)
}
func (s *Splash) layoutRev(t *tview.TextView, rev string, styles *config.Styles) {
fmt.Fprintf(t, "[%s::b]Revision [red::b]%s", styles.Body().FgColor, rev)
_, _ = fmt.Fprintf(t, "[%s::b]Revision [red::b]%s", styles.Body().FgColor, rev)
}

View File

@ -55,6 +55,7 @@ type Table struct {
mx sync.RWMutex
readOnly bool
noIcon bool
fullGVR bool
}
// NewTable returns a new table view.
@ -73,6 +74,14 @@ func NewTable(gvr client.GVR) *Table {
}
}
// SetFullGVR toggles full GVR title display.
func (t *Table) SetFullGVR(b bool) {
t.mx.Lock()
defer t.mx.Unlock()
t.fullGVR = b
}
// SetNoIcon toggles no icon mode.
func (t *Table) SetNoIcon(b bool) {
t.mx.Lock()
@ -547,11 +556,16 @@ func (t *Table) styleTitle() string {
ns = t.Extras
}
resource := t.gvr.R()
if t.fullGVR {
resource = t.gvr.String()
}
var title string
if ns == client.ClusterScope {
title = SkinTitle(fmt.Sprintf(TitleFmt, t.gvr, render.AsThousands(rc)), t.styles.Frame())
title = SkinTitle(fmt.Sprintf(TitleFmt, resource, render.AsThousands(rc)), t.styles.Frame())
} else {
title = SkinTitle(fmt.Sprintf(NSTitleFmt, t.gvr, ns, render.AsThousands(rc)), t.styles.Frame())
title = SkinTitle(fmt.Sprintf(NSTitleFmt, resource, ns, render.AsThousands(rc)), t.styles.Frame())
}
if ic := ROIndicator(t.readOnly, t.noIcon); ic != "" {
title = " " + ic + title
@ -573,13 +587,12 @@ func (t *Table) styleTitle() string {
// ROIndicator returns an icon showing whether the session is in readonly mode or not.
func ROIndicator(ro, noIC bool) string {
if noIC {
switch {
case noIC:
return ""
}
if ro {
case ro:
return lockedIC
default:
return unlockedIC
}
return unlockedIC
}

View File

@ -72,12 +72,12 @@ func SkinTitle(fmat string, style config.Frame) string {
if bgColor == config.DefaultColor {
bgColor = config.TransparentColor
}
fmat = strings.Replace(fmat, "[fg:bg", "["+style.Title.FgColor.String()+":"+bgColor.String(), -1)
fmat = strings.ReplaceAll(fmat, "[fg:bg", "["+style.Title.FgColor.String()+":"+bgColor.String())
fmat = strings.Replace(fmat, "[hilite", "["+style.Title.HighlightColor.String(), 1)
fmat = strings.Replace(fmat, "[key", "["+style.Menu.NumKeyColor.String(), 1)
fmat = strings.Replace(fmat, "[filter", "["+style.Title.FilterColor.String(), 1)
fmat = strings.Replace(fmat, "[count", "["+style.Title.CounterColor.String(), 1)
fmat = strings.Replace(fmat, ":bg:", ":"+bgColor.String()+":", -1)
fmat = strings.ReplaceAll(fmat, ":bg:", ":"+bgColor.String()+":")
return fmat
}

View File

@ -70,36 +70,37 @@ type mockModel struct{}
var _ ui.Tabular = &mockModel{}
func (t *mockModel) SetViewSetting(context.Context, *config.ViewSetting) {}
func (t *mockModel) SetInstance(string) {}
func (t *mockModel) SetLabelFilter(string) {}
func (t *mockModel) GetLabelFilter() string { return "" }
func (t *mockModel) Empty() bool { return false }
func (t *mockModel) RowCount() int { return 1 }
func (t *mockModel) HasMetrics() bool { return true }
func (t *mockModel) Peek() *model1.TableData { return makeTableData() }
func (t *mockModel) Refresh(context.Context) error { return nil }
func (t *mockModel) ClusterWide() bool { return false }
func (t *mockModel) GetNamespace() string { return "blee" }
func (t *mockModel) SetNamespace(string) {}
func (t *mockModel) ToggleToast() {}
func (t *mockModel) AddListener(model.TableListener) {}
func (t *mockModel) RemoveListener(model.TableListener) {}
func (t *mockModel) Watch(context.Context) error { return nil }
func (t *mockModel) Get(ctx context.Context, path string) (runtime.Object, error) {
return nil, nil
}
func (t *mockModel) SetViewSetting(context.Context, *config.ViewSetting) {}
func (t *mockModel) SetInstance(string) {}
func (t *mockModel) SetLabelFilter(string) {}
func (t *mockModel) GetLabelFilter() string { return "" }
func (t *mockModel) Empty() bool { return false }
func (t *mockModel) RowCount() int { return 1 }
func (t *mockModel) HasMetrics() bool { return true }
func (t *mockModel) Peek() *model1.TableData { return makeTableData() }
func (t *mockModel) Refresh(context.Context) error { return nil }
func (t *mockModel) ClusterWide() bool { return false }
func (t *mockModel) GetNamespace() string { return "blee" }
func (t *mockModel) SetNamespace(string) {}
func (t *mockModel) ToggleToast() {}
func (t *mockModel) AddListener(model.TableListener) {}
func (t *mockModel) RemoveListener(model.TableListener) {}
func (t *mockModel) Watch(context.Context) error { return nil }
func (t *mockModel) Get(ctx context.Context, path string) (runtime.Object, error) { return nil, nil }
func (t *mockModel) InNamespace(string) bool { return true }
func (t *mockModel) SetRefreshRate(time.Duration) {}
func (t *mockModel) Delete(context.Context, string, *metav1.DeletionPropagation, dao.Grace) error {
return nil
}
func (t *mockModel) Describe(context.Context, string) (string, error) {
return "", nil
}
func (t *mockModel) ToYAML(ctx context.Context, path string) (string, error) {
return "", nil
}
func (t *mockModel) InNamespace(string) bool { return true }
func (t *mockModel) SetRefreshRate(time.Duration) {}
func makeTableData() *model1.TableData {
return model1.NewTableDataWithRows(

View File

@ -16,8 +16,8 @@ import (
)
const (
unlockedIC = "✍️ "
lockedIC = "🔒"
unlockedIC = "🖍"
lockedIC = "🔑"
)
// Namespaceable tracks namespaces.

View File

@ -100,8 +100,8 @@ func (a *App) Init(version string, rate int) error {
if err := a.Content.Init(ctx); err != nil {
return err
}
a.Content.Stack.AddListener(a.Crumbs())
a.Content.Stack.AddListener(a.Menu())
a.Content.AddListener(a.Crumbs())
a.Content.AddListener(a.Menu())
a.App.Init()
a.SetInputCapture(a.keyboard)
@ -198,7 +198,7 @@ func (a *App) suggestCommand() model.SuggestionFunc {
}
ls := strings.ToLower(s)
for _, k := range a.command.alias.Aliases.Keys() {
for _, k := range a.command.alias.Keys() {
if suggest, ok := cmd.ShouldAddSuggest(ls, k); ok {
entries = append(entries, suggest)
}
@ -776,7 +776,7 @@ func (a *App) inject(c model.Component, clearStack bool) error {
return err
}
if clearStack {
a.Content.Stack.Clear()
a.Content.Clear()
}
a.Content.Push(c)

View File

@ -91,8 +91,9 @@ func (b *Browser) Init(ctx context.Context) error {
if b.App().IsRunning() {
b.app.CmdBuff().Reset()
}
b.Table.SetReadOnly(b.app.Config.IsReadOnly())
b.Table.SetNoIcon(b.app.Config.K9s.UI.NoIcons)
b.SetReadOnly(b.app.Config.IsReadOnly())
b.SetNoIcon(b.app.Config.K9s.UI.NoIcons)
b.SetFullGVR(b.app.Config.K9s.UI.UseFullGVRTitle)
b.bindKeys(b.Actions())
for _, f := range b.bindKeysFn {

View File

@ -103,7 +103,8 @@ func TestFlagsNew(t *testing.T) {
fuzzyKey: "blee",
labelKey: "app=fred",
nsKey: "ns1",
contextKey: "Dev"},
contextKey: "Dev",
},
},
"ctx": {
i: NewInterpreter("ctx"),

View File

@ -85,16 +85,16 @@ func (c *Context) showRenameModal(name string, ok func(form *tview.Form, context
app.Content.Pages.RemovePage(renamePage)
}).
AddButton("Cancel", func() {
app.Content.Pages.RemovePage(renamePage)
app.Content.RemovePage(renamePage)
})
m := tview.NewModalForm("<Rename>", f)
m.SetText(fmt.Sprintf("Rename context %q?", name))
m.SetDoneFunc(func(int, string) {
app.Content.Pages.RemovePage(renamePage)
app.Content.RemovePage(renamePage)
})
app.Content.Pages.AddPage(renamePage, m, false, false)
app.Content.Pages.ShowPage(renamePage)
app.Content.AddPage(renamePage, m, false, false)
app.Content.ShowPage(renamePage)
for i := 0; i < f.GetButtonCount(); i++ {
f.GetButton(i).

View File

@ -238,11 +238,11 @@ func (d *Details) toggleFullScreenCmd(evt *tcell.EventKey) *tcell.EventKey {
func (d *Details) setFullScreen(isFullScreen bool) {
d.fullScreen = isFullScreen
d.SetFullScreen(isFullScreen)
d.Box.SetBorder(!isFullScreen)
d.SetBorder(!isFullScreen)
if isFullScreen {
d.Box.SetBorderPadding(0, 0, 0, 0)
d.SetBorderPadding(0, 0, 0, 0)
} else {
d.Box.SetBorderPadding(0, 0, 1, 1)
d.SetBorderPadding(0, 0, 1, 1)
}
}

View File

@ -6,10 +6,9 @@ package view_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/view"
"github.com/stretchr/testify/assert"
)
func TestDeploy(t *testing.T) {

View File

@ -63,7 +63,7 @@ func (e Env) Substitute(arg string) (string, error) {
}
v = fmt.Sprintf("%t", b)
}
arg = strings.Replace(arg, m[0], v, -1)
arg = strings.ReplaceAll(arg, m[0], v)
}
return arg, nil

View File

@ -17,12 +17,11 @@ import (
"syscall"
"time"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/fatih/color"
v1 "k8s.io/api/core/v1"
@ -447,7 +446,7 @@ func k9sShellPodName() string {
func k9sShellPod(node string, cfg config.ShellPod) *v1.Pod {
var grace int64
var priv bool = true
var priv = true
slog.Debug("Shell pod config", slogs.ShellPodCfg, cfg)
c := v1.Container{

View File

@ -292,11 +292,11 @@ func (v *LiveView) toggleFullScreenCmd(evt *tcell.EventKey) *tcell.EventKey {
func (v *LiveView) setFullScreen(isFullScreen bool) {
v.fullScreen = isFullScreen
v.SetFullScreen(isFullScreen)
v.Box.SetBorder(!isFullScreen)
v.SetBorder(!isFullScreen)
if isFullScreen {
v.Box.SetBorderPadding(0, 0, 0, 0)
v.SetBorderPadding(0, 0, 0, 0)
} else {
v.Box.SetBorderPadding(0, 0, 1, 1)
v.SetBorderPadding(0, 0, 1, 1)
}
}

View File

@ -395,7 +395,7 @@ func (l *Log) toggleAllContainers(evt *tcell.EventKey) *tcell.EventKey {
func (l *Log) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
if !l.logs.cmdBuff.IsActive() {
fmt.Fprintln(l.ansiWriter)
_, _ = fmt.Fprintln(l.ansiWriter)
return evt
}
@ -460,7 +460,7 @@ func (l *Log) clearCmd(*tcell.EventKey) *tcell.EventKey {
func (l *Log) markCmd(*tcell.EventKey) *tcell.EventKey {
_, _, w, _ := l.GetRect()
fmt.Fprintf(l.ansiWriter, "\n[%s:-:b]%s[-:-:-]", l.app.Styles.Views().Log.FgColor.String(), strings.Repeat("-", w-4))
_, _ = fmt.Fprintf(l.ansiWriter, "\n[%s:-:b]%s[-:-:-]", l.app.Styles.Views().Log.FgColor.String(), strings.Repeat("-", w-4))
l.follow = true
return nil
@ -516,7 +516,7 @@ func (l *Log) toggleFullScreenCmd(evt *tcell.EventKey) *tcell.EventKey {
func (l *Log) toggleFullScreen() {
l.SetFullScreen(l.indicator.FullScreen())
l.Box.SetBorder(!l.indicator.FullScreen())
l.SetBorder(!l.indicator.FullScreen())
if l.indicator.FullScreen() {
l.logs.SetBorderPadding(0, 0, 0, 0)
} else {

View File

@ -85,14 +85,14 @@ func BenchmarkLogFlush(b *testing.B) {
func TestLogAnsi(t *testing.T) {
buff := bytes.NewBufferString("")
w := tview.ANSIWriter(buff, "white", "black")
fmt.Fprintf(w, "[YELLOW] ok")
_, _ = fmt.Fprintf(w, "[YELLOW] ok")
assert.Equal(t, "[YELLOW] ok", buff.String())
v := tview.NewTextView()
v.SetDynamicColors(true)
aw := tview.ANSIWriter(v, "white", "black")
s := "[2019-03-27T15:05:15,246][INFO ][o.e.c.r.a.AllocationService] [es-0] Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[.monitoring-es-6-2019.03.27][0]]"
fmt.Fprintf(aw, "%s", s)
_, _ = fmt.Fprintf(aw, "%s", s)
assert.Equal(t, s+"\n", v.GetText(false))
}

View File

@ -29,7 +29,7 @@ func (p *PageStack) Init(ctx context.Context) (err error) {
if p.app, err = extractApp(ctx); err != nil {
return err
}
p.Stack.AddListener(p)
p.AddListener(p)
return nil
}

View File

@ -7,16 +7,15 @@ import (
"errors"
"testing"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/watch"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/watch"
)
func TestEnsurePodPortFwdAllowed(t *testing.T) {

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