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 cache-dependency-path: go.sum
- name: Lint - name: Lint
uses: golangci/golangci-lint-action@v6.5.2 uses: golangci/golangci-lint-action@v7.0.0
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-pr-check 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: run:
# default concurrency is an available CPU number
concurrency: 8 concurrency: 8
# timeout for analysis, e.g. 30s, 5m, default is 1m # 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 # exit code when at least one issue was found, default is 1
issues-exit-code: 1 issues-exit-code: 1
# include test files or not, default is true
tests: true tests: true
# which dirs to skip: they won't be analyzed; formatters:
# can use regexp here: generated.*, regexp is applied on full path; enable:
# default value is empty list, but next dirs are always skipped independently - gci
# from this option's value: - gofmt
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ # - gofumpt
# skip-dirs: - goimports
# - ^test.* # - golines
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": linters:
# If invoked with -mod=readonly, the go command is disallowed from the implicit disable-all: true
# automatic updating of go.mod described above. Instead, it fails when any changes enable:
# to go.mod are needed. This setting is most useful to check that go.mod does - sloglint
# not need updates, such as in a continuous integration and testing system. exclusions:
# If invoked with -mod=vendor, the go command assumes that the vendor generated: lax
# directory holds the correct copies of dependencies and ignores paths:
# the dependency descriptions in go.mod. - third_party$
modules-download-mode: readonly - builtin$
- examples$
- \\.(generated\\.deepcopy|pb)\\.go$
# which files to skip: they will be analyzed, but issues from them settings:
# won't be reported. Default value is empty list, but there is gocyclo:
# 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)
min-complexity: 35 min-complexity: 35
govet: govet:
enable: enable:
- nilness - nilness
goimports:
local-prefixes: github.com/cilium/cilium goimports:
unused: local-prefixes: github.com/derailed/k9s
parameters-are-used: true
local-variables-are-used: true unused:
field-writes-are-uses: true parameters-are-used: true
post-statements-are-reads: true local-variables-are-used: true
exported-fields-are-used: true field-writes-are-uses: true
generated-is-used: true post-statements-are-reads: true
goheader: exported-fields-are-used: true
values: generated-is-used: true
regexp:
PROJECT: 'K9s' goheader:
template: |- values:
SPDX-License-Identifier: Apache-2.0 regexp:
Copyright Authors of {{ PROJECT }} PROJECT: 'K9s'
gosec: template: |-
includes: SPDX-License-Identifier: Apache-2.0
- G402 Copyright Authors of {{ PROJECT }}
sloglint:
# Enforce not mixing key-value pairs and attributes. gosec:
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-mixed-arguments includes:
# Default: true - G402
no-mixed-args: true
# Enforce using key-value pairs only (overrides no-mixed-args, incompatible with attr-only). sloglint:
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#key-value-pairs-only # Enforce not mixing key-value pairs and attributes.
# Default: false # https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-mixed-arguments
kv-only: true # Default: true
# Enforce using attributes only (overrides no-mixed-args, incompatible with kv-only). no-mixed-args: true
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#attributes-only # Enforce using key-value pairs only (overrides no-mixed-args, incompatible with attr-only).
# Default: false # https://github.com/go-simpler/sloglint?tab=readme-ov-file#key-value-pairs-only
attr-only: false # Default: false
# Enforce not using global loggers. kv-only: true
# Values: # Enforce using attributes only (overrides no-mixed-args, incompatible with kv-only).
# - "": disabled # https://github.com/go-simpler/sloglint?tab=readme-ov-file#attributes-only
# - "all": report all global loggers # Default: false
# - "default": report only the default slog logger attr-only: false
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-global # Enforce not using global loggers.
# Default: "" # Values:
no-global: "" # - "": disabled
# Enforce using methods that accept a context. # - "all": report all global loggers
# Values: # - "default": report only the default slog logger
# - "": disabled # https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-global
# - "all": report all contextless calls # Default: ""
# - "scope": report only if a context exists in the scope of the outermost function no-global: ""
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#context-only # Enforce using methods that accept a context.
# Default: "" # Values:
context: "" # - "": disabled
# Enforce using static values for log messages. # - "all": report all contextless calls
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#static-messages # - "scope": report only if a context exists in the scope of the outermost function
# Default: false # https://github.com/go-simpler/sloglint?tab=readme-ov-file#context-only
static-msg: false # Default: ""
# Enforce using constants instead of raw keys. context: ""
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-raw-keys # Enforce using static values for log messages.
# Default: false # https://github.com/go-simpler/sloglint?tab=readme-ov-file#static-messages
no-raw-keys: true # Default: false
# Enforce a single key naming convention. static-msg: false
# Values: snake, kebab, camel, pascal # Enforce using constants instead of raw keys.
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#key-naming-convention # https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-raw-keys
# Default: "" # Default: false
key-naming-case: camel no-raw-keys: true
# Enforce not using specific keys. # Enforce a single key naming convention.
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#forbidden-keys # Values: snake, kebab, camel, pascal
# Default: [] # https://github.com/go-simpler/sloglint?tab=readme-ov-file#key-naming-convention
forbidden-keys: # Default: ""
- time key-naming-case: camel
- level # Enforce not using specific keys.
- msg # https://github.com/go-simpler/sloglint?tab=readme-ov-file#forbidden-keys
- source # Default: []
# Enforce putting arguments on separate lines. forbidden-keys:
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#arguments-on-separate-lines - time
# Default: false - level
args-on-sep-lines: false - 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: issues:
@ -132,10 +126,13 @@ issues:
exclude-rules: exclude-rules:
- linters: [staticcheck] - linters: [staticcheck]
text: "SA1019" # this is rule for deprecated method text: "SA1019" # this is rule for deprecated method
- linters: [staticcheck] - linters: [staticcheck]
text: "SA9003: empty branch" text: "SA9003: empty branch"
- linters: [staticcheck] - linters: [staticcheck]
text: "SA2001: empty critical section" text: "SA2001: empty critical section"
- linters: [err113] - linters: [err113]
text: "do not define dynamic errors, use wrapped static errors instead" # This rule to avoid opinionated check fmt.Errorf("text") 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 # Skip goimports check on generated files
@ -146,27 +143,3 @@ issues:
- path: "pkg/ipam/(cidrset|service)/.+\\.go" - path: "pkg/ipam/(cidrset|service)/.+\\.go"
linters: linters:
- goheader - 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 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 # Allows to set certain views default fullscreen mode. (yaml, helm history, describe, value_extender, details, logs) Default false
defaultsToFullScreen: 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. # Toggles icons display as not all terminal support these chars.
noIcons: false noIcons: false
# Toggles whether k9s should check for the latest revision from the GitHub repository releases. Default is 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) { func printLogo(c color.Paint) {
for _, l := range ui.LogoSmall { 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. // 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/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/lmittmann/tint"
"github.com/mattn/go-colorable" "github.com/mattn/go-colorable"
"github.com/spf13/cobra" "github.com/spf13/cobra"

View File

@ -44,8 +44,8 @@ func printVersion(short bool) {
func printTuple(fmat, section, value string, outputColor color.Paint) { func printTuple(fmat, section, value string, outputColor color.Paint) {
if outputColor != -1 { if outputColor != -1 {
fmt.Fprintf(out, fmat, color.Colorize(section+":", outputColor), value) _, _ = fmt.Fprintf(out, fmat, color.Colorize(section+":", outputColor), value)
return 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 module github.com/derailed/k9s
go 1.24.0 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-20241115144204-29e89f9fa837 github.com/anchore/clio v0.0.0-20241115144204-29e89f9fa837
github.com/anchore/grype v0.86.1 github.com/anchore/grype v0.87.0
github.com/anchore/syft v1.20.0 github.com/anchore/syft v1.21.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
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/derailed/tview v0.8.5
github.com/fatih/color v1.18.0 github.com/fatih/color v1.18.0
github.com/fsnotify/fsnotify v1.8.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-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 // 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/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/andybalholm/brotli v1.1.1 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // 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-pep440-version v0.0.1 // indirect
github.com/aquasecurity/go-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/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go v1.44.288 // indirect github.com/aws/aws-sdk-go v1.44.288 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // 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/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/charmbracelet/lipgloss v1.0.0 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/x/ansi v0.4.5 // indirect github.com/charmbracelet/lipgloss v1.1.0 // indirect
github.com/cloudflare/circl v1.3.8 // 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/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
github.com/containerd/cgroups v1.1.0 // indirect github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/containerd v1.7.27 // 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/stargz-snapshotter/estargz v0.16.3 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.1.1 // 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da // indirect github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da // indirect
github.com/distribution/reference v0.6.0 // 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/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/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // 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/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
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-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.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/go-test/deep v1.1.1 // indirect
github.com/gobwas/glob v0.2.3 // indirect github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // 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/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.0.1 // indirect github.com/google/btree v1.0.1 // indirect
github.com/google/gnostic-models v0.6.8 // 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/go-containerregistry v0.20.3 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/google/licensecheck v0.3.1 // 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/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // 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-multierror v1.1.1 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-version v1.7.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/json-iterator/go v1.1.12 // indirect
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 // indirect github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 // indirect
github.com/kevinburke/ssh_config v1.2.0 // 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/klauspost/pgzip v1.2.6 // indirect
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f // 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 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/masahiro331/go-mvn-version v0.0.0-20210429150710-d3157d602a08 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // 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/copystructure v1.2.0 // indirect
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
@ -234,7 +237,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/ncruces/go-strftime v0.1.9 // 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/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.35.1 // indirect github.com/onsi/gomega v1.35.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // 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/runtime-spec v1.1.0 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect github.com/opencontainers/selinux v1.11.0 // indirect
github.com/openvex/go-vex v0.2.5 // 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 v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // 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/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect github.com/pkg/profile v1.7.0 // indirect
@ -266,6 +269,7 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/rubenv/sql-migrate v1.7.1 // indirect github.com/rubenv/sql-migrate v1.7.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // 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/saferwall/pe v1.5.6 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.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/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/shopspring/decimal v1.4.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // 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/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/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/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/viper v1.19.0 // indirect github.com/spf13/viper v1.19.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/sylabs/sif/v2 v2.20.2 // 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/therootcompany/xz v1.0.1 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect github.com/ulikunitz/xz v0.5.12 // indirect
github.com/vbatts/go-mtree v0.5.4 // indirect github.com/vbatts/go-mtree v0.5.4 // indirect
@ -315,7 +320,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.36.0 // indirect golang.org/x/crypto v0.36.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // 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/net v0.37.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/sync v0.12.0 // indirect golang.org/x/sync v0.12.0 // indirect
@ -342,7 +347,7 @@ require (
modernc.org/libc v1.61.13 // indirect modernc.org/libc v1.61.13 // indirect
modernc.org/mathutil v1.7.1 // indirect modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.2 // 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 oras.land/oras-go v1.2.5 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/kustomize/api v0.18.0 // 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" cacheNSKey = "validNamespaces"
) )
var ( var supportedMetricsAPIVersions = []string{"v1beta1"}
supportedMetricsAPIVersions = []string{"v1beta1"}
)
// NamespaceNames tracks a collection of namespace names. // NamespaceNames tracks a collection of namespace names.
type NamespaceNames map[string]struct{} type NamespaceNames map[string]struct{}

View File

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

View File

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

View File

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

View File

@ -556,6 +556,7 @@ func TestConfigSaveFile(t *testing.T) {
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
cfg.K9s.UI.UseFullGVRTitle = true
cfg.Validate("ct-1-1", "cl-1") cfg.Validate("ct-1-1", "cl-1")
path := filepath.Join("/tmp", "k9s.yaml") 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) { func (c *Config) Dump(w io.Writer) {
bb, _ := yaml.Marshal(&c) 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 ct.Namespace, ct.ClusterName = NewActiveNamespace(cfg.Namespace), cfg.Cluster
return ct return ct
} }
// NewContextFromKubeConfig returns a new instance based on kubesettings or an error. // 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) { func TestEnsureDirPathNone(t *testing.T) {
var mod os.FileMode = 0744 const mod = 0744
dir := filepath.Join("/tmp", "k9s-test") dir := filepath.Join("/tmp", "k9s-test")
os.Remove(dir) _ = os.Remove(dir)
path := filepath.Join(dir, "duh.yaml") path := filepath.Join(dir, "duh.yaml")
assert.NoError(t, data.EnsureDirPath(path, mod)) assert.NoError(t, data.EnsureDirPath(path, mod))

View File

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

View File

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

View File

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

View File

@ -28,7 +28,8 @@
"noIcons": {"type": "boolean"}, "noIcons": {"type": "boolean"},
"reactive": {"type": "boolean"}, "reactive": {"type": "boolean"},
"skin": {"type": "string"}, "skin": {"type": "string"},
"defaultsToFullScreen": {"type": "boolean"} "defaultsToFullScreen": {"type": "boolean"},
"useFullGVRTitle": {"type": "boolean"}
} }
}, },
"shellPod": { "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.sl, u.k.IsSplashless())
assert.Equal(t, u.hl, u.k.IsHeadless()) assert.Equal(t, u.hl, u.k.IsHeadless())
assert.Equal(t, u.ll, u.k.IsLogoless()) assert.Equal(t, u.ll, u.k.IsLogoless())
}) })
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -38,6 +38,9 @@ type UI struct {
// DefaultsToFullScreen toggles fullscreen on views like logs, yaml, details. // DefaultsToFullScreen toggles fullscreen on views like logs, yaml, details.
DefaultsToFullScreen bool `json:"defaultsToFullScreen" yaml:"defaultsToFullScreen"` 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 manualHeadless *bool
manualLogoless *bool manualLogoless *bool
manualCrumbsless *bool manualCrumbsless *bool

View File

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

View File

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

View File

@ -321,8 +321,8 @@ func hasConfigMap(spec *v1.PodSpec, name string) bool {
} }
for _, v := range spec.Volumes { for _, v := range spec.Volumes {
if cm := v.VolumeSource.ConfigMap; cm != nil { if cm := v.ConfigMap; cm != nil {
if cm.LocalObjectReference.Name == name { if cm.Name == name {
return true 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 { for _, v := range spec.Volumes {
if sec := v.VolumeSource.Secret; sec != nil { if sec := v.Secret; sec != nil {
if sec.SecretName == name { if sec.SecretName == name {
return true, nil return true, nil
} }

View File

@ -9,14 +9,13 @@ import (
"strconv" "strconv"
"strings" "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"
"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"
"github.com/derailed/k9s/internal/render/helm" "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 ( var (

View File

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

View File

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

View File

@ -62,7 +62,7 @@ func (j *Job) List(ctx context.Context, ns string) ([]runtime.Object, error) {
continue continue
} }
for _, r := range j.ObjectMeta.OwnerReferences { for _, r := range j.OwnerReferences {
if r.Name == n { if r.Name == n {
ll = append(ll, o) 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) dd, errs := h.GetPodsForDeletion(path)
if len(errs) != 0 { if len(errs) != 0 {
for _, e := range errs { 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 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 { if err := h.DeleteOrEvictPods(dd.Pods()); err != nil {
return err return err
} }
fmt.Fprintf(h.Out, "Node %s drained!", path) _, _ = fmt.Fprintf(h.Out, "Node %s drained!", path)
return nil return nil
} }

View File

@ -3,9 +3,7 @@
package dao package dao
var ( var _ Accessor = (*Namespace)(nil)
_ Accessor = (*Namespace)(nil)
)
// Namespace represents a namespace resource. // Namespace represents a namespace resource.
type Namespace struct { 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") return nil, errors.New("expecting Deployment resource")
} }
// Just pick controller less pods... // Just pick controller less pods...
if len(pod.ObjectMeta.OwnerReferences) > 0 { if len(pod.OwnerReferences) > 0 {
continue continue
} }
if serviceAccountMatches(pod.Spec.ServiceAccountName, n) { 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") return nil, errors.New("expecting Pod resource")
} }
// Just pick controller less pods... // Just pick controller less pods...
if len(pod.ObjectMeta.OwnerReferences) > 0 { if len(pod.OwnerReferences) > 0 {
continue continue
} }
switch gvr { switch gvr {

View File

@ -29,7 +29,7 @@ func TestGetDefaultContainer(t *testing.T) {
"container_not_present": { "container_not_present": {
po: &v1.Pod{ po: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"kubectl.kubernetes.io/default-container": "container1"}, Annotations: map[string]string{DefaultContainerAnnotation: "container1"},
}, },
}, },
wantContainer: "", wantContainer: "",
@ -38,7 +38,7 @@ func TestGetDefaultContainer(t *testing.T) {
"container_found": { "container_found": {
po: &v1.Pod{ po: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"kubectl.kubernetes.io/default-container": "container1"}, Annotations: map[string]string{DefaultContainerAnnotation: "container1"},
}, },
Spec: v1.PodSpec{ Spec: v1.PodSpec{
Containers: []v1.Container{{Name: "container1"}}, 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) { 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 { if rs.Status.Replicas != 0 {
return 0, errors.New("can not rollback current replica") 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) { func controllerInfo(rs *appsv1.ReplicaSet) (string, string, string, error) {
for _, ref := range rs.ObjectMeta.OwnerReferences { for _, ref := range rs.OwnerReferences {
if ref.Controller == nil { if ref.Controller == nil {
continue continue
} }
@ -80,7 +80,7 @@ func controllerInfo(rs *appsv1.ReplicaSet) (string, string, string, error) {
} }
return ref.Name, ref.Kind, group, nil 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. // Rollback reverses the last deployment.

View File

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

View File

@ -16,46 +16,41 @@ import (
// Secret represents a secret K8s resource. // Secret represents a secret K8s resource.
type Secret struct { type Secret struct {
Resource Resource
decode bool decodeData bool
} }
// Describe describes a secret that can be encoded or decoded. // Describe describes a secret that can be encoded or decoded.
func (s *Secret) Describe(path string) (string, error) { func (s *Secret) Describe(path string) (string, error) {
encodedDescription, err := s.Generic.Describe(path) encodedDescription, err := s.Generic.Describe(path)
if err != nil { if err != nil {
return "", err return "", err
} }
if !s.decodeData {
if !s.decode {
return encodedDescription, nil return encodedDescription, nil
} }
return s.Decode(encodedDescription, path) return s.Decode(encodedDescription, path)
} }
// SetDecode sets the decode flag. // SetDecodeData toggles decode mode.
func (s *Secret) SetDecode(flag bool) { func (s *Secret) SetDecodeData(b bool) {
s.decode = flag s.decodeData = b
} }
// Decode removes the encoded part from the secret's description and appends the // Decode removes the encoded part from the secret's description and appends the
// secret's decoded data. // secret's decoded data.
func (s *Secret) Decode(encodedDescription, path string) (string, error) { func (s *Secret) Decode(encodedDescription, path string) (string, error) {
o, err := s.getFactory().Get(s.GVR(), path, true, labels.Everything()) o, err := s.getFactory().Get(s.GVR(), path, true, labels.Everything())
if err != nil { if err != nil {
return "", err return "", err
} }
dataEndIndex := strings.Index(encodedDescription, "====") dataEndIndex := strings.Index(encodedDescription, "====")
if dataEndIndex == -1 { if dataEndIndex == -1 {
return "", fmt.Errorf("unable to find data section in secret description") return "", fmt.Errorf("unable to find data section in secret description")
} }
dataEndIndex += 4 dataEndIndex += 4
if dataEndIndex >= len(encodedDescription) { if dataEndIndex >= len(encodedDescription) {
return "", fmt.Errorf("data section in secret description is invalid") 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 // 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] body := encodedDescription[0:dataEndIndex]
d, err := ExtractSecrets(o.(*unstructured.Unstructured)) data, err := ExtractSecrets(o)
if err != nil { if err != nil {
return "", err return "", err
} }
decodedSecrets := []string{} decodedSecrets := make([]string, 0, len(data))
for k, v := range data {
for k, v := range d { line := fmt.Sprintf("%s: %s", k, v)
decodedSecrets = append(decodedSecrets, "\n", k, ":\t", 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 // 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 // It returns a map where the keys are the secret data keys and the values are
// the corresponding secret data values. // the corresponding secret data values.
// If the conversion fails, it returns an error. // 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 var secret v1.Secret
err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.Object, &secret) err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &secret)
if err != nil { if err != nil {
return nil, err return nil, err
} }
secretData := make(map[string]string, len(secret.Data)) secretData := make(map[string]string, len(secret.Data))
for k, val := range secret.Data { for k, val := range secret.Data {
secretData[k] = string(val) secretData[k] = string(val)
} }

View File

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

View File

@ -8,15 +8,14 @@ import (
"io" "io"
"time" "time"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/watch"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
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/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
restclient "k8s.io/client-go/rest" 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. // 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) { 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) oo, err := a.Table.List(ctx, ns)
if err != nil { if err != nil {
return nil, err return nil, err
@ -113,14 +113,12 @@ func (a *Workload) List(ctx context.Context, ns string) ([]runtime.Object, error
for _, r := range table.Rows { for _, r := range table.Rows {
if obj := r.Object.Object; obj != nil { if obj := r.Object.Object; obj != nil {
if m, err := meta.Accessor(obj); err == nil { if m, err := meta.Accessor(obj); err == nil {
ns = m.GetNamespace() ns, ts = m.GetNamespace(), m.GetCreationTimestamp()
ts = m.GetCreationTimestamp()
} }
} else { } else {
var m metav1.PartialObjectMetadata var m metav1.PartialObjectMetadata
if err := json.Unmarshal(r.Object.Raw, &m); err == nil { if err := json.Unmarshal(r.Object.Raw, &m); err == nil {
ns = m.GetNamespace() ns, ts = m.GetNamespace(), m.CreationTimestamp
ts = m.CreationTimestamp
} }
} }
stat := status(gvr, r, table.ColumnDefinitions) stat := status(gvr, r, table.ColumnDefinitions)

View File

@ -13,11 +13,10 @@ import (
"sync" "sync"
"time" "time"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/dao" "github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/slogs"
"k8s.io/apimachinery/pkg/util/cache" "k8s.io/apimachinery/pkg/util/cache"
) )
@ -239,5 +238,5 @@ func fetchLatestRev() (string, error) {
return v.(string), nil 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 { if desc, ok := meta.DAO.(*dao.Secret); ok {
desc.SetDecode(d.decode) desc.SetDecodeData(d.decode)
} }
return desc.Describe(path) return desc.Describe(path)

View File

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

View File

@ -113,7 +113,7 @@ func (p *Pulse) reconcile(ctx context.Context) error {
for _, o := range oo { for _, o := range oo {
c, ok := o.(*health.Check) c, ok := o.(*health.Check)
if !ok { 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.data = append(p.data, c)
p.firePulseChanged(c) p.firePulseChanged(c)

View File

@ -113,10 +113,6 @@ var Registry = map[string]ResourceMeta{
Renderer: &render.Service{}, Renderer: &render.Service{},
TreeRenderer: &xray.Service{}, TreeRenderer: &xray.Service{},
}, },
"v1/events": {
DAO: &dao.Table{},
Renderer: &render.Event{},
},
"v1/serviceaccounts": { "v1/serviceaccounts": {
Renderer: &render.ServiceAccount{}, Renderer: &render.ServiceAccount{},
}, },
@ -193,14 +189,4 @@ var Registry = map[string]ResourceMeta{
"rbac.authorization.k8s.io/v1/rolebindings": { "rbac.authorization.k8s.io/v1/rolebindings": {
Renderer: &render.RoleBinding{}, 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{}, accessor: &dao.Node{},
renderer: &render.Node{}, renderer: &render.Node{},
}, },
"table": {
gvr: "v1/events",
accessor: &dao.Table{},
renderer: &render.Event{},
},
} }
for k := range uu { for k := range uu {

View File

@ -82,7 +82,6 @@ func (t *Text) RemoveListener(listener TextListener) {
break break
} }
} }
if victim >= 0 { if victim >= 0 {
t.listeners = append(t.listeners[:victim], t.listeners[victim+1:]...) 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) valuer, ok := accessor.(dao.Valuer)
if !ok { 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) 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 { 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) return sortorder.NaturalLess(v1, v2)
} }

View File

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

View File

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

View File

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

View File

@ -41,7 +41,7 @@ func ParsePlainPF(ann string) (*PFAnn, error) {
var pf PFAnn var pf PFAnn
mm := pfPlainRX.FindStringSubmatch(strings.TrimSpace(ann)) mm := pfPlainRX.FindStringSubmatch(strings.TrimSpace(ann))
if len(mm) < 3 { 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 { if len(mm[2]) == 0 {
pf.ContainerPort = intstr.Parse(mm[1]) pf.ContainerPort = intstr.Parse(mm[1])

View File

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

View File

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

View File

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

View File

@ -98,7 +98,7 @@ func (cc ColumnSpecs) Header(rh model1.Header) model1.Header {
for _, h := range rh { for _, h := range rh {
if idx, ok := hh.IndexOf(h.Name, true); ok { 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 continue
} }
hh = append(hh, h) 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 return c
} }
} }
// Header returns a header row. // 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 phase := pv.Status.Phase
if pv.ObjectMeta.DeletionTimestamp != nil { if pv.DeletionTimestamp != nil {
phase = terminatingPhase phase = terminatingPhase
} }
var claim string var claim string

View File

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

View File

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

View File

@ -38,7 +38,10 @@ var _ os.FileInfo = fileInfo{}
func (f fileInfo) Name() string { return "bob" } func (f fileInfo) Name() string { return "bob" }
func (f fileInfo) Size() int64 { return 100 } 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) ModTime() time.Time { return testTime() }
func (f fileInfo) IsDir() bool { return false } func (f fileInfo) IsDir() bool { return false }
func (f fileInfo) Sys() interface{} { return nil } 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" "fmt"
"strconv" "strconv"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/model1" "github.com/derailed/k9s/internal/model1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
) )
// Secret renders a K8s Secret to screen. // 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.ID = client.MetaFQN(svc.ObjectMeta)
r.Fields = model1.Fields{ r.Fields = model1.Fields{
svc.Namespace, svc.Namespace,
svc.ObjectMeta.Name, svc.Name,
string(svc.Spec.Type), string(svc.Spec.Type),
toIP(svc.Spec.ClusterIP), toIP(svc.Spec.ClusterIP),
toIPs(svc.Spec.Type, getSvcExtIPS(&svc)), toIPs(svc.Spec.Type, getSvcExtIPS(&svc)),

View File

@ -5,7 +5,6 @@ package render
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"log/slog" "log/slog"
"strings" "strings"
@ -138,39 +137,3 @@ func (t *Table) defaultRow(row *metav1.TableRow, ns string, r *model1.Row) error
return nil 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" "os"
"path/filepath" "path/filepath"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model" "github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/model1" "github.com/derailed/k9s/internal/model1"
"github.com/derailed/k9s/internal/slogs" "github.com/derailed/k9s/internal/slogs"
"github.com/derailed/k9s/internal/config"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
) )

View File

@ -14,7 +14,7 @@ import (
) )
func Test_activeConfig(t *testing.T) { 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()) assert.NoError(t, config.InitLocs())
cl, ct := "cl-1", "ct-1-1" cl, ct := "cl-1", "ct-1-1"

View File

@ -21,7 +21,7 @@ import (
) )
func TestSkinnedContext(t *testing.T) { 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()) assert.NoError(t, config.InitLocs())
defer assert.NoError(t, os.RemoveAll(config.K9sEnvConfigDir)) defer assert.NoError(t, os.RemoveAll(config.K9sEnvConfigDir))
@ -52,7 +52,7 @@ func TestSkinnedContext(t *testing.T) {
} }
func TestBenchConfig(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()) assert.NoError(t, config.InitLocs())
defer assert.NoError(t, os.RemoveAll(config.K9sEnvConfigDir)) defer assert.NoError(t, os.RemoveAll(config.K9sEnvConfigDir))

View File

@ -66,9 +66,9 @@ func (c *Crumbs) refresh(crumbs []string) {
if i == last { if i == last {
bgColor = c.styles.Frame().Crumb.ActiveColor 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, c.styles.Frame().Crumb.FgColor,
bgColor, strings.Replace(strings.ToLower(crumb), " ", "", -1), bgColor, strings.ReplaceAll(strings.ToLower(crumb), " ", ""),
c.styles.Body().BgColor) c.styles.Body().BgColor)
} }
} }

View File

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

View File

@ -105,9 +105,9 @@ func (l *Logo) refreshLogo(c config.Color) {
defer l.mx.Unlock() defer l.mx.Unlock()
l.logo.Clear() l.logo.Clear()
for i, s := range LogoSmall { 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) { 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 { func formatNSMenu(i int, name string, styles config.Frame) string {
fmat := strings.Replace(menuIndexFmt, "[key", "["+styles.Menu.NumKeyColor.String(), 1) 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, "[fg", "["+styles.Menu.FgColor.String(), 1)
fmat = strings.Replace(fmat, "fgstyle]", styles.Menu.FgStyle.ToShortString()+"]", 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 " menuFmt := " [key:-:b]%-" + strconv.Itoa(size+2) + "s [fg:-:fgstyle]%s "
fmat := strings.Replace(menuFmt, "[key", "["+styles.Menu.KeyColor.String(), 1) fmat := strings.Replace(menuFmt, "[key", "["+styles.Menu.KeyColor.String(), 1)
fmat = strings.Replace(fmat, "[fg", "["+styles.Menu.FgColor.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) fmat = strings.Replace(fmat, "fgstyle]", styles.Menu.FgStyle.ToShortString()+"]", 1)
return fmt.Sprintf(fmat, ToMnemonic(h.Mnemonic), h.Description) return fmt.Sprintf(fmat, ToMnemonic(h.Mnemonic), h.Description)

View File

@ -24,7 +24,7 @@ func NewPages() *Pages {
Pages: tview.NewPages(), Pages: tview.NewPages(),
Stack: model.NewStack(), Stack: model.NewStack(),
} }
p.Stack.AddListener(&p) p.AddListener(&p)
return &p return &p
} }
@ -74,7 +74,7 @@ func (p *Pages) delete(c model.Component) {
// Dump for debug. // Dump for debug.
func (p *Pages) Dump() { func (p *Pages) Dump() {
slog.Debug("Dumping Pages", slogs.Page, p) 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)))) 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) txt += fmt.Sprintf("[%s::-]%s", p.styles.Prompt().SuggestColor, suggest)
} }
p.StylesChanged(p.styles) 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 ( import (
"testing" "testing"
"github.com/derailed/tcell/v2"
"github.com/derailed/k9s/internal/config" "github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model" "github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/ui" "github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
"github.com/stretchr/testify/assert" "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) { func (s *Splash) layoutLogo(t *tview.TextView, styles *config.Styles) {
logo := strings.Join(LogoBig, fmt.Sprintf("\n[%s::b]", styles.Body().LogoColor)) 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), strings.Repeat("\n", 2),
styles.Body().LogoColor, styles.Body().LogoColor,
logo) logo)
} }
func (s *Splash) layoutRev(t *tview.TextView, rev string, styles *config.Styles) { 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 mx sync.RWMutex
readOnly bool readOnly bool
noIcon bool noIcon bool
fullGVR bool
} }
// NewTable returns a new table view. // 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. // SetNoIcon toggles no icon mode.
func (t *Table) SetNoIcon(b bool) { func (t *Table) SetNoIcon(b bool) {
t.mx.Lock() t.mx.Lock()
@ -547,11 +556,16 @@ func (t *Table) styleTitle() string {
ns = t.Extras ns = t.Extras
} }
resource := t.gvr.R()
if t.fullGVR {
resource = t.gvr.String()
}
var title string var title string
if ns == client.ClusterScope { 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 { } 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 != "" { if ic := ROIndicator(t.readOnly, t.noIcon); ic != "" {
title = " " + ic + title 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. // ROIndicator returns an icon showing whether the session is in readonly mode or not.
func ROIndicator(ro, noIC bool) string { func ROIndicator(ro, noIC bool) string {
if noIC { switch {
case noIC:
return "" return ""
} case ro:
if ro {
return lockedIC 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 { if bgColor == config.DefaultColor {
bgColor = config.TransparentColor 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, "[hilite", "["+style.Title.HighlightColor.String(), 1)
fmat = strings.Replace(fmat, "[key", "["+style.Menu.NumKeyColor.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, "[filter", "["+style.Title.FilterColor.String(), 1)
fmat = strings.Replace(fmat, "[count", "["+style.Title.CounterColor.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 return fmat
} }

View File

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

View File

@ -16,8 +16,8 @@ import (
) )
const ( const (
unlockedIC = "✍️ " unlockedIC = "🖍"
lockedIC = "🔒" lockedIC = "🔑"
) )
// Namespaceable tracks namespaces. // 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 { if err := a.Content.Init(ctx); err != nil {
return err return err
} }
a.Content.Stack.AddListener(a.Crumbs()) a.Content.AddListener(a.Crumbs())
a.Content.Stack.AddListener(a.Menu()) a.Content.AddListener(a.Menu())
a.App.Init() a.App.Init()
a.SetInputCapture(a.keyboard) a.SetInputCapture(a.keyboard)
@ -198,7 +198,7 @@ func (a *App) suggestCommand() model.SuggestionFunc {
} }
ls := strings.ToLower(s) 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 { if suggest, ok := cmd.ShouldAddSuggest(ls, k); ok {
entries = append(entries, suggest) entries = append(entries, suggest)
} }
@ -776,7 +776,7 @@ func (a *App) inject(c model.Component, clearStack bool) error {
return err return err
} }
if clearStack { if clearStack {
a.Content.Stack.Clear() a.Content.Clear()
} }
a.Content.Push(c) a.Content.Push(c)

View File

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

View File

@ -103,7 +103,8 @@ func TestFlagsNew(t *testing.T) {
fuzzyKey: "blee", fuzzyKey: "blee",
labelKey: "app=fred", labelKey: "app=fred",
nsKey: "ns1", nsKey: "ns1",
contextKey: "Dev"}, contextKey: "Dev",
},
}, },
"ctx": { "ctx": {
i: NewInterpreter("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) app.Content.Pages.RemovePage(renamePage)
}). }).
AddButton("Cancel", func() { AddButton("Cancel", func() {
app.Content.Pages.RemovePage(renamePage) app.Content.RemovePage(renamePage)
}) })
m := tview.NewModalForm("<Rename>", f) m := tview.NewModalForm("<Rename>", f)
m.SetText(fmt.Sprintf("Rename context %q?", name)) m.SetText(fmt.Sprintf("Rename context %q?", name))
m.SetDoneFunc(func(int, string) { m.SetDoneFunc(func(int, string) {
app.Content.Pages.RemovePage(renamePage) app.Content.RemovePage(renamePage)
}) })
app.Content.Pages.AddPage(renamePage, m, false, false) app.Content.AddPage(renamePage, m, false, false)
app.Content.Pages.ShowPage(renamePage) app.Content.ShowPage(renamePage)
for i := 0; i < f.GetButtonCount(); i++ { for i := 0; i < f.GetButtonCount(); i++ {
f.GetButton(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) { func (d *Details) setFullScreen(isFullScreen bool) {
d.fullScreen = isFullScreen d.fullScreen = isFullScreen
d.SetFullScreen(isFullScreen) d.SetFullScreen(isFullScreen)
d.Box.SetBorder(!isFullScreen) d.SetBorder(!isFullScreen)
if isFullScreen { if isFullScreen {
d.Box.SetBorderPadding(0, 0, 0, 0) d.SetBorderPadding(0, 0, 0, 0)
} else { } else {
d.Box.SetBorderPadding(0, 0, 1, 1) d.SetBorderPadding(0, 0, 1, 1)
} }
} }

View File

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

View File

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

View File

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

View File

@ -292,11 +292,11 @@ func (v *LiveView) toggleFullScreenCmd(evt *tcell.EventKey) *tcell.EventKey {
func (v *LiveView) setFullScreen(isFullScreen bool) { func (v *LiveView) setFullScreen(isFullScreen bool) {
v.fullScreen = isFullScreen v.fullScreen = isFullScreen
v.SetFullScreen(isFullScreen) v.SetFullScreen(isFullScreen)
v.Box.SetBorder(!isFullScreen) v.SetBorder(!isFullScreen)
if isFullScreen { if isFullScreen {
v.Box.SetBorderPadding(0, 0, 0, 0) v.SetBorderPadding(0, 0, 0, 0)
} else { } 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 { func (l *Log) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
if !l.logs.cmdBuff.IsActive() { if !l.logs.cmdBuff.IsActive() {
fmt.Fprintln(l.ansiWriter) _, _ = fmt.Fprintln(l.ansiWriter)
return evt return evt
} }
@ -460,7 +460,7 @@ func (l *Log) clearCmd(*tcell.EventKey) *tcell.EventKey {
func (l *Log) markCmd(*tcell.EventKey) *tcell.EventKey { func (l *Log) markCmd(*tcell.EventKey) *tcell.EventKey {
_, _, w, _ := l.GetRect() _, _, 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 l.follow = true
return nil return nil
@ -516,7 +516,7 @@ func (l *Log) toggleFullScreenCmd(evt *tcell.EventKey) *tcell.EventKey {
func (l *Log) toggleFullScreen() { func (l *Log) toggleFullScreen() {
l.SetFullScreen(l.indicator.FullScreen()) l.SetFullScreen(l.indicator.FullScreen())
l.Box.SetBorder(!l.indicator.FullScreen()) l.SetBorder(!l.indicator.FullScreen())
if l.indicator.FullScreen() { if l.indicator.FullScreen() {
l.logs.SetBorderPadding(0, 0, 0, 0) l.logs.SetBorderPadding(0, 0, 0, 0)
} else { } else {

View File

@ -85,14 +85,14 @@ func BenchmarkLogFlush(b *testing.B) {
func TestLogAnsi(t *testing.T) { func TestLogAnsi(t *testing.T) {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
w := tview.ANSIWriter(buff, "white", "black") w := tview.ANSIWriter(buff, "white", "black")
fmt.Fprintf(w, "[YELLOW] ok") _, _ = fmt.Fprintf(w, "[YELLOW] ok")
assert.Equal(t, "[YELLOW] ok", buff.String()) assert.Equal(t, "[YELLOW] ok", buff.String())
v := tview.NewTextView() v := tview.NewTextView()
v.SetDynamicColors(true) v.SetDynamicColors(true)
aw := tview.ANSIWriter(v, "white", "black") 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]]" 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)) 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 { if p.app, err = extractApp(ctx); err != nil {
return err return err
} }
p.Stack.AddListener(p) p.AddListener(p)
return nil return nil
} }

View File

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

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