Rel v0.50.6 (#3338)
* update deps * add epslice support * update pulses * fix #3334 * rel notesmine
parent
b68b68e23f
commit
13cb55bb66
|
|
@ -24,4 +24,5 @@ demos
|
|||
kind
|
||||
*.snap
|
||||
/stresser
|
||||
__debug_bin*
|
||||
__debug_bin*
|
||||
fg.yaml
|
||||
|
|
@ -11,6 +11,8 @@ run:
|
|||
tests: true
|
||||
|
||||
linters:
|
||||
disable:
|
||||
- staticcheck
|
||||
enable:
|
||||
- sloglint
|
||||
- bodyclose
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -1,5 +1,5 @@
|
|||
NAME := k9s
|
||||
VERSION ?= v0.50.5
|
||||
VERSION ?= v0.50.6
|
||||
PACKAGE := github.com/derailed/$(NAME)
|
||||
OUTPUT_BIN ?= execs/${NAME}
|
||||
GO_FLAGS ?=
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||
|
||||
# Release v0.50.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/zt-3360a389v-ElLHrb0Dp1kAXqYUItSAFA)
|
||||
|
||||
## Maintenance Release!
|
||||
|
||||
---
|
||||
|
||||
## Resolved Issues
|
||||
|
||||
* [#3334](https://github.com/derailed/k9s/issues/3334) Watcher failed for events.k8s.io/v1/events -- expecting a meta table but got *unstructured.Unstructure
|
||||
|
||||
---
|
||||
|
||||
## 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!!
|
||||
|
||||
* [#3332](https://github.com/derailed/k9s/pull/3332) fix: pre-check for get permissions only on port-forward
|
||||
* [#3311](https://github.com/derailed/k9s/pull/3311) Fix concurrent read writes
|
||||
* [#3310](https://github.com/derailed/k9s/pull/3310) fix: use full path of date to avoid conflict
|
||||
|
||||
---
|
||||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2025 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
21
go.mod
21
go.mod
|
|
@ -30,15 +30,14 @@ require (
|
|||
golang.org/x/text v0.24.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
helm.sh/helm/v3 v3.17.3
|
||||
k8s.io/api v0.32.3
|
||||
k8s.io/apiextensions-apiserver v0.32.3
|
||||
k8s.io/api v0.33.0
|
||||
k8s.io/apiextensions-apiserver v0.33.0
|
||||
k8s.io/apimachinery v0.33.0
|
||||
k8s.io/cli-runtime v0.32.3
|
||||
k8s.io/client-go v0.32.3
|
||||
k8s.io/cli-runtime v0.33.0
|
||||
k8s.io/client-go v0.33.0
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
k8s.io/kubectl v0.32.3
|
||||
k8s.io/kubernetes v1.33.0
|
||||
k8s.io/metrics v0.32.3
|
||||
k8s.io/kubectl v0.33.0
|
||||
k8s.io/metrics v0.33.0
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
|
|
@ -171,7 +170,6 @@ require (
|
|||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/gohugoio/hashstructure v0.5.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
|
|
@ -330,7 +328,7 @@ require (
|
|||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/oauth2 v0.29.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/term v0.31.0 // indirect
|
||||
|
|
@ -348,8 +346,9 @@ require (
|
|||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gorm.io/gorm v1.25.12 // indirect
|
||||
k8s.io/apiserver v0.32.3 // indirect
|
||||
k8s.io/component-base v0.32.3 // indirect
|
||||
k8s.io/apiserver v0.33.0 // indirect
|
||||
k8s.io/component-base v0.33.0 // indirect
|
||||
k8s.io/component-helpers v0.33.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
modernc.org/libc v1.62.1 // indirect
|
||||
|
|
|
|||
42
go.sum
42
go.sum
|
|
@ -1148,8 +1148,6 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
|
|||
github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI=
|
||||
github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs=
|
||||
github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
|
|
@ -2006,8 +2004,8 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec
|
|||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
@ -2596,30 +2594,30 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
|
|||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
|
||||
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
|
||||
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
|
||||
k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY=
|
||||
k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss=
|
||||
k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
|
||||
k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM=
|
||||
k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs=
|
||||
k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc=
|
||||
k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
|
||||
k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/apiserver v0.32.3 h1:kOw2KBuHOA+wetX1MkmrxgBr648ksz653j26ESuWNY8=
|
||||
k8s.io/apiserver v0.32.3/go.mod h1:q1x9B8E/WzShF49wh3ADOh6muSfpmFL0I2t+TG0Zdgc=
|
||||
k8s.io/cli-runtime v0.32.3 h1:khLF2ivU2T6Q77H97atx3REY9tXiA3OLOjWJxUrdvss=
|
||||
k8s.io/cli-runtime v0.32.3/go.mod h1:vZT6dZq7mZAca53rwUfdFSZjdtLyfF61mkf/8q+Xjak=
|
||||
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
|
||||
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
|
||||
k8s.io/component-base v0.32.3 h1:98WJvvMs3QZ2LYHBzvltFSeJjEx7t5+8s71P7M74u8k=
|
||||
k8s.io/component-base v0.32.3/go.mod h1:LWi9cR+yPAv7cu2X9rZanTiFKB2kHA+JjmhkKjCZRpI=
|
||||
k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc=
|
||||
k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8=
|
||||
k8s.io/cli-runtime v0.33.0 h1:Lbl/pq/1o8BaIuyn+aVLdEPHVN665tBAXUePs8wjX7c=
|
||||
k8s.io/cli-runtime v0.33.0/go.mod h1:QcA+r43HeUM9jXFJx7A+yiTPfCooau/iCcP1wQh4NFw=
|
||||
k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98=
|
||||
k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg=
|
||||
k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk=
|
||||
k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU=
|
||||
k8s.io/component-helpers v0.33.0 h1:0AdW0A0mIgljLgtG0hJDdJl52PPqTrtMgOgtm/9i/Ys=
|
||||
k8s.io/component-helpers v0.33.0/go.mod h1:9SRiXfLldPw9lEEuSsapMtvT8j/h1JyFFapbtybwKvU=
|
||||
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-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/kubectl v0.32.3 h1:VMi584rbboso+yjfv0d8uBHwwxbC438LKq+dXd5tOAI=
|
||||
k8s.io/kubectl v0.32.3/go.mod h1:6Euv2aso5GKzo/UVMacV6C7miuyevpfI91SvBvV9Zdg=
|
||||
k8s.io/kubernetes v1.33.0 h1:BP5Y5yIzUZVeBuE/ESZvnw6TNxjXbLsCckIkljE+R0U=
|
||||
k8s.io/kubernetes v1.33.0/go.mod h1:2nWuPk0seE4+6sd0x60wQ6rYEXcV7SoeMbU0YbFm/5k=
|
||||
k8s.io/metrics v0.32.3 h1:2vsBvw0v8rIIlczZ/lZ8Kcqk9tR6Fks9h+dtFNbc2a4=
|
||||
k8s.io/metrics v0.32.3/go.mod h1:9R1Wk5cb+qJpCQon9h52mgkVCcFeYxcY+YkumfwHVCU=
|
||||
k8s.io/kubectl v0.33.0 h1:HiRb1yqibBSCqic4pRZP+viiOBAnIdwYDpzUFejs07g=
|
||||
k8s.io/kubectl v0.33.0/go.mod h1:gAlGBuS1Jq1fYZ9AjGWbI/5Vk3M/VW2DK4g10Fpyn/0=
|
||||
k8s.io/metrics v0.33.0 h1:sKe5sC9qb1RakMhs8LWYNuN2ne6OTCWexj8Jos3rO2Y=
|
||||
k8s.io/metrics v0.33.0/go.mod h1:XewckTFXmE2AJiP7PT3EXaY7hi7bler3t2ZLyOdQYzU=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ var (
|
|||
NodeGVR = NewGVR("v1/nodes")
|
||||
SvcGVR = NewGVR("v1/services")
|
||||
|
||||
// Discovery...
|
||||
EpsGVR = NewGVR("discovery.k8s.io/v1/endpointslices")
|
||||
|
||||
// Autoscaling...
|
||||
HpaGVR = NewGVR("autoscaling/v1/horizontalpodautoscalers")
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package model
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
|
|
@ -11,14 +10,12 @@ import (
|
|||
"github.com/derailed/k9s/internal/health"
|
||||
)
|
||||
|
||||
const defaultRefreshRate = 1 * time.Minute
|
||||
|
||||
// PulseListener represents a health model listener.
|
||||
type PulseListener interface {
|
||||
// PulseChanged notifies the model data changed.
|
||||
PulseChanged(*health.Check)
|
||||
|
||||
// TreeFailed notifies the health check failed.
|
||||
// PulseFailed notifies the health check failed.
|
||||
PulseFailed(error)
|
||||
|
||||
// MetricsChanged update metrics time series.
|
||||
|
|
@ -27,18 +24,16 @@ type PulseListener interface {
|
|||
|
||||
// Pulse tracks multiple resources health.
|
||||
type Pulse struct {
|
||||
gvr *client.GVR
|
||||
namespace string
|
||||
listeners []PulseListener
|
||||
refreshRate time.Duration
|
||||
health *PulseHealth
|
||||
gvr *client.GVR
|
||||
namespace string
|
||||
listeners []PulseListener
|
||||
health *PulseHealth
|
||||
}
|
||||
|
||||
// NewPulse returns a new pulse.
|
||||
func NewPulse(gvr *client.GVR) *Pulse {
|
||||
return &Pulse{
|
||||
gvr: gvr,
|
||||
refreshRate: defaultRefreshRate,
|
||||
gvr: gvr,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
const pulseRate = 15 * time.Second
|
||||
|
|
@ -96,6 +98,7 @@ func (h *PulseHealth) Watch(ctx context.Context, ns string) HealthChan {
|
|||
}
|
||||
|
||||
func (h *PulseHealth) checkPulse(ctx context.Context, ns string, c HealthChan) error {
|
||||
slog.Debug("Checking pulses...")
|
||||
for _, gvr := range PulseGVRs {
|
||||
check, err := h.check(ctx, ns, gvr)
|
||||
if err != nil {
|
||||
|
|
@ -110,8 +113,8 @@ func (h *PulseHealth) check(ctx context.Context, ns string, gvr *client.GVR) (He
|
|||
meta, ok := Registry[gvr]
|
||||
if !ok {
|
||||
meta = ResourceMeta{
|
||||
DAO: &dao.Table{},
|
||||
Renderer: &render.Generic{},
|
||||
DAO: new(dao.Table),
|
||||
Renderer: new(render.Table),
|
||||
}
|
||||
}
|
||||
if meta.DAO == nil {
|
||||
|
|
@ -124,11 +127,31 @@ func (h *PulseHealth) check(ctx context.Context, ns string, gvr *client.GVR) (He
|
|||
return HealthPoint{}, err
|
||||
}
|
||||
c := HealthPoint{GVR: gvr, Total: len(oo)}
|
||||
for _, o := range oo {
|
||||
if err := meta.Renderer.Healthy(ctx, o); err != nil {
|
||||
c.Faults++
|
||||
if isTable(oo) {
|
||||
ta := oo[0].(*metav1.Table)
|
||||
c.Total = len(ta.Rows)
|
||||
for _, row := range ta.Rows {
|
||||
if err := meta.Renderer.Healthy(ctx, row); err != nil {
|
||||
c.Faults++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, o := range oo {
|
||||
if err := meta.Renderer.Healthy(ctx, o); err != nil {
|
||||
c.Faults++
|
||||
}
|
||||
}
|
||||
}
|
||||
slog.Debug("Checked", slogs.GVR, gvr, slogs.Config, c)
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func isTable(oo []runtime.Object) bool {
|
||||
if len(oo) == 0 || len(oo) > 1 {
|
||||
return false
|
||||
}
|
||||
_, ok := oo[0].(*metav1.Table)
|
||||
|
||||
return ok
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,11 @@ var Registry = map[*client.GVR]ResourceMeta{
|
|||
Renderer: new(render.Alias),
|
||||
},
|
||||
|
||||
// Discovery...
|
||||
client.EpsGVR: {
|
||||
Renderer: new(render.EndpointSlice),
|
||||
},
|
||||
|
||||
// Core...
|
||||
client.EpGVR: {
|
||||
Renderer: new(render.Endpoints),
|
||||
|
|
@ -124,6 +129,7 @@ var Registry = map[*client.GVR]ResourceMeta{
|
|||
Renderer: new(render.PersistentVolumeClaim),
|
||||
},
|
||||
client.EvGVR: {
|
||||
DAO: new(dao.Table),
|
||||
Renderer: new(render.Event),
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ func (t *TableData) Render(_ context.Context, r Renderer, oo []runtime.Object) e
|
|||
t.Update(rows)
|
||||
t.SetHeader(t.namespace, r.Header(t.namespace))
|
||||
if t.HeaderCount() == 0 {
|
||||
return fmt.Errorf("fail to list resource %s", t.gvr)
|
||||
return fmt.Errorf("no data found for resource %s", t.gvr)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ func (aa PFAnns) ToTunnels(address string, _ ContainerPortSpecs, available PortC
|
|||
return pts, err
|
||||
}
|
||||
if !available(pt) {
|
||||
return pts, fmt.Errorf("Port %s is not available on host", pt.LocalPort)
|
||||
return pts, fmt.Errorf("port %s is not available on host", pt.LocalPort)
|
||||
}
|
||||
pts = append(pts, pt)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@ func TestEndpointsRender(t *testing.T) {
|
|||
r := model1.NewRow(4)
|
||||
|
||||
require.NoError(t, c.Render(load(t, "ep"), "", &r))
|
||||
assert.Equal(t, "default/dictionary1", r.ID)
|
||||
assert.Equal(t, model1.Fields{"default", "dictionary1", "<none>"}, r.Fields[:3])
|
||||
assert.Equal(t, "ns-1/blee", r.ID)
|
||||
assert.Equal(t, model1.Fields{"ns-1", "blee", "10.0.0.67:8080"}, r.Fields[:3])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Authors of K9s
|
||||
|
||||
package render
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/model1"
|
||||
discoveryv1 "k8s.io/api/discovery/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
var defaultEPsHeader = model1.Header{
|
||||
model1.HeaderColumn{Name: "NAMESPACE"},
|
||||
model1.HeaderColumn{Name: "NAME"},
|
||||
model1.HeaderColumn{Name: "ADDRESSTYPE"},
|
||||
model1.HeaderColumn{Name: "PORTS"},
|
||||
model1.HeaderColumn{Name: "ENDPOINTS"},
|
||||
model1.HeaderColumn{Name: "AGE", Attrs: model1.Attrs{Time: true}},
|
||||
}
|
||||
|
||||
// EndpointSlice renders a K8s EndpointSlice to screen.
|
||||
type EndpointSlice struct {
|
||||
Base
|
||||
}
|
||||
|
||||
// Header returns a header row.
|
||||
func (e EndpointSlice) Header(_ string) model1.Header {
|
||||
return e.doHeader(defaultEPsHeader)
|
||||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (e EndpointSlice) Render(o any, ns string, row *model1.Row) error {
|
||||
if err := e.defaultRow(o, ns, row); err != nil {
|
||||
return err
|
||||
}
|
||||
if e.specs.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
cols, err := e.specs.realize(o.(*unstructured.Unstructured), defaultEPsHeader, row)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cols.hydrateRow(row)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e EndpointSlice) defaultRow(o any, ns string, r *model1.Row) error {
|
||||
raw, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected Unstructured, but got %T", o)
|
||||
}
|
||||
var eps discoveryv1.EndpointSlice
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &eps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.ID = client.MetaFQN(&eps.ObjectMeta)
|
||||
r.Fields = make(model1.Fields, 0, len(e.Header(ns)))
|
||||
r.Fields = model1.Fields{
|
||||
eps.Namespace,
|
||||
eps.Name,
|
||||
string(eps.AddressType),
|
||||
toPorts(eps.Ports),
|
||||
toEPss(eps.Endpoints),
|
||||
ToAge(eps.GetCreationTimestamp()),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func toEPss(ee []discoveryv1.Endpoint) string {
|
||||
if len(ee) == 0 {
|
||||
return UnsetValue
|
||||
}
|
||||
|
||||
aa := make([]string, 0, len(ee))
|
||||
for _, e := range ee {
|
||||
aa = append(aa, e.Addresses...)
|
||||
}
|
||||
|
||||
return strings.Join(aa, ",")
|
||||
}
|
||||
|
||||
func toPorts(ee []discoveryv1.EndpointPort) string {
|
||||
if len(ee) == 0 {
|
||||
return UnsetValue
|
||||
}
|
||||
|
||||
aa := make([]string, 0, len(ee))
|
||||
for _, e := range ee {
|
||||
if e.Port != nil {
|
||||
aa = append(aa, strconv.Itoa(int(*e.Port)))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(aa, ",")
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Authors of K9s
|
||||
|
||||
package render_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/model1"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEndpointSliceRender(t *testing.T) {
|
||||
c := render.EndpointSlice{}
|
||||
r := model1.NewRow(4)
|
||||
|
||||
require.NoError(t, c.Render(load(t, "eps"), "", &r))
|
||||
assert.Equal(t, "blee/fred", r.ID)
|
||||
assert.Equal(t, model1.Fields{"blee", "fred", "IPv4", "4244", "172.20.0.2,172.20.0.3"}, r.Fields[:5])
|
||||
}
|
||||
|
|
@ -8,9 +8,7 @@ import (
|
|||
"log/slog"
|
||||
|
||||
"github.com/derailed/k9s/internal/slogs"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Event renders a event resource to screen.
|
||||
|
|
@ -20,19 +18,14 @@ type Event struct {
|
|||
|
||||
// Healthy checks component health.
|
||||
func (*Event) Healthy(_ context.Context, o any) error {
|
||||
u, ok := o.(*unstructured.Unstructured)
|
||||
r, ok := o.(metav1.TableRow)
|
||||
if !ok {
|
||||
slog.Error("Expected Unstructured", slogs.Type, fmt.Sprintf("%T", o))
|
||||
slog.Error("Expected TableRow", slogs.Type, fmt.Sprintf("%T", o))
|
||||
return nil
|
||||
}
|
||||
var ev api.Event
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &ev)
|
||||
if err != nil {
|
||||
slog.Error("Failed to convert unstructured to Node", slogs.Error, err)
|
||||
return nil
|
||||
}
|
||||
if ev.Type != "Normal" {
|
||||
return fmt.Errorf("event is not normal: %s", ev.Type)
|
||||
idx := 2
|
||||
if idx < len(r.Cells) && r.Cells[idx] != "Normal" {
|
||||
return fmt.Errorf("event is not normal: %s", r.Cells[idx])
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -3,10 +3,29 @@
|
|||
"kind": "Endpoints",
|
||||
"metadata": {
|
||||
"creationTimestamp": "2019-07-10T23:10:43Z",
|
||||
"name": "dictionary1",
|
||||
"namespace": "default",
|
||||
"resourceVersion": "36684456",
|
||||
"selfLink": "/api/v1/namespaces/default/endpoints/dictionary1",
|
||||
"uid": "f119c74f-a367-11e9-990f-42010a800218"
|
||||
}
|
||||
"name": "blee",
|
||||
"namespace": "ns-1"
|
||||
},
|
||||
"subsets": [
|
||||
{
|
||||
"addresses": [
|
||||
{
|
||||
"ip": "10.0.0.67",
|
||||
"nodeName": "n-1",
|
||||
"targetRef": {
|
||||
"kind": "Pod",
|
||||
"name": "blah",
|
||||
"namespace": "blee"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 8080,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"apiVersion": "discovery.k8s.io/v1",
|
||||
"kind": "EndpointSlice",
|
||||
"metadata": {
|
||||
"creationTimestamp": "2025-04-17T22:14:13Z",
|
||||
"name": "fred",
|
||||
"namespace": "blee"
|
||||
},
|
||||
"addressType": "IPv4",
|
||||
"endpoints": [
|
||||
{
|
||||
"addresses": [
|
||||
"172.20.0.2"
|
||||
],
|
||||
"conditions": {
|
||||
"ready": true,
|
||||
"serving": true,
|
||||
"terminating": false
|
||||
},
|
||||
"nodeName": "n-1",
|
||||
"targetRef": {
|
||||
"kind": "Pod",
|
||||
"name": "zorg",
|
||||
"namespace": "kube-system"
|
||||
}
|
||||
},
|
||||
{
|
||||
"addresses": [
|
||||
"172.20.0.3"
|
||||
],
|
||||
"conditions": {
|
||||
"ready": true,
|
||||
"serving": true,
|
||||
"terminating": false
|
||||
},
|
||||
"nodeName": "n-1",
|
||||
"targetRef": {
|
||||
"kind": "Pod",
|
||||
"name": "zorg",
|
||||
"namespace": "kube-system"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
{
|
||||
"name": "peer-service",
|
||||
"port": 4244,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ const (
|
|||
UnknownValue = "<unknown>"
|
||||
|
||||
// UnsetValue represent an unset value.
|
||||
UnsetValue = ""
|
||||
UnsetValue = "<unset>"
|
||||
|
||||
// ZeroValue represents a zero value.
|
||||
ZeroValue = "0"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
name: k9s
|
||||
base: core22
|
||||
version: 'v0.50.5'
|
||||
version: 'v0.50.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