diff --git a/.golangci.yml b/.golangci.yml
index f880f84a8d..b91459128d 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -53,10 +53,8 @@ linters-settings:
- nilness
goimports:
local-prefixes: github.com/cilium/cilium
- staticcheck:
- go: "1.20"
unused:
- go: "1.20"
+ go: "1.23"
goheader:
values:
regexp:
@@ -93,7 +91,7 @@ issues:
text: "SA9003: empty branch"
- linters: [staticcheck]
text: "SA2001: empty critical section"
- - linters: [goerr113]
+ - linters: [err113]
text: "do not define dynamic errors, use wrapped static errors instead" # This rule to avoid opinionated check fmt.Errorf("text")
# Skip goimports check on generated files
- path: \\.(generated\\.deepcopy|pb)\\.go$
@@ -107,13 +105,12 @@ issues:
linters:
disable-all: true
enable:
- - goerr113
+ - err113
- gofmt
- goimports
- govet
- ineffassign
- misspell
- - staticcheck
- unused
- goheader
- gosec
diff --git a/.goreleaser.yml b/.goreleaser.yml
index b2ffc71807..bb65270347 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -1,3 +1,5 @@
+version: 2
+
project_name: k9s
before:
@@ -90,7 +92,7 @@ brews:
commit_author:
name: derailed
email: fernand@imhotep.io
- folder: Formula
+ directory: Formula
homepage: https://k9scli.io/
description: Kubernetes CLI To Manage Your Clusters In Style!
test: |
diff --git a/.travis.yml b/.travis.yml
index dc64775bc6..09bf491802 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
language: go
go_import_path: github.com/derailed/k9s
go:
- - "1.15"
+ - "1.23"
jobs:
include:
diff --git a/Makefile b/Makefile
index a2b5a10ba3..686bfb1c3b 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
else
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
endif
-VERSION ?= v0.32.5
+VERSION ?= v0.32.6
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}
diff --git a/change_logs/release_v0.32.6.md b/change_logs/release_v0.32.6.md
new file mode 100644
index 0000000000..edb420208d
--- /dev/null
+++ b/change_logs/release_v0.32.6.md
@@ -0,0 +1,97 @@
+
+
+# Release v0.32.6
+
+## Notes
+
+Thank you to all that contributed with flushing out issues and enhancements for K9s!
+I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev
+and see if we're happier with some of the fixes!
+If you've filed an issue please help me verify and close.
+
+Your support, kindness and awesome suggestions to make K9s better are, as ever, very much noted and appreciated!
+Also big thanks to all that have allocated their own time to help others on both slack and on this repo!!
+
+As you may know, K9s is not pimped out by corps with deep pockets, thus if you feel K9s is helping your Kubernetes journey,
+please consider joining our [sponsorship program](https://github.com/sponsors/derailed) and/or make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
+
+On Slack? Please join us [K9slackers](https://join.slack.com/t/k9sers/shared_invite/enQtOTA5MDEyNzI5MTU0LWQ1ZGI3MzliYzZhZWEyNzYxYzA3NjE0YTk1YmFmNzViZjIyNzhkZGI0MmJjYzhlNjdlMGJhYzE2ZGU1NjkyNTM)
+
+## Maintenance Release!
+
+---
+
+## Videos Are In The Can!
+
+Please dial [K9s Channel](https://www.youtube.com/channel/UC897uwPygni4QIjkPCpgjmw) for up coming content...
+
+* [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
+
+* [#2947](https://github.com/derailed/k9s/issues/2947) CTRL+Z causes k9s to crash
+* [#2938](https://github.com/derailed/k9s/issues/2938) Critical Vulnerability CVE-2024-41110 in v26.0.1 of docker included in k9s
+* [#2929](https://github.com/derailed/k9s/issues/2929) conflicting plugins shortcuts
+* [#2896](https://github.com/derailed/k9s/issues/2896) Add a plugin to disable/enable a keda ScaledObject
+* [#2811](https://github.com/derailed/k9s/issues/2811) Dockerfile build step fails due to misaligned Go versions (1.21.5 vs 1.22.0)
+* [#2767](https://github.com/derailed/k9s/issues/2767) Manually triggered jobs don't get automatically cleaned up
+* [#2761](https://github.com/derailed/k9s/issues/2761) Enable "jump to owner" for more kinds
+* [#2754](https://github.com/derailed/k9s/issues/2754) Plugins not loaded/shown in UI
+* [#2747](https://github.com/derailed/k9s/issues/2747) Combining context and namespace switching only works sporadically (e.g. ":pod foo-ns @ctx-dev")
+* [#2746](https://github.com/derailed/k9s/issues/2746) k9s does not display "[::]" string in its logs
+* [#2738](https://github.com/derailed/k9s/issues/2738) "Faults" view should show all Terminating pods
+
+---
+
+## 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!!
+
+* [#2937](https://github.com/derailed/k9s/pull/2937) Adding Argo Rollouts plugin version for PowerShell
+* [#2935](https://github.com/derailed/k9s/pull/2935) fix: show all terminating pods in Faults view (#2738)
+* [#2933](https://github.com/derailed/k9s/pull/2933) chore: broken url in build-status tag in the readme.md
+* [#2932](https://github.com/derailed/k9s/pull/2932) fix: add kubeconfig if k9s is launched with --kubeconfig
+* [#2930](https://github.com/derailed/k9s/pull/2930) fixed conflicting plugin shortcuts, and added 2 new plugins
+* [#2927](https://github.com/derailed/k9s/pull/2927) Fix "Mark Range": reduce maximum namespaces in favorites, fix shadowing of ctrl+space
+* [#2926](https://github.com/derailed/k9s/pull/2926) chore(plugins,remove-finalizers): make sure the resources api group is respected
+* [#2921](https://github.com/derailed/k9s/pull/2921) feat: Add plugins for kubectl node-shell
+* [#2920](https://github.com/derailed/k9s/pull/2920) eat: added StartupProbes status (S) to the PROBES column in the container render
+* [#2914](https://github.com/derailed/k9s/pull/2914) Adding eks-node-viewer plugin
+* [#2898](https://github.com/derailed/k9s/pull/2898) Add argocd plugin to community plugins
+* [#2896](https://github.com/derailed/k9s/pull/2896) feat(2896): Add toggle keda plugin
+* [#2890](https://github.com/derailed/k9s/pull/2890) Update README.md
+* [#2881](https://github.com/derailed/k9s/pull/2881) Fix Mark-Range command: ensure that NS Favorite doesn't exceed the limit
+* [#2861](https://github.com/derailed/k9s/pull/2861) chore: fix function name
+* [#2856](https://github.com/derailed/k9s/pull/2856) fix internal/render/hpa.go merge issue
+* [#2848](https://github.com/derailed/k9s/pull/2848) Include sidecar containers requests and limits
+* [#2844](https://github.com/derailed/k9s/pull/2844) Update README GO Version Required
+* [#2830](https://github.com/derailed/k9s/pull/2830) update tview to fix log escaping problem completely
+* [#2822](https://github.com/derailed/k9s/pull/2822) Adding HolmesGPT plugin
+* [#2821](https://github.com/derailed/k9s/pull/2821) Add a spark-operator plugin
+* [#2817](https://github.com/derailed/k9s/pull/2817) Add comment about Escape keybinding
+* [#2812](https://github.com/derailed/k9s/pull/2812) fix: align build image Go version with go.mod
+* [#2795](https://github.com/derailed/k9s/pull/2795) add new plugin current-ctx-terminal
+* [#2791](https://github.com/derailed/k9s/pull/2791) Add leading space to Kubernetes context suggestions
+* [#2789](https://github.com/derailed/k9s/pull/2789) Create kubectl-get-in-shell.yaml
+* [#2788](https://github.com/derailed/k9s/pull/2788) Update README.md plugin format
+* [#2787](https://github.com/derailed/k9s/pull/2787) Update helm-purge.yaml
+* [#2786](https://github.com/derailed/k9s/pull/2786) Update README.md with plugin dangerous field
+* [#2780](https://github.com/derailed/k9s/pull/2780) install copyright file into correct location
+* [#2775](https://github.com/derailed/k9s/pull/2775) fix freebsd build failure
+* [#2780](https://github.com/derailed/k9s/pull/2780) install copyright file into correct location
+* [#2772](https://github.com/derailed/k9s/pull/2772) proper handle OwnerReference for manually created job
+* [#2771](https://github.com/derailed/k9s/pull/2771) feat: add duplik8s plugin
+* [#2770](https://github.com/derailed/k9s/pull/2770) feat: allow plugins block in plugin files
+* [#2765](https://github.com/derailed/k9s/pull/2765) fix: Shellin -> ShellIn
+* [#2763](https://github.com/derailed/k9s/pull/2763) enable "jump to owner" for more kinds
+* [#2755](https://github.com/derailed/k9s/pull/2755) Loki plugin
+* [#2751](https://github.com/derailed/k9s/pull/2751) container logs should be escaped when printed
+* [#2750](https://github.com/derailed/k9s/pull/2750) fix: should switching ctx before ns
+
+---
+
+ © 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
\ No newline at end of file
diff --git a/go.mod b/go.mod
index db00a1f254..8ad7a138f5 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/derailed/k9s
-go 1.22.0
+go 1.23.0
require (
github.com/adrg/xdg v0.5.0
@@ -314,8 +314,8 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gorm.io/gorm v1.25.9 // indirect
- k8s.io/apiserver v0.31.1 // indirect
- k8s.io/component-base v0.31.1 // indirect
+ k8s.io/apiserver v0.31.2 // indirect
+ k8s.io/component-base v0.31.2 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
modernc.org/libc v1.41.0 // indirect
diff --git a/go.sum b/go.sum
index 39c4e5e116..8b3aeeef8e 100644
--- a/go.sum
+++ b/go.sum
@@ -1864,14 +1864,14 @@ k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/
k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ=
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
-k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c=
-k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM=
+k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4=
+k8s.io/apiserver v0.31.2/go.mod h1:o3nKZR7lPlJqkU5I3Ove+Zx3JuoFjQobGX1Gctw6XuE=
k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk=
k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U=
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
-k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8=
-k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w=
+k8s.io/component-base v0.31.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA=
+k8s.io/component-base v0.31.2/go.mod h1:9PeyyFN/drHjtJZMCTkSpQJS3U9OXORnHQqMLDz0sUQ=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
diff --git a/internal/client/metrics.go b/internal/client/metrics.go
index 13485c71a4..f79aeac4d8 100644
--- a/internal/client/metrics.go
+++ b/internal/client/metrics.go
@@ -98,7 +98,7 @@ func (m *MetricsServer) checkAccess(ns, gvr, msg string) error {
return err
}
if !auth {
- return fmt.Errorf(msg)
+ return errors.New(msg)
}
return nil
}
diff --git a/internal/config/data/ns.go b/internal/config/data/ns.go
index 0e0e2228df..14b1c667fe 100644
--- a/internal/config/data/ns.go
+++ b/internal/config/data/ns.go
@@ -72,7 +72,7 @@ func (n *Namespace) Validate(c client.Connection) {
if len(n.Favorites) > MaxFavoritesNS {
log.Debug().Msgf("[Namespace] Number of favorite exceeds hard limit of %v. Trimming.", MaxFavoritesNS)
for _, ns := range n.Favorites[MaxFavoritesNS:] {
- n.rmFavNS(ns)
+ n.rmFavNS(ns)
}
}
}
diff --git a/internal/config/data/ns_test.go b/internal/config/data/ns_test.go
index a6e94aa36f..5d39cd378a 100644
--- a/internal/config/data/ns_test.go
+++ b/internal/config/data/ns_test.go
@@ -36,14 +36,12 @@ func TestNSValidateNoNS(t *testing.T) {
}
func TestNsValidateMaxNS(t *testing.T) {
- allNS := []string{"ns9","ns8","ns7","ns6","ns5","ns4", "ns3", "ns2", "ns1", "all", "default"}
- ns := data.NewNamespace()
-
- ns.Favorites = allNS
-
- ns.Validate(mock.NewMockConnection())
+ allNS := []string{"ns9", "ns8", "ns7", "ns6", "ns5", "ns4", "ns3", "ns2", "ns1", "all", "default"}
+ ns := data.NewNamespace()
+ ns.Favorites = allNS
- assert.Equal(t, data.MaxFavoritesNS, len(ns.Favorites))
+ ns.Validate(mock.NewMockConnection())
+ assert.Equal(t, data.MaxFavoritesNS, len(ns.Favorites))
}
func TestNSSetActive(t *testing.T) {
diff --git a/internal/config/feature.go b/internal/config/feature.go
deleted file mode 100644
index 4b1fd75bcd..0000000000
--- a/internal/config/feature.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-// Copyright Authors of K9s
-
-package config
-
-// // FeatureGates represents K9s opt-in features.
-// type FeatureGates struct {
-// NodeShell bool `yaml:"nodeShell"`
-// }
-
-// // NewFeatureGates returns a new feature gate.
-// func NewFeatureGates() *FeatureGates {
-// return &FeatureGates{}
-// }
diff --git a/internal/config/views.go b/internal/config/views.go
index cafb9053da..41c6b9b5e6 100644
--- a/internal/config/views.go
+++ b/internal/config/views.go
@@ -47,7 +47,7 @@ func (v *ViewSetting) SortCol() (string, bool, error) {
return "", false, fmt.Errorf("invalid sort column spec: %q. must be col-name:asc|desc", v.SortColumn)
}
- return tt[0], tt[1] == "desc", nil
+ return tt[0], tt[1] == "asc", nil
}
func (v *ViewSetting) Equals(vs *ViewSetting) bool {
@@ -116,8 +116,8 @@ func (v *CustomView) RemoveListener(gvr string) {
func (v *CustomView) fireConfigChanged() {
for gvr, list := range v.listeners {
- if v, ok := v.Views[gvr]; ok {
- list.ViewSettingsChanged(v)
+ if view, ok := v.Views[gvr]; ok {
+ list.ViewSettingsChanged(view)
} else {
list.ViewSettingsChanged(ViewSetting{})
}
diff --git a/internal/dao/alias.go b/internal/dao/alias.go
index bf4bf308d5..dc4e3df914 100644
--- a/internal/dao/alias.go
+++ b/internal/dao/alias.go
@@ -84,7 +84,7 @@ func (a *Alias) AsGVR(c string) (client.GVR, string, bool) {
// Get fetch a resource.
func (a *Alias) Get(_ context.Context, _ string) (runtime.Object, error) {
- return nil, errors.New("NYI!!")
+ return nil, errors.New("nyi")
}
// Ensure makes sure alias are loaded.
diff --git a/internal/dao/container.go b/internal/dao/container.go
index 2074607f26..9ccae861c1 100644
--- a/internal/dao/container.go
+++ b/internal/dao/container.go
@@ -6,6 +6,7 @@ package dao
import (
"context"
"fmt"
+ "strconv"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
@@ -22,6 +23,12 @@ var (
_ Loggable = (*Container)(nil)
)
+const (
+ initIDX = "I"
+ mainIDX = "M"
+ ephIDX = "E"
+)
+
// Container represents a pod's container dao.
type Container struct {
NonResource
@@ -46,12 +53,15 @@ func (c *Container) List(ctx context.Context, _ string) ([]runtime.Object, error
if err != nil {
return nil, err
}
- res := make([]runtime.Object, 0, len(po.Spec.InitContainers)+len(po.Spec.Containers))
- for _, co := range po.Spec.InitContainers {
- res = append(res, makeContainerRes(co, po, cmx[co.Name], true))
+ res := make([]runtime.Object, 0, len(po.Spec.InitContainers)+len(po.Spec.Containers)+len(po.Spec.EphemeralContainers))
+ for i, co := range po.Spec.InitContainers {
+ res = append(res, makeContainerRes(initIDX, i, co, po, cmx[co.Name]))
+ }
+ for i, co := range po.Spec.Containers {
+ res = append(res, makeContainerRes(mainIDX, i, co, po, cmx[co.Name]))
}
- for _, co := range po.Spec.Containers {
- res = append(res, makeContainerRes(co, po, cmx[co.Name], false))
+ for i, co := range po.Spec.EphemeralContainers {
+ res = append(res, makeContainerRes(ephIDX, i, v1.Container(co.EphemeralContainerCommon), po, cmx[co.Name]))
}
return res, nil
@@ -68,25 +78,29 @@ func (c *Container) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan,
// ----------------------------------------------------------------------------
// Helpers...
-func makeContainerRes(co v1.Container, po *v1.Pod, cmx *mv1beta1.ContainerMetrics, isInit bool) render.ContainerRes {
+func makeContainerRes(kind string, idx int, co v1.Container, po *v1.Pod, cmx *mv1beta1.ContainerMetrics) render.ContainerRes {
return render.ContainerRes{
+ Idx: kind + strconv.Itoa(idx+1),
Container: &co,
- Status: getContainerStatus(co.Name, po.Status),
+ Status: getContainerStatus(kind, idx, po.Status),
MX: cmx,
- IsInit: isInit,
Age: po.GetCreationTimestamp(),
}
}
-func getContainerStatus(co string, status v1.PodStatus) *v1.ContainerStatus {
- for _, c := range status.ContainerStatuses {
- if c.Name == co {
- return &c
+func getContainerStatus(kind string, idx int, status v1.PodStatus) *v1.ContainerStatus {
+ switch kind {
+ case mainIDX:
+ if idx < len(status.ContainerStatuses) {
+ return &status.ContainerStatuses[idx]
}
- }
- for _, c := range status.InitContainerStatuses {
- if c.Name == co {
- return &c
+ case initIDX:
+ if idx < len(status.InitContainerStatuses) {
+ return &status.InitContainerStatuses[idx]
+ }
+ case ephIDX:
+ if idx < len(status.EphemeralContainerStatuses) {
+ return &status.EphemeralContainerStatuses[idx]
}
}
diff --git a/internal/dao/dir.go b/internal/dao/dir.go
index 95239cc070..ef9c67baf1 100644
--- a/internal/dao/dir.go
+++ b/internal/dao/dir.go
@@ -37,7 +37,7 @@ var yamlRX = regexp.MustCompile(`.*\.(yml|yaml|json)`)
func (a *Dir) List(ctx context.Context, _ string) ([]runtime.Object, error) {
dir, ok := ctx.Value(internal.KeyPath).(string)
if !ok {
- return nil, errors.New("No dir in context")
+ return nil, errors.New("no dir in context")
}
files, err := os.ReadDir(dir)
@@ -61,5 +61,5 @@ func (a *Dir) List(ctx context.Context, _ string) ([]runtime.Object, error) {
// Get fetch a resource.
func (a *Dir) Get(_ context.Context, _ string) (runtime.Object, error) {
- return nil, errors.New("NYI!!")
+ return nil, errors.New("nyi")
}
diff --git a/internal/dao/dp.go b/internal/dao/dp.go
index 5df3432113..600ee6c1a7 100644
--- a/internal/dao/dp.go
+++ b/internal/dao/dp.go
@@ -130,7 +130,7 @@ func (d *Deployment) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan,
return nil, err
}
if dp.Spec.Selector == nil || len(dp.Spec.Selector.MatchLabels) == 0 {
- return nil, fmt.Errorf("No valid selector found on Deployment %s", opts.Path)
+ return nil, fmt.Errorf("no valid selector found on deployment: %s", opts.Path)
}
return podLogs(ctx, dp.Spec.Selector.MatchLabels, opts)
diff --git a/internal/dao/job.go b/internal/dao/job.go
index 286cde59be..0c3ba1a12c 100644
--- a/internal/dao/job.go
+++ b/internal/dao/job.go
@@ -85,7 +85,7 @@ func (j *Job) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error)
}
if job.Spec.Selector == nil || len(job.Spec.Selector.MatchLabels) == 0 {
- return nil, fmt.Errorf("No valid selector found on Job %s", opts.Path)
+ return nil, fmt.Errorf("no valid selector found for job: %s", opts.Path)
}
return podLogs(ctx, job.Spec.Selector.MatchLabels, opts)
diff --git a/internal/dao/non_resource.go b/internal/dao/non_resource.go
index 9840532ec4..0dc7208661 100644
--- a/internal/dao/non_resource.go
+++ b/internal/dao/non_resource.go
@@ -53,5 +53,5 @@ func (n *NonResource) GVR() string {
// Get returns the given resource.
func (n *NonResource) Get(context.Context, string) (runtime.Object, error) {
- return nil, fmt.Errorf("NYI!")
+ return nil, fmt.Errorf("nyi")
}
diff --git a/internal/dao/pod.go b/internal/dao/pod.go
index 6e5919381a..f4ee453dbe 100644
--- a/internal/dao/pod.go
+++ b/internal/dao/pod.go
@@ -385,11 +385,11 @@ func readLogs(ctx context.Context, wg *sync.WaitGroup, stream io.ReadCloser, out
item = opts.ToLogItem(tview.EscapeBytes(bytes))
} else {
if errors.Is(err, io.EOF) {
- e := fmt.Errorf("Stream closed %w for %s", err, opts.Info())
+ e := fmt.Errorf("stream closed %w for %s", err, opts.Info())
item = opts.ToErrLogItem(e)
log.Warn().Err(e).Msg("log-reader EOF")
} else {
- e := fmt.Errorf("Stream canceled %w for %s", err, opts.Info())
+ e := fmt.Errorf("stream canceled %w for %s", err, opts.Info())
item = opts.ToErrLogItem(e)
log.Warn().Err(e).Msg("log-reader canceled")
}
@@ -439,7 +439,7 @@ func (p *Pod) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs)
return err
}
if isManaged {
- return fmt.Errorf("Unable to set image. This pod is managed by %s. Please set the image on the controller", manager)
+ return fmt.Errorf("unable to set image. This pod is managed by %s. Please set the image on the controller", manager)
}
jsonPatch, err := GetJsonPatch(imageSpecs)
if err != nil {
diff --git a/internal/dao/port_forwarder.go b/internal/dao/port_forwarder.go
index 454af94285..e0bef73f40 100644
--- a/internal/dao/port_forwarder.go
+++ b/internal/dao/port_forwarder.go
@@ -72,6 +72,11 @@ func (p *PortForwarder) Port() string {
return p.tunnel.PortMap()
}
+// Address returns the port Address.
+func (p *PortForwarder) Address() string {
+ return p.tunnel.Address
+}
+
// ContainerPort returns the container port.
func (p *PortForwarder) ContainerPort() string {
return p.tunnel.ContainerPort
diff --git a/internal/dao/reference.go b/internal/dao/reference.go
index 8ea76f5159..3ea129f6c4 100644
--- a/internal/dao/reference.go
+++ b/internal/dao/reference.go
@@ -24,7 +24,7 @@ type Reference struct {
func (r *Reference) List(ctx context.Context, ns string) ([]runtime.Object, error) {
gvr, ok := ctx.Value(internal.KeyGVR).(client.GVR)
if !ok {
- return nil, errors.New("No context GVR found")
+ return nil, errors.New("no context for gvr found")
}
switch gvr {
case SaGVR:
diff --git a/internal/dao/registry.go b/internal/dao/registry.go
index 85416a752b..f512bc60cd 100644
--- a/internal/dao/registry.go
+++ b/internal/dao/registry.go
@@ -11,6 +11,7 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/rs/zerolog/log"
+ apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
@@ -413,11 +414,32 @@ func loadCRDs(f Factory, m ResourceMetas) {
}
for _, o := range oo {
- meta, errs := extractMeta(o)
- if len(errs) > 0 {
- log.Error().Err(errs[0]).Msgf("Fail to extract CRD meta (%d) errors", len(errs))
+ var crd apiext.CustomResourceDefinition
+ err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &crd)
+ if err != nil {
+ log.Err(err).Msg("boom")
continue
}
+
+ var meta metav1.APIResource
+ meta.Kind = crd.Spec.Names.Kind
+ meta.Group = crd.Spec.Group
+ meta.Name = crd.Name
+ meta.SingularName = crd.Spec.Names.Singular
+ meta.ShortNames = crd.Spec.Names.ShortNames
+ meta.Namespaced = crd.Spec.Scope == apiext.NamespaceScoped
+ for _, v := range crd.Spec.Versions {
+ if v.Served && !v.Deprecated {
+ meta.Version = v.Name
+ break
+ }
+ }
+
+ // meta, errs := extractMeta(o)
+ // if len(errs) > 0 {
+ // log.Error().Err(errs[0]).Msgf("Fail to extract CRD meta (%d) errors", len(errs))
+ // continue
+ // }
meta.Categories = append(meta.Categories, crdCat)
gvr := client.NewGVRFromMeta(meta)
m[gvr] = meta
diff --git a/internal/dao/rs.go b/internal/dao/rs.go
index fea9d3265d..07ad2fa378 100644
--- a/internal/dao/rs.go
+++ b/internal/dao/rs.go
@@ -80,7 +80,7 @@ func controllerInfo(rs *appsv1.ReplicaSet) (string, string, string, error) {
}
return ref.Name, ref.Kind, group, nil
}
- return "", "", "", fmt.Errorf("Unable to find controller for ReplicaSet %s", rs.ObjectMeta.Name)
+ return "", "", "", fmt.Errorf("unable to find controller for replicaset: %s", rs.ObjectMeta.Name)
}
// Rollback reverses the last deployment.
diff --git a/internal/dao/sts.go b/internal/dao/sts.go
index 9c111d523f..4e352f164b 100644
--- a/internal/dao/sts.go
+++ b/internal/dao/sts.go
@@ -152,7 +152,7 @@ func (s *StatefulSet) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan
return nil, errors.New("expecting StatefulSet resource")
}
if sts.Spec.Selector == nil || len(sts.Spec.Selector.MatchLabels) == 0 {
- return nil, fmt.Errorf("No valid selector found on StatefulSet %s", opts.Path)
+ return nil, fmt.Errorf("no valid selector found on statefulset: %s", opts.Path)
}
return podLogs(ctx, sts.Spec.Selector.MatchLabels, opts)
diff --git a/internal/dao/svc.go b/internal/dao/svc.go
index 1e8fcfe877..cee13178d5 100644
--- a/internal/dao/svc.go
+++ b/internal/dao/svc.go
@@ -32,7 +32,7 @@ func (s *Service) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, er
if err != nil {
return nil, err
}
- if svc.Spec.Selector == nil || len(svc.Spec.Selector) == 0 {
+ if len(svc.Spec.Selector) == 0 {
return nil, fmt.Errorf("no valid selector found on Service %s", opts.Path)
}
diff --git a/internal/dao/table.go b/internal/dao/table.go
index 6936772f82..362ce0c21a 100644
--- a/internal/dao/table.go
+++ b/internal/dao/table.go
@@ -10,7 +10,6 @@ import (
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/rest"
@@ -66,7 +65,7 @@ func (t *Table) List(ctx context.Context, ns string) ([]runtime.Object, error) {
LabelSelector: labelSel,
FieldSelector: fieldSel,
ResourceVersion: "0",
- ResourceVersionMatch: v1.ResourceVersionMatchNotOlderThan,
+ ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
}, p).
Do(ctx).Get()
if err != nil {
@@ -102,7 +101,7 @@ func (t *Table) getClient(f serializer.CodecFactory) (*rest.RESTClient, error) {
func (t *Table) codec() (serializer.CodecFactory, runtime.ParameterCodec) {
var tt metav1.Table
- opts := metav1.TableOptions{IncludeObject: v1.IncludeObject}
+ opts := metav1.TableOptions{IncludeObject: metav1.IncludeObject}
gv := t.gvr.GV()
metav1.AddToGroupVersion(genScheme, gv)
genScheme.AddKnownTypes(gv, &tt, &opts)
diff --git a/internal/model/helpers_int_test.go b/internal/model/helpers_int_test.go
index 5c3f39a911..3e7e54064f 100644
--- a/internal/model/helpers_int_test.go
+++ b/internal/model/helpers_int_test.go
@@ -36,6 +36,33 @@ func Test_rxFilter(t *testing.T) {
},
},
},
+ "start-rx-match": {
+ q: "(?i)^foo",
+ lines: []string{"foo", "fob", "barfoo"},
+ e: fuzzy.Matches{
+ {
+ Str: "(?i)^foo",
+ Index: 0,
+ MatchedIndexes: []int{0, 1, 2},
+ },
+ },
+ },
+ "end-rx-match": {
+ q: "foo$",
+ lines: []string{"foo", "fob", "barfoo"},
+ e: fuzzy.Matches{
+ {
+ Str: "foo$",
+ Index: 0,
+ MatchedIndexes: []int{0, 1, 2},
+ },
+ {
+ Str: "foo$",
+ Index: 2,
+ MatchedIndexes: []int{3, 4, 5},
+ },
+ },
+ },
"multiple-matches": {
q: "foo",
lines: []string{"foo", "bar", "foo bar foo", "baz"},
@@ -58,6 +85,7 @@ func Test_rxFilter(t *testing.T) {
},
},
}
+
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
diff --git a/internal/model1/header.go b/internal/model1/header.go
index 798f1d92ce..d4f6d4c850 100644
--- a/internal/model1/header.go
+++ b/internal/model1/header.go
@@ -16,7 +16,6 @@ type HeaderColumn struct {
Name string
Align int
Decorator DecoratorFunc
- Hide bool
Wide bool
MX bool
Time bool
diff --git a/internal/model1/row_event.go b/internal/model1/row_event.go
index 628ddab057..c475f38d38 100644
--- a/internal/model1/row_event.go
+++ b/internal/model1/row_event.go
@@ -255,7 +255,7 @@ func (r *RowEvents) FindIndex(id string) (int, bool) {
// Sort rows based on column index and order.
func (r *RowEvents) Sort(ns string, sortCol int, isDuration, numCol, isCapacity, asc bool) {
- if sortCol == -1 {
+ if sortCol == -1 || r == nil {
return
}
@@ -290,6 +290,7 @@ func (r RowEventSorter) Len() int {
}
func (r RowEventSorter) Swap(i, j int) {
+
r.Events.events[i], r.Events.events[j] = r.Events.events[j], r.Events.events[i]
}
diff --git a/internal/model1/table_data.go b/internal/model1/table_data.go
index 13ef48ce81..aa2c2f2269 100644
--- a/internal/model1/table_data.go
+++ b/internal/model1/table_data.go
@@ -167,19 +167,22 @@ func (t *TableData) rxFilter(q string, inverse bool) (*RowEvents, error) {
return nil, fmt.Errorf("invalid rx filter %q: %w", q, err)
}
- ageIndex, ok := t.header.IndexOf("AGE", true)
-
- rr := NewRowEvents(t.RowCount() / 2)
+ var startIndex int
+ if _, ok := t.header.IndexOf("NAMESPACE", true); ok && client.IsNamespaced(t.namespace) {
+ startIndex = 1
+ }
+ rr := NewRowEvents(50)
+ ageIndex, _ := t.header.IndexOf("AGE", true)
t.rowEvents.Range(func(_ int, re RowEvent) bool {
- ff := re.Row.Fields
- if ok && ageIndex+1 <= len(ff) {
+ ff := re.Row.Fields[startIndex:]
+ if ageIndex >= 0 && ageIndex+1 <= len(ff) {
ff = append(ff[0:ageIndex], ff[ageIndex+1:]...)
}
- fields := strings.Join(ff, spacer)
- if (inverse && !rx.MatchString(fields)) ||
- ((!inverse) && rx.MatchString(fields)) {
+ match := rx.MatchString(strings.Join(ff, spacer))
+ if (inverse && !match) || (!inverse && match) {
rr.Add(re)
}
+
return true
})
diff --git a/internal/render/container.go b/internal/render/container.go
index c4982b3b38..95f3e834f4 100644
--- a/internal/render/container.go
+++ b/internal/render/container.go
@@ -72,12 +72,12 @@ func (c Container) ColorerFunc() model1.ColorerFunc {
// Header returns a header row.
func (Container) Header(ns string) model1.Header {
return model1.Header{
+ model1.HeaderColumn{Name: "IDX"},
model1.HeaderColumn{Name: "NAME"},
model1.HeaderColumn{Name: "PF"},
model1.HeaderColumn{Name: "IMAGE"},
model1.HeaderColumn{Name: "READY"},
model1.HeaderColumn{Name: "STATE"},
- model1.HeaderColumn{Name: "INIT"},
model1.HeaderColumn{Name: "RESTARTS", Align: tview.AlignRight},
model1.HeaderColumn{Name: "PROBES(L:R:S)"},
model1.HeaderColumn{Name: "CPU", Align: tview.AlignRight, MX: true},
@@ -109,12 +109,12 @@ func (c Container) Render(o interface{}, name string, r *model1.Row) error {
r.ID = co.Container.Name
r.Fields = model1.Fields{
+ co.Idx,
co.Container.Name,
"●",
co.Container.Image,
ready,
state,
- boolToStr(co.IsInit),
restarts,
probe(co.Container.LivenessProbe) + ":" + probe(co.Container.ReadinessProbe) + ":" + probe(co.Container.StartupProbe),
toMc(cur.cpu),
@@ -241,7 +241,7 @@ type ContainerRes struct {
Container *v1.Container
Status *v1.ContainerStatus
MX *mv1beta1.ContainerMetrics
- IsInit bool
+ Idx string
Age metav1.Time
}
diff --git a/internal/render/container_test.go b/internal/render/container_test.go
index b05d775ae9..d7f4f6f3d5 100644
--- a/internal/render/container_test.go
+++ b/internal/render/container_test.go
@@ -24,19 +24,18 @@ func TestContainer(t *testing.T) {
Container: makeContainer(),
Status: makeContainerStatus(),
MX: makeContainerMetrics(),
- IsInit: false,
Age: makeAge(),
}
var r model1.Row
assert.Nil(t, c.Render(cres, "blee", &r))
assert.Equal(t, "fred", r.ID)
assert.Equal(t, model1.Fields{
+ "",
"fred",
"●",
"img",
"false",
"Running",
- "false",
"0",
"off:off:off",
"10",
@@ -61,7 +60,6 @@ func BenchmarkContainerRender(b *testing.B) {
Container: makeContainer(),
Status: makeContainerStatus(),
MX: makeContainerMetrics(),
- IsInit: false,
Age: makeAge(),
}
var r model1.Row
diff --git a/internal/render/node.go b/internal/render/node.go
index b207f61a2b..353816de0b 100644
--- a/internal/render/node.go
+++ b/internal/render/node.go
@@ -39,6 +39,7 @@ func (Node) Header(string) model1.Header {
model1.HeaderColumn{Name: "ARCH", Wide: true},
model1.HeaderColumn{Name: "TAINTS"},
model1.HeaderColumn{Name: "VERSION"},
+ model1.HeaderColumn{Name: "OS-IMAGE", Wide: true},
model1.HeaderColumn{Name: "KERNEL", Wide: true},
model1.HeaderColumn{Name: "INTERNAL-IP", Wide: true},
model1.HeaderColumn{Name: "EXTERNAL-IP", Wide: true},
@@ -95,6 +96,7 @@ func (n Node) Render(o interface{}, ns string, r *model1.Row) error {
no.Status.NodeInfo.Architecture,
strconv.Itoa(len(no.Spec.Taints)),
no.Status.NodeInfo.KubeletVersion,
+ no.Status.NodeInfo.OSImage,
no.Status.NodeInfo.KernelVersion,
iIP,
eIP,
diff --git a/internal/render/node_test.go b/internal/render/node_test.go
index 09fb4a6889..156b054cf1 100644
--- a/internal/render/node_test.go
+++ b/internal/render/node_test.go
@@ -25,8 +25,8 @@ func TestNodeRender(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, "minikube", r.ID)
- e := model1.Fields{"minikube", "Ready", "master", "amd64", "0", "v1.15.2", "4.15.0", "192.168.64.107", "", "0", "10", "20", "0", "0", "4000", "7874"}
- assert.Equal(t, e, r.Fields[:16])
+ e := model1.Fields{"minikube", "Ready", "master", "amd64", "0", "v1.15.2", "Buildroot 2018.05.3", "4.15.0", "192.168.64.107", "", "0", "10", "20", "0", "0", "4000", "7874"}
+ assert.Equal(t, e, r.Fields[:17])
}
func BenchmarkNodeRender(b *testing.B) {
diff --git a/internal/render/pod.go b/internal/render/pod.go
index 33ce957304..7366257d1f 100644
--- a/internal/render/pod.go
+++ b/internal/render/pod.go
@@ -194,7 +194,7 @@ func asReadinessGate(pod v1.Pod) string {
return MissingValue
}
- trueConditions := 0
+ var trueConditions int
for _, readinessGate := range pod.Spec.ReadinessGates {
conditionType := readinessGate.ConditionType
for _, condition := range pod.Status.Conditions {
@@ -228,7 +228,7 @@ func (p *PodWithMetrics) DeepCopyObject() runtime.Object {
func gatherCoMX(spec *v1.PodSpec, ccmx []mv1beta1.ContainerMetrics) (c, r metric) {
cc := make([]v1.Container, 0, len(spec.InitContainers)+len(spec.Containers))
- cc = append(cc, filterRestartableInitCO(spec.InitContainers)...)
+ cc = append(cc, filterSidecarCO(spec.InitContainers)...)
cc = append(cc, spec.Containers...)
rcpu, rmem := cosRequests(cc)
@@ -498,12 +498,13 @@ func restartableInitCO(p *v1.ContainerRestartPolicy) bool {
return p != nil && *p == v1.ContainerRestartPolicyAlways
}
-func filterRestartableInitCO(cc []v1.Container) []v1.Container {
+func filterSidecarCO(cc []v1.Container) []v1.Container {
rcc := make([]v1.Container, 0, len(cc))
for _, c := range cc {
if c.RestartPolicy != nil && *c.RestartPolicy == v1.ContainerRestartPolicyAlways {
rcc = append(rcc, c)
}
}
+
return rcc
}
diff --git a/internal/render/pod_int_test.go b/internal/render/pod_int_test.go
index 08ca4ed402..c29f230d87 100644
--- a/internal/render/pod_int_test.go
+++ b/internal/render/pod_int_test.go
@@ -295,12 +295,11 @@ func Test_restartableInitCO(t *testing.T) {
}
}
-func Test_filterRestartableInitCO(t *testing.T) {
+func Test_filterSidecarCO(t *testing.T) {
always := v1.ContainerRestartPolicyAlways
uu := map[string]struct {
- cc []v1.Container
- ecc []v1.Container
+ cc, ecc []v1.Container
}{
"empty": {
cc: []v1.Container{},
@@ -350,7 +349,7 @@ func Test_filterRestartableInitCO(t *testing.T) {
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
- assert.Equal(t, u.ecc, filterRestartableInitCO(u.cc))
+ assert.Equal(t, u.ecc, filterSidecarCO(u.cc))
})
}
}
diff --git a/internal/render/port_forward_test.go b/internal/render/port_forward_test.go
index 6c4cd18819..20e0683ac0 100644
--- a/internal/render/port_forward_test.go
+++ b/internal/render/port_forward_test.go
@@ -66,3 +66,7 @@ func (f fwd) Active() bool {
func (f fwd) Age() time.Time {
return testTime()
}
+
+func (f fwd) Address() string {
+ return ""
+}
diff --git a/internal/render/portforward.go b/internal/render/portforward.go
index 267be33cf6..915e325b4b 100644
--- a/internal/render/portforward.go
+++ b/internal/render/portforward.go
@@ -24,9 +24,12 @@ type Forwarder interface {
// Container returns a container name.
Container() string
- // Ports returns container exposed ports.
+ // Port returns container exposed port.
Port() string
+ // Address returns the host address.
+ Address() string
+
// Active returns forwarder current state.
Active() bool
@@ -77,7 +80,7 @@ func (f PortForward) Render(o interface{}, gvr string, r *model1.Row) error {
trimContainer(n),
pf.Container(),
pf.Port(),
- UrlFor(pf.Config.Host, pf.Config.Path, ports[0]),
+ UrlFor(pf.Config.Host, pf.Config.Path, ports[0], pf.Address()),
AsThousands(int64(pf.Config.C)),
AsThousands(int64(pf.Config.N)),
"",
@@ -100,9 +103,9 @@ func trimContainer(n string) string {
}
// UrlFor computes fq url for a given benchmark configuration.
-func UrlFor(host, path, port string) string {
+func UrlFor(host, path, port, address string) string {
if host == "" {
- host = "localhost"
+ host = address
}
if path == "" {
path = "/"
diff --git a/internal/ui/dialog/selection.go b/internal/ui/dialog/selection.go
index fc6e545cca..592105c4db 100644
--- a/internal/ui/dialog/selection.go
+++ b/internal/ui/dialog/selection.go
@@ -1,3 +1,6 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright Authors of K9s
+
package dialog
import (
diff --git a/internal/ui/modal_list.go b/internal/ui/modal_list.go
index 1827561a36..ac331a9eaf 100644
--- a/internal/ui/modal_list.go
+++ b/internal/ui/modal_list.go
@@ -1,3 +1,6 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright Authors of K9s
+
package ui
import (
diff --git a/internal/ui/pages.go b/internal/ui/pages.go
index b5864e3bec..eed87e5d53 100644
--- a/internal/ui/pages.go
+++ b/internal/ui/pages.go
@@ -5,6 +5,7 @@ package ui
import (
"fmt"
+
"github.com/derailed/k9s/internal/model"
"github.com/derailed/tview"
"github.com/rs/zerolog/log"
diff --git a/internal/view/container.go b/internal/view/container.go
index 01f4f1e5e5..babcb2b8db 100644
--- a/internal/view/container.go
+++ b/internal/view/container.go
@@ -33,6 +33,7 @@ func NewContainer(gvr client.GVR) ResourceViewer {
c.SetEnvFn(c.k9sEnv)
c.GetTable().SetEnterFn(c.viewLogs)
c.GetTable().SetDecorateFn(c.decorateRows)
+ c.GetTable().SetSortCol("IDX", true)
c.AddBindKeysFn(c.bindKeys)
c.GetTable().SetDecorateFn(c.portForwardIndicator)
@@ -90,6 +91,7 @@ func (c *Container) bindKeys(aa *ui.KeyActions) {
ui.KeyF: ui.NewKeyAction("Show PortForward", c.showPFCmd, true),
ui.KeyShiftF: ui.NewKeyAction("PortForward", c.portFwdCmd, true),
ui.KeyShiftT: ui.NewKeyAction("Sort Restart", c.GetTable().SortColCmd("RESTARTS", false), false),
+ ui.KeyShiftI: ui.NewKeyAction("Sort Idx", c.GetTable().SortColCmd("IDX", true), false),
})
aa.Merge(resourceSorters(c.GetTable()))
}
diff --git a/internal/view/container_test.go b/internal/view/container_test.go
index cc1133e88a..84787f6d1b 100644
--- a/internal/view/container_test.go
+++ b/internal/view/container_test.go
@@ -16,5 +16,5 @@ func TestContainerNew(t *testing.T) {
assert.Nil(t, c.Init(makeCtx()))
assert.Equal(t, "Containers", c.Name())
- assert.Equal(t, 18, len(c.Hints()))
+ assert.Equal(t, 19, len(c.Hints()))
}
diff --git a/internal/view/helpers.go b/internal/view/helpers.go
index 8ec027a9b0..db90747299 100644
--- a/internal/view/helpers.go
+++ b/internal/view/helpers.go
@@ -153,7 +153,7 @@ func showPods(app *App, path, labelSel, fieldSel string) {
}
}
-func podCtx(app *App, path, fieldSel string) ContextFunc {
+func podCtx(_ *App, path, fieldSel string) ContextFunc {
return func(ctx context.Context) context.Context {
ctx = context.WithValue(ctx, internal.KeyPath, path)
return context.WithValue(ctx, internal.KeyFields, fieldSel)
diff --git a/internal/view/owner_extender.go b/internal/view/owner_extender.go
index 89b42449ec..52679c6522 100644
--- a/internal/view/owner_extender.go
+++ b/internal/view/owner_extender.go
@@ -1,23 +1,25 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright Authors of K9s
+
package view
import (
"context"
"fmt"
- "github.com/derailed/k9s/internal/ui/dialog"
- "github.com/rs/zerolog/log"
+ "github.com/derailed/k9s/internal"
+ "github.com/derailed/k9s/internal/client"
+ "github.com/derailed/k9s/internal/dao"
+ "github.com/derailed/k9s/internal/render"
+ "github.com/derailed/k9s/internal/ui"
+ "github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/tcell/v2"
"github.com/go-errors/errors"
+ "github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
-
- "github.com/derailed/k9s/internal"
- "github.com/derailed/k9s/internal/client"
- "github.com/derailed/k9s/internal/dao"
- "github.com/derailed/k9s/internal/render"
- "github.com/derailed/k9s/internal/ui"
)
// OwnerExtender adds owner actions to a given viewer.
diff --git a/internal/view/reference.go b/internal/view/reference.go
index 2f08cc7bba..5d2e97f42a 100644
--- a/internal/view/reference.go
+++ b/internal/view/reference.go
@@ -54,8 +54,9 @@ func (r *Reference) gotoCmd(evt *tcell.EventKey) *tcell.EventKey {
}
path := r.GetTable().GetSelectedItem()
+ ns, _ := client.Namespaced(path)
gvr := ui.TrimCell(r.GetTable().SelectTable, row, 2)
- r.App().gotoResource(client.NewGVR(gvr).R(), path, false)
+ r.App().gotoResource(client.NewGVR(gvr).R()+" "+ns, path, false)
return evt
}
diff --git a/internal/view/table.go b/internal/view/table.go
index d0f60d37a0..47a8d6163b 100644
--- a/internal/view/table.go
+++ b/internal/view/table.go
@@ -11,6 +11,7 @@ import (
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
+ "github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui"
@@ -46,16 +47,16 @@ func (t *Table) Init(ctx context.Context) (err error) {
if t.app.Conn() != nil {
ctx = context.WithValue(ctx, internal.KeyHasMetrics, t.app.Conn().HasMetrics())
}
+ t.app.CustomView = config.NewCustomView()
ctx = context.WithValue(ctx, internal.KeyStyles, t.app.Styles)
+ ctx = context.WithValue(ctx, internal.KeyViewConfig, t.app.CustomView)
+ t.Table.Init(ctx)
if !t.app.Config.K9s.UI.Reactive {
if err := t.app.RefreshCustomViews(); err != nil {
log.Warn().Err(err).Msg("CustomViews load failed")
t.app.Logo().Warn("Views load failed!")
}
}
-
- ctx = context.WithValue(ctx, internal.KeyViewConfig, t.app.CustomView)
- t.Table.Init(ctx)
t.SetInputCapture(t.keyboard)
t.bindKeys()
t.GetModel().SetRefreshRate(time.Duration(t.app.Config.K9s.GetRefreshRate()) * time.Second)
diff --git a/internal/view/workload.go b/internal/view/workload.go
index 4f0ba02fcd..78bba38fa2 100644
--- a/internal/view/workload.go
+++ b/internal/view/workload.go
@@ -79,7 +79,7 @@ func parsePath(path string) (client.GVR, string, bool) {
func (w *Workload) showRes(app *App, _ ui.Tabular, _ client.GVR, path string) {
gvr, fqn, ok := parsePath(path)
if !ok {
- app.Flash().Err(fmt.Errorf("Unable to parse path: %q", path))
+ app.Flash().Err(fmt.Errorf("unable to parse path: %q", path))
return
}
app.gotoResource(gvr.R(), fqn, false)
@@ -130,7 +130,7 @@ func (w *Workload) resourceDelete(selections []string, msg string) {
for _, sel := range selections {
gvr, fqn, ok := parsePath(sel)
if !ok {
- w.App().Flash().Err(fmt.Errorf("Unable to parse path: %q", sel))
+ w.App().Flash().Err(fmt.Errorf("unable to parse path: %q", sel))
return
}
@@ -157,7 +157,7 @@ func (w *Workload) describeCmd(evt *tcell.EventKey) *tcell.EventKey {
}
gvr, fqn, ok := parsePath(path)
if !ok {
- w.App().Flash().Err(fmt.Errorf("Unable to parse path: %q", path))
+ w.App().Flash().Err(fmt.Errorf("unable to parse path: %q", path))
return evt
}
@@ -173,7 +173,7 @@ func (w *Workload) editCmd(evt *tcell.EventKey) *tcell.EventKey {
}
gvr, fqn, ok := parsePath(path)
if !ok {
- w.App().Flash().Err(fmt.Errorf("Unable to parse path: %q", path))
+ w.App().Flash().Err(fmt.Errorf("unable to parse path: %q", path))
return evt
}
@@ -193,7 +193,7 @@ func (w *Workload) yamlCmd(evt *tcell.EventKey) *tcell.EventKey {
}
gvr, fqn, ok := parsePath(path)
if !ok {
- w.App().Flash().Err(fmt.Errorf("Unable to parse path: %q", path))
+ w.App().Flash().Err(fmt.Errorf("unable to parse path: %q", path))
return evt
}
diff --git a/internal/watch/forwarders.go b/internal/watch/forwarders.go
index 8eab5c0657..0c16f38ddf 100644
--- a/internal/watch/forwarders.go
+++ b/internal/watch/forwarders.go
@@ -29,6 +29,9 @@ type Forwarder interface {
// Port returns the port mapping.
Port() string
+ // Address returns the host address.
+ Address() string
+
// FQN returns the full port-forward name.
FQN() string
diff --git a/internal/watch/forwarders_test.go b/internal/watch/forwarders_test.go
index 19e847853b..eae8ae633c 100644
--- a/internal/watch/forwarders_test.go
+++ b/internal/watch/forwarders_test.go
@@ -173,15 +173,16 @@ func newNoOpForwarder() noOpForwarder {
return noOpForwarder{}
}
-func (m noOpForwarder) Start(path string, tunnel port.PortTunnel) (*portforward.PortForwarder, error) {
+func (noOpForwarder) Start(path string, tunnel port.PortTunnel) (*portforward.PortForwarder, error) {
return nil, nil
}
-func (m noOpForwarder) Stop() {}
-func (m noOpForwarder) ID() string { return "" }
-func (m noOpForwarder) Container() string { return "" }
-func (m noOpForwarder) Port() string { return "" }
-func (m noOpForwarder) FQN() string { return "" }
-func (m noOpForwarder) Active() bool { return false }
-func (m noOpForwarder) SetActive(bool) {}
-func (m noOpForwarder) Age() time.Time { return time.Now() }
-func (m noOpForwarder) HasPortMapping(string) bool { return false }
+func (noOpForwarder) Stop() {}
+func (noOpForwarder) ID() string { return "" }
+func (noOpForwarder) Container() string { return "" }
+func (noOpForwarder) Port() string { return "" }
+func (noOpForwarder) FQN() string { return "" }
+func (noOpForwarder) Active() bool { return false }
+func (noOpForwarder) SetActive(bool) {}
+func (noOpForwarder) Age() time.Time { return time.Now() }
+func (noOpForwarder) HasPortMapping(string) bool { return false }
+func (noOpForwarder) Address() string { return "" }
diff --git a/internal/xray/pod.go b/internal/xray/pod.go
index dbcbf92008..0a618ef0e8 100644
--- a/internal/xray/pod.go
+++ b/internal/xray/pod.go
@@ -41,7 +41,7 @@ func (p *Pod) Render(ctx context.Context, ns string, o interface{}) error {
node := NewTreeNode("v1/pods", client.FQN(po.Namespace, po.Name))
parent, ok := ctx.Value(KeyParent).(*TreeNode)
if !ok {
- return fmt.Errorf("Expecting a TreeNode but got %T", ctx.Value(KeyParent))
+ return fmt.Errorf("expecting a TreeNode but got %T", ctx.Value(KeyParent))
}
if err := p.containerRefs(ctx, node, po.Namespace, po.Spec); err != nil {
@@ -95,6 +95,11 @@ func (*Pod) containerRefs(ctx context.Context, parent *TreeNode, ns string, spec
return err
}
}
+ for i := 0; i < len(spec.EphemeralContainers); i++ {
+ if err := cre.Render(ctx, ns, render.ContainerRes{Container: &spec.Containers[i]}); err != nil {
+ return err
+ }
+ }
return nil
}
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index d6ef0648a2..46510bf778 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,6 +1,6 @@
name: k9s
base: core22
-version: 'v0.32.5'
+version: 'v0.32.6'
summary: K9s is a CLI to view and manage your Kubernetes clusters.
description: |
K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of your clusters in a single powerful session.