Release v0.32.6 (#2955)
* update releaser * fix err messages * make pf display the correct address * fix regex filters * fix cust res loading * add container index * update deps + clean up * release v0.32.6mine
parent
99d47ab7e7
commit
9984e3f4bf
|
|
@ -53,10 +53,8 @@ linters-settings:
|
||||||
- nilness
|
- nilness
|
||||||
goimports:
|
goimports:
|
||||||
local-prefixes: github.com/cilium/cilium
|
local-prefixes: github.com/cilium/cilium
|
||||||
staticcheck:
|
|
||||||
go: "1.20"
|
|
||||||
unused:
|
unused:
|
||||||
go: "1.20"
|
go: "1.23"
|
||||||
goheader:
|
goheader:
|
||||||
values:
|
values:
|
||||||
regexp:
|
regexp:
|
||||||
|
|
@ -93,7 +91,7 @@ issues:
|
||||||
text: "SA9003: empty branch"
|
text: "SA9003: empty branch"
|
||||||
- linters: [staticcheck]
|
- linters: [staticcheck]
|
||||||
text: "SA2001: empty critical section"
|
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")
|
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
|
||||||
- path: \\.(generated\\.deepcopy|pb)\\.go$
|
- path: \\.(generated\\.deepcopy|pb)\\.go$
|
||||||
|
|
@ -107,13 +105,12 @@ issues:
|
||||||
linters:
|
linters:
|
||||||
disable-all: true
|
disable-all: true
|
||||||
enable:
|
enable:
|
||||||
- goerr113
|
- err113
|
||||||
- gofmt
|
- gofmt
|
||||||
- goimports
|
- goimports
|
||||||
- govet
|
- govet
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- misspell
|
- misspell
|
||||||
- staticcheck
|
|
||||||
- unused
|
- unused
|
||||||
- goheader
|
- goheader
|
||||||
- gosec
|
- gosec
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
version: 2
|
||||||
|
|
||||||
project_name: k9s
|
project_name: k9s
|
||||||
|
|
||||||
before:
|
before:
|
||||||
|
|
@ -90,7 +92,7 @@ brews:
|
||||||
commit_author:
|
commit_author:
|
||||||
name: derailed
|
name: derailed
|
||||||
email: fernand@imhotep.io
|
email: fernand@imhotep.io
|
||||||
folder: Formula
|
directory: Formula
|
||||||
homepage: https://k9scli.io/
|
homepage: https://k9scli.io/
|
||||||
description: Kubernetes CLI To Manage Your Clusters In Style!
|
description: Kubernetes CLI To Manage Your Clusters In Style!
|
||||||
test: |
|
test: |
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
language: go
|
language: go
|
||||||
go_import_path: github.com/derailed/k9s
|
go_import_path: github.com/derailed/k9s
|
||||||
go:
|
go:
|
||||||
- "1.15"
|
- "1.23"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
|
|
|
||||||
2
Makefile
2
Makefile
|
|
@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
|
||||||
else
|
else
|
||||||
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
endif
|
endif
|
||||||
VERSION ?= v0.32.5
|
VERSION ?= v0.32.6
|
||||||
IMG_NAME := derailed/k9s
|
IMG_NAME := derailed/k9s
|
||||||
IMAGE := ${IMG_NAME}:${VERSION}
|
IMAGE := ${IMG_NAME}:${VERSION}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
6
go.mod
6
go.mod
|
|
@ -1,6 +1,6 @@
|
||||||
module github.com/derailed/k9s
|
module github.com/derailed/k9s
|
||||||
|
|
||||||
go 1.22.0
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/adrg/xdg v0.5.0
|
github.com/adrg/xdg v0.5.0
|
||||||
|
|
@ -314,8 +314,8 @@ require (
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
gorm.io/gorm v1.25.9 // indirect
|
gorm.io/gorm v1.25.9 // indirect
|
||||||
k8s.io/apiserver v0.31.1 // indirect
|
k8s.io/apiserver v0.31.2 // indirect
|
||||||
k8s.io/component-base v0.31.1 // indirect
|
k8s.io/component-base v0.31.2 // indirect
|
||||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
|
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
|
||||||
modernc.org/libc v1.41.0 // indirect
|
modernc.org/libc v1.41.0 // indirect
|
||||||
|
|
|
||||||
8
go.sum
8
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/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 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
|
||||||
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||||
k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c=
|
k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4=
|
||||||
k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM=
|
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 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk=
|
||||||
k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U=
|
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 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
|
||||||
k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
|
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.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA=
|
||||||
k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w=
|
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 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ func (m *MetricsServer) checkAccess(ns, gvr, msg string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !auth {
|
if !auth {
|
||||||
return fmt.Errorf(msg)
|
return errors.New(msg)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,9 @@ func TestNSValidateNoNS(t *testing.T) {
|
||||||
func TestNsValidateMaxNS(t *testing.T) {
|
func TestNsValidateMaxNS(t *testing.T) {
|
||||||
allNS := []string{"ns9", "ns8", "ns7", "ns6", "ns5", "ns4", "ns3", "ns2", "ns1", "all", "default"}
|
allNS := []string{"ns9", "ns8", "ns7", "ns6", "ns5", "ns4", "ns3", "ns2", "ns1", "all", "default"}
|
||||||
ns := data.NewNamespace()
|
ns := data.NewNamespace()
|
||||||
|
|
||||||
ns.Favorites = allNS
|
ns.Favorites = allNS
|
||||||
|
|
||||||
ns.Validate(mock.NewMockConnection())
|
ns.Validate(mock.NewMockConnection())
|
||||||
|
|
||||||
assert.Equal(t, data.MaxFavoritesNS, len(ns.Favorites))
|
assert.Equal(t, data.MaxFavoritesNS, len(ns.Favorites))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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{}
|
|
||||||
// }
|
|
||||||
|
|
@ -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 "", 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 {
|
func (v *ViewSetting) Equals(vs *ViewSetting) bool {
|
||||||
|
|
@ -116,8 +116,8 @@ func (v *CustomView) RemoveListener(gvr string) {
|
||||||
|
|
||||||
func (v *CustomView) fireConfigChanged() {
|
func (v *CustomView) fireConfigChanged() {
|
||||||
for gvr, list := range v.listeners {
|
for gvr, list := range v.listeners {
|
||||||
if v, ok := v.Views[gvr]; ok {
|
if view, ok := v.Views[gvr]; ok {
|
||||||
list.ViewSettingsChanged(v)
|
list.ViewSettingsChanged(view)
|
||||||
} else {
|
} else {
|
||||||
list.ViewSettingsChanged(ViewSetting{})
|
list.ViewSettingsChanged(ViewSetting{})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ func (a *Alias) AsGVR(c string) (client.GVR, string, bool) {
|
||||||
|
|
||||||
// Get fetch a resource.
|
// Get fetch a resource.
|
||||||
func (a *Alias) Get(_ context.Context, _ string) (runtime.Object, error) {
|
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.
|
// Ensure makes sure alias are loaded.
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ package dao
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
|
|
@ -22,6 +23,12 @@ var (
|
||||||
_ Loggable = (*Container)(nil)
|
_ Loggable = (*Container)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
initIDX = "I"
|
||||||
|
mainIDX = "M"
|
||||||
|
ephIDX = "E"
|
||||||
|
)
|
||||||
|
|
||||||
// Container represents a pod's container dao.
|
// Container represents a pod's container dao.
|
||||||
type Container struct {
|
type Container struct {
|
||||||
NonResource
|
NonResource
|
||||||
|
|
@ -46,12 +53,15 @@ func (c *Container) List(ctx context.Context, _ string) ([]runtime.Object, error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res := make([]runtime.Object, 0, len(po.Spec.InitContainers)+len(po.Spec.Containers))
|
res := make([]runtime.Object, 0, len(po.Spec.InitContainers)+len(po.Spec.Containers)+len(po.Spec.EphemeralContainers))
|
||||||
for _, co := range po.Spec.InitContainers {
|
for i, co := range po.Spec.InitContainers {
|
||||||
res = append(res, makeContainerRes(co, po, cmx[co.Name], true))
|
res = append(res, makeContainerRes(initIDX, i, co, po, cmx[co.Name]))
|
||||||
}
|
}
|
||||||
for _, co := range po.Spec.Containers {
|
for i, co := range po.Spec.Containers {
|
||||||
res = append(res, makeContainerRes(co, po, cmx[co.Name], false))
|
res = append(res, makeContainerRes(mainIDX, i, co, po, cmx[co.Name]))
|
||||||
|
}
|
||||||
|
for i, co := range po.Spec.EphemeralContainers {
|
||||||
|
res = append(res, makeContainerRes(ephIDX, i, v1.Container(co.EphemeralContainerCommon), po, cmx[co.Name]))
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
|
|
@ -68,25 +78,29 @@ func (c *Container) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan,
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// 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{
|
return render.ContainerRes{
|
||||||
|
Idx: kind + strconv.Itoa(idx+1),
|
||||||
Container: &co,
|
Container: &co,
|
||||||
Status: getContainerStatus(co.Name, po.Status),
|
Status: getContainerStatus(kind, idx, po.Status),
|
||||||
MX: cmx,
|
MX: cmx,
|
||||||
IsInit: isInit,
|
|
||||||
Age: po.GetCreationTimestamp(),
|
Age: po.GetCreationTimestamp(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getContainerStatus(co string, status v1.PodStatus) *v1.ContainerStatus {
|
func getContainerStatus(kind string, idx int, status v1.PodStatus) *v1.ContainerStatus {
|
||||||
for _, c := range status.ContainerStatuses {
|
switch kind {
|
||||||
if c.Name == co {
|
case mainIDX:
|
||||||
return &c
|
if idx < len(status.ContainerStatuses) {
|
||||||
|
return &status.ContainerStatuses[idx]
|
||||||
}
|
}
|
||||||
|
case initIDX:
|
||||||
|
if idx < len(status.InitContainerStatuses) {
|
||||||
|
return &status.InitContainerStatuses[idx]
|
||||||
}
|
}
|
||||||
for _, c := range status.InitContainerStatuses {
|
case ephIDX:
|
||||||
if c.Name == co {
|
if idx < len(status.EphemeralContainerStatuses) {
|
||||||
return &c
|
return &status.EphemeralContainerStatuses[idx]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ var yamlRX = regexp.MustCompile(`.*\.(yml|yaml|json)`)
|
||||||
func (a *Dir) List(ctx context.Context, _ string) ([]runtime.Object, error) {
|
func (a *Dir) List(ctx context.Context, _ string) ([]runtime.Object, error) {
|
||||||
dir, ok := ctx.Value(internal.KeyPath).(string)
|
dir, ok := ctx.Value(internal.KeyPath).(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("No dir in context")
|
return nil, errors.New("no dir in context")
|
||||||
}
|
}
|
||||||
|
|
||||||
files, err := os.ReadDir(dir)
|
files, err := os.ReadDir(dir)
|
||||||
|
|
@ -61,5 +61,5 @@ func (a *Dir) List(ctx context.Context, _ string) ([]runtime.Object, error) {
|
||||||
|
|
||||||
// Get fetch a resource.
|
// Get fetch a resource.
|
||||||
func (a *Dir) Get(_ context.Context, _ string) (runtime.Object, error) {
|
func (a *Dir) Get(_ context.Context, _ string) (runtime.Object, error) {
|
||||||
return nil, errors.New("NYI!!")
|
return nil, errors.New("nyi")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ func (d *Deployment) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if dp.Spec.Selector == nil || len(dp.Spec.Selector.MatchLabels) == 0 {
|
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)
|
return podLogs(ctx, dp.Spec.Selector.MatchLabels, opts)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
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)
|
return podLogs(ctx, job.Spec.Selector.MatchLabels, opts)
|
||||||
|
|
|
||||||
|
|
@ -53,5 +53,5 @@ func (n *NonResource) GVR() string {
|
||||||
|
|
||||||
// Get returns the given resource.
|
// Get returns the given resource.
|
||||||
func (n *NonResource) Get(context.Context, string) (runtime.Object, error) {
|
func (n *NonResource) Get(context.Context, string) (runtime.Object, error) {
|
||||||
return nil, fmt.Errorf("NYI!")
|
return nil, fmt.Errorf("nyi")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -385,11 +385,11 @@ func readLogs(ctx context.Context, wg *sync.WaitGroup, stream io.ReadCloser, out
|
||||||
item = opts.ToLogItem(tview.EscapeBytes(bytes))
|
item = opts.ToLogItem(tview.EscapeBytes(bytes))
|
||||||
} else {
|
} else {
|
||||||
if errors.Is(err, io.EOF) {
|
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)
|
item = opts.ToErrLogItem(e)
|
||||||
log.Warn().Err(e).Msg("log-reader EOF")
|
log.Warn().Err(e).Msg("log-reader EOF")
|
||||||
} else {
|
} 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)
|
item = opts.ToErrLogItem(e)
|
||||||
log.Warn().Err(e).Msg("log-reader canceled")
|
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
|
return err
|
||||||
}
|
}
|
||||||
if isManaged {
|
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)
|
jsonPatch, err := GetJsonPatch(imageSpecs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,11 @@ func (p *PortForwarder) Port() string {
|
||||||
return p.tunnel.PortMap()
|
return p.tunnel.PortMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Address returns the port Address.
|
||||||
|
func (p *PortForwarder) Address() string {
|
||||||
|
return p.tunnel.Address
|
||||||
|
}
|
||||||
|
|
||||||
// ContainerPort returns the container port.
|
// ContainerPort returns the container port.
|
||||||
func (p *PortForwarder) ContainerPort() string {
|
func (p *PortForwarder) ContainerPort() string {
|
||||||
return p.tunnel.ContainerPort
|
return p.tunnel.ContainerPort
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ type Reference struct {
|
||||||
func (r *Reference) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
func (r *Reference) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||||
gvr, ok := ctx.Value(internal.KeyGVR).(client.GVR)
|
gvr, ok := ctx.Value(internal.KeyGVR).(client.GVR)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("No context GVR found")
|
return nil, errors.New("no context for gvr found")
|
||||||
}
|
}
|
||||||
switch gvr {
|
switch gvr {
|
||||||
case SaGVR:
|
case SaGVR:
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
|
@ -413,11 +414,32 @@ func loadCRDs(f Factory, m ResourceMetas) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range oo {
|
for _, o := range oo {
|
||||||
meta, errs := extractMeta(o)
|
var crd apiext.CustomResourceDefinition
|
||||||
if len(errs) > 0 {
|
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &crd)
|
||||||
log.Error().Err(errs[0]).Msgf("Fail to extract CRD meta (%d) errors", len(errs))
|
if err != nil {
|
||||||
|
log.Err(err).Msg("boom")
|
||||||
continue
|
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)
|
meta.Categories = append(meta.Categories, crdCat)
|
||||||
gvr := client.NewGVRFromMeta(meta)
|
gvr := client.NewGVRFromMeta(meta)
|
||||||
m[gvr] = meta
|
m[gvr] = meta
|
||||||
|
|
|
||||||
|
|
@ -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.ObjectMeta.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rollback reverses the last deployment.
|
// Rollback reverses the last deployment.
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ func (s *StatefulSet) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan
|
||||||
return nil, errors.New("expecting StatefulSet resource")
|
return nil, errors.New("expecting StatefulSet resource")
|
||||||
}
|
}
|
||||||
if sts.Spec.Selector == nil || len(sts.Spec.Selector.MatchLabels) == 0 {
|
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)
|
return podLogs(ctx, sts.Spec.Selector.MatchLabels, opts)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ func (s *Service) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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)
|
return nil, fmt.Errorf("no valid selector found on Service %s", opts.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
|
|
@ -66,7 +65,7 @@ func (t *Table) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||||
LabelSelector: labelSel,
|
LabelSelector: labelSel,
|
||||||
FieldSelector: fieldSel,
|
FieldSelector: fieldSel,
|
||||||
ResourceVersion: "0",
|
ResourceVersion: "0",
|
||||||
ResourceVersionMatch: v1.ResourceVersionMatchNotOlderThan,
|
ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
|
||||||
}, p).
|
}, p).
|
||||||
Do(ctx).Get()
|
Do(ctx).Get()
|
||||||
if err != nil {
|
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) {
|
func (t *Table) codec() (serializer.CodecFactory, runtime.ParameterCodec) {
|
||||||
var tt metav1.Table
|
var tt metav1.Table
|
||||||
opts := metav1.TableOptions{IncludeObject: v1.IncludeObject}
|
opts := metav1.TableOptions{IncludeObject: metav1.IncludeObject}
|
||||||
gv := t.gvr.GV()
|
gv := t.gvr.GV()
|
||||||
metav1.AddToGroupVersion(genScheme, gv)
|
metav1.AddToGroupVersion(genScheme, gv)
|
||||||
genScheme.AddKnownTypes(gv, &tt, &opts)
|
genScheme.AddKnownTypes(gv, &tt, &opts)
|
||||||
|
|
|
||||||
|
|
@ -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": {
|
"multiple-matches": {
|
||||||
q: "foo",
|
q: "foo",
|
||||||
lines: []string{"foo", "bar", "foo bar foo", "baz"},
|
lines: []string{"foo", "bar", "foo bar foo", "baz"},
|
||||||
|
|
@ -58,6 +85,7 @@ func Test_rxFilter(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) {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ type HeaderColumn struct {
|
||||||
Name string
|
Name string
|
||||||
Align int
|
Align int
|
||||||
Decorator DecoratorFunc
|
Decorator DecoratorFunc
|
||||||
Hide bool
|
|
||||||
Wide bool
|
Wide bool
|
||||||
MX bool
|
MX bool
|
||||||
Time bool
|
Time bool
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ func (r *RowEvents) FindIndex(id string) (int, bool) {
|
||||||
|
|
||||||
// Sort rows based on column index and order.
|
// Sort rows based on column index and order.
|
||||||
func (r *RowEvents) Sort(ns string, sortCol int, isDuration, numCol, isCapacity, asc bool) {
|
func (r *RowEvents) Sort(ns string, sortCol int, isDuration, numCol, isCapacity, asc bool) {
|
||||||
if sortCol == -1 {
|
if sortCol == -1 || r == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,6 +290,7 @@ 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]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
return nil, fmt.Errorf("invalid rx filter %q: %w", q, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ageIndex, ok := t.header.IndexOf("AGE", true)
|
var startIndex int
|
||||||
|
if _, ok := t.header.IndexOf("NAMESPACE", true); ok && client.IsNamespaced(t.namespace) {
|
||||||
rr := NewRowEvents(t.RowCount() / 2)
|
startIndex = 1
|
||||||
|
}
|
||||||
|
rr := NewRowEvents(50)
|
||||||
|
ageIndex, _ := t.header.IndexOf("AGE", true)
|
||||||
t.rowEvents.Range(func(_ int, re RowEvent) bool {
|
t.rowEvents.Range(func(_ int, re RowEvent) bool {
|
||||||
ff := re.Row.Fields
|
ff := re.Row.Fields[startIndex:]
|
||||||
if ok && ageIndex+1 <= len(ff) {
|
if ageIndex >= 0 && ageIndex+1 <= len(ff) {
|
||||||
ff = append(ff[0:ageIndex], ff[ageIndex+1:]...)
|
ff = append(ff[0:ageIndex], ff[ageIndex+1:]...)
|
||||||
}
|
}
|
||||||
fields := strings.Join(ff, spacer)
|
match := rx.MatchString(strings.Join(ff, spacer))
|
||||||
if (inverse && !rx.MatchString(fields)) ||
|
if (inverse && !match) || (!inverse && match) {
|
||||||
((!inverse) && rx.MatchString(fields)) {
|
|
||||||
rr.Add(re)
|
rr.Add(re)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,12 +72,12 @@ func (c Container) ColorerFunc() model1.ColorerFunc {
|
||||||
// Header returns a header row.
|
// Header returns a header row.
|
||||||
func (Container) Header(ns string) model1.Header {
|
func (Container) Header(ns string) model1.Header {
|
||||||
return model1.Header{
|
return model1.Header{
|
||||||
|
model1.HeaderColumn{Name: "IDX"},
|
||||||
model1.HeaderColumn{Name: "NAME"},
|
model1.HeaderColumn{Name: "NAME"},
|
||||||
model1.HeaderColumn{Name: "PF"},
|
model1.HeaderColumn{Name: "PF"},
|
||||||
model1.HeaderColumn{Name: "IMAGE"},
|
model1.HeaderColumn{Name: "IMAGE"},
|
||||||
model1.HeaderColumn{Name: "READY"},
|
model1.HeaderColumn{Name: "READY"},
|
||||||
model1.HeaderColumn{Name: "STATE"},
|
model1.HeaderColumn{Name: "STATE"},
|
||||||
model1.HeaderColumn{Name: "INIT"},
|
|
||||||
model1.HeaderColumn{Name: "RESTARTS", Align: tview.AlignRight},
|
model1.HeaderColumn{Name: "RESTARTS", Align: tview.AlignRight},
|
||||||
model1.HeaderColumn{Name: "PROBES(L:R:S)"},
|
model1.HeaderColumn{Name: "PROBES(L:R:S)"},
|
||||||
model1.HeaderColumn{Name: "CPU", Align: tview.AlignRight, MX: true},
|
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.ID = co.Container.Name
|
||||||
r.Fields = model1.Fields{
|
r.Fields = model1.Fields{
|
||||||
|
co.Idx,
|
||||||
co.Container.Name,
|
co.Container.Name,
|
||||||
"●",
|
"●",
|
||||||
co.Container.Image,
|
co.Container.Image,
|
||||||
ready,
|
ready,
|
||||||
state,
|
state,
|
||||||
boolToStr(co.IsInit),
|
|
||||||
restarts,
|
restarts,
|
||||||
probe(co.Container.LivenessProbe) + ":" + probe(co.Container.ReadinessProbe) + ":" + probe(co.Container.StartupProbe),
|
probe(co.Container.LivenessProbe) + ":" + probe(co.Container.ReadinessProbe) + ":" + probe(co.Container.StartupProbe),
|
||||||
toMc(cur.cpu),
|
toMc(cur.cpu),
|
||||||
|
|
@ -241,7 +241,7 @@ type ContainerRes struct {
|
||||||
Container *v1.Container
|
Container *v1.Container
|
||||||
Status *v1.ContainerStatus
|
Status *v1.ContainerStatus
|
||||||
MX *mv1beta1.ContainerMetrics
|
MX *mv1beta1.ContainerMetrics
|
||||||
IsInit bool
|
Idx string
|
||||||
Age metav1.Time
|
Age metav1.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,19 +24,18 @@ func TestContainer(t *testing.T) {
|
||||||
Container: makeContainer(),
|
Container: makeContainer(),
|
||||||
Status: makeContainerStatus(),
|
Status: makeContainerStatus(),
|
||||||
MX: makeContainerMetrics(),
|
MX: makeContainerMetrics(),
|
||||||
IsInit: false,
|
|
||||||
Age: makeAge(),
|
Age: makeAge(),
|
||||||
}
|
}
|
||||||
var r model1.Row
|
var r model1.Row
|
||||||
assert.Nil(t, c.Render(cres, "blee", &r))
|
assert.Nil(t, c.Render(cres, "blee", &r))
|
||||||
assert.Equal(t, "fred", r.ID)
|
assert.Equal(t, "fred", r.ID)
|
||||||
assert.Equal(t, model1.Fields{
|
assert.Equal(t, model1.Fields{
|
||||||
|
"",
|
||||||
"fred",
|
"fred",
|
||||||
"●",
|
"●",
|
||||||
"img",
|
"img",
|
||||||
"false",
|
"false",
|
||||||
"Running",
|
"Running",
|
||||||
"false",
|
|
||||||
"0",
|
"0",
|
||||||
"off:off:off",
|
"off:off:off",
|
||||||
"10",
|
"10",
|
||||||
|
|
@ -61,7 +60,6 @@ func BenchmarkContainerRender(b *testing.B) {
|
||||||
Container: makeContainer(),
|
Container: makeContainer(),
|
||||||
Status: makeContainerStatus(),
|
Status: makeContainerStatus(),
|
||||||
MX: makeContainerMetrics(),
|
MX: makeContainerMetrics(),
|
||||||
IsInit: false,
|
|
||||||
Age: makeAge(),
|
Age: makeAge(),
|
||||||
}
|
}
|
||||||
var r model1.Row
|
var r model1.Row
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ func (Node) Header(string) model1.Header {
|
||||||
model1.HeaderColumn{Name: "ARCH", Wide: true},
|
model1.HeaderColumn{Name: "ARCH", Wide: true},
|
||||||
model1.HeaderColumn{Name: "TAINTS"},
|
model1.HeaderColumn{Name: "TAINTS"},
|
||||||
model1.HeaderColumn{Name: "VERSION"},
|
model1.HeaderColumn{Name: "VERSION"},
|
||||||
|
model1.HeaderColumn{Name: "OS-IMAGE", Wide: true},
|
||||||
model1.HeaderColumn{Name: "KERNEL", Wide: true},
|
model1.HeaderColumn{Name: "KERNEL", Wide: true},
|
||||||
model1.HeaderColumn{Name: "INTERNAL-IP", Wide: true},
|
model1.HeaderColumn{Name: "INTERNAL-IP", Wide: true},
|
||||||
model1.HeaderColumn{Name: "EXTERNAL-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,
|
no.Status.NodeInfo.Architecture,
|
||||||
strconv.Itoa(len(no.Spec.Taints)),
|
strconv.Itoa(len(no.Spec.Taints)),
|
||||||
no.Status.NodeInfo.KubeletVersion,
|
no.Status.NodeInfo.KubeletVersion,
|
||||||
|
no.Status.NodeInfo.OSImage,
|
||||||
no.Status.NodeInfo.KernelVersion,
|
no.Status.NodeInfo.KernelVersion,
|
||||||
iIP,
|
iIP,
|
||||||
eIP,
|
eIP,
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ func TestNodeRender(t *testing.T) {
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, "minikube", r.ID)
|
assert.Equal(t, "minikube", r.ID)
|
||||||
e := model1.Fields{"minikube", "Ready", "master", "amd64", "0", "v1.15.2", "4.15.0", "192.168.64.107", "<none>", "0", "10", "20", "0", "0", "4000", "7874"}
|
e := model1.Fields{"minikube", "Ready", "master", "amd64", "0", "v1.15.2", "Buildroot 2018.05.3", "4.15.0", "192.168.64.107", "<none>", "0", "10", "20", "0", "0", "4000", "7874"}
|
||||||
assert.Equal(t, e, r.Fields[:16])
|
assert.Equal(t, e, r.Fields[:17])
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkNodeRender(b *testing.B) {
|
func BenchmarkNodeRender(b *testing.B) {
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ func asReadinessGate(pod v1.Pod) string {
|
||||||
return MissingValue
|
return MissingValue
|
||||||
}
|
}
|
||||||
|
|
||||||
trueConditions := 0
|
var trueConditions int
|
||||||
for _, readinessGate := range pod.Spec.ReadinessGates {
|
for _, readinessGate := range pod.Spec.ReadinessGates {
|
||||||
conditionType := readinessGate.ConditionType
|
conditionType := readinessGate.ConditionType
|
||||||
for _, condition := range pod.Status.Conditions {
|
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) {
|
func gatherCoMX(spec *v1.PodSpec, ccmx []mv1beta1.ContainerMetrics) (c, r metric) {
|
||||||
cc := make([]v1.Container, 0, len(spec.InitContainers)+len(spec.Containers))
|
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...)
|
cc = append(cc, spec.Containers...)
|
||||||
|
|
||||||
rcpu, rmem := cosRequests(cc)
|
rcpu, rmem := cosRequests(cc)
|
||||||
|
|
@ -498,12 +498,13 @@ func restartableInitCO(p *v1.ContainerRestartPolicy) bool {
|
||||||
return p != nil && *p == v1.ContainerRestartPolicyAlways
|
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))
|
rcc := make([]v1.Container, 0, len(cc))
|
||||||
for _, c := range cc {
|
for _, c := range cc {
|
||||||
if c.RestartPolicy != nil && *c.RestartPolicy == v1.ContainerRestartPolicyAlways {
|
if c.RestartPolicy != nil && *c.RestartPolicy == v1.ContainerRestartPolicyAlways {
|
||||||
rcc = append(rcc, c)
|
rcc = append(rcc, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rcc
|
return rcc
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
always := v1.ContainerRestartPolicyAlways
|
||||||
|
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
cc []v1.Container
|
cc, ecc []v1.Container
|
||||||
ecc []v1.Container
|
|
||||||
}{
|
}{
|
||||||
"empty": {
|
"empty": {
|
||||||
cc: []v1.Container{},
|
cc: []v1.Container{},
|
||||||
|
|
@ -350,7 +349,7 @@ func Test_filterRestartableInitCO(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) {
|
||||||
assert.Equal(t, u.ecc, filterRestartableInitCO(u.cc))
|
assert.Equal(t, u.ecc, filterSidecarCO(u.cc))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,3 +66,7 @@ func (f fwd) Active() bool {
|
||||||
func (f fwd) Age() time.Time {
|
func (f fwd) Age() time.Time {
|
||||||
return testTime()
|
return testTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f fwd) Address() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,12 @@ type Forwarder interface {
|
||||||
// Container returns a container name.
|
// Container returns a container name.
|
||||||
Container() string
|
Container() string
|
||||||
|
|
||||||
// Ports returns container exposed ports.
|
// Port returns container exposed port.
|
||||||
Port() string
|
Port() string
|
||||||
|
|
||||||
|
// Address returns the host address.
|
||||||
|
Address() string
|
||||||
|
|
||||||
// Active returns forwarder current state.
|
// Active returns forwarder current state.
|
||||||
Active() bool
|
Active() bool
|
||||||
|
|
||||||
|
|
@ -77,7 +80,7 @@ func (f PortForward) Render(o interface{}, gvr string, r *model1.Row) error {
|
||||||
trimContainer(n),
|
trimContainer(n),
|
||||||
pf.Container(),
|
pf.Container(),
|
||||||
pf.Port(),
|
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.C)),
|
||||||
AsThousands(int64(pf.Config.N)),
|
AsThousands(int64(pf.Config.N)),
|
||||||
"",
|
"",
|
||||||
|
|
@ -100,9 +103,9 @@ func trimContainer(n string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UrlFor computes fq url for a given benchmark configuration.
|
// 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 == "" {
|
if host == "" {
|
||||||
host = "localhost"
|
host = address
|
||||||
}
|
}
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = "/"
|
path = "/"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright Authors of K9s
|
||||||
|
|
||||||
package dialog
|
package dialog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright Authors of K9s
|
||||||
|
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/model"
|
"github.com/derailed/k9s/internal/model"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ func NewContainer(gvr client.GVR) ResourceViewer {
|
||||||
c.SetEnvFn(c.k9sEnv)
|
c.SetEnvFn(c.k9sEnv)
|
||||||
c.GetTable().SetEnterFn(c.viewLogs)
|
c.GetTable().SetEnterFn(c.viewLogs)
|
||||||
c.GetTable().SetDecorateFn(c.decorateRows)
|
c.GetTable().SetDecorateFn(c.decorateRows)
|
||||||
|
c.GetTable().SetSortCol("IDX", true)
|
||||||
c.AddBindKeysFn(c.bindKeys)
|
c.AddBindKeysFn(c.bindKeys)
|
||||||
c.GetTable().SetDecorateFn(c.portForwardIndicator)
|
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.KeyF: ui.NewKeyAction("Show PortForward", c.showPFCmd, true),
|
||||||
ui.KeyShiftF: ui.NewKeyAction("PortForward", c.portFwdCmd, true),
|
ui.KeyShiftF: ui.NewKeyAction("PortForward", c.portFwdCmd, true),
|
||||||
ui.KeyShiftT: ui.NewKeyAction("Sort Restart", c.GetTable().SortColCmd("RESTARTS", false), false),
|
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()))
|
aa.Merge(resourceSorters(c.GetTable()))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,5 +16,5 @@ func TestContainerNew(t *testing.T) {
|
||||||
|
|
||||||
assert.Nil(t, c.Init(makeCtx()))
|
assert.Nil(t, c.Init(makeCtx()))
|
||||||
assert.Equal(t, "Containers", c.Name())
|
assert.Equal(t, "Containers", c.Name())
|
||||||
assert.Equal(t, 18, len(c.Hints()))
|
assert.Equal(t, 19, len(c.Hints()))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
return func(ctx context.Context) context.Context {
|
||||||
ctx = context.WithValue(ctx, internal.KeyPath, path)
|
ctx = context.WithValue(ctx, internal.KeyPath, path)
|
||||||
return context.WithValue(ctx, internal.KeyFields, fieldSel)
|
return context.WithValue(ctx, internal.KeyFields, fieldSel)
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,25 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright Authors of K9s
|
||||||
|
|
||||||
package view
|
package view
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/derailed/k9s/internal/ui/dialog"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
|
|
||||||
"github.com/derailed/tcell/v2"
|
|
||||||
"github.com/go-errors/errors"
|
|
||||||
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"
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/dao"
|
"github.com/derailed/k9s/internal/dao"
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OwnerExtender adds owner actions to a given viewer.
|
// OwnerExtender adds owner actions to a given viewer.
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,9 @@ func (r *Reference) gotoCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
path := r.GetTable().GetSelectedItem()
|
path := r.GetTable().GetSelectedItem()
|
||||||
|
ns, _ := client.Namespaced(path)
|
||||||
gvr := ui.TrimCell(r.GetTable().SelectTable, row, 2)
|
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
|
return evt
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"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"
|
||||||
"github.com/derailed/k9s/internal/model"
|
"github.com/derailed/k9s/internal/model"
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
|
|
@ -46,16 +47,16 @@ func (t *Table) Init(ctx context.Context) (err error) {
|
||||||
if t.app.Conn() != nil {
|
if t.app.Conn() != nil {
|
||||||
ctx = context.WithValue(ctx, internal.KeyHasMetrics, t.app.Conn().HasMetrics())
|
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.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 !t.app.Config.K9s.UI.Reactive {
|
||||||
if err := t.app.RefreshCustomViews(); err != nil {
|
if err := t.app.RefreshCustomViews(); err != nil {
|
||||||
log.Warn().Err(err).Msg("CustomViews load failed")
|
log.Warn().Err(err).Msg("CustomViews load failed")
|
||||||
t.app.Logo().Warn("Views 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.SetInputCapture(t.keyboard)
|
||||||
t.bindKeys()
|
t.bindKeys()
|
||||||
t.GetModel().SetRefreshRate(time.Duration(t.app.Config.K9s.GetRefreshRate()) * time.Second)
|
t.GetModel().SetRefreshRate(time.Duration(t.app.Config.K9s.GetRefreshRate()) * time.Second)
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ func parsePath(path string) (client.GVR, string, bool) {
|
||||||
func (w *Workload) showRes(app *App, _ ui.Tabular, _ client.GVR, path string) {
|
func (w *Workload) showRes(app *App, _ ui.Tabular, _ client.GVR, path string) {
|
||||||
gvr, fqn, ok := parsePath(path)
|
gvr, fqn, ok := parsePath(path)
|
||||||
if !ok {
|
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
|
return
|
||||||
}
|
}
|
||||||
app.gotoResource(gvr.R(), fqn, false)
|
app.gotoResource(gvr.R(), fqn, false)
|
||||||
|
|
@ -130,7 +130,7 @@ func (w *Workload) resourceDelete(selections []string, msg string) {
|
||||||
for _, sel := range selections {
|
for _, sel := range selections {
|
||||||
gvr, fqn, ok := parsePath(sel)
|
gvr, fqn, ok := parsePath(sel)
|
||||||
if !ok {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,7 +157,7 @@ func (w *Workload) describeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
gvr, fqn, ok := parsePath(path)
|
gvr, fqn, ok := parsePath(path)
|
||||||
if !ok {
|
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
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,7 +173,7 @@ func (w *Workload) editCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
gvr, fqn, ok := parsePath(path)
|
gvr, fqn, ok := parsePath(path)
|
||||||
if !ok {
|
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
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,7 +193,7 @@ func (w *Workload) yamlCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
gvr, fqn, ok := parsePath(path)
|
gvr, fqn, ok := parsePath(path)
|
||||||
if !ok {
|
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
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ type Forwarder interface {
|
||||||
// Port returns the port mapping.
|
// Port returns the port mapping.
|
||||||
Port() string
|
Port() string
|
||||||
|
|
||||||
|
// Address returns the host address.
|
||||||
|
Address() string
|
||||||
|
|
||||||
// FQN returns the full port-forward name.
|
// FQN returns the full port-forward name.
|
||||||
FQN() string
|
FQN() string
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -173,15 +173,16 @@ func newNoOpForwarder() noOpForwarder {
|
||||||
return 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
|
return nil, nil
|
||||||
}
|
}
|
||||||
func (m noOpForwarder) Stop() {}
|
func (noOpForwarder) Stop() {}
|
||||||
func (m noOpForwarder) ID() string { return "" }
|
func (noOpForwarder) ID() string { return "" }
|
||||||
func (m noOpForwarder) Container() string { return "" }
|
func (noOpForwarder) Container() string { return "" }
|
||||||
func (m noOpForwarder) Port() string { return "" }
|
func (noOpForwarder) Port() string { return "" }
|
||||||
func (m noOpForwarder) FQN() string { return "" }
|
func (noOpForwarder) FQN() string { return "" }
|
||||||
func (m noOpForwarder) Active() bool { return false }
|
func (noOpForwarder) Active() bool { return false }
|
||||||
func (m noOpForwarder) SetActive(bool) {}
|
func (noOpForwarder) SetActive(bool) {}
|
||||||
func (m noOpForwarder) Age() time.Time { return time.Now() }
|
func (noOpForwarder) Age() time.Time { return time.Now() }
|
||||||
func (m noOpForwarder) HasPortMapping(string) bool { return false }
|
func (noOpForwarder) HasPortMapping(string) bool { return false }
|
||||||
|
func (noOpForwarder) Address() string { return "" }
|
||||||
|
|
|
||||||
|
|
@ -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))
|
node := NewTreeNode("v1/pods", client.FQN(po.Namespace, po.Name))
|
||||||
parent, ok := ctx.Value(KeyParent).(*TreeNode)
|
parent, ok := ctx.Value(KeyParent).(*TreeNode)
|
||||||
if !ok {
|
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 {
|
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
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name: k9s
|
name: k9s
|
||||||
base: core22
|
base: core22
|
||||||
version: 'v0.32.5'
|
version: 'v0.32.6'
|
||||||
summary: K9s is a CLI to view and manage your Kubernetes clusters.
|
summary: K9s is a CLI to view and manage your Kubernetes clusters.
|
||||||
description: |
|
description: |
|
||||||
K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of your clusters in a single powerful session.
|
K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of your clusters in a single powerful session.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue