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
|
||||
|
||||
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
|
||||
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)
|
||||
SOURCE_DATE_EPOCH ?= $(shell date +%s)
|
||||
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
|
||||
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
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
replace (
|
||||
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/cenkalti/backoff v2.2.1+incompatible
|
||||
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/fatih/color v1.13.0
|
||||
github.com/fsnotify/fsnotify v1.5.1
|
||||
github.com/fvbommel/sortorder v1.0.2
|
||||
github.com/gdamore/tcell/v2 v2.4.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/petergtz/pegomock v2.9.0+incompatible
|
||||
github.com/rakyll/hey v0.1.4
|
||||
github.com/rs/zerolog v1.26.0
|
||||
github.com/sahilm/fuzzy v0.1.0
|
||||
github.com/spf13/cobra v1.2.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/stretchr/testify v1.7.1
|
||||
golang.org/x/text v0.3.7
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
helm.sh/helm/v3 v3.7.1
|
||||
k8s.io/api v0.22.3
|
||||
k8s.io/apimachinery v0.22.3
|
||||
k8s.io/cli-runtime v0.22.3
|
||||
k8s.io/client-go v0.22.3
|
||||
k8s.io/klog/v2 v2.30.0
|
||||
k8s.io/kubectl v0.22.3
|
||||
k8s.io/metrics v0.22.3
|
||||
helm.sh/helm/v3 v3.9.0
|
||||
k8s.io/api v0.24.2
|
||||
k8s.io/apimachinery v0.24.2
|
||||
k8s.io/cli-runtime v0.24.2
|
||||
k8s.io/client-go v0.24.2
|
||||
k8s.io/klog/v2 v2.60.1
|
||||
k8s.io/kubectl v0.24.2
|
||||
k8s.io/metrics v0.24.2
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
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-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.18 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.20 // 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/logger v0.2.1 // 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/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.17 // indirect
|
||||
github.com/Microsoft/hcsshim v0.8.21 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/Microsoft/hcsshim v0.9.2 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
|
||||
github.com/aws/aws-sdk-go v1.35.21 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||
github.com/containerd/containerd v1.5.7 // indirect
|
||||
github.com/containerd/continuity v0.1.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
||||
github.com/containerd/containerd v1.6.3 // 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/docker/cli v20.10.7+incompatible // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.6.3 // indirect
|
||||
github.com/docker/cli v20.10.11+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/docker/docker v20.10.14+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.6.4 // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // 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/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/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/jsonreference v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // 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/google/btree v1.0.1 // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/google/gofuzz v1.1.0 // indirect
|
||||
github.com/google/gnostic v0.5.7-v3refs // 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/uuid v1.2.0 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gosuri/uitable v0.0.4 // 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/inconshreveable/mousetrap v1.0.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/json-iterator/go v1.1.11 // indirect
|
||||
github.com/klauspost/compress v1.11.13 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // 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/lucasb-eyer/go-colorful v1.2.0 // 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/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/reflectwalk v1.0.1 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/locker v1.0.1 // 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/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/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/image-spec v1.0.1 // indirect
|
||||
github.com/opencontainers/runc v1.0.2 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
github.com/opencontainers/runc v1.1.1 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pkg/errors v0.9.1 // 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/common v0.26.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // 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/shopspring/decimal v1.2.0 // 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/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
|
||||
golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72 // indirect
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||
google.golang.org/grpc v1.38.0 // indirect
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
gopkg.in/gorp.v1 v1.7.2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 // indirect
|
||||
google.golang.org/grpc v1.43.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
k8s.io/apiextensions-apiserver v0.22.1 // indirect
|
||||
k8s.io/apiserver v0.22.1 // indirect
|
||||
k8s.io/component-base v0.22.3 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect
|
||||
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect
|
||||
oras.land/oras-go v0.4.0 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.8.11 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.24.0 // indirect
|
||||
k8s.io/apiserver v0.24.0 // indirect
|
||||
k8s.io/component-base v0.24.2 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
|
||||
oras.land/oras-go v1.1.1 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.11.4 // 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 {
|
||||
screenDumpDir := k.ScreenDumpDir
|
||||
|
||||
if k.manualScreenDumpDir != nil && *k.manualScreenDumpDir != "" {
|
||||
screenDumpDir = *k.manualScreenDumpDir
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@ package dao
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/rs/zerolog/log"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
|
@ -28,21 +26,17 @@ func (h *HorizontalPodAutoscaler) List(ctx context.Context, ns string) ([]runtim
|
|||
lsel = sel.AsSelector()
|
||||
}
|
||||
|
||||
gvrs := []string{
|
||||
"autoscaling/v2beta2/horizontalpodautoscalers",
|
||||
"autoscaling/v2beta1/horizontalpodautoscalers",
|
||||
"autoscaling/v1/horizontalpodautoscalers",
|
||||
rev, err := h.Factory.Client().ServerVersion()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, gvr := range gvrs {
|
||||
oo, err := h.list(gvr, ns, lsel)
|
||||
if err == nil && len(oo) > 0 {
|
||||
return oo, nil
|
||||
}
|
||||
gvr := "autoscaling/v1/horizontalpodautoscalers"
|
||||
if rev.Minor >= "23" {
|
||||
gvr = "autoscaling/v2/horizontalpodautoscalers"
|
||||
}
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -109,14 +109,19 @@ func (n *Node) Drain(path string, opts DrainOptions, w io.Writer) error {
|
|||
|
||||
// Get returns a node resource.
|
||||
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 {
|
||||
return o, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expecting *unstructured.Unstructured but got `%T", o)
|
||||
var raw *unstructured.Unstructured
|
||||
for _, o := range oo {
|
||||
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
|
||||
|
|
@ -124,7 +129,7 @@ func (n *Node) Get(ctx context.Context, path string) (runtime.Object, error) {
|
|||
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.
|
||||
|
|
|
|||
|
|
@ -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/v1beta1/cronjobs"): &CronJob{},
|
||||
client.NewGVR("batch/v1/jobs"): &Job{},
|
||||
client.NewGVR("v1/namespaces"): &Namespace{},
|
||||
// BOZO!! Revamp with latest...
|
||||
// client.NewGVR("openfaas"): &OpenFaas{},
|
||||
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.Fields = make(Fields, 0, len(g.Header(ns)))
|
||||
r.Fields = append(r.Fields, nns)
|
||||
var ageCell interface{}
|
||||
var duration interface{}
|
||||
for i, c := range row.Cells {
|
||||
if g.ageIndex > 0 && i == g.ageIndex {
|
||||
ageCell = c
|
||||
duration = c
|
||||
continue
|
||||
}
|
||||
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))
|
||||
}
|
||||
if ageCell != nil {
|
||||
r.Fields = append(r.Fields, fmt.Sprintf("%v", ageCell))
|
||||
if d, ok := duration.(string); ok {
|
||||
r.Fields = append(r.Fields, d)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ func TestGenericRender(t *testing.T) {
|
|||
ns: client.ClusterScope,
|
||||
table: makeAgeGeneric(),
|
||||
eID: "-/fred",
|
||||
eFields: render.Fields{"-", "c1", "c2", "Age"},
|
||||
eFields: render.Fields{"-", "c1", "c2", "2d"},
|
||||
eHeader: render.Header{
|
||||
render.HeaderColumn{Name: "NAMESPACE"},
|
||||
render.HeaderColumn{Name: "A"},
|
||||
|
|
@ -172,7 +172,7 @@ func makeAgeGeneric() *metav1beta1.Table {
|
|||
},
|
||||
Cells: []interface{}{
|
||||
"c1",
|
||||
"Age",
|
||||
"2d",
|
||||
"c2",
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -16,25 +16,25 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/duration"
|
||||
)
|
||||
|
||||
func runesToNum(rr []rune) int {
|
||||
var n int
|
||||
m := 1
|
||||
func runesToNum(rr []rune) int64 {
|
||||
var r int64
|
||||
var m int64 = 1
|
||||
for i := len(rr) - 1; i >= 0; i-- {
|
||||
v := int(rr[i] - '0')
|
||||
n += v * m
|
||||
v := int64(rr[i] - '0')
|
||||
r += v * m
|
||||
m *= 10
|
||||
}
|
||||
|
||||
return n
|
||||
return r
|
||||
}
|
||||
|
||||
func durationToSeconds(duration string) string {
|
||||
func durationToSeconds(duration string) int64 {
|
||||
if len(duration) == 0 {
|
||||
return duration
|
||||
return 0
|
||||
}
|
||||
|
||||
num := make([]rune, 0, 5)
|
||||
var n, m int
|
||||
var n, m int64
|
||||
for _, r := range duration {
|
||||
switch r {
|
||||
case 'y':
|
||||
|
|
@ -54,7 +54,7 @@ func durationToSeconds(duration string) string {
|
|||
n, num = n+runesToNum(num)*m, num[:0]
|
||||
}
|
||||
|
||||
return strconv.Itoa(n)
|
||||
return n
|
||||
}
|
||||
|
||||
// AsThousands prints a number with thousand separator.
|
||||
|
|
@ -194,17 +194,25 @@ func boolToStr(b bool) string {
|
|||
}
|
||||
}
|
||||
|
||||
func toAge(timestamp metav1.Time) string {
|
||||
return time.Since(timestamp.Time).String()
|
||||
func toAge(t metav1.Time) string {
|
||||
if t.IsZero() {
|
||||
return UnknownValue
|
||||
}
|
||||
|
||||
return duration.HumanDuration(time.Since(t.Time))
|
||||
}
|
||||
|
||||
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 {
|
||||
return NAValue
|
||||
}
|
||||
|
||||
return duration.HumanDuration(d)
|
||||
return duration.HumanDuration(time.Since(t))
|
||||
}
|
||||
|
||||
// Truncate a string to the given l and suffix ellipsis if needed.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package render
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -54,17 +55,18 @@ func TestLabelize(t *testing.T) {
|
|||
|
||||
func TestDurationToSecond(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
s, e string
|
||||
s string
|
||||
e int64
|
||||
}{
|
||||
"seconds": {s: "22s", e: "22"},
|
||||
"minutes": {s: "22m", e: "1320"},
|
||||
"hours": {s: "12h", e: "43200"},
|
||||
"days": {s: "3d", e: "259200"},
|
||||
"day_hour": {s: "3d9h", e: "291600"},
|
||||
"day_hour_minute": {s: "2d22h3m", e: "252180"},
|
||||
"day_hour_minute_seconds": {s: "2d22h3m50s", e: "252230"},
|
||||
"year": {s: "3y", e: "94608000"},
|
||||
"year_day": {s: "1y2d", e: "31708800"},
|
||||
"seconds": {s: "22s", e: 22},
|
||||
"minutes": {s: "22m", e: 1320},
|
||||
"hours": {s: "12h", e: 43200},
|
||||
"days": {s: "3d", e: 259200},
|
||||
"day_hour": {s: "3d9h", e: 291600},
|
||||
"day_hour_minute": {s: "2d22h3m", e: 252180},
|
||||
"day_hour_minute_seconds": {s: "2d22h3m50s", e: 252230},
|
||||
"year": {s: "3y", e: 94608000},
|
||||
"year_day": {s: "1y2d", e: 31708800},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
|
|
@ -90,36 +92,42 @@ func TestToAge(t *testing.T) {
|
|||
t time.Time
|
||||
e string
|
||||
}{
|
||||
"zero": {
|
||||
t: time.Time{},
|
||||
e: UnknownValue,
|
||||
},
|
||||
"good": {
|
||||
t: time.Now().Add(-10 * time.Second),
|
||||
e: "10",
|
||||
t: testTime().Add(-10 * time.Second),
|
||||
e: "3y196d",
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
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 {
|
||||
t time.Time
|
||||
e string
|
||||
t, e string
|
||||
}{
|
||||
"blank": {
|
||||
t: "",
|
||||
e: UnknownValue,
|
||||
},
|
||||
"good": {
|
||||
t: time.Now().Add(-10 * time.Second),
|
||||
e: "10",
|
||||
t: time.Now().Add(-10 * time.Second).Format(time.RFC3339Nano),
|
||||
e: "10s",
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
ti := toAge(metav1.Time{Time: uc.t})
|
||||
assert.Equal(t, uc.e, toAgeHuman(ti)[:2])
|
||||
assert.Equal(t, u.e, toAgeHuman(u.t))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -376,7 +384,7 @@ func BenchmarkMapToStr(b *testing.B) {
|
|||
func TestRunesToNum(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
rr []rune
|
||||
e int
|
||||
e int64
|
||||
}{
|
||||
"0": {
|
||||
rr: []rune(""),
|
||||
|
|
@ -466,3 +474,13 @@ func BenchmarkIntToStr(b *testing.B) {
|
|||
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/tview"
|
||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||
autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1"
|
||||
autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
|
||||
autoscalingv2 "k8s.io/api/autoscaling/v2"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
|
@ -46,10 +45,8 @@ func (h HorizontalPodAutoscaler) Render(o interface{}, ns string, r *Row) error
|
|||
switch v {
|
||||
case "autoscaling/v1":
|
||||
return h.renderV1(raw, ns, r)
|
||||
case "autoscaling/v2beta1":
|
||||
return h.renderV2b1(raw, ns, r)
|
||||
case "autoscaling/v2beta2":
|
||||
return h.renderV2b2(raw, ns, r)
|
||||
case "autoscaling/v2":
|
||||
return h.renderV2(raw, ns, r)
|
||||
default:
|
||||
return fmt.Errorf("Unhandled HPA version %q", v)
|
||||
}
|
||||
|
|
@ -78,8 +75,8 @@ func (h HorizontalPodAutoscaler) renderV1(raw *unstructured.Unstructured, _ stri
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h HorizontalPodAutoscaler) renderV2b1(raw *unstructured.Unstructured, _ string, r *Row) error {
|
||||
var hpa autoscalingv2beta1.HorizontalPodAutoscaler
|
||||
func (h HorizontalPodAutoscaler) renderV2(raw *unstructured.Unstructured, _ string, r *Row) error {
|
||||
var hpa autoscalingv2.HorizontalPodAutoscaler
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &hpa)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -90,30 +87,7 @@ func (h HorizontalPodAutoscaler) renderV2b1(raw *unstructured.Unstructured, _ st
|
|||
hpa.Namespace,
|
||||
hpa.ObjectMeta.Name,
|
||||
hpa.Spec.ScaleTargetRef.Name,
|
||||
toMetricsV2b1(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),
|
||||
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)),
|
||||
|
|
@ -140,30 +114,7 @@ func toMetricsV1(spec autoscalingv1.HorizontalPodAutoscalerSpec, status autoscal
|
|||
return current + "/" + target + "%"
|
||||
}
|
||||
|
||||
func toMetricsV2b1(specs []autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.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 {
|
||||
func toMetricsV2(specs []autoscalingv2.MetricSpec, statuses []autoscalingv2.MetricStatus) string {
|
||||
if len(specs) == 0 {
|
||||
return MissingValue
|
||||
}
|
||||
|
|
@ -174,20 +125,20 @@ func toMetricsV2b2(specs []autoscalingv2beta2.MetricSpec, statuses []autoscaling
|
|||
|
||||
// nolint:exhaustive
|
||||
switch spec.Type {
|
||||
case autoscalingv2beta2.ExternalMetricSourceType:
|
||||
list = append(list, externalMetricsV2b2(i, spec, statuses))
|
||||
case autoscalingv2beta2.PodsMetricSourceType:
|
||||
case autoscalingv2.ExternalMetricSourceType:
|
||||
list = append(list, externalMetricsV2(i, spec, statuses))
|
||||
case autoscalingv2.PodsMetricSourceType:
|
||||
if len(statuses) > i && statuses[i].Pods != nil {
|
||||
current = statuses[i].Pods.Current.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 {
|
||||
current = statuses[i].Object.Current.Value.String()
|
||||
}
|
||||
list = append(list, current+"/"+spec.Object.Target.Value.String())
|
||||
case autoscalingv2beta2.ResourceMetricSourceType:
|
||||
list = append(list, resourceMetricsV2b2(i, spec, statuses))
|
||||
case autoscalingv2.ResourceMetricSourceType:
|
||||
list = append(list, resourceMetricsV2(i, spec, statuses))
|
||||
default:
|
||||
list = append(list, "<unknown type>")
|
||||
}
|
||||
|
|
@ -206,31 +157,31 @@ func toMetricsV2b2(specs []autoscalingv2beta2.MetricSpec, statuses []autoscaling
|
|||
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>"
|
||||
|
||||
// nolint:exhaustive
|
||||
switch spec.Type {
|
||||
case autoscalingv2beta1.ExternalMetricSourceType:
|
||||
return externalMetricsV2b1(i, spec, statuses)
|
||||
case autoscalingv2beta1.PodsMetricSourceType:
|
||||
case autoscalingv2.ExternalMetricSourceType:
|
||||
return externalMetricsV2(i, spec, statuses)
|
||||
case autoscalingv2.PodsMetricSourceType:
|
||||
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()
|
||||
case autoscalingv2beta1.ObjectMetricSourceType:
|
||||
return current + "/" + spec.Pods.Target.AverageValue.String()
|
||||
case autoscalingv2.ObjectMetricSourceType:
|
||||
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()
|
||||
case autoscalingv2beta1.ResourceMetricSourceType:
|
||||
return resourceMetricsV2b1(i, spec, statuses)
|
||||
return current + "/" + spec.Object.Target.Value.String()
|
||||
case autoscalingv2.ResourceMetricSourceType:
|
||||
return resourceMetricsV2(i, spec, statuses)
|
||||
}
|
||||
|
||||
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>"
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
func resourceMetricsV2b2(i int, spec autoscalingv2beta2.MetricSpec, statuses []autoscalingv2beta2.MetricStatus) string {
|
||||
func resourceMetricsV2(i int, spec autoscalingv2.MetricSpec, statuses []autoscalingv2.MetricStatus) string {
|
||||
current := "<unknown>"
|
||||
|
||||
if spec.Resource.Target.AverageValue != nil {
|
||||
|
|
@ -268,48 +219,48 @@ func resourceMetricsV2b2(i int, spec autoscalingv2beta2.MetricSpec, statuses []a
|
|||
return current + "/" + target
|
||||
}
|
||||
|
||||
func externalMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
||||
current := "<unknown>"
|
||||
if spec.External.TargetAverageValue != nil {
|
||||
if len(statuses) > i && statuses[i].External != nil && statuses[i].External.CurrentAverageValue != nil {
|
||||
current = statuses[i].External.CurrentAverageValue.String()
|
||||
}
|
||||
return current + "/" + spec.External.TargetAverageValue.String() + " (avg)"
|
||||
}
|
||||
if len(statuses) > i && statuses[i].External != nil {
|
||||
current = statuses[i].External.CurrentValue.String()
|
||||
}
|
||||
// func externalMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
||||
// current := "<unknown>"
|
||||
// if spec.External.TargetAverageValue != nil {
|
||||
// if len(statuses) > i && statuses[i].External != nil && statuses[i].External.CurrentAverageValue != nil {
|
||||
// current = statuses[i].External.CurrentAverageValue.String()
|
||||
// }
|
||||
// return current + "/" + spec.External.TargetAverageValue.String() + " (avg)"
|
||||
// }
|
||||
// if len(statuses) > i && statuses[i].External != nil {
|
||||
// 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 {
|
||||
current := "<unknown>"
|
||||
// func resourceMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
||||
// current := "<unknown>"
|
||||
|
||||
if status := checkTargetMetricsV2b1(i, spec, statuses); status != "" {
|
||||
return status
|
||||
}
|
||||
// if status := checkTargetMetricsV2b1(i, spec, statuses); status != "" {
|
||||
// return status
|
||||
// }
|
||||
|
||||
if len(statuses) > i && statuses[i].Resource != nil && statuses[i].Resource.CurrentAverageUtilization != nil {
|
||||
current = IntToStr(int(*statuses[i].Resource.CurrentAverageUtilization))
|
||||
}
|
||||
// if len(statuses) > i && statuses[i].Resource != nil && statuses[i].Resource.CurrentAverageUtilization != nil {
|
||||
// current = IntToStr(int(*statuses[i].Resource.CurrentAverageUtilization))
|
||||
// }
|
||||
|
||||
target := "<auto>"
|
||||
if spec.Resource.TargetAverageUtilization != nil {
|
||||
target = IntToStr(int(*spec.Resource.TargetAverageUtilization))
|
||||
}
|
||||
// target := "<auto>"
|
||||
// if spec.Resource.TargetAverageUtilization != nil {
|
||||
// target = IntToStr(int(*spec.Resource.TargetAverageUtilization))
|
||||
// }
|
||||
|
||||
return current + "/" + target
|
||||
}
|
||||
// return current + "/" + target
|
||||
// }
|
||||
|
||||
func checkTargetMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
||||
if spec.Resource.TargetAverageValue == nil {
|
||||
return ""
|
||||
}
|
||||
// func checkTargetMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
||||
// if spec.Resource.TargetAverageValue == nil {
|
||||
// return ""
|
||||
// }
|
||||
|
||||
var current string
|
||||
if len(statuses) > i && statuses[i].Resource != nil {
|
||||
current = statuses[i].Resource.CurrentAverageValue.String()
|
||||
}
|
||||
return current + "/" + spec.Resource.TargetAverageValue.String()
|
||||
}
|
||||
// var current string
|
||||
// if len(statuses) > i && statuses[i].Resource != nil {
|
||||
// current = statuses[i].Resource.CurrentAverageValue.String()
|
||||
// }
|
||||
// return current + "/" + spec.Resource.TargetAverageValue.String()
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ func (Pod) Header(ns string) Header {
|
|||
HeaderColumn{Name: "VALID", Wide: true},
|
||||
HeaderColumn{Name: "NOMINATED NODE", 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 (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fvbommel/sortorder"
|
||||
)
|
||||
|
|
@ -177,32 +175,33 @@ func (s RowSorter) Swap(i, j int) {
|
|||
}
|
||||
|
||||
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...
|
||||
|
||||
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.
|
||||
func Less(asc, isNumber, isDuration bool, c1, c2 string) bool {
|
||||
if isNumber {
|
||||
c1, c2 = strings.Replace(c1, ",", "", -1), strings.Replace(c2, ",", "", -1)
|
||||
func Less(asc, isNumber, isDuration bool, id1, id2, v1, v2 string) bool {
|
||||
var less bool
|
||||
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 {
|
||||
return b
|
||||
return less
|
||||
}
|
||||
return !b
|
||||
return !less
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,6 +195,10 @@ func (r RowEvents) FindIndex(id string) (int, bool) {
|
|||
return 0, false
|
||||
}
|
||||
|
||||
type tuple struct {
|
||||
field, id string
|
||||
}
|
||||
|
||||
// Sort rows based on column index and order.
|
||||
func (r RowEvents) Sort(ns string, sortCol int, isDuration, numCol, asc bool) {
|
||||
if sortCol == -1 {
|
||||
|
|
@ -210,24 +214,6 @@ func (r RowEvents) Sort(ns string, sortCol int, isDuration, numCol, asc bool) {
|
|||
IsDuration: isDuration,
|
||||
}
|
||||
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 {
|
||||
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 (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -409,39 +410,24 @@ func TestRowEventsDelete(t *testing.T) {
|
|||
|
||||
func TestRowEventsSort(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
re render.RowEvents
|
||||
col int
|
||||
age, num, asc bool
|
||||
e render.RowEvents
|
||||
re render.RowEvents
|
||||
col int
|
||||
duration, num, asc bool
|
||||
e render.RowEvents
|
||||
}{
|
||||
"age_time": {
|
||||
re: render.RowEvents{
|
||||
{Row: render.Row{ID: "A", Fields: render.Fields{"1", "2", "1h10m10.5s"}}},
|
||||
{Row: render.Row{ID: "B", Fields: render.Fields{"0", "2", "10.5s"}}},
|
||||
{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()}}},
|
||||
{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", testTime().String()}}},
|
||||
},
|
||||
col: 2,
|
||||
asc: true,
|
||||
age: true,
|
||||
col: 2,
|
||||
asc: true,
|
||||
duration: true,
|
||||
e: render.RowEvents{
|
||||
{Row: render.Row{ID: "B", Fields: render.Fields{"0", "2", "10.5s"}}},
|
||||
{Row: render.Row{ID: "A", Fields: render.Fields{"1", "2", "1h10m10.5s"}}},
|
||||
{Row: render.Row{ID: "C", Fields: render.Fields{"10", "2", "3h20m5.2s"}}},
|
||||
},
|
||||
},
|
||||
"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"}}},
|
||||
{Row: render.Row{ID: "C", Fields: render.Fields{"10", "2", testTime().String()}}},
|
||||
{Row: render.Row{ID: "B", Fields: render.Fields{"0", "2", testTime().Add(10 * time.Second).String()}}},
|
||||
{Row: render.Row{ID: "A", Fields: render.Fields{"1", "2", testTime().Add(20 * time.Second).String()}}},
|
||||
},
|
||||
},
|
||||
"col0": {
|
||||
|
|
@ -483,7 +469,7 @@ func TestRowEventsSort(t *testing.T) {
|
|||
for k := range uu {
|
||||
u := uu[k]
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -318,25 +319,25 @@ func TestRowsSortDuration(t *testing.T) {
|
|||
}{
|
||||
"durationAsc": {
|
||||
rows: render.Rows{
|
||||
{Fields: []string{"10m10s", "duh"}},
|
||||
{Fields: []string{"19s", "blee"}},
|
||||
{Fields: []string{testTime().Add(10 * time.Second).String(), "duh"}},
|
||||
{Fields: []string{testTime().String(), "blee"}},
|
||||
},
|
||||
col: 0,
|
||||
asc: true,
|
||||
e: render.Rows{
|
||||
{Fields: []string{"19s", "blee"}},
|
||||
{Fields: []string{"10m10s", "duh"}},
|
||||
{Fields: []string{testTime().String(), "blee"}},
|
||||
{Fields: []string{testTime().Add(10 * time.Second).String(), "duh"}},
|
||||
},
|
||||
},
|
||||
"durationDesc": {
|
||||
rows: render.Rows{
|
||||
{Fields: []string{"10m10s", "duh"}},
|
||||
{Fields: []string{"19s", "blee"}},
|
||||
{Fields: []string{testTime().Add(10 * time.Second).String(), "duh"}},
|
||||
{Fields: []string{testTime().String(), "blee"}},
|
||||
},
|
||||
col: 0,
|
||||
e: render.Rows{
|
||||
{Fields: []string{"10m10s", "duh"}},
|
||||
{Fields: []string{"19s", "blee"}},
|
||||
{Fields: []string{testTime().Add(10 * time.Second).String(), "duh"}},
|
||||
{Fields: []string{testTime().String(), "blee"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,6 @@ func ComputeMaxColumns(pads MaxyPad, sortColName string, header render.Header, e
|
|||
var row int
|
||||
for _, e := range ee {
|
||||
for index, field := range e.Row.Fields {
|
||||
if header.IsTimeCol(index) {
|
||||
field = toAgeHuman(field)
|
||||
}
|
||||
width := len(field) + colPadding
|
||||
if index < len(pads) && width > pads[index] {
|
||||
pads[index] = width
|
||||
|
|
@ -60,10 +57,10 @@ func Pad(s string, width int) string {
|
|||
}
|
||||
|
||||
func toAgeHuman(s string) string {
|
||||
d, err := time.ParseDuration(s)
|
||||
t, err := time.Parse(time.RFC3339Nano, s)
|
||||
if err != nil {
|
||||
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 {
|
||||
s.SetSelectionChangedFunc(nil)
|
||||
}
|
||||
if s.model.Count() > 0 && r >= s.model.Count() {
|
||||
r = s.model.Count()
|
||||
if c := s.model.Count(); c > 0 && r-1 > c {
|
||||
r = c + 1
|
||||
}
|
||||
defer s.SetSelectionChangedFunc(s.selectionChanged)
|
||||
s.Select(r, 0)
|
||||
|
|
|
|||
|
|
@ -406,6 +406,10 @@ func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
return nil
|
||||
}
|
||||
|
||||
if client.IsAllNamespace(ns) {
|
||||
b.GetTable().SetSortCol("NAMESPACE", true)
|
||||
}
|
||||
|
||||
if err := b.app.switchNS(ns); err != nil {
|
||||
b.App().Flash().Err(err)
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -163,8 +163,7 @@ func (c *Command) defaultCmd() error {
|
|||
|
||||
if err := c.run(cmd, "", true); err != nil {
|
||||
log.Error().Err(err).Msgf("Default run command failed %q", cmd)
|
||||
c.app.cowCmd(err.Error())
|
||||
return err
|
||||
return c.run("pod", "", true)
|
||||
}
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// 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)]
|
||||
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 inverse {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -17,7 +16,7 @@ func TestEnvReplace(t *testing.T) {
|
|||
"simple": {arg: "$A", e: "10"},
|
||||
"substring": {arg: "$A and $AA", e: "10 and 20"},
|
||||
"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"},
|
||||
"dash": {arg: "$col0", e: "fred"},
|
||||
"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)
|
||||
|
||||
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, "<a>", strings.TrimSpace(v.GetCell(1, 0).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 {
|
||||
break
|
||||
}
|
||||
log.Debug().Msgf("FLUSH %q", string(lines[i]))
|
||||
_, _ = l.ansiWriter.Write(lines[i])
|
||||
}
|
||||
if l.follow {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ func (p *PortForwardExtender) portFwdCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
return nil
|
||||
}
|
||||
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
|
||||
}
|
||||
if err := showFwdDialog(p, podName, startFwdCB); err != nil {
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ func (p *Pod) bindKeys(aa 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.KeyShiftR: ui.NewKeyAction("Sort Ready", p.GetTable().SortColCmd(readyCol, true), 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...
|
||||
|
||||
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 {
|
||||
path := p.GetTable().GetSelectedItem()
|
||||
if path == "" {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ func TestPodNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, po.Init(makeCtx()))
|
||||
assert.Equal(t, "Pods", po.Name())
|
||||
assert.Equal(t, 24, len(po.Hints()))
|
||||
assert.Equal(t, 25, len(po.Hints()))
|
||||
}
|
||||
|
||||
// Helpers...
|
||||
|
|
|
|||
Loading…
Reference in New Issue