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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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: |
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
language: go
|
||||
go_import_path: github.com/derailed/k9s
|
||||
go:
|
||||
- "1.15"
|
||||
- "1.23"
|
||||
|
||||
jobs:
|
||||
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
|
||||
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}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
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
|
||||
|
|
|
|||
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/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=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
allNS := []string{"ns9", "ns8", "ns7", "ns6", "ns5", "ns4", "ns3", "ns2", "ns1", "all", "default"}
|
||||
ns := data.NewNamespace()
|
||||
ns.Favorites = allNS
|
||||
|
||||
ns.Favorites = allNS
|
||||
|
||||
ns.Validate(mock.NewMockConnection())
|
||||
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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 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{})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 _, co := range po.Spec.Containers {
|
||||
res = append(res, makeContainerRes(co, po, cmx[co.Name], false))
|
||||
for i, co := range po.Spec.Containers {
|
||||
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
|
||||
|
|
@ -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]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ type HeaderColumn struct {
|
|||
Name string
|
||||
Align int
|
||||
Decorator DecoratorFunc
|
||||
Hide bool
|
||||
Wide bool
|
||||
MX bool
|
||||
Time bool
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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", "<none>", "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", "<none>", "0", "10", "20", "0", "0", "4000", "7874"}
|
||||
assert.Equal(t, e, r.Fields[:17])
|
||||
}
|
||||
|
||||
func BenchmarkNodeRender(b *testing.B) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,3 +66,7 @@ func (f fwd) Active() bool {
|
|||
func (f fwd) Age() time.Time {
|
||||
return testTime()
|
||||
}
|
||||
|
||||
func (f fwd) Address() string {
|
||||
return ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = "/"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Authors of K9s
|
||||
|
||||
package dialog
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Authors of K9s
|
||||
|
||||
package ui
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ package ui
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
|
|
|||
|
|
@ -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()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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/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/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"
|
||||
)
|
||||
|
||||
// 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()
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 "" }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue