parent
69dc096717
commit
bb5ba1306d
|
|
@ -13,7 +13,7 @@ RUN apk --no-cache add make git gcc libc-dev curl && make build
|
||||||
# Build the final Docker image
|
# Build the final Docker image
|
||||||
|
|
||||||
FROM alpine:3.14.3
|
FROM alpine:3.14.3
|
||||||
ARG KUBECTL_VERSION="v1.21.2"
|
ARG KUBECTL_VERSION="v1.24.2"
|
||||||
|
|
||||||
COPY --from=build /k9s/execs/k9s /bin/k9s
|
COPY --from=build /k9s/execs/k9s /bin/k9s
|
||||||
RUN apk add --update ca-certificates \
|
RUN apk add --update ca-certificates \
|
||||||
|
|
|
||||||
2
Makefile
2
Makefile
|
|
@ -5,7 +5,7 @@ PACKAGE := github.com/derailed/$(NAME)
|
||||||
GIT_REV ?= $(shell git rev-parse --short HEAD)
|
GIT_REV ?= $(shell git rev-parse --short HEAD)
|
||||||
SOURCE_DATE_EPOCH ?= $(shell date +%s)
|
SOURCE_DATE_EPOCH ?= $(shell date +%s)
|
||||||
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")
|
||||||
VERSION ?= v0.25.18
|
VERSION ?= v0.25.19
|
||||||
IMG_NAME := derailed/k9s
|
IMG_NAME := derailed/k9s
|
||||||
IMAGE := ${IMG_NAME}:${VERSION}
|
IMAGE := ${IMG_NAME}:${VERSION}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||||
|
|
||||||
|
# Release v0.25.19
|
||||||
|
|
||||||
|
## 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 this repo!!
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resolved Issues
|
||||||
|
|
||||||
|
* [Issue #1609](https://github.com/derailed/k9s/issues/1609) K9s fails to launch when active view does not exist
|
||||||
|
* [Issue #1593](https://github.com/derailed/k9s/issues/1593) Selection of namespace is changed automatically
|
||||||
|
* [Issue #1572](https://github.com/derailed/k9s/issues/1572) Wrong resource configuration being display after updating ingress
|
||||||
|
* [Issue #1569](https://github.com/derailed/k9s/issues/1569) Slight wording error when port forward already existS!
|
||||||
|
* [Issue #1565](https://github.com/derailed/k9s/issues/1565) Popeye stopped working
|
||||||
|
|
||||||
|
## Resolved PR
|
||||||
|
|
||||||
|
* [PR #1601](https://github.com/derailed/k9s/pull/1601) Ensure correct text in prompt when suspending cronjob
|
||||||
|
* [PR #1600](https://github.com/derailed/k9s/pull/1600) Fix typo in fastforwards annotation name
|
||||||
|
* [PR #1566](https://github.com/derailed/k9s/pull/1566) Correct typo in skins
|
||||||
|
* [PR #1555](https://github.com/derailed/k9s/pull/1555) Update benchmark command in readme
|
||||||
|
* [PR #1553](https://github.com/derailed/k9s/pull/1553) Allow `all` deletion propagation policy
|
||||||
|
* [PR #1539](https://github.com/derailed/k9s/pull/1539) Plugin to allow default chart values retrieval
|
||||||
|
* [PR #1529](https://github.com/derailed/k9s/pull/1529) Update example k9s config file
|
||||||
|
* [PR #1518](https://github.com/derailed/k9s/pull/1519) Add Helm values support
|
||||||
|
* [PR #1493](https://github.com/derailed/k9s/pull/1493) Fix padding is not 0 in fullscreen
|
||||||
|
* [PR #1422](https://github.com/derailed/k9s/pull/1422) Fix typo in README
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2021 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
142
go.mod
142
go.mod
|
|
@ -1,6 +1,6 @@
|
||||||
module github.com/derailed/k9s
|
module github.com/derailed/k9s
|
||||||
|
|
||||||
go 1.17
|
go 1.18
|
||||||
|
|
||||||
replace (
|
replace (
|
||||||
github.com/docker/distribution => github.com/docker/distribution v0.0.0-20191216044856-a8371794149d
|
github.com/docker/distribution => github.com/docker/distribution v0.0.0-20191216044856-a8371794149d
|
||||||
|
|
@ -13,86 +13,90 @@ require (
|
||||||
github.com/atotto/clipboard v0.1.4
|
github.com/atotto/clipboard v0.1.4
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||||
github.com/cenkalti/backoff/v4 v4.1.2
|
github.com/cenkalti/backoff/v4 v4.1.2
|
||||||
github.com/derailed/popeye v0.9.8
|
github.com/derailed/popeye v0.10.0
|
||||||
github.com/derailed/tview v0.6.6
|
github.com/derailed/tview v0.6.6
|
||||||
github.com/fatih/color v1.13.0
|
github.com/fatih/color v1.13.0
|
||||||
github.com/fsnotify/fsnotify v1.5.1
|
github.com/fsnotify/fsnotify v1.5.1
|
||||||
github.com/fvbommel/sortorder v1.0.2
|
github.com/fvbommel/sortorder v1.0.2
|
||||||
github.com/gdamore/tcell/v2 v2.4.0
|
github.com/gdamore/tcell/v2 v2.4.0
|
||||||
github.com/ghodss/yaml v1.0.0
|
github.com/ghodss/yaml v1.0.0
|
||||||
|
github.com/mattn/go-colorable v0.1.12
|
||||||
github.com/mattn/go-runewidth v0.0.13
|
github.com/mattn/go-runewidth v0.0.13
|
||||||
github.com/petergtz/pegomock v2.9.0+incompatible
|
github.com/petergtz/pegomock v2.9.0+incompatible
|
||||||
github.com/rakyll/hey v0.1.4
|
github.com/rakyll/hey v0.1.4
|
||||||
github.com/rs/zerolog v1.26.0
|
github.com/rs/zerolog v1.26.0
|
||||||
github.com/sahilm/fuzzy v0.1.0
|
github.com/sahilm/fuzzy v0.1.0
|
||||||
github.com/spf13/cobra v1.2.1
|
github.com/spf13/cobra v1.4.0
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.1
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
helm.sh/helm/v3 v3.7.1
|
helm.sh/helm/v3 v3.9.0
|
||||||
k8s.io/api v0.22.3
|
k8s.io/api v0.24.2
|
||||||
k8s.io/apimachinery v0.22.3
|
k8s.io/apimachinery v0.24.2
|
||||||
k8s.io/cli-runtime v0.22.3
|
k8s.io/cli-runtime v0.24.2
|
||||||
k8s.io/client-go v0.22.3
|
k8s.io/client-go v0.24.2
|
||||||
k8s.io/klog/v2 v2.30.0
|
k8s.io/klog/v2 v2.60.1
|
||||||
k8s.io/kubectl v0.22.3
|
k8s.io/kubectl v0.24.2
|
||||||
k8s.io/metrics v0.22.3
|
k8s.io/metrics v0.24.2
|
||||||
sigs.k8s.io/yaml v1.3.0
|
sigs.k8s.io/yaml v1.3.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.81.0 // indirect
|
cloud.google.com/go v0.99.0 // indirect
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||||
github.com/Azure/go-autorest/autorest v0.11.18 // indirect
|
github.com/Azure/go-autorest/autorest v0.11.20 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
|
github.com/Azure/go-autorest/autorest/adal v0.9.15 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
github.com/BurntSushi/toml v1.0.0 // indirect
|
||||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect
|
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect
|
||||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||||
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
|
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
|
||||||
github.com/Masterminds/squirrel v1.5.0 // indirect
|
github.com/Masterminds/squirrel v1.5.2 // indirect
|
||||||
github.com/Microsoft/go-winio v0.4.17 // indirect
|
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||||
github.com/Microsoft/hcsshim v0.8.21 // indirect
|
github.com/Microsoft/hcsshim v0.9.2 // indirect
|
||||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
|
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.35.21 // indirect
|
github.com/aws/aws-sdk-go v1.35.21 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
github.com/containerd/containerd v1.5.7 // indirect
|
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
||||||
github.com/containerd/continuity v0.1.0 // indirect
|
github.com/containerd/containerd v1.6.3 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.2.2 // indirect
|
github.com/containerd/continuity v0.2.2 // indirect
|
||||||
|
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/docker/cli v20.10.7+incompatible // indirect
|
github.com/docker/cli v20.10.11+incompatible // indirect
|
||||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible // indirect
|
github.com/docker/docker v20.10.14+incompatible // indirect
|
||||||
github.com/docker/docker-credential-helpers v0.6.3 // indirect
|
github.com/docker/docker-credential-helpers v0.6.4 // indirect
|
||||||
github.com/docker/go-connections v0.4.0 // indirect
|
github.com/docker/go-connections v0.4.0 // indirect
|
||||||
github.com/docker/go-metrics v0.0.1 // indirect
|
github.com/docker/go-metrics v0.0.1 // indirect
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
github.com/evanphx/json-patch v4.11.0+incompatible // indirect
|
github.com/emicklei/go-restful v2.9.5+incompatible // indirect
|
||||||
|
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
||||||
github.com/fatih/camelcase v1.0.0 // indirect
|
github.com/fatih/camelcase v1.0.0 // indirect
|
||||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
|
|
||||||
github.com/gdamore/encoding v1.0.0 // indirect
|
github.com/gdamore/encoding v1.0.0 // indirect
|
||||||
github.com/go-errors/errors v1.0.1 // indirect
|
github.com/go-errors/errors v1.0.1 // indirect
|
||||||
github.com/go-logr/logr v1.2.0 // indirect
|
github.com/go-gorp/gorp/v3 v3.0.2 // indirect
|
||||||
|
github.com/go-logr/logr v1.2.2 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.19.5 // indirect
|
github.com/go-openapi/jsonreference v0.19.5 // indirect
|
||||||
github.com/go-openapi/swag v0.19.14 // indirect
|
github.com/go-openapi/swag v0.19.14 // indirect
|
||||||
github.com/gobwas/glob v0.2.3 // indirect
|
github.com/gobwas/glob v0.2.3 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.0.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/btree v1.0.1 // indirect
|
github.com/google/btree v1.0.1 // indirect
|
||||||
github.com/google/go-cmp v0.5.5 // indirect
|
github.com/google/gnostic v0.5.7-v3refs // indirect
|
||||||
github.com/google/gofuzz v1.1.0 // indirect
|
github.com/google/go-cmp v0.5.6 // indirect
|
||||||
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
github.com/google/uuid v1.2.0 // indirect
|
github.com/google/uuid v1.2.0 // indirect
|
||||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
github.com/gosuri/uitable v0.0.4 // indirect
|
github.com/gosuri/uitable v0.0.4 // indirect
|
||||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
|
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
|
||||||
|
|
@ -100,74 +104,74 @@ require (
|
||||||
github.com/imdario/mergo v0.3.12 // indirect
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/jmoiron/sqlx v1.3.1 // indirect
|
github.com/jmoiron/sqlx v1.3.4 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.11 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.11.13 // indirect
|
github.com/klauspost/compress v1.13.6 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||||
github.com/lib/pq v1.10.0 // indirect
|
github.com/lib/pq v1.10.4 // indirect
|
||||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/mailru/easyjson v0.7.6 // indirect
|
github.com/mailru/easyjson v0.7.6 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||||
github.com/mitchellh/copystructure v1.1.1 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.1 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/moby/locker v1.0.1 // indirect
|
github.com/moby/locker v1.0.1 // indirect
|
||||||
github.com/moby/spdystream v0.2.0 // indirect
|
github.com/moby/spdystream v0.2.0 // indirect
|
||||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
|
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/onsi/gomega v1.10.3 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/onsi/gomega v1.15.0 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||||
github.com/opencontainers/runc v1.0.2 // indirect
|
github.com/opencontainers/runc v1.1.1 // indirect
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_golang v1.11.0 // indirect
|
github.com/prometheus/client_golang v1.12.1 // indirect
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
github.com/prometheus/common v0.26.0 // indirect
|
github.com/prometheus/common v0.32.1 // indirect
|
||||||
github.com/prometheus/procfs v0.6.0 // indirect
|
github.com/prometheus/procfs v0.7.3 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc // indirect
|
github.com/rubenv/sql-migrate v1.1.1 // indirect
|
||||||
github.com/russross/blackfriday v1.5.2 // indirect
|
github.com/russross/blackfriday v1.5.2 // indirect
|
||||||
github.com/shopspring/decimal v1.2.0 // indirect
|
github.com/shopspring/decimal v1.2.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||||
github.com/spf13/cast v1.3.1 // indirect
|
github.com/spf13/cast v1.4.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
|
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
|
||||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
|
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 // indirect
|
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
|
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
|
||||||
golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72 // indirect
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 // indirect
|
||||||
google.golang.org/grpc v1.38.0 // indirect
|
google.golang.org/grpc v1.43.0 // indirect
|
||||||
google.golang.org/protobuf v1.26.0 // indirect
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
gopkg.in/gorp.v1 v1.7.2 // indirect
|
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
k8s.io/apiextensions-apiserver v0.22.1 // indirect
|
k8s.io/apiextensions-apiserver v0.24.0 // indirect
|
||||||
k8s.io/apiserver v0.22.1 // indirect
|
k8s.io/apiserver v0.24.0 // indirect
|
||||||
k8s.io/component-base v0.22.3 // indirect
|
k8s.io/component-base v0.24.2 // indirect
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect
|
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
|
||||||
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect
|
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
|
||||||
oras.land/oras-go v0.4.0 // indirect
|
oras.land/oras-go v1.1.1 // indirect
|
||||||
sigs.k8s.io/kustomize/api v0.8.11 // indirect
|
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0 // indirect
|
sigs.k8s.io/kustomize/api v0.11.4 // indirect
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
|
sigs.k8s.io/kustomize/kyaml v0.13.6 // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,6 @@ func (k *K9s) ActiveCluster() *Cluster {
|
||||||
|
|
||||||
func (k *K9s) GetScreenDumpDir() string {
|
func (k *K9s) GetScreenDumpDir() string {
|
||||||
screenDumpDir := k.ScreenDumpDir
|
screenDumpDir := k.ScreenDumpDir
|
||||||
|
|
||||||
if k.manualScreenDumpDir != nil && *k.manualScreenDumpDir != "" {
|
if k.manualScreenDumpDir != nil && *k.manualScreenDumpDir != "" {
|
||||||
screenDumpDir = *k.manualScreenDumpDir
|
screenDumpDir = *k.manualScreenDumpDir
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,8 @@ package dao
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
@ -28,21 +26,17 @@ func (h *HorizontalPodAutoscaler) List(ctx context.Context, ns string) ([]runtim
|
||||||
lsel = sel.AsSelector()
|
lsel = sel.AsSelector()
|
||||||
}
|
}
|
||||||
|
|
||||||
gvrs := []string{
|
rev, err := h.Factory.Client().ServerVersion()
|
||||||
"autoscaling/v2beta2/horizontalpodautoscalers",
|
if err != nil {
|
||||||
"autoscaling/v2beta1/horizontalpodautoscalers",
|
return nil, err
|
||||||
"autoscaling/v1/horizontalpodautoscalers",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, gvr := range gvrs {
|
gvr := "autoscaling/v1/horizontalpodautoscalers"
|
||||||
oo, err := h.list(gvr, ns, lsel)
|
if rev.Minor >= "23" {
|
||||||
if err == nil && len(oo) > 0 {
|
gvr = "autoscaling/v2/horizontalpodautoscalers"
|
||||||
return oo, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
log.Error().Err(fmt.Errorf("No results for any known HPA versions"))
|
|
||||||
|
|
||||||
return []runtime.Object{}, nil
|
return h.list(gvr, ns, lsel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HorizontalPodAutoscaler) list(gvr, ns string, sel labels.Selector) ([]runtime.Object, error) {
|
func (h *HorizontalPodAutoscaler) list(gvr, ns string, sel labels.Selector) ([]runtime.Object, error) {
|
||||||
|
|
|
||||||
|
|
@ -109,14 +109,19 @@ func (n *Node) Drain(path string, opts DrainOptions, w io.Writer) error {
|
||||||
|
|
||||||
// Get returns a node resource.
|
// Get returns a node resource.
|
||||||
func (n *Node) Get(ctx context.Context, path string) (runtime.Object, error) {
|
func (n *Node) Get(ctx context.Context, path string) (runtime.Object, error) {
|
||||||
o, err := n.Resource.Get(ctx, path)
|
oo, err := n.Resource.List(ctx, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return o, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
u, ok := o.(*unstructured.Unstructured)
|
var raw *unstructured.Unstructured
|
||||||
if !ok {
|
for _, o := range oo {
|
||||||
return nil, fmt.Errorf("expecting *unstructured.Unstructured but got `%T", o)
|
if u, ok := o.(*unstructured.Unstructured); ok && u.GetName() == path {
|
||||||
|
raw = u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if raw == nil {
|
||||||
|
return nil, fmt.Errorf("unable to locate node %s", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
var nmx *mv1beta1.NodeMetrics
|
var nmx *mv1beta1.NodeMetrics
|
||||||
|
|
@ -124,7 +129,7 @@ func (n *Node) Get(ctx context.Context, path string) (runtime.Object, error) {
|
||||||
nmx, _ = client.DialMetrics(n.Client()).FetchNodeMetrics(ctx, path)
|
nmx, _ = client.DialMetrics(n.Client()).FetchNodeMetrics(ctx, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &render.NodeWithMetrics{Raw: u, MX: nmx}, nil
|
return &render.NodeWithMetrics{Raw: raw, MX: nmx}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// List returns a collection of node resources.
|
// List returns a collection of node resources.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package dao
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ Accessor = (*Pod)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Namespace represents a namespace resource.
|
||||||
|
type Namespace struct {
|
||||||
|
Generic
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns a collection of nodes.
|
||||||
|
func (n *Namespace) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||||
|
oo, err := n.Generic.List(ctx, ns)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return oo, nil
|
||||||
|
}
|
||||||
|
|
@ -92,6 +92,7 @@ func AccessorFor(f Factory, gvr client.GVR) (Accessor, error) {
|
||||||
client.NewGVR("batch/v1/cronjobs"): &CronJob{},
|
client.NewGVR("batch/v1/cronjobs"): &CronJob{},
|
||||||
client.NewGVR("batch/v1beta1/cronjobs"): &CronJob{},
|
client.NewGVR("batch/v1beta1/cronjobs"): &CronJob{},
|
||||||
client.NewGVR("batch/v1/jobs"): &Job{},
|
client.NewGVR("batch/v1/jobs"): &Job{},
|
||||||
|
client.NewGVR("v1/namespaces"): &Namespace{},
|
||||||
// BOZO!! Revamp with latest...
|
// BOZO!! Revamp with latest...
|
||||||
// client.NewGVR("openfaas"): &OpenFaas{},
|
// client.NewGVR("openfaas"): &OpenFaas{},
|
||||||
client.NewGVR("popeye"): &Popeye{},
|
client.NewGVR("popeye"): &Popeye{},
|
||||||
|
|
|
||||||
|
|
@ -72,10 +72,10 @@ func (g *Generic) Render(o interface{}, ns string, r *Row) error {
|
||||||
r.ID = client.FQN(nns, name)
|
r.ID = client.FQN(nns, name)
|
||||||
r.Fields = make(Fields, 0, len(g.Header(ns)))
|
r.Fields = make(Fields, 0, len(g.Header(ns)))
|
||||||
r.Fields = append(r.Fields, nns)
|
r.Fields = append(r.Fields, nns)
|
||||||
var ageCell interface{}
|
var duration interface{}
|
||||||
for i, c := range row.Cells {
|
for i, c := range row.Cells {
|
||||||
if g.ageIndex > 0 && i == g.ageIndex {
|
if g.ageIndex > 0 && i == g.ageIndex {
|
||||||
ageCell = c
|
duration = c
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if c == nil {
|
if c == nil {
|
||||||
|
|
@ -84,8 +84,8 @@ func (g *Generic) Render(o interface{}, ns string, r *Row) error {
|
||||||
}
|
}
|
||||||
r.Fields = append(r.Fields, fmt.Sprintf("%v", c))
|
r.Fields = append(r.Fields, fmt.Sprintf("%v", c))
|
||||||
}
|
}
|
||||||
if ageCell != nil {
|
if d, ok := duration.(string); ok {
|
||||||
r.Fields = append(r.Fields, fmt.Sprintf("%v", ageCell))
|
r.Fields = append(r.Fields, d)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ func TestGenericRender(t *testing.T) {
|
||||||
ns: client.ClusterScope,
|
ns: client.ClusterScope,
|
||||||
table: makeAgeGeneric(),
|
table: makeAgeGeneric(),
|
||||||
eID: "-/fred",
|
eID: "-/fred",
|
||||||
eFields: render.Fields{"-", "c1", "c2", "Age"},
|
eFields: render.Fields{"-", "c1", "c2", "2d"},
|
||||||
eHeader: render.Header{
|
eHeader: render.Header{
|
||||||
render.HeaderColumn{Name: "NAMESPACE"},
|
render.HeaderColumn{Name: "NAMESPACE"},
|
||||||
render.HeaderColumn{Name: "A"},
|
render.HeaderColumn{Name: "A"},
|
||||||
|
|
@ -172,7 +172,7 @@ func makeAgeGeneric() *metav1beta1.Table {
|
||||||
},
|
},
|
||||||
Cells: []interface{}{
|
Cells: []interface{}{
|
||||||
"c1",
|
"c1",
|
||||||
"Age",
|
"2d",
|
||||||
"c2",
|
"c2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -16,25 +16,25 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/util/duration"
|
"k8s.io/apimachinery/pkg/util/duration"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runesToNum(rr []rune) int {
|
func runesToNum(rr []rune) int64 {
|
||||||
var n int
|
var r int64
|
||||||
m := 1
|
var m int64 = 1
|
||||||
for i := len(rr) - 1; i >= 0; i-- {
|
for i := len(rr) - 1; i >= 0; i-- {
|
||||||
v := int(rr[i] - '0')
|
v := int64(rr[i] - '0')
|
||||||
n += v * m
|
r += v * m
|
||||||
m *= 10
|
m *= 10
|
||||||
}
|
}
|
||||||
|
|
||||||
return n
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func durationToSeconds(duration string) string {
|
func durationToSeconds(duration string) int64 {
|
||||||
if len(duration) == 0 {
|
if len(duration) == 0 {
|
||||||
return duration
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
num := make([]rune, 0, 5)
|
num := make([]rune, 0, 5)
|
||||||
var n, m int
|
var n, m int64
|
||||||
for _, r := range duration {
|
for _, r := range duration {
|
||||||
switch r {
|
switch r {
|
||||||
case 'y':
|
case 'y':
|
||||||
|
|
@ -54,7 +54,7 @@ func durationToSeconds(duration string) string {
|
||||||
n, num = n+runesToNum(num)*m, num[:0]
|
n, num = n+runesToNum(num)*m, num[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return strconv.Itoa(n)
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsThousands prints a number with thousand separator.
|
// AsThousands prints a number with thousand separator.
|
||||||
|
|
@ -194,17 +194,25 @@ func boolToStr(b bool) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toAge(timestamp metav1.Time) string {
|
func toAge(t metav1.Time) string {
|
||||||
return time.Since(timestamp.Time).String()
|
if t.IsZero() {
|
||||||
|
return UnknownValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return duration.HumanDuration(time.Since(t.Time))
|
||||||
}
|
}
|
||||||
|
|
||||||
func toAgeHuman(s string) string {
|
func toAgeHuman(s string) string {
|
||||||
d, err := time.ParseDuration(s)
|
if len(s) == 0 {
|
||||||
|
return UnknownValue
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := time.Parse(time.RFC3339, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return NAValue
|
return NAValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return duration.HumanDuration(d)
|
return duration.HumanDuration(time.Since(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate a string to the given l and suffix ellipsis if needed.
|
// Truncate a string to the given l and suffix ellipsis if needed.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package render
|
package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -54,17 +55,18 @@ func TestLabelize(t *testing.T) {
|
||||||
|
|
||||||
func TestDurationToSecond(t *testing.T) {
|
func TestDurationToSecond(t *testing.T) {
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
s, e string
|
s string
|
||||||
|
e int64
|
||||||
}{
|
}{
|
||||||
"seconds": {s: "22s", e: "22"},
|
"seconds": {s: "22s", e: 22},
|
||||||
"minutes": {s: "22m", e: "1320"},
|
"minutes": {s: "22m", e: 1320},
|
||||||
"hours": {s: "12h", e: "43200"},
|
"hours": {s: "12h", e: 43200},
|
||||||
"days": {s: "3d", e: "259200"},
|
"days": {s: "3d", e: 259200},
|
||||||
"day_hour": {s: "3d9h", e: "291600"},
|
"day_hour": {s: "3d9h", e: 291600},
|
||||||
"day_hour_minute": {s: "2d22h3m", e: "252180"},
|
"day_hour_minute": {s: "2d22h3m", e: 252180},
|
||||||
"day_hour_minute_seconds": {s: "2d22h3m50s", e: "252230"},
|
"day_hour_minute_seconds": {s: "2d22h3m50s", e: 252230},
|
||||||
"year": {s: "3y", e: "94608000"},
|
"year": {s: "3y", e: 94608000},
|
||||||
"year_day": {s: "1y2d", e: "31708800"},
|
"year_day": {s: "1y2d", e: 31708800},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
|
|
@ -90,36 +92,42 @@ func TestToAge(t *testing.T) {
|
||||||
t time.Time
|
t time.Time
|
||||||
e string
|
e string
|
||||||
}{
|
}{
|
||||||
|
"zero": {
|
||||||
|
t: time.Time{},
|
||||||
|
e: UnknownValue,
|
||||||
|
},
|
||||||
"good": {
|
"good": {
|
||||||
t: time.Now().Add(-10 * time.Second),
|
t: testTime().Add(-10 * time.Second),
|
||||||
e: "10",
|
e: "3y196d",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
uc := uu[k]
|
uc := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
assert.Equal(t, uc.e, toAge(metav1.Time{Time: uc.t})[:2])
|
assert.Equal(t, uc.e, toAge(metav1.Time{Time: uc.t}))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestToAgeHuma(t *testing.T) {
|
func TestToAgeHuman(t *testing.T) {
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
t time.Time
|
t, e string
|
||||||
e string
|
|
||||||
}{
|
}{
|
||||||
|
"blank": {
|
||||||
|
t: "",
|
||||||
|
e: UnknownValue,
|
||||||
|
},
|
||||||
"good": {
|
"good": {
|
||||||
t: time.Now().Add(-10 * time.Second),
|
t: time.Now().Add(-10 * time.Second).Format(time.RFC3339Nano),
|
||||||
e: "10",
|
e: "10s",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
uc := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
ti := toAge(metav1.Time{Time: uc.t})
|
assert.Equal(t, u.e, toAgeHuman(u.t))
|
||||||
assert.Equal(t, uc.e, toAgeHuman(ti)[:2])
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -376,7 +384,7 @@ func BenchmarkMapToStr(b *testing.B) {
|
||||||
func TestRunesToNum(t *testing.T) {
|
func TestRunesToNum(t *testing.T) {
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
rr []rune
|
rr []rune
|
||||||
e int
|
e int64
|
||||||
}{
|
}{
|
||||||
"0": {
|
"0": {
|
||||||
rr: []rune(""),
|
rr: []rune(""),
|
||||||
|
|
@ -466,3 +474,13 @@ func BenchmarkIntToStr(b *testing.B) {
|
||||||
IntToStr(v)
|
IntToStr(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
|
||||||
|
func testTime() time.Time {
|
||||||
|
t, err := time.Parse(time.RFC3339, "2018-12-14T10:36:43.326972-07:00")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("TestTime Failed", err)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||||
autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1"
|
autoscalingv2 "k8s.io/api/autoscaling/v2"
|
||||||
autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
|
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
@ -46,10 +45,8 @@ func (h HorizontalPodAutoscaler) Render(o interface{}, ns string, r *Row) error
|
||||||
switch v {
|
switch v {
|
||||||
case "autoscaling/v1":
|
case "autoscaling/v1":
|
||||||
return h.renderV1(raw, ns, r)
|
return h.renderV1(raw, ns, r)
|
||||||
case "autoscaling/v2beta1":
|
case "autoscaling/v2":
|
||||||
return h.renderV2b1(raw, ns, r)
|
return h.renderV2(raw, ns, r)
|
||||||
case "autoscaling/v2beta2":
|
|
||||||
return h.renderV2b2(raw, ns, r)
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unhandled HPA version %q", v)
|
return fmt.Errorf("Unhandled HPA version %q", v)
|
||||||
}
|
}
|
||||||
|
|
@ -78,8 +75,8 @@ func (h HorizontalPodAutoscaler) renderV1(raw *unstructured.Unstructured, _ stri
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HorizontalPodAutoscaler) renderV2b1(raw *unstructured.Unstructured, _ string, r *Row) error {
|
func (h HorizontalPodAutoscaler) renderV2(raw *unstructured.Unstructured, _ string, r *Row) error {
|
||||||
var hpa autoscalingv2beta1.HorizontalPodAutoscaler
|
var hpa autoscalingv2.HorizontalPodAutoscaler
|
||||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &hpa)
|
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &hpa)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -90,30 +87,7 @@ func (h HorizontalPodAutoscaler) renderV2b1(raw *unstructured.Unstructured, _ st
|
||||||
hpa.Namespace,
|
hpa.Namespace,
|
||||||
hpa.ObjectMeta.Name,
|
hpa.ObjectMeta.Name,
|
||||||
hpa.Spec.ScaleTargetRef.Name,
|
hpa.Spec.ScaleTargetRef.Name,
|
||||||
toMetricsV2b1(hpa.Spec.Metrics, hpa.Status.CurrentMetrics),
|
toMetricsV2(hpa.Spec.Metrics, hpa.Status.CurrentMetrics),
|
||||||
strconv.Itoa(int(*hpa.Spec.MinReplicas)),
|
|
||||||
strconv.Itoa(int(hpa.Spec.MaxReplicas)),
|
|
||||||
strconv.Itoa(int(hpa.Status.CurrentReplicas)),
|
|
||||||
"",
|
|
||||||
toAge(hpa.ObjectMeta.CreationTimestamp),
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h HorizontalPodAutoscaler) renderV2b2(raw *unstructured.Unstructured, _ string, r *Row) error {
|
|
||||||
var hpa autoscalingv2beta2.HorizontalPodAutoscaler
|
|
||||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &hpa)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
r.ID = client.MetaFQN(hpa.ObjectMeta)
|
|
||||||
r.Fields = Fields{
|
|
||||||
hpa.Namespace,
|
|
||||||
hpa.ObjectMeta.Name,
|
|
||||||
hpa.Spec.ScaleTargetRef.Name,
|
|
||||||
toMetricsV2b2(hpa.Spec.Metrics, hpa.Status.CurrentMetrics),
|
|
||||||
strconv.Itoa(int(*hpa.Spec.MinReplicas)),
|
strconv.Itoa(int(*hpa.Spec.MinReplicas)),
|
||||||
strconv.Itoa(int(hpa.Spec.MaxReplicas)),
|
strconv.Itoa(int(hpa.Spec.MaxReplicas)),
|
||||||
strconv.Itoa(int(hpa.Status.CurrentReplicas)),
|
strconv.Itoa(int(hpa.Status.CurrentReplicas)),
|
||||||
|
|
@ -140,30 +114,7 @@ func toMetricsV1(spec autoscalingv1.HorizontalPodAutoscalerSpec, status autoscal
|
||||||
return current + "/" + target + "%"
|
return current + "/" + target + "%"
|
||||||
}
|
}
|
||||||
|
|
||||||
func toMetricsV2b1(specs []autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
func toMetricsV2(specs []autoscalingv2.MetricSpec, statuses []autoscalingv2.MetricStatus) string {
|
||||||
if len(specs) == 0 {
|
|
||||||
return MissingValue
|
|
||||||
}
|
|
||||||
|
|
||||||
list := make([]string, 0, len(specs))
|
|
||||||
for i, spec := range specs {
|
|
||||||
list = append(list, checkHPAType(i, spec, statuses))
|
|
||||||
}
|
|
||||||
|
|
||||||
max, more := 10, false
|
|
||||||
if len(list) > max {
|
|
||||||
list, more = list[:max], true
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := strings.Join(list, ", ")
|
|
||||||
if more {
|
|
||||||
return ret + " + " + strconv.Itoa(len(list)-max) + "more..."
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func toMetricsV2b2(specs []autoscalingv2beta2.MetricSpec, statuses []autoscalingv2beta2.MetricStatus) string {
|
|
||||||
if len(specs) == 0 {
|
if len(specs) == 0 {
|
||||||
return MissingValue
|
return MissingValue
|
||||||
}
|
}
|
||||||
|
|
@ -174,20 +125,20 @@ func toMetricsV2b2(specs []autoscalingv2beta2.MetricSpec, statuses []autoscaling
|
||||||
|
|
||||||
// nolint:exhaustive
|
// nolint:exhaustive
|
||||||
switch spec.Type {
|
switch spec.Type {
|
||||||
case autoscalingv2beta2.ExternalMetricSourceType:
|
case autoscalingv2.ExternalMetricSourceType:
|
||||||
list = append(list, externalMetricsV2b2(i, spec, statuses))
|
list = append(list, externalMetricsV2(i, spec, statuses))
|
||||||
case autoscalingv2beta2.PodsMetricSourceType:
|
case autoscalingv2.PodsMetricSourceType:
|
||||||
if len(statuses) > i && statuses[i].Pods != nil {
|
if len(statuses) > i && statuses[i].Pods != nil {
|
||||||
current = statuses[i].Pods.Current.AverageValue.String()
|
current = statuses[i].Pods.Current.AverageValue.String()
|
||||||
}
|
}
|
||||||
list = append(list, current+"/"+spec.Pods.Target.AverageValue.String())
|
list = append(list, current+"/"+spec.Pods.Target.AverageValue.String())
|
||||||
case autoscalingv2beta2.ObjectMetricSourceType:
|
case autoscalingv2.ObjectMetricSourceType:
|
||||||
if len(statuses) > i && statuses[i].Object != nil {
|
if len(statuses) > i && statuses[i].Object != nil {
|
||||||
current = statuses[i].Object.Current.Value.String()
|
current = statuses[i].Object.Current.Value.String()
|
||||||
}
|
}
|
||||||
list = append(list, current+"/"+spec.Object.Target.Value.String())
|
list = append(list, current+"/"+spec.Object.Target.Value.String())
|
||||||
case autoscalingv2beta2.ResourceMetricSourceType:
|
case autoscalingv2.ResourceMetricSourceType:
|
||||||
list = append(list, resourceMetricsV2b2(i, spec, statuses))
|
list = append(list, resourceMetricsV2(i, spec, statuses))
|
||||||
default:
|
default:
|
||||||
list = append(list, "<unknown type>")
|
list = append(list, "<unknown type>")
|
||||||
}
|
}
|
||||||
|
|
@ -206,31 +157,31 @@ func toMetricsV2b2(specs []autoscalingv2beta2.MetricSpec, statuses []autoscaling
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkHPAType(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
func checkHPAType(i int, spec autoscalingv2.MetricSpec, statuses []autoscalingv2.MetricStatus) string {
|
||||||
current := "<unknown>"
|
current := "<unknown>"
|
||||||
|
|
||||||
// nolint:exhaustive
|
// nolint:exhaustive
|
||||||
switch spec.Type {
|
switch spec.Type {
|
||||||
case autoscalingv2beta1.ExternalMetricSourceType:
|
case autoscalingv2.ExternalMetricSourceType:
|
||||||
return externalMetricsV2b1(i, spec, statuses)
|
return externalMetricsV2(i, spec, statuses)
|
||||||
case autoscalingv2beta1.PodsMetricSourceType:
|
case autoscalingv2.PodsMetricSourceType:
|
||||||
if len(statuses) > i && statuses[i].Pods != nil {
|
if len(statuses) > i && statuses[i].Pods != nil {
|
||||||
current = statuses[i].Pods.CurrentAverageValue.String()
|
current = statuses[i].Pods.Current.AverageValue.String()
|
||||||
}
|
}
|
||||||
return current + "/" + spec.Pods.TargetAverageValue.String()
|
return current + "/" + spec.Pods.Target.AverageValue.String()
|
||||||
case autoscalingv2beta1.ObjectMetricSourceType:
|
case autoscalingv2.ObjectMetricSourceType:
|
||||||
if len(statuses) > i && statuses[i].Object != nil {
|
if len(statuses) > i && statuses[i].Object != nil {
|
||||||
current = statuses[i].Object.CurrentValue.String()
|
current = statuses[i].Object.Current.Value.String()
|
||||||
}
|
}
|
||||||
return current + "/" + spec.Object.TargetValue.String()
|
return current + "/" + spec.Object.Target.Value.String()
|
||||||
case autoscalingv2beta1.ResourceMetricSourceType:
|
case autoscalingv2.ResourceMetricSourceType:
|
||||||
return resourceMetricsV2b1(i, spec, statuses)
|
return resourceMetricsV2(i, spec, statuses)
|
||||||
}
|
}
|
||||||
|
|
||||||
return "<unknown type>"
|
return "<unknown type>"
|
||||||
}
|
}
|
||||||
|
|
||||||
func externalMetricsV2b2(i int, spec autoscalingv2beta2.MetricSpec, statuses []autoscalingv2beta2.MetricStatus) string {
|
func externalMetricsV2(i int, spec autoscalingv2.MetricSpec, statuses []autoscalingv2.MetricStatus) string {
|
||||||
current := "<unknown>"
|
current := "<unknown>"
|
||||||
|
|
||||||
if spec.External.Target.AverageValue != nil {
|
if spec.External.Target.AverageValue != nil {
|
||||||
|
|
@ -246,7 +197,7 @@ func externalMetricsV2b2(i int, spec autoscalingv2beta2.MetricSpec, statuses []a
|
||||||
return current + "/" + spec.External.Target.Value.String()
|
return current + "/" + spec.External.Target.Value.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceMetricsV2b2(i int, spec autoscalingv2beta2.MetricSpec, statuses []autoscalingv2beta2.MetricStatus) string {
|
func resourceMetricsV2(i int, spec autoscalingv2.MetricSpec, statuses []autoscalingv2.MetricStatus) string {
|
||||||
current := "<unknown>"
|
current := "<unknown>"
|
||||||
|
|
||||||
if spec.Resource.Target.AverageValue != nil {
|
if spec.Resource.Target.AverageValue != nil {
|
||||||
|
|
@ -268,48 +219,48 @@ func resourceMetricsV2b2(i int, spec autoscalingv2beta2.MetricSpec, statuses []a
|
||||||
return current + "/" + target
|
return current + "/" + target
|
||||||
}
|
}
|
||||||
|
|
||||||
func externalMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
// func externalMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
||||||
current := "<unknown>"
|
// current := "<unknown>"
|
||||||
if spec.External.TargetAverageValue != nil {
|
// if spec.External.TargetAverageValue != nil {
|
||||||
if len(statuses) > i && statuses[i].External != nil && statuses[i].External.CurrentAverageValue != nil {
|
// if len(statuses) > i && statuses[i].External != nil && statuses[i].External.CurrentAverageValue != nil {
|
||||||
current = statuses[i].External.CurrentAverageValue.String()
|
// current = statuses[i].External.CurrentAverageValue.String()
|
||||||
}
|
// }
|
||||||
return current + "/" + spec.External.TargetAverageValue.String() + " (avg)"
|
// return current + "/" + spec.External.TargetAverageValue.String() + " (avg)"
|
||||||
}
|
// }
|
||||||
if len(statuses) > i && statuses[i].External != nil {
|
// if len(statuses) > i && statuses[i].External != nil {
|
||||||
current = statuses[i].External.CurrentValue.String()
|
// current = statuses[i].External.CurrentValue.String()
|
||||||
}
|
// }
|
||||||
|
|
||||||
return current + "/" + spec.External.TargetValue.String()
|
// return current + "/" + spec.External.TargetValue.String()
|
||||||
}
|
// }
|
||||||
|
|
||||||
func resourceMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
// func resourceMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
||||||
current := "<unknown>"
|
// current := "<unknown>"
|
||||||
|
|
||||||
if status := checkTargetMetricsV2b1(i, spec, statuses); status != "" {
|
// if status := checkTargetMetricsV2b1(i, spec, statuses); status != "" {
|
||||||
return status
|
// return status
|
||||||
}
|
// }
|
||||||
|
|
||||||
if len(statuses) > i && statuses[i].Resource != nil && statuses[i].Resource.CurrentAverageUtilization != nil {
|
// if len(statuses) > i && statuses[i].Resource != nil && statuses[i].Resource.CurrentAverageUtilization != nil {
|
||||||
current = IntToStr(int(*statuses[i].Resource.CurrentAverageUtilization))
|
// current = IntToStr(int(*statuses[i].Resource.CurrentAverageUtilization))
|
||||||
}
|
// }
|
||||||
|
|
||||||
target := "<auto>"
|
// target := "<auto>"
|
||||||
if spec.Resource.TargetAverageUtilization != nil {
|
// if spec.Resource.TargetAverageUtilization != nil {
|
||||||
target = IntToStr(int(*spec.Resource.TargetAverageUtilization))
|
// target = IntToStr(int(*spec.Resource.TargetAverageUtilization))
|
||||||
}
|
// }
|
||||||
|
|
||||||
return current + "/" + target
|
// return current + "/" + target
|
||||||
}
|
// }
|
||||||
|
|
||||||
func checkTargetMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
// func checkTargetMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
||||||
if spec.Resource.TargetAverageValue == nil {
|
// if spec.Resource.TargetAverageValue == nil {
|
||||||
return ""
|
// return ""
|
||||||
}
|
// }
|
||||||
|
|
||||||
var current string
|
// var current string
|
||||||
if len(statuses) > i && statuses[i].Resource != nil {
|
// if len(statuses) > i && statuses[i].Resource != nil {
|
||||||
current = statuses[i].Resource.CurrentAverageValue.String()
|
// current = statuses[i].Resource.CurrentAverageValue.String()
|
||||||
}
|
// }
|
||||||
return current + "/" + spec.Resource.TargetAverageValue.String()
|
// return current + "/" + spec.Resource.TargetAverageValue.String()
|
||||||
}
|
// }
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ func (Pod) Header(ns string) Header {
|
||||||
HeaderColumn{Name: "VALID", Wide: true},
|
HeaderColumn{Name: "VALID", Wide: true},
|
||||||
HeaderColumn{Name: "NOMINATED NODE", Wide: true},
|
HeaderColumn{Name: "NOMINATED NODE", Wide: true},
|
||||||
HeaderColumn{Name: "READINESS GATES", Wide: true},
|
HeaderColumn{Name: "READINESS GATES", Wide: true},
|
||||||
HeaderColumn{Name: "AGE", Time: true, Decorator: AgeDecorator},
|
HeaderColumn{Name: "AGE", Time: true},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@ package render
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fvbommel/sortorder"
|
"github.com/fvbommel/sortorder"
|
||||||
)
|
)
|
||||||
|
|
@ -177,32 +175,33 @@ func (s RowSorter) Swap(i, j int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s RowSorter) Less(i, j int) bool {
|
func (s RowSorter) Less(i, j int) bool {
|
||||||
return Less(s.Asc, s.IsNumber, s.IsDuration, s.Rows[i].Fields[s.Index], s.Rows[j].Fields[s.Index])
|
v1, v2 := s.Rows[i].Fields[s.Index], s.Rows[j].Fields[s.Index]
|
||||||
|
id1, id2 := s.Rows[i].ID, s.Rows[j].ID
|
||||||
|
return Less(s.Asc, s.IsNumber, s.IsDuration, id1, id2, v1, v2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
func toAgeDuration(dur string) string {
|
|
||||||
d, err := time.ParseDuration(dur)
|
|
||||||
if err != nil {
|
|
||||||
return durationToSeconds(dur)
|
|
||||||
}
|
|
||||||
|
|
||||||
return strconv.Itoa(int(d.Seconds()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Less return true if c1 < c2.
|
// Less return true if c1 < c2.
|
||||||
func Less(asc, isNumber, isDuration bool, c1, c2 string) bool {
|
func Less(asc, isNumber, isDuration bool, id1, id2, v1, v2 string) bool {
|
||||||
if isNumber {
|
var less bool
|
||||||
c1, c2 = strings.Replace(c1, ",", "", -1), strings.Replace(c2, ",", "", -1)
|
switch {
|
||||||
|
case isNumber:
|
||||||
|
v1, v2 = strings.Replace(v1, ",", "", -1), strings.Replace(v2, ",", "", -1)
|
||||||
|
less = sortorder.NaturalLess(v1, v2)
|
||||||
|
case isDuration:
|
||||||
|
d1, d2 := durationToSeconds(v1), durationToSeconds(v2)
|
||||||
|
less = d1 <= d2
|
||||||
|
default:
|
||||||
|
less = sortorder.NaturalLess(v1, v2)
|
||||||
}
|
}
|
||||||
if isDuration {
|
|
||||||
c1, c2 = toAgeDuration(c1), toAgeDuration(c2)
|
if v1 == v2 {
|
||||||
|
return sortorder.NaturalLess(id1, id2)
|
||||||
}
|
}
|
||||||
b := sortorder.NaturalLess(c1, c2)
|
|
||||||
if asc {
|
if asc {
|
||||||
return b
|
return less
|
||||||
}
|
}
|
||||||
return !b
|
return !less
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,10 @@ func (r RowEvents) FindIndex(id string) (int, bool) {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type tuple struct {
|
||||||
|
field, id string
|
||||||
|
}
|
||||||
|
|
||||||
// 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, asc bool) {
|
func (r RowEvents) Sort(ns string, sortCol int, isDuration, numCol, asc bool) {
|
||||||
if sortCol == -1 {
|
if sortCol == -1 {
|
||||||
|
|
@ -210,24 +214,6 @@ func (r RowEvents) Sort(ns string, sortCol int, isDuration, numCol, asc bool) {
|
||||||
IsDuration: isDuration,
|
IsDuration: isDuration,
|
||||||
}
|
}
|
||||||
sort.Sort(t)
|
sort.Sort(t)
|
||||||
|
|
||||||
iids, fields := map[string][]string{}, make(StringSet, 0, len(r))
|
|
||||||
for _, re := range r {
|
|
||||||
field := re.Row.Fields[sortCol]
|
|
||||||
if isDuration {
|
|
||||||
field = toAgeDuration(field)
|
|
||||||
}
|
|
||||||
fields = fields.Add(field)
|
|
||||||
iids[field] = append(iids[field], re.Row.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
ids := make([]string, 0, len(r))
|
|
||||||
for _, field := range fields {
|
|
||||||
sort.StringSlice(iids[field]).Sort()
|
|
||||||
ids = append(ids, iids[field]...)
|
|
||||||
}
|
|
||||||
s := IdSorter{Ids: ids, Events: r}
|
|
||||||
sort.Sort(s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
@ -252,7 +238,8 @@ func (r RowEventSorter) Swap(i, j int) {
|
||||||
|
|
||||||
func (r RowEventSorter) Less(i, j int) bool {
|
func (r RowEventSorter) Less(i, j int) bool {
|
||||||
f1, f2 := r.Events[i].Row.Fields, r.Events[j].Row.Fields
|
f1, f2 := r.Events[i].Row.Fields, r.Events[j].Row.Fields
|
||||||
return Less(r.Asc, r.IsNumber, r.IsDuration, f1[r.Index], f2[r.Index])
|
id1, id2 := r.Events[i].Row.ID, r.Events[j].Row.ID
|
||||||
|
return Less(r.Asc, r.IsNumber, r.IsDuration, id1, id2, f1[r.Index], f2[r.Index])
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package render_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -409,39 +410,24 @@ func TestRowEventsDelete(t *testing.T) {
|
||||||
|
|
||||||
func TestRowEventsSort(t *testing.T) {
|
func TestRowEventsSort(t *testing.T) {
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
re render.RowEvents
|
re render.RowEvents
|
||||||
col int
|
col int
|
||||||
age, num, asc bool
|
duration, num, asc bool
|
||||||
e render.RowEvents
|
e render.RowEvents
|
||||||
}{
|
}{
|
||||||
"age_time": {
|
"age_time": {
|
||||||
re: render.RowEvents{
|
re: render.RowEvents{
|
||||||
{Row: render.Row{ID: "A", Fields: render.Fields{"1", "2", "1h10m10.5s"}}},
|
{Row: render.Row{ID: "A", Fields: render.Fields{"1", "2", testTime().Add(20 * time.Second).String()}}},
|
||||||
{Row: render.Row{ID: "B", Fields: render.Fields{"0", "2", "10.5s"}}},
|
{Row: render.Row{ID: "B", Fields: render.Fields{"0", "2", testTime().Add(10 * time.Second).String()}}},
|
||||||
{Row: render.Row{ID: "C", Fields: render.Fields{"10", "2", "3h20m5.2s"}}},
|
{Row: render.Row{ID: "C", Fields: render.Fields{"10", "2", testTime().String()}}},
|
||||||
},
|
},
|
||||||
col: 2,
|
col: 2,
|
||||||
asc: true,
|
asc: true,
|
||||||
age: true,
|
duration: true,
|
||||||
e: render.RowEvents{
|
e: render.RowEvents{
|
||||||
{Row: render.Row{ID: "B", Fields: render.Fields{"0", "2", "10.5s"}}},
|
{Row: render.Row{ID: "C", Fields: render.Fields{"10", "2", testTime().String()}}},
|
||||||
{Row: render.Row{ID: "A", Fields: render.Fields{"1", "2", "1h10m10.5s"}}},
|
{Row: render.Row{ID: "B", Fields: render.Fields{"0", "2", testTime().Add(10 * time.Second).String()}}},
|
||||||
{Row: render.Row{ID: "C", Fields: render.Fields{"10", "2", "3h20m5.2s"}}},
|
{Row: render.Row{ID: "A", Fields: render.Fields{"1", "2", testTime().Add(20 * time.Second).String()}}},
|
||||||
},
|
|
||||||
},
|
|
||||||
"age_duration": {
|
|
||||||
re: render.RowEvents{
|
|
||||||
{Row: render.Row{ID: "A", Fields: render.Fields{"1", "2", "32d"}}},
|
|
||||||
{Row: render.Row{ID: "B", Fields: render.Fields{"0", "2", "1m10s"}}},
|
|
||||||
{Row: render.Row{ID: "C", Fields: render.Fields{"10", "2", "3h20m5s"}}},
|
|
||||||
},
|
|
||||||
col: 2,
|
|
||||||
asc: true,
|
|
||||||
age: true,
|
|
||||||
e: render.RowEvents{
|
|
||||||
{Row: render.Row{ID: "B", Fields: render.Fields{"0", "2", "1m10s"}}},
|
|
||||||
{Row: render.Row{ID: "C", Fields: render.Fields{"10", "2", "3h20m5s"}}},
|
|
||||||
{Row: render.Row{ID: "A", Fields: render.Fields{"1", "2", "32d"}}},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"col0": {
|
"col0": {
|
||||||
|
|
@ -483,7 +469,7 @@ func TestRowEventsSort(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) {
|
||||||
u.re.Sort("", u.col, u.age, u.num, u.asc)
|
u.re.Sort("", u.col, u.duration, u.num, u.asc)
|
||||||
assert.Equal(t, u.e, u.re)
|
assert.Equal(t, u.e, u.re)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -318,25 +319,25 @@ func TestRowsSortDuration(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
"durationAsc": {
|
"durationAsc": {
|
||||||
rows: render.Rows{
|
rows: render.Rows{
|
||||||
{Fields: []string{"10m10s", "duh"}},
|
{Fields: []string{testTime().Add(10 * time.Second).String(), "duh"}},
|
||||||
{Fields: []string{"19s", "blee"}},
|
{Fields: []string{testTime().String(), "blee"}},
|
||||||
},
|
},
|
||||||
col: 0,
|
col: 0,
|
||||||
asc: true,
|
asc: true,
|
||||||
e: render.Rows{
|
e: render.Rows{
|
||||||
{Fields: []string{"19s", "blee"}},
|
{Fields: []string{testTime().String(), "blee"}},
|
||||||
{Fields: []string{"10m10s", "duh"}},
|
{Fields: []string{testTime().Add(10 * time.Second).String(), "duh"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"durationDesc": {
|
"durationDesc": {
|
||||||
rows: render.Rows{
|
rows: render.Rows{
|
||||||
{Fields: []string{"10m10s", "duh"}},
|
{Fields: []string{testTime().Add(10 * time.Second).String(), "duh"}},
|
||||||
{Fields: []string{"19s", "blee"}},
|
{Fields: []string{testTime().String(), "blee"}},
|
||||||
},
|
},
|
||||||
col: 0,
|
col: 0,
|
||||||
e: render.Rows{
|
e: render.Rows{
|
||||||
{Fields: []string{"10m10s", "duh"}},
|
{Fields: []string{testTime().Add(10 * time.Second).String(), "duh"}},
|
||||||
{Fields: []string{"19s", "blee"}},
|
{Fields: []string{testTime().String(), "blee"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,6 @@ func ComputeMaxColumns(pads MaxyPad, sortColName string, header render.Header, e
|
||||||
var row int
|
var row int
|
||||||
for _, e := range ee {
|
for _, e := range ee {
|
||||||
for index, field := range e.Row.Fields {
|
for index, field := range e.Row.Fields {
|
||||||
if header.IsTimeCol(index) {
|
|
||||||
field = toAgeHuman(field)
|
|
||||||
}
|
|
||||||
width := len(field) + colPadding
|
width := len(field) + colPadding
|
||||||
if index < len(pads) && width > pads[index] {
|
if index < len(pads) && width > pads[index] {
|
||||||
pads[index] = width
|
pads[index] = width
|
||||||
|
|
@ -60,10 +57,10 @@ func Pad(s string, width int) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func toAgeHuman(s string) string {
|
func toAgeHuman(s string) string {
|
||||||
d, err := time.ParseDuration(s)
|
t, err := time.Parse(time.RFC3339Nano, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return render.NAValue
|
return render.NAValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return duration.HumanDuration(d)
|
return duration.HumanDuration(time.Since(t))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,8 @@ func (s *SelectTable) SelectRow(r int, broadcast bool) {
|
||||||
if !broadcast {
|
if !broadcast {
|
||||||
s.SetSelectionChangedFunc(nil)
|
s.SetSelectionChangedFunc(nil)
|
||||||
}
|
}
|
||||||
if s.model.Count() > 0 && r >= s.model.Count() {
|
if c := s.model.Count(); c > 0 && r-1 > c {
|
||||||
r = s.model.Count()
|
r = c + 1
|
||||||
}
|
}
|
||||||
defer s.SetSelectionChangedFunc(s.selectionChanged)
|
defer s.SetSelectionChangedFunc(s.selectionChanged)
|
||||||
s.Select(r, 0)
|
s.Select(r, 0)
|
||||||
|
|
|
||||||
|
|
@ -406,6 +406,10 @@ func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if client.IsAllNamespace(ns) {
|
||||||
|
b.GetTable().SetSortCol("NAMESPACE", true)
|
||||||
|
}
|
||||||
|
|
||||||
if err := b.app.switchNS(ns); err != nil {
|
if err := b.app.switchNS(ns); err != nil {
|
||||||
b.App().Flash().Err(err)
|
b.App().Flash().Err(err)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -163,8 +163,7 @@ func (c *Command) defaultCmd() error {
|
||||||
|
|
||||||
if err := c.run(cmd, "", true); err != nil {
|
if err := c.run(cmd, "", true); err != nil {
|
||||||
log.Error().Err(err).Msgf("Default run command failed %q", cmd)
|
log.Error().Err(err).Msgf("Default run command failed %q", cmd)
|
||||||
c.app.cowCmd(err.Error())
|
return c.run("pod", "", true)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ func (c *Container) portFwdCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := c.App().factory.ForwarderFor(fwFQN(c.GetTable().Path, path)); ok {
|
if _, ok := c.App().factory.ForwarderFor(fwFQN(c.GetTable().Path, path)); ok {
|
||||||
c.App().Flash().Err(fmt.Errorf("A port-forward already exist on container %s", c.GetTable().Path))
|
c.App().Flash().Err(fmt.Errorf("A port-forward already exists on container %s", c.GetTable().Path))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Env represent K9s and K8s available environment variables.
|
// Env represent K9s and K8s available environment variables.
|
||||||
|
|
@ -34,7 +36,8 @@ func (e Env) Substitute(arg string) (string, error) {
|
||||||
}
|
}
|
||||||
v, ok := e[strings.ToUpper(key)]
|
v, ok := e[strings.ToUpper(key)]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("no environment matching key %q:%q", k, key)
|
log.Warn().Msgf("no k9s environment matching key %q:%q", k, key)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
if b, err := strconv.ParseBool(v); err == nil {
|
if b, err := strconv.ParseBool(v); err == nil {
|
||||||
if inverse {
|
if inverse {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package view
|
package view
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -17,7 +16,7 @@ func TestEnvReplace(t *testing.T) {
|
||||||
"simple": {arg: "$A", e: "10"},
|
"simple": {arg: "$A", e: "10"},
|
||||||
"substring": {arg: "$A and $AA", e: "10 and 20"},
|
"substring": {arg: "$A and $AA", e: "10 and 20"},
|
||||||
"with-text": {arg: "Something $A", e: "Something 10"},
|
"with-text": {arg: "Something $A", e: "Something 10"},
|
||||||
"noMatch": {arg: "blah blah and $BLEE", err: errors.New(`no environment matching key "$BLEE":"BLEE"`), e: ""},
|
"noMatch": {arg: "blah blah and $BLEE", e: "blah blah and $BLEE"},
|
||||||
"lower": {arg: "And then $b happened", e: "And then blee happened"},
|
"lower": {arg: "And then $b happened", e: "And then blee happened"},
|
||||||
"dash": {arg: "$col0", e: "fred"},
|
"dash": {arg: "$col0", e: "fred"},
|
||||||
"mix": {arg: "$col0 and then $a but $B", e: "fred and then 10 but blee"},
|
"mix": {arg: "$col0 and then $a but $B", e: "fred and then 10 but blee"},
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ func TestHelp(t *testing.T) {
|
||||||
v := view.NewHelp(app)
|
v := view.NewHelp(app)
|
||||||
|
|
||||||
assert.Nil(t, v.Init(ctx))
|
assert.Nil(t, v.Init(ctx))
|
||||||
assert.Equal(t, 25, v.GetRowCount())
|
assert.Equal(t, 26, v.GetRowCount())
|
||||||
assert.Equal(t, 6, v.GetColumnCount())
|
assert.Equal(t, 6, v.GetColumnCount())
|
||||||
assert.Equal(t, "<a>", strings.TrimSpace(v.GetCell(1, 0).Text))
|
assert.Equal(t, "<a>", strings.TrimSpace(v.GetCell(1, 0).Text))
|
||||||
assert.Equal(t, "Attach", strings.TrimSpace(v.GetCell(1, 1).Text))
|
assert.Equal(t, "Attach", strings.TrimSpace(v.GetCell(1, 1).Text))
|
||||||
|
|
|
||||||
|
|
@ -350,7 +350,6 @@ func (l *Log) Flush(lines [][]byte) {
|
||||||
if l.cancelUpdates {
|
if l.cancelUpdates {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
log.Debug().Msgf("FLUSH %q", string(lines[i]))
|
|
||||||
_, _ = l.ansiWriter.Write(lines[i])
|
_, _ = l.ansiWriter.Write(lines[i])
|
||||||
}
|
}
|
||||||
if l.follow {
|
if l.follow {
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ func (p *PortForwardExtender) portFwdCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if p.App().factory.Forwarders().IsPodForwarded(path) {
|
if p.App().factory.Forwarders().IsPodForwarded(path) {
|
||||||
p.App().Flash().Errf("A PortForward already exist for pod %s", pod.Name)
|
p.App().Flash().Errf("A PortForward already exists for pod %s", pod.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := showFwdDialog(p, podName, startFwdCB); err != nil {
|
if err := showFwdDialog(p, podName, startFwdCB); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ func (p *Pod) bindKeys(aa ui.KeyActions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
aa.Add(ui.KeyActions{
|
aa.Add(ui.KeyActions{
|
||||||
|
ui.KeyN: ui.NewKeyAction("Show Node", p.showNode, true),
|
||||||
ui.KeyF: ui.NewKeyAction("Show PortForward", p.showPFCmd, true),
|
ui.KeyF: ui.NewKeyAction("Show PortForward", p.showPFCmd, true),
|
||||||
ui.KeyShiftR: ui.NewKeyAction("Sort Ready", p.GetTable().SortColCmd(readyCol, true), false),
|
ui.KeyShiftR: ui.NewKeyAction("Sort Ready", p.GetTable().SortColCmd(readyCol, true), false),
|
||||||
ui.KeyShiftT: ui.NewKeyAction("Sort Restart", p.GetTable().SortColCmd("RESTARTS", false), false),
|
ui.KeyShiftT: ui.NewKeyAction("Sort Restart", p.GetTable().SortColCmd("RESTARTS", false), false),
|
||||||
|
|
@ -130,6 +131,37 @@ func (p *Pod) coContext(ctx context.Context) context.Context {
|
||||||
|
|
||||||
// Handlers...
|
// Handlers...
|
||||||
|
|
||||||
|
func (p *Pod) showNode(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
path := p.GetTable().GetSelectedItem()
|
||||||
|
if path == "" {
|
||||||
|
return evt
|
||||||
|
}
|
||||||
|
pod, err := fetchPod(p.App().factory, path)
|
||||||
|
if err != nil {
|
||||||
|
p.App().Flash().Err(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if pod.Spec.NodeName == "" {
|
||||||
|
p.App().Flash().Err(errors.New("no node assigned"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
no := NewNode(client.NewGVR("v1/nodes"))
|
||||||
|
no.SetInstance(pod.Spec.NodeName)
|
||||||
|
//no.SetContextFn(nodeContext(pod.Spec.NodeName))
|
||||||
|
if err := p.App().inject(no); err != nil {
|
||||||
|
p.App().Flash().Err(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeContext(path string) ContextFunc {
|
||||||
|
return func(ctx context.Context) context.Context {
|
||||||
|
ctx = context.WithValue(ctx, internal.KeyPath, path)
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Pod) showPFCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (p *Pod) showPFCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
path := p.GetTable().GetSelectedItem()
|
path := p.GetTable().GetSelectedItem()
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ func TestPodNew(t *testing.T) {
|
||||||
|
|
||||||
assert.Nil(t, po.Init(makeCtx()))
|
assert.Nil(t, po.Init(makeCtx()))
|
||||||
assert.Equal(t, "Pods", po.Name())
|
assert.Equal(t, "Pods", po.Name())
|
||||||
assert.Equal(t, 24, len(po.Hints()))
|
assert.Equal(t, 25, len(po.Hints()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue