K9s/rel v0.28.0 (#2274)

* check for plausible overflow when seting up env

* Various fixes and clean up

* Introduce File transfer command

- Add ability to upload/download files from/to pods

* release v0.28.0
mine
Fernand Galiana 2023-11-07 17:22:41 -07:00 committed by GitHub
parent f4d2ca621a
commit d19c792feb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 543 additions and 171 deletions

View File

@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
else
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
endif
VERSION ?= v0.27.4
VERSION ?= v0.28.0
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}

View File

@ -0,0 +1,113 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
# Release v0.28.0
## Notes
Thank you to all that contributed with flushing out issues and enhancements for K9s! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make K9s better are, as ever, very much noted and appreciated! Also big thanks to all that have allocated their own time to help others on both slack and on this repo!!
As you may know, K9s is not pimped out by corps with deep pockets, thus if you feel K9s is helping your Kubernetes journey, please consider joining our [sponsorship program](https://github.com/sponsors/derailed) and/or make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
On Slack? Please join us [K9slackers](https://join.slack.com/t/k9sers/shared_invite/enQtOTA5MDEyNzI5MTU0LWQ1ZGI3MzliYzZhZWEyNzYxYzA3NjE0YTk1YmFmNzViZjIyNzhkZGI0MmJjYzhlNjdlMGJhYzE2ZGU1NjkyNTM)
---
## ♫ Sounds Behind The Release ♭
* [Moonlight Invasions - TribalNeed](https://www.youtube.com/watch?v=mJBnMSNIJL4&list=RDmJBnMSNIJL4&start_radio=1)
* [Teardrops - Neil Frances](https://www.youtube.com/watch?v=823_KoZr4mo)
* [Memory - Øystein Sevåg](https://www.youtube.com/watch?v=GKEM6lgkogY)
* [Tell me straight - Rolling Stones (Generated by KeithGPT 🐭)](https://www.youtube.com/watch?v=YxcxLi-Ld3E)
---
## A Word From Our Sponsors...
To all the good folks below that opted to `pay it forward` and join our sponsorship program, I salute you!!
* [Hyeon Woo Jo](https://github.com/dokdo2013)
* [Artsiom Kaval](https://github.com/lezeroq)
* [Grant Linville](https://github.com/g-linville)
* [Andrew Brown](https://github.com/andrew-werdna)
* [Patrik Votoček](https://github.com/Vrtak-CZ)
* [Erik Hebisch](https://github.com/flegelleicht)
* [Juliet Boyd](https://github.com/julietrb1)
* [Chris Vertonghen](https://github.com/chrisv)
* [Acsone](https://github.com/acsone)
* [Alex Viscreanu](https://github.com/aexvir)
* [Joey Guerra](https://github.com/joeyguerra)
* [Kijana Woodard](https://github.com/kijanawoodard)
* [Tom Saleeba](https://github.com/tomsaleeba)
> Sponsorship cancellations since the last release: `11` ;(
---
## Feature Release
### File Transfers in Da House!
Added ability to exchange files from your local machine to a pod or from a pod to your local machine. The pod view now surfaces a new command `t` to initiate the download/upload file transfers.
---
## Resolved Issues
* [Issue #2249](https://github.com/derailed/k9s/issues/2249) Sort on the capacity column should consider Gi and Mb also
* [Issue #2225](https://github.com/derailed/k9s/issues/2225) View logs of all pods of a given deployment
* [Issue #2195](https://github.com/derailed/k9s/issues/2195) Some pod logs are not displayed. But I can display it when I use the command
* [Issue #2194](https://github.com/derailed/k9s/issues/2194) 0.27.4 broke custom sort orders via views.yml
* [Issue #2185](https://github.com/derailed/k9s/issues/2185) No binaries for Linux_x86_64
* [Issue #2169](https://github.com/derailed/k9s/issues/2169) Add namespace name in ServiceAccount view with RoleBinding
* [Issue #2152](https://github.com/derailed/k9s/issues/2152) Latest opened namespace not being saved between k9s sessions
* [Issue #2131](https://github.com/derailed/k9s/issues/2131) deployments are not showing up, whereas kubectl gives a list
* [Issue #2130](https://github.com/derailed/k9s/issues/2130) Pending pods show 0/0 Ready instead of 0/x Ready
* [Issue #2128](https://github.com/derailed/k9s/issues/2128) k9s command not found after snap install
* [Issue #2121](https://github.com/derailed/k9s/issues/2121) colors for crds
* [Issue #2120](https://github.com/derailed/k9s/issues/2120) kustomize deletion not working as expected
* [Issue #2106](https://github.com/derailed/k9s/issues/2106) k9s delete behaves differently with kubectl
* [Issue #2085](https://github.com/derailed/k9s/issues/2085) When specifying the context command via the -c flag, selecting a cluster always returns to the context view
* [Issue #658](https://github.com/derailed/k9s/issues/658) Feature request: Easy way to copy/download files from a pod/pv to your local PC
---
## Contributed PRs
Please give `Big Thanks!` and `ATTA Girls/Boys!` to all the fine contributors for making K9s better for all of us!!
* [PR #2258](https://github.com/derailed/k9s/pull/2258) fix fsnotify watcher not fully working
* [PR #2253](https://github.com/derailed/k9s/pull/2253) fix manual sorting not working when sortColumn is configured
* [PR #2252](https://github.com/derailed/k9s/pull/2252) consider units when sorting capacity of pv and pvc
* [PR #2243](https://github.com/derailed/k9s/pull/2243) fix(typo): pdb header typo
* [PR #2239](https://github.com/derailed/k9s/pull/2239) fix: honor defaults from drain dialog in request
* [PR #2235](https://github.com/derailed/k9s/pull/2235) docs: add plugin.yml JSON schema
* [PR #2229](https://github.com/derailed/k9s/pull/2229) fix(log): clear bold log format after timestamp
* [PR #2188](https://github.com/derailed/k9s/pull/2188) Alias qa to quit
* [PR #2180](https://github.com/derailed/k9s/pull/2180) feat: Added support for arm in dockerfile
* [PR #2179](https://github.com/derailed/k9s/pull/2179) Focus command bar if active on startup
* [PR #2170](https://github.com/derailed/k9s/pull/2170) Add namespace for rolebinding on a clusterrole
* [PR #2161](https://github.com/derailed/k9s/pull/2161) Only apply keyConv to mnemonic in menus
* [PR #2158](https://github.com/derailed/k9s/pull/2158) Show the default container as the first entry
* [PR #2153](https://github.com/derailed/k9s/pull/2153) Changed checksums extension to checksums.sha256
* [PR #2158](https://github.com/derailed/k9s/pull/2158) Show the default container as the first entry
* [PR #2151](https://github.com/derailed/k9s/pull/2151) chore: pkg imported more than once
* [PR #2147](https://github.com/derailed/k9s/pull/2147) feat: plugin for adding an ephemeral debug container
* [PR #2141](https://github.com/derailed/k9s/pull/2141) Update plugin flux.yml with shortcuts for helm repo and oci repos
* [PR #2137](https://github.com/derailed/k9s/pull/2137) Correctly display the numbers in the Ready column of the pods view
* [PR #2136](https://github.com/derailed/k9s/pull/2136) Prompt window uses border styles
* [PR #2134](https://github.com/derailed/k9s/pull/2134) Remove unsupported key binding on users view
* [PR #2124](https://github.com/derailed/k9s/pull/2124) fix: add correct flags when deleting resources from Dir
* [PR #2119](https://github.com/derailed/k9s/pull/2119) feat: add indicator to title if toast is toggled
* [PR #2117](https://github.com/derailed/k9s/pull/2117) Add instruction how to install k9s through winget
* [PR #2112](https://github.com/derailed/k9s/pull/2112) Fix for styles
* [PR #2105](https://github.com/derailed/k9s/pull/2105) Fix the wrong/redundant icon in the prompt bar
* [PR #2103](https://github.com/derailed/k9s/pull/2103) Update carvel.yml to include contexts
* [PR #2096](https://github.com/derailed/k9s/pull/2096) fix: (config) only respect the --command flag once
* [PR #2091](https://github.com/derailed/k9s/pull/2091) Add get-all plugin specific for namespace view
* [PR #2089](https://github.com/derailed/k9s/pull/2089) Resources are rendered using skin.yaml colors
* [PR #2082](https://github.com/derailed/k9s/pull/2082) Fix typo introduced in #2045
---
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2023 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)

14
go.mod
View File

@ -1,6 +1,6 @@
module github.com/derailed/k9s
go 1.20
go 1.21
require (
github.com/adrg/xdg v0.4.0
@ -25,12 +25,12 @@ require (
gopkg.in/yaml.v2 v2.4.0
helm.sh/helm/v3 v3.13.1
k8s.io/api v0.28.3
k8s.io/apiextensions-apiserver v0.28.2
k8s.io/apiextensions-apiserver v0.28.3
k8s.io/apimachinery v0.28.3
k8s.io/cli-runtime v0.28.2
k8s.io/cli-runtime v0.28.3
k8s.io/client-go v0.28.3
k8s.io/klog/v2 v2.110.1
k8s.io/kubectl v0.28.2
k8s.io/klog/v2 v2.100.1
k8s.io/kubectl v0.28.3
k8s.io/metrics v0.28.3
sigs.k8s.io/yaml v1.3.0
)
@ -152,8 +152,8 @@ require (
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiserver v0.28.2 // indirect
k8s.io/component-base v0.28.2 // indirect
k8s.io/apiserver v0.28.3 // indirect
k8s.io/component-base v0.28.3 // indirect
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
oras.land/oras-go v1.2.4 // indirect

71
go.sum
View File

@ -7,6 +7,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
@ -19,14 +20,17 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/hcsshim v0.11.0 h1:7EFNIY4igHEXUdj1zXgAyU3fLc7QfOKHbkldRVTBdiM=
github.com/Microsoft/hcsshim v0.11.0/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
@ -38,9 +42,13 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -53,13 +61,16 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8=
github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4=
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -72,6 +83,7 @@ github.com/derailed/tcell/v2 v2.3.1-rc.3/go.mod h1:nf68BEL8fjmXQHJT3xZjoZFs2uXOz
github.com/derailed/tview v0.8.1 h1:hvNR3LLrWEuaQbPYfBnRn7bYkxCP26K6nX9J+MGlhyw=
github.com/derailed/tview v0.8.1/go.mod h1:q+odnnhO6QDPpBT+0dqaWj+X+uoJ6MJehXj9shgP+Cw=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI=
github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY=
github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
@ -83,11 +95,13 @@ github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNk
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ=
github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -101,8 +115,11 @@ github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwo
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI=
github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
@ -136,9 +153,13 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU=
github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs=
github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0=
github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY=
github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY=
github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@ -147,6 +168,7 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -162,6 +184,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
@ -179,12 +202,14 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@ -198,6 +223,7 @@ github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
@ -221,6 +247,7 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
@ -229,6 +256,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -247,11 +275,15 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI=
github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc=
github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY=
github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI=
github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@ -262,10 +294,12 @@ github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWV
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg=
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
@ -279,6 +313,7 @@ github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQ
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -303,6 +338,7 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE=
github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
@ -316,12 +352,14 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR
github.com/petergtz/pegomock v2.9.0+incompatible h1:BKfb5XfkJfehe5T+O1xD4Zm26Sb9dnRj7tHxLYwUPiI=
github.com/petergtz/pegomock v2.9.0+incompatible/go.mod h1:nuBLWZpVyv/fLo56qTwt/AUau7jgouO1h7bEvZCq82o=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY=
github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
@ -347,6 +385,7 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
@ -357,6 +396,7 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@ -397,9 +437,13 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
@ -422,6 +466,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -506,6 +551,7 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -558,30 +604,31 @@ gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
helm.sh/helm/v3 v3.13.1 h1:DG+XLGzBJeZvMLlMbm6bPDLV1dGaVW9eZsDoUd1/LM0=
helm.sh/helm/v3 v3.13.1/go.mod h1:TdQRMiq46CSWcc68Hb0uVhvAWusaN90YwAV54cz6JzU=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
k8s.io/apiextensions-apiserver v0.28.2 h1:J6/QRWIKV2/HwBhHRVITMLYoypCoPY1ftigDM0Kn+QU=
k8s.io/apiextensions-apiserver v0.28.2/go.mod h1:5tnkxLGa9nefefYzWuAlWZ7RZYuN/765Au8cWLA6SRg=
k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08=
k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc=
k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
k8s.io/apiserver v0.28.2 h1:rBeYkLvF94Nku9XfXyUIirsVzCzJBs6jMn3NWeHieyI=
k8s.io/apiserver v0.28.2/go.mod h1:f7D5e8wH8MWcKD7azq6Csw9UN+CjdtXIVQUyUhrtb+E=
k8s.io/cli-runtime v0.28.2 h1:64meB2fDj10/ThIMEJLO29a1oujSm0GQmKzh1RtA/uk=
k8s.io/cli-runtime v0.28.2/go.mod h1:bTpGOvpdsPtDKoyfG4EG041WIyFZLV9qq4rPlkyYfDA=
k8s.io/apiserver v0.28.3 h1:8Ov47O1cMyeDzTXz0rwcfIIGAP/dP7L8rWbEljRcg5w=
k8s.io/apiserver v0.28.3/go.mod h1:YIpM+9wngNAv8Ctt0rHG4vQuX/I5rvkEMtZtsxW2rNM=
k8s.io/cli-runtime v0.28.3 h1:lvuJYVkwCqHEvpS6KuTZsUVwPePFjBfSGvuaLl2SxzA=
k8s.io/cli-runtime v0.28.3/go.mod h1:jeX37ZPjIcENVuXDDTskG3+FnVuZms5D9omDXS/2Jjc=
k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
k8s.io/component-base v0.28.2 h1:Yc1yU+6AQSlpJZyvehm/NkJBII72rzlEsd6MkBQ+G0E=
k8s.io/component-base v0.28.2/go.mod h1:4IuQPQviQCg3du4si8GpMrhAIegxpsgPngPRR/zWpzc=
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI=
k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8=
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ=
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
k8s.io/kubectl v0.28.2 h1:fOWOtU6S0smdNjG1PB9WFbqEIMlkzU5ahyHkc7ESHgM=
k8s.io/kubectl v0.28.2/go.mod h1:6EQWTPySF1fn7yKoQZHYf9TPwIl2AygHEcJoxFekr64=
k8s.io/kubectl v0.28.3 h1:H1Peu1O3EbN9zHkJCcvhiJ4NUj6lb88sGPO5wrWIM6k=
k8s.io/kubectl v0.28.3/go.mod h1:RDAudrth/2wQ3Sg46fbKKl4/g+XImzvbsSRZdP2RiyE=
k8s.io/metrics v0.28.3 h1:w2s3kVi7HulXqCVDFkF4hN/OsL1tXTTb4Biif995h/g=
k8s.io/metrics v0.28.3/go.mod h1:OZZ23AHFojPzU6r3xoHGRUcV3I9pauLua+07sAUbwLc=
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=

View File

@ -19,6 +19,7 @@ import (
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
metricsapi "k8s.io/metrics/pkg/apis/metrics"
"k8s.io/metrics/pkg/client/clientset/versioned"
)
@ -32,6 +33,9 @@ const (
var supportedMetricsAPIVersions = []string{"v1beta1"}
// Namespaces tracks a collection of namespace names.
type Namespaces map[string]struct{}
// APIClient represents a Kubernetes api client.
type APIClient struct {
client, logClient kubernetes.Interface
@ -209,7 +213,7 @@ func (a *APIClient) ServerVersion() (*version.Info, error) {
// ValidNamespaces returns all available namespaces.
func (a *APIClient) ValidNamespaces() ([]v1.Namespace, error) {
if a == nil {
return []v1.Namespace{}, nil
return nil, fmt.Errorf("validNamespaces: no available client found")
}
if nn, ok := a.cache.Get("validNamespaces"); ok {
@ -282,6 +286,9 @@ func (a *APIClient) Config() *Config {
// HasMetrics checks if the cluster supports metrics.
func (a *APIClient) HasMetrics() bool {
err := a.supportsMetricsResources()
if err != nil {
log.Debug().Msgf("Metrics server detect failed: %s", err)
}
return err == nil
}
@ -348,6 +355,7 @@ func (a *APIClient) CachedDiscovery() (*disk.CachedDiscoveryClient, error) {
if err != nil {
return nil, err
}
httpCacheDir := filepath.Join(mustHomeDir(), ".kube", "http-cache")
discCacheDir := filepath.Join(mustHomeDir(), ".kube", "cache", "discovery", toHostDir(cfg.Host))
@ -446,7 +454,9 @@ func (a *APIClient) supportsMetricsResources() error {
a.cache.Add(cacheMXAPIKey, supported, cacheExpiry)
}()
dial, err := a.CachedDiscovery()
cfg := cmdutil.NewMatchVersionFlags(a.config.flags)
f := cmdutil.NewFactory(cfg)
dial, err := f.ToDiscoveryClient()
if err != nil {
log.Warn().Err(err).Msgf("Unable to dial discovery API")
return err

View File

@ -149,8 +149,8 @@ func (c *Config) RenameContext(old string, new string) error {
if err != nil {
return err
}
if err := clientcmd.ModifyConfig(acc, cfg, true); err != nil {
return err
if e := clientcmd.ModifyConfig(acc, cfg, true); e != nil {
return e
}
current, err := c.CurrentContextName()
if err != nil {

View File

@ -70,8 +70,8 @@ func (l *LogItem) Render(paint string, showTime bool, bb *bytes.Buffer) {
bb.WriteString("[gray::b]")
bb.Write(l.Bytes[:index])
bb.WriteString(" ")
for i := len(l.Bytes[:index]); i < 30; i++ {
bb.WriteByte(' ')
if l := 30 - len(l.Bytes[:index]); l > 0 {
bb.Write(bytes.Repeat([]byte{' '}, l))
}
bb.WriteString("[-::-]")
}

View File

@ -91,7 +91,7 @@ func TestLogItemRender(t *testing.T) {
}
}
func BenchmarkLogItemRender(b *testing.B) {
func BenchmarkLogItemRenderTS(b *testing.B) {
s := []byte(fmt.Sprintf("%s %s\n", "2018-12-14T10:36:43.326972-07:00", "Testing 1,2,3..."))
i := dao.NewLogItem(s)
i.Pod, i.Container = "fred", "blee"

View File

@ -2,6 +2,7 @@ package dao
import (
"context"
"errors"
"fmt"
"io"
@ -83,7 +84,9 @@ func (o DrainOptions) toDrainHelper(k kubernetes.Interface, w io.Writer) drain.H
// Drain drains a node.
func (n *Node) Drain(path string, opts DrainOptions, w io.Writer) error {
_ = n.ToggleCordon(path, true)
if err := n.ToggleCordon(path, true); err != nil {
return err
}
dial, err := n.GetFactory().Client().Dial()
if err != nil {
@ -97,7 +100,7 @@ func (n *Node) Drain(path string, opts DrainOptions, w io.Writer) error {
return err
}
}
return errs[0]
return errors.Join(errs...)
}
if err := h.DeleteOrEvictPods(dd.Pods()); err != nil {

View File

@ -181,7 +181,7 @@ func (p *Pod) GetInstance(fqn string) (*v1.Pod, error) {
func (p *Pod) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {
fac, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
if !ok {
return nil, errors.New("No factory in context")
return nil, errors.New("no factory in context")
}
o, err := fac.Get(p.gvr.String(), opts.Path, true, labels.Everything())
if err != nil {

View File

@ -0,0 +1,107 @@
package dialog
import (
"strings"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tview"
)
const confirmKey = "confirm"
type TransferFn func(from, to, co string, download, no_preserve bool) bool
type TransferDialogOpts struct {
Containers []string
Pod string
Title, Message string
Ack TransferFn
Cancel cancelFunc
}
func ShowUploads(styles config.Dialog, pages *ui.Pages, opts TransferDialogOpts) {
f := tview.NewForm()
f.SetItemPadding(0)
f.SetButtonsAlign(tview.AlignCenter).
SetButtonBackgroundColor(styles.ButtonBgColor.Color()).
SetButtonTextColor(styles.ButtonFgColor.Color()).
SetLabelColor(styles.LabelFgColor.Color()).
SetFieldTextColor(styles.FieldFgColor.Color())
f.AddButton("Cancel", func() {
dismissConfirm(pages)
opts.Cancel()
})
modal := tview.NewModalForm("<"+opts.Title+">", f)
from, to := opts.Pod, ""
var fromField, toField *tview.InputField
download := true
f.AddCheckbox("Download:", download, func(_ string, flag bool) {
if flag {
modal.SetText(strings.Replace(opts.Message, "Upload", "Download", 1))
} else {
modal.SetText(strings.Replace(opts.Message, "Download", "Upload", 1))
}
download = flag
from, to = to, from
fromField.SetText(from)
toField.SetText(to)
})
f.AddInputField("From:", from, 40, nil, func(t string) {
from = t
})
f.AddInputField("To:", to, 40, nil, func(t string) {
to = t
})
fromField, _ = f.GetFormItemByLabel("From:").(*tview.InputField)
toField, _ = f.GetFormItemByLabel("To:").(*tview.InputField)
var no_preserve bool
f.AddCheckbox("NoPreserve:", no_preserve, func(_ string, f bool) {
no_preserve = f
})
var co string
if len(opts.Containers) > 0 {
co = opts.Containers[0]
}
f.AddInputField("Container:", co, 30, nil, func(t string) {
co = t
})
f.AddButton("OK", func() {
if !opts.Ack(from, to, co, download, no_preserve) {
return
}
dismissConfirm(pages)
opts.Cancel()
})
for i := 0; i < 2; i++ {
b := f.GetButton(i)
if b == nil {
continue
}
b.SetBackgroundColorActivated(styles.ButtonFocusBgColor.Color())
b.SetLabelColorActivated(styles.ButtonFocusFgColor.Color())
}
f.SetFocus(0)
message := opts.Message
if len(opts.Containers) > 1 {
message += "\nAvailable Containers:" + strings.Join(opts.Containers, ",")
}
modal.SetText(message)
modal.SetTextColor(styles.FgColor.Color())
modal.SetDoneFunc(func(int, string) {
dismissConfirm(pages)
opts.Cancel()
})
pages.AddPage(confirmKey, modal, false, false)
pages.ShowPage(confirmKey)
}
func dismissConfirm(pages *ui.Pages) {
pages.RemovePage(confirmKey)
}

View File

@ -169,10 +169,9 @@ func (m *Menu) formatMenu(h model.MenuHint, size int) string {
// Helpers...
func keyConv(s string) string {
if !strings.Contains(s, "alt") {
if s == "" || !strings.Contains(s, "alt") {
return s
}
if runtime.GOOS != "darwin" {
return s
}
@ -185,8 +184,8 @@ func Truncate(str string, width int) string {
return runewidth.Truncate(str, width, string(tview.SemigraphicsHorizontalEllipsis))
}
func toMnemonic(s string) string {
if len(s) == 0 {
func ToMnemonic(s string) string {
if s == "" {
return s
}
@ -197,6 +196,7 @@ func formatNSMenu(i int, name string, styles config.Frame) string {
fmat := strings.Replace(menuIndexFmt, "[key", "["+styles.Menu.NumKeyColor.String(), 1)
fmat = strings.Replace(fmat, ":bg:", ":"+styles.Title.BgColor.String()+":", -1)
fmat = strings.Replace(fmat, "[fg", "["+styles.Menu.FgColor.String(), 1)
return fmt.Sprintf(fmat, i, name)
}
@ -205,5 +205,6 @@ func formatPlainMenu(h model.MenuHint, size int, styles config.Frame) string {
fmat := strings.Replace(menuFmt, "[key", "["+styles.Menu.KeyColor.String(), 1)
fmat = strings.Replace(fmat, "[fg", "["+styles.Menu.FgColor.String(), 1)
fmat = strings.Replace(fmat, ":bg:", ":"+styles.Title.BgColor.String()+":", -1)
return fmt.Sprintf(fmat, toMnemonic(h.Mnemonic), h.Description)
return fmt.Sprintf(fmat, ToMnemonic(h.Mnemonic), h.Description)
}

View File

@ -31,11 +31,12 @@ type (
// Table represents tabular data.
type Table struct {
gvr client.GVR
sortCol SortColumn
header render.Header
Path string
Extras string
gvr client.GVR
sortCol SortColumn
manualSort bool
header render.Header
Path string
Extras string
*SelectTable
actions KeyActions
cmdBuff *model.FishBuff
@ -46,7 +47,6 @@ type Table struct {
wide bool
toast bool
hasMetrics bool
manualSort bool
}
// NewTable returns a new table view.
@ -86,8 +86,7 @@ func (t *Table) GVR() client.GVR { return t.gvr }
// ViewSettingsChanged notifies listener the view configuration changed.
func (t *Table) ViewSettingsChanged(settings config.ViewSetting) {
t.manualSort = false // make user changes to the sortColumn take effect
t.viewSetting = &settings
t.viewSetting, t.manualSort = &settings, false
t.Refresh()
}
@ -208,7 +207,7 @@ func (t *Table) doUpdate(data *render.TableData) {
// if the sortCol has not been modified manually
if t.viewSetting != nil && t.viewSetting.SortColumn != "" && !t.manualSort {
tokens := strings.Split(t.viewSetting.SortColumn, ":")
if custData.Header.IndexOf(tokens[0], false) >= 0 {
if custData.Header.IndexOf(tokens[0], false) >= 0 && !t.manualSort {
t.sortCol.name, t.sortCol.asc = tokens[0], true
if len(tokens) == 2 && tokens[1] == "desc" {
t.sortCol.asc = false
@ -315,6 +314,7 @@ func (t *Table) buildRow(r int, re, ore render.RowEvent, h render.Header, pads M
// SortColCmd designates a sorted column.
func (t *Table) SortColCmd(name string, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
return func(evt *tcell.EventKey) *tcell.EventKey {
t.manualSort = true
t.sortCol.asc = !t.sortCol.asc
if t.sortCol.name != name {
t.sortCol.asc = asc

View File

@ -1,6 +1,7 @@
package view
import (
"errors"
"fmt"
"strings"
@ -101,7 +102,7 @@ func pluginActions(r Runner, aa ui.KeyActions) {
}
_, ok := aa[key]
if ok {
log.Warn().Err(fmt.Errorf("Doh! you are trying to override an existing command `%s", k)).Msg("Invalid shortcut")
log.Warn().Msgf("Invalid shortcut. You are trying to override an existing command `%s", k)
continue
}
aa[key] = ui.NewKeyAction(
@ -139,11 +140,20 @@ func pluginAction(r Runner, p config.Plugin) ui.ActionHandler {
pipes: p.Pipes,
args: args,
}
if run(r.App(), opts) {
r.App().Flash().Info("Plugin command launched successfully!")
suspend, errChan := run(r.App(), opts)
if !suspend {
r.App().Flash().Info("Plugin command failed!")
return
}
r.App().Flash().Info("Plugin command failed!")
var errs error
for e := range errChan {
errs = errors.Join(errs, e)
}
if errs != nil {
r.App().cowCmd(errs.Error())
return
}
r.App().Flash().Info("Plugin command launched successfully!")
}
if p.Confirm {
msg := fmt.Sprintf("Run?\n%s %s", p.Command, strings.Join(args, " "))

View File

@ -89,14 +89,14 @@ func (a *App) Init(version string, rate int) error {
a.SetInputCapture(a.keyboard)
a.bindKeys()
if a.Conn() == nil {
return errors.New("No client connection detected")
return errors.New("no client connection detected")
}
ns := a.Config.ActiveNamespace()
a.factory = watch.NewFactory(a.Conn())
ok, err := a.isValidNS(ns)
if !ok && err == nil {
return fmt.Errorf("Invalid namespace %s", ns)
return fmt.Errorf("invalid namespace %s", ns)
}
a.initFactory(ns)
@ -206,8 +206,7 @@ func (a *App) ActiveView() model.Component {
}
func (a *App) toggleHeader(header, logo bool) {
a.showHeader = header
a.showLogo = logo
a.showHeader, a.showLogo = header, logo
flex, ok := a.Main.GetPrimitive("main").(*tview.Flex)
if !ok {
log.Fatal().Msg("Expecting valid flex view")
@ -285,7 +284,7 @@ func (a *App) Resume() {
}
func (a *App) clusterUpdater(ctx context.Context) {
if err := a.refreshCluster(); err != nil {
if err := a.refreshCluster(ctx); err != nil {
log.Error().Err(err).Msgf("Cluster updater failed!")
return
}
@ -298,7 +297,7 @@ func (a *App) clusterUpdater(ctx context.Context) {
log.Debug().Msg("ClusterInfo updater canceled!")
return
case <-time.After(delay):
if err := a.refreshCluster(); err != nil {
if err := a.refreshCluster(ctx); err != nil {
log.Error().Err(err).Msgf("ClusterUpdater failed")
if delay = bf.NextBackOff(); delay == backoff.Stop {
a.BailOut()
@ -312,7 +311,7 @@ func (a *App) clusterUpdater(ctx context.Context) {
}
}
func (a *App) refreshCluster() error {
func (a *App) refreshCluster(context.Context) error {
c := a.Content.Top()
if ok := a.Conn().CheckConnectivity(); ok {
if atomic.LoadInt32(&a.conRetry) > 0 {
@ -338,7 +337,7 @@ func (a *App) refreshCluster() error {
}
if count > 0 {
a.Status(model.FlashWarn, fmt.Sprintf("Dial K8s Toast [%d/%d]", count, maxConnRetry))
return fmt.Errorf("Conn check failed (%d/%d)", count, maxConnRetry)
return fmt.Errorf("conn check failed (%d/%d)", count, maxConnRetry)
}
// Reload alias
@ -367,7 +366,7 @@ func (a *App) switchNS(ns string) error {
return err
}
if !ok {
return fmt.Errorf("Invalid namespace %q", ns)
return fmt.Errorf("invalid namespace %q", ns)
}
if err := a.Config.SetActiveNamespace(ns); err != nil {
return err

View File

@ -2,7 +2,6 @@ package view
import (
"context"
"errors"
"fmt"
"sort"
"strconv"
@ -145,7 +144,7 @@ func (b *Browser) Start() {
b.Table.Start()
b.CmdBuff().AddListener(b)
if err := b.GetModel().Watch(b.prepareContext()); err != nil {
b.App().Flash().Err(fmt.Errorf("Watcher failed for %s -- %w", b.GVR(), err))
b.App().Flash().Errf("Watcher failed for %s -- %s", b.GVR(), err)
}
}
@ -373,7 +372,7 @@ func (b *Browser) editCmd(evt *tcell.EventKey) *tcell.EventKey {
ns = n
}
if ok, err := b.app.Conn().CanI(ns, b.GVR().String(), []string{"patch"}); !ok || err != nil {
b.App().Flash().Err(fmt.Errorf("Current user can't edit resource %s", b.GVR()))
b.App().Flash().Errf("Current user can't edit resource %s", b.GVR())
return nil
}
@ -386,8 +385,8 @@ func (b *Browser) editCmd(evt *tcell.EventKey) *tcell.EventKey {
if ns != client.AllNamespaces {
args = append(args, "-n", ns)
}
if !runK(b.app, shellOpts{clear: true, args: args}) {
b.app.Flash().Err(errors.New("Edit exec failed"))
if err := runK(b.app, shellOpts{clear: true, args: args}); err != nil {
b.app.Flash().Errf("Edit command failed: %s", err)
}
}
@ -543,7 +542,7 @@ func (b *Browser) simpleDelete(selections []string, msg string) {
}
func (b *Browser) resourceDelete(selections []string, msg string) {
dialog.ShowDelete(b.app.Styles.Dialog(), b.app.Content.Pages, msg, func(propagation *metav1.DeletionPropagation, force bool) {
okFn := func(propagation *metav1.DeletionPropagation, force bool) {
b.ShowDeleted()
if len(selections) > 1 {
b.app.Flash().Infof("Delete %d marked %s", len(selections), b.GVR())
@ -563,5 +562,6 @@ func (b *Browser) resourceDelete(selections []string, msg string) {
b.GetTable().DeleteMark(sel)
}
b.refresh()
}, func() {})
}
dialog.ShowDelete(b.app.Styles.Dialog(), b.app.Content.Pages, msg, okFn, func() {})
}

View File

@ -83,7 +83,7 @@ func allowedXRay(gvr client.GVR) bool {
func (c *Command) xrayCmd(cmd string) error {
tokens := strings.Split(cmd, " ")
if len(tokens) < 2 {
return errors.New("You must specify a resource")
return errors.New("you must specify a resource")
}
gvr, ok := c.alias.AsGVR(tokens[1])
if !ok {
@ -108,7 +108,7 @@ func (c *Command) xrayCmd(cmd string) error {
return c.exec(cmd, "xrays", x, true)
}
// Exec the Command by showing associated display.
// Run execs the command by showing associated display.
func (c *Command) run(cmd, path string, clearStack bool) error {
if c.specialCmd(cmd, path) {
return nil
@ -119,6 +119,11 @@ func (c *Command) run(cmd, path string, clearStack bool) error {
if err != nil {
return err
}
var cns string
tt := strings.Split(gvr, " ")
if len(tt) == 2 {
gvr, cns = tt[0], tt[1]
}
switch command {
case "ctx", "context", "contexts":
@ -128,7 +133,7 @@ func (c *Command) run(cmd, path string, clearStack bool) error {
return c.exec(cmd, gvr, c.componentFor(gvr, path, v), clearStack)
case "dir":
if len(cmds) != 2 {
return errors.New("You must specify a directory")
return errors.New("you must specify a directory")
}
return c.app.dirCmd(cmds[1])
default:
@ -137,6 +142,9 @@ func (c *Command) run(cmd, path string, clearStack bool) error {
if len(cmds) == 2 {
ns = cmds[1]
}
if cns != "" {
ns = cns
}
if err := c.app.switchNS(ns); err != nil {
return err
}
@ -254,12 +262,12 @@ func (c *Command) exec(cmd, gvr string, comp model.Component, clearStack bool) (
} else {
_ = c.run(hh[0], "", true)
}
err = fmt.Errorf("Invalid command %q", cmd)
err = fmt.Errorf("invalid command %q", cmd)
}
}()
if comp == nil {
return fmt.Errorf("No component found for %s", gvr)
return fmt.Errorf("no component found for %s", gvr)
}
c.app.Flash().Infof("Viewing %s...", client.NewGVR(gvr).R())
if tokens := strings.Split(cmd, " "); len(tokens) >= 2 {

View File

@ -170,7 +170,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 exists on container %s", c.GetTable().Path))
c.App().Flash().Errf("A port-forward already exists on container %s", c.GetTable().Path)
return nil
}

View File

@ -118,7 +118,7 @@ func useContext(app *App, name string) error {
}
switcher, ok := res.(dao.Switchable)
if !ok {
return errors.New("Expecting a switchable resource")
return errors.New("expecting a switchable resource")
}
if err := switcher.Switch(name); err != nil {
log.Error().Err(err).Msgf("Context switch failed")

View File

@ -2,7 +2,6 @@ package view
import (
"context"
"errors"
"fmt"
"os"
"path"
@ -121,7 +120,7 @@ func (d *Dir) editCmd(evt *tcell.EventKey) *tcell.EventKey {
d.Stop()
defer d.Start()
if !edit(d.App(), shellOpts{clear: true, args: []string{sel}}) {
d.App().Flash().Err(errors.New("Failed to launch editor"))
d.App().Flash().Errf("Failed to launch editor")
}
return nil
@ -230,18 +229,18 @@ func (d *Dir) delCmd(evt *tcell.EventKey) *tcell.EventKey {
}
opts := []string{"-f"}
msgRessource := "manifest"
msgResource := "manifest"
if containsDir(sel) {
opts = append(opts, "-R")
}
if isKustomized(sel) {
opts = []string{"-k"}
msgRessource = "kustomization"
msgResource = "kustomization"
}
d.Stop()
defer d.Start()
msg := fmt.Sprintf("Delete resource(s) in %s %s", msgRessource, sel)
msg := fmt.Sprintf("Delete resource(s) in %s %s", msgResource, sel)
dialog.ShowConfirm(d.App().Styles.Dialog(), d.App().Content.Pages, "Confirm Delete", msg, func() {
args := make([]string, 0, 10)
args = append(args, "delete")

View File

@ -39,15 +39,17 @@ type shellOpts struct {
args []string
}
func runK(a *App, opts shellOpts) bool {
func (s shellOpts) String() string {
return fmt.Sprintf("%s %s", s.binary, strings.Join(s.args, " "))
}
func runK(a *App, opts shellOpts) error {
bin, err := exec.LookPath("kubectl")
if errors.Is(err, exec.ErrDot) {
log.Error().Err(err).Msgf("kubectl command must not be in the current working directory")
return false
return fmt.Errorf("kubectl command must not be in the current working directory: %w", err)
}
if err != nil {
log.Error().Err(err).Msgf("kubectl command is not in your path")
return false
return fmt.Errorf("kubectl command is not in your path: %w", err)
}
args := []string{opts.args[0]}
if u, err := a.Conn().Config().ImpersonateUser(); err == nil {
@ -66,20 +68,42 @@ func runK(a *App, opts shellOpts) bool {
if len(args) > 0 {
opts.args = append(args, opts.args[1:]...)
}
opts.binary, opts.background = bin, false
opts.binary = bin
return run(a, opts)
suspended, errChan := run(a, opts)
if !suspended {
return fmt.Errorf("unable to run command")
}
var errs error
for e := range errChan {
errs = errors.Join(errs, e)
}
return errs
}
func run(a *App, opts shellOpts) bool {
func run(a *App, opts shellOpts) (bool, chan error) {
errChan := make(chan error, 1)
if opts.background {
if err := execute(opts); err != nil {
errChan <- err
a.Flash().Errf("Exec failed %q: %s", opts, err)
}
close(errChan)
return true, errChan
}
a.Halt()
defer a.Resume()
return a.Suspend(func() {
if err := execute(opts); err != nil {
a.Flash().Errf("Command exited: %v", err)
errChan <- err
a.Flash().Errf("Exec failed %q: %s", opts, err)
}
})
close(errChan)
}), errChan
}
func edit(a *App, opts shellOpts) bool {
@ -93,7 +117,15 @@ func edit(a *App, opts shellOpts) bool {
}
opts.binary, opts.background = bin, false
return run(a, opts)
suspended, errChan := run(a, opts)
if !suspended {
a.Flash().Errf("edit command failed")
}
for e := range errChan {
a.Flash().Err(e)
return false
}
return true
}
func execute(opts shellOpts) error {
@ -113,8 +145,8 @@ func execute(opts shellOpts) error {
go func(cancel context.CancelFunc) {
defer log.Debug().Msgf("SIGNAL_GOR - BAILED!!")
select {
case <-sigChan:
log.Debug().Msgf("Command canceled with signal!")
case sig := <-sigChan:
log.Debug().Msgf("Command canceled with signal! %#v", sig)
cancel()
case <-ctx.Done():
log.Debug().Msgf("SIGNAL Context CANCELED!")
@ -123,7 +155,7 @@ func execute(opts shellOpts) error {
cmds := make([]*exec.Cmd, 0, 1)
cmd := exec.CommandContext(ctx, opts.binary, opts.args...)
log.Debug().Msgf("RUNNING> %s", cmd)
log.Debug().Msgf("RUNNING> %s", opts)
cmds = append(cmds, cmd)
for _, p := range opts.pipes {
@ -136,7 +168,14 @@ func execute(opts shellOpts) error {
cmds = append(cmds, cmd)
}
return pipe(ctx, opts, cmds...)
var o, e bytes.Buffer
err := pipe(ctx, opts, &o, &e, cmds...)
if err != nil {
log.Err(err).Msgf("Command failed")
return errors.Join(err, fmt.Errorf("%s", e.String()))
}
return nil
}
func runKu(a *App, opts shellOpts) (string, error) {
@ -209,18 +248,20 @@ func ssh(a *App, node string) error {
}
cl := a.Config.K9s.ActiveCluster()
if cl == nil {
return fmt.Errorf("no active cluster detected")
}
ns := cl.ShellPod.Namespace
sshIn(a, client.FQN(ns, k9sShellPodName()), k9sShell)
return nil
return sshIn(a, client.FQN(ns, k9sShellPodName()), k9sShell)
}
func sshIn(a *App, fqn, co string) {
func sshIn(a *App, fqn, co string) error {
cl := a.Config.K9s.ActiveCluster()
cfg := cl.ShellPod
os, err := getPodOS(a.factory, fqn)
if err != nil {
log.Warn().Err(err).Msgf("os detect failed")
return fmt.Errorf("os detect failed: %w", err)
}
args := buildShellArgs("exec", fqn, co, a.Conn().Config().Flags().KubeConfig)
@ -237,9 +278,12 @@ func sshIn(a *App, fqn, co string) {
log.Debug().Msgf("ARGS %#v", args)
c := color.New(color.BgGreen).Add(color.FgBlack).Add(color.Bold)
if !runK(a, shellOpts{clear: true, banner: c.Sprintf(bannerFmt, fqn, co), args: args}) {
a.Flash().Err(errors.New("Shell exec failed"))
err = runK(a, shellOpts{clear: true, banner: c.Sprintf(bannerFmt, fqn, co), args: args})
if err != nil {
return fmt.Errorf("shell exec failed: %w", err)
}
return nil
}
func nukeK9sShell(a *App) error {
@ -300,7 +344,7 @@ func launchShellPod(a *App, node string) error {
time.Sleep(k9sShellRetryDelay)
}
return fmt.Errorf("Unable to launch shell pod on node %s", node)
return fmt.Errorf("unable to launch shell pod on node %s", node)
}
func k9sShellPodName() string {
@ -376,7 +420,7 @@ func asResource(r config.Limits) v1.ResourceRequirements {
}
}
func pipe(_ context.Context, opts shellOpts, cmds ...*exec.Cmd) error {
func pipe(_ context.Context, opts shellOpts, w, e io.Writer, cmds ...*exec.Cmd) error {
if len(cmds) == 0 {
return nil
}
@ -384,31 +428,17 @@ func pipe(_ context.Context, opts shellOpts, cmds ...*exec.Cmd) error {
if len(cmds) == 1 {
cmd := cmds[0]
if opts.background {
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, log.Logger, log.Logger
return cmd.Start()
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, w, e
return cmd.Run()
}
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
// BOZO!!
//cmd.SysProcAttr = &syscall.SysProcAttr{
//// //Setpgid: true,
//// //Setctty: true,
// Foreground: true,
//}
_, _ = cmd.Stdout.Write([]byte(opts.banner))
log.Debug().Msgf("Running Start")
err := cmd.Run()
log.Debug().Msgf("Running Done")
return err
log.Debug().Msgf("Running Done: %s", err)
// BOZO!!
// select {
// case <-ctx.Done():
// return errors.New("canceled by operator")
// default:
// log.Debug().Msgf("PIPE RETURN %s", err)
// return err
// }
return err
}
last := len(cmds) - 1
@ -428,8 +458,5 @@ func pipe(_ context.Context, opts shellOpts, cmds ...*exec.Cmd) error {
}
}
log.Debug().Msgf("WAITING!!!")
err := cmds[len(cmds)-1].Wait()
log.Debug().Msgf("DONE WAITING!!!")
return err
return cmds[len(cmds)-1].Wait()
}

View File

@ -3,10 +3,8 @@ package view
import (
"context"
"fmt"
"runtime"
"sort"
"strconv"
"strings"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
@ -296,7 +294,7 @@ func (h *Help) addSection(c int, title string, hh model.MenuHints) {
for _, hint := range hh {
col := c
h.SetCell(row, col, padCellWithRef(toMnemonic(hint.Mnemonic), h.maxKey, hint.Mnemonic))
h.SetCell(row, col, padCellWithRef(ui.ToMnemonic(hint.Mnemonic), h.maxKey, hint.Mnemonic))
col++
h.SetCell(row, col, padCell(hint.Description, h.maxDesc))
row++
@ -348,14 +346,6 @@ func (h *Help) updateStyle() {
// ----------------------------------------------------------------------------
// Helpers...
func toMnemonic(s string) string {
if len(s) == 0 {
return s
}
return "<" + keyConv(strings.ToLower(s)) + ">"
}
func extractRef(c *tview.TableCell) string {
if ref, ok := c.GetReference().(string); ok {
return ref
@ -364,18 +354,6 @@ func extractRef(c *tview.TableCell) string {
return c.Text
}
func keyConv(s string) string {
if !strings.Contains(s, "alt") {
return s
}
if runtime.GOOS != "darwin" {
return s
}
return strings.Replace(s, "alt", "opt", 1)
}
func (h *Help) titleCell(title string) *tview.TableCell {
c := tview.NewTableCell(title)
c.SetTextColor(h.Styles().K9s.Help.SectionColor.Color())

View File

@ -21,7 +21,7 @@ func TestHelp(t *testing.T) {
v := view.NewHelp(app)
assert.Nil(t, v.Init(ctx))
assert.Equal(t, 26, v.GetRowCount())
assert.Equal(t, 27, 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))

View File

@ -81,7 +81,10 @@ func defaultEnv(c *client.Config, path string, header render.Header, row render.
env := k8sEnv(c)
env["NAMESPACE"], env["NAME"] = client.Namespaced(path)
for _, col := range header.Columns(true) {
env["COL-"+col] = row.Fields[header.IndexOf(col, true)]
i := header.IndexOf(col, true)
if i >= 0 && i < len(row.Fields) {
env["COL-"+col] = row.Fields[i]
}
}
return env
@ -140,7 +143,7 @@ func podCtx(app *App, path, labelSel, fieldSel string) ContextFunc {
func extractApp(ctx context.Context) (*App, error) {
app, ok := ctx.Value(internal.KeyApp).(*App)
if !ok {
return nil, errors.New("No application found in context")
return nil, errors.New("no application found in context")
}
return app, nil
@ -154,7 +157,7 @@ func asKey(key string) (tcell.Key, error) {
}
}
return 0, fmt.Errorf("No matching key found %s", key)
return 0, fmt.Errorf("no matching key found %s", key)
}
// FwFQN returns a fully qualified ns/name:container id.

View File

@ -62,7 +62,7 @@ func TestExtractApp(t *testing.T) {
err error
}{
"cool": {app: app},
"not-cool": {err: errors.New("No application found in context")},
"not-cool": {err: errors.New("no application found in context")},
}
for k := range uu {
@ -103,7 +103,7 @@ func TestAsKey(t *testing.T) {
e tcell.Key
}{
"cool": {k: "Ctrl-A", e: tcell.KeyCtrlA},
"miss": {k: "fred", e: 0, err: errors.New("No matching key found fred")},
"miss": {k: "fred", e: 0, err: errors.New("no matching key found fred")},
}
for k := range uu {

View File

@ -180,7 +180,7 @@ var selRx = regexp.MustCompile(`\A([\w-]+)/([\w-]+)\|([\w-]+)?\|(\d+):(\d+)`)
func pfToHuman(s string) (string, error) {
mm := selRx.FindStringSubmatch(s)
if len(mm) < 6 {
return "", fmt.Errorf("Unable to parse selection %s", s)
return "", fmt.Errorf("unable to parse selection %s", s)
}
return fmt.Sprintf("%s::%s %s->%s", mm[2], mm[3], mm[4], mm[5]), nil

View File

@ -113,7 +113,7 @@ func startFwdCB(v ResourceViewer, path string, pts port.PortTunnels) error {
tt := make([]string, 0, len(pts))
for _, pt := range pts {
if _, ok := v.App().factory.ForwarderFor(dao.PortForwardID(path, pt.Container, pt.PortMap())); ok {
return fmt.Errorf("A port-forward is already active on pod %s", path)
return fmt.Errorf("port-forward is already active on pod %s", path)
}
pf := dao.NewPortForwarder(v.App().factory)
fwd, err := pf.Start(path, pt)

View File

@ -4,6 +4,8 @@ import (
"context"
"errors"
"fmt"
"os"
"strings"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
@ -11,6 +13,7 @@ import (
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/tcell/v2"
"github.com/fatih/color"
"github.com/rs/zerolog/log"
@ -26,6 +29,8 @@ const (
powerShell = "powershell"
osBetaSelector = "beta.kubernetes.io/os"
osSelector = "kubernetes.io/os"
trUpload = "Upload"
trDownload = "Download"
)
// Pod represents a pod viewer.
@ -65,6 +70,7 @@ func (p *Pod) bindDangerousKeys(aa ui.KeyActions) {
tcell.KeyCtrlK: ui.NewKeyAction("Kill", p.killCmd, true),
ui.KeyS: ui.NewKeyAction("Shell", p.shellCmd, true),
ui.KeyA: ui.NewKeyAction("Attach", p.attachCmd, true),
ui.KeyT: ui.NewKeyAction("Transfer", p.transferCmd, true),
})
}
@ -249,6 +255,69 @@ func (p *Pod) attachCmd(evt *tcell.EventKey) *tcell.EventKey {
return nil
}
func (p *Pod) transferCmd(evt *tcell.EventKey) *tcell.EventKey {
path := p.GetTable().GetSelectedItem()
if path == "" {
return nil
}
ns, n := client.Namespaced(path)
ack := func(from, to, co string, download, no_preserve bool) bool {
local := to
if !download {
local = from
}
if _, err := os.Stat(local); !download && os.IsNotExist(err) {
p.App().Flash().Err(err)
return false
}
args := make([]string, 0, 10)
args = append(args, "cp")
args = append(args, strings.TrimSpace(from))
args = append(args, strings.TrimSpace(to))
args = append(args, fmt.Sprintf("--no-preserve=%t", no_preserve))
if co != "" {
args = append(args, "-c="+co)
}
opts := shellOpts{
background: true,
args: args,
}
op := trUpload
if download {
op = trDownload
}
fqn := path + ":" + co
if err := runK(p.App(), opts); err != nil {
p.App().cowCmd(err.Error())
} else {
p.App().Flash().Infof("%s successful on %s!", op, fqn)
}
return true
}
pod, err := fetchPod(p.App().factory, path)
if err != nil {
p.App().Flash().Err(err)
return nil
}
opts := dialog.TransferDialogOpts{
Title: "Transfer",
Containers: fetchContainers(pod.ObjectMeta, pod.Spec, false),
Message: "Download Files",
Pod: fmt.Sprintf("%s/%s:", ns, n),
Ack: ack,
Cancel: func() {},
}
dialog.ShowUploads(p.App().Styles.Dialog(), p.App().Content.Pages, opts)
return nil
}
// ----------------------------------------------------------------------------
// Helpers...
@ -291,8 +360,9 @@ func shellIn(a *App, fqn, co string) {
args := computeShellArgs(fqn, co, a.Conn().Config().Flags().KubeConfig, os)
c := color.New(color.BgGreen).Add(color.FgBlack).Add(color.Bold)
if !runK(a, shellOpts{clear: true, banner: c.Sprintf(bannerFmt, fqn, co), args: args}) {
a.Flash().Err(errors.New("Shell exec failed"))
err = runK(a, shellOpts{clear: true, banner: c.Sprintf(bannerFmt, fqn, co), args: args})
if err != nil {
a.Flash().Errf("Shell exec failed: %s", err)
}
}
@ -333,8 +403,8 @@ func resumeAttachIn(a *App, c model.Component, path, co string) {
func attachIn(a *App, path, co string) {
args := buildShellArgs("attach", path, co, a.Conn().Config().Flags().KubeConfig)
c := color.New(color.BgGreen).Add(color.FgBlack).Add(color.Bold)
if !runK(a, shellOpts{clear: true, banner: c.Sprintf(bannerFmt, path, co), args: args}) {
a.Flash().Err(errors.New("Attach exec failed"))
if err := runK(a, shellOpts{clear: true, banner: c.Sprintf(bannerFmt, path, co), args: args}); err != nil {
a.Flash().Errf("Attach exec failed: %s", err)
}
}

View File

@ -16,7 +16,7 @@ func TestPodNew(t *testing.T) {
assert.Nil(t, po.Init(makeCtx()))
assert.Equal(t, "Pods", po.Name())
assert.Equal(t, 25, len(po.Hints()))
assert.Equal(t, 26, len(po.Hints()))
}
// Helpers...

View File

@ -2,7 +2,6 @@ package view
import (
"context"
"errors"
"path/filepath"
"github.com/derailed/k9s/internal"
@ -49,6 +48,6 @@ func (s *ScreenDump) edit(app *App, model ui.Tabular, gvr, path string) {
s.Stop()
defer s.Start()
if !edit(app, shellOpts{clear: true, args: []string{path}}) {
app.Flash().Err(errors.New("Failed to launch editor"))
app.Flash().Errf("Failed to launch editor")
}
}

View File

@ -69,7 +69,7 @@ func (s *Service) showPods(a *App, _ ui.Tabular, gvr, path string) {
func (s *Service) checkSvc(svc *v1.Service) error {
if svc.Spec.Type != "NodePort" && svc.Spec.Type != "LoadBalancer" {
return errors.New("You must select a reachable service")
return errors.New("you must select a reachable service")
}
return nil
}
@ -83,7 +83,7 @@ func (s *Service) getExternalPort(svc *v1.Service) (string, error) {
// Grab the first port pair for now...
tokens := strings.Split(pp[0], "►")
if len(tokens) < 2 {
return "", errors.New("No ports pair found")
return "", errors.New("no ports pair found")
}
return tokens[1], nil
@ -142,7 +142,7 @@ func (s *Service) toggleBenchCmd(evt *tcell.EventKey) *tcell.EventKey {
// BOZO!! Refactor used by forwards.
func (s *Service) runBenchmark(port string, cfg config.BenchConfig) error {
if cfg.HTTP.Host == "" {
return fmt.Errorf("Invalid benchmark host %q", cfg.HTTP.Host)
return fmt.Errorf("invalid benchmark host %q", cfg.HTTP.Host)
}
var err error

View File

@ -2,7 +2,6 @@ package view
import (
"context"
"errors"
"fmt"
"regexp"
"strings"
@ -413,8 +412,8 @@ func (x *Xray) editCmd(evt *tcell.EventKey) *tcell.EventKey {
if cfg := x.app.Conn().Config().Flags().KubeConfig; cfg != nil && *cfg != "" {
args = append(args, "--kubeconfig", *cfg)
}
if !runK(x.app, shellOpts{args: append(args, n)}) {
x.app.Flash().Err(errors.New("Edit exec failed"))
if err := runK(x.app, shellOpts{args: append(args, n)}); err != nil {
x.app.Flash().Errf("Edit exec failed: %s", err)
}
}

View File

@ -63,7 +63,7 @@ func (ff Forwarders) IsPodForwarded(fqn string) bool {
// IsContainerForwarded checks if pod has a forward.
func (ff Forwarders) IsContainerForwarded(fqn, co string) bool {
prefix := fqn+"|"+co
prefix := fqn + "|" + co
for k := range ff {
if strings.HasPrefix(k, prefix) {
return true
@ -86,12 +86,11 @@ func (ff Forwarders) DeleteAll() {
func (ff Forwarders) Kill(path string) int {
var stats int
for k, f := range ff {
victim := k
if victim == path {
if strings.HasPrefix(k, path) {
stats++
log.Debug().Msgf("Stop + Delete port-forward %s", victim)
log.Debug().Msgf("Stop + Delete port-forward %s", k)
f.Stop()
delete(ff, victim)
delete(ff, k)
}
}