diff --git a/Makefile b/Makefile
index 098d42e459..3954a30aed 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
else
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
endif
-VERSION ?= v0.27.4
+VERSION ?= v0.28.0
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}
diff --git a/change_logs/release_v0.28.0.md b/change_logs/release_v0.28.0.md
new file mode 100644
index 0000000000..705ba72e74
--- /dev/null
+++ b/change_logs/release_v0.28.0.md
@@ -0,0 +1,113 @@
+
+
+# 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
+
+---
+
+ © 2023 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
diff --git a/go.mod b/go.mod
index a91a272f59..3aba6b95c3 100644
--- a/go.mod
+++ b/go.mod
@@ -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
diff --git a/go.sum b/go.sum
index 4ed6620b26..a3e09e5fe8 100644
--- a/go.sum
+++ b/go.sum
@@ -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=
diff --git a/internal/client/client.go b/internal/client/client.go
index 1eef5d60fd..a7fcf55889 100644
--- a/internal/client/client.go
+++ b/internal/client/client.go
@@ -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
diff --git a/internal/client/config.go b/internal/client/config.go
index bb6d63f954..e9373dfde6 100644
--- a/internal/client/config.go
+++ b/internal/client/config.go
@@ -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 {
diff --git a/internal/dao/log_item.go b/internal/dao/log_item.go
index 2deb3f177a..2a814bca4b 100644
--- a/internal/dao/log_item.go
+++ b/internal/dao/log_item.go
@@ -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("[-::-]")
}
diff --git a/internal/dao/log_item_test.go b/internal/dao/log_item_test.go
index 065dbfa0ae..0e4e78d8a9 100644
--- a/internal/dao/log_item_test.go
+++ b/internal/dao/log_item_test.go
@@ -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"
diff --git a/internal/dao/node.go b/internal/dao/node.go
index 09540d865e..b878d0ea81 100644
--- a/internal/dao/node.go
+++ b/internal/dao/node.go
@@ -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 {
diff --git a/internal/dao/pod.go b/internal/dao/pod.go
index dfa111f9bf..10eaa888a8 100644
--- a/internal/dao/pod.go
+++ b/internal/dao/pod.go
@@ -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 {
diff --git a/internal/ui/dialog/transfer.go b/internal/ui/dialog/transfer.go
new file mode 100644
index 0000000000..7d51cba408
--- /dev/null
+++ b/internal/ui/dialog/transfer.go
@@ -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)
+}
diff --git a/internal/ui/menu.go b/internal/ui/menu.go
index 41c02b8400..02cbedbe79 100644
--- a/internal/ui/menu.go
+++ b/internal/ui/menu.go
@@ -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)
}
diff --git a/internal/ui/table.go b/internal/ui/table.go
index ff9813e632..08d57436e9 100644
--- a/internal/ui/table.go
+++ b/internal/ui/table.go
@@ -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
diff --git a/internal/view/actions.go b/internal/view/actions.go
index 658b199bf2..ff6b2a1c71 100644
--- a/internal/view/actions.go
+++ b/internal/view/actions.go
@@ -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, " "))
diff --git a/internal/view/app.go b/internal/view/app.go
index 7c99521aa4..b0bf03566c 100644
--- a/internal/view/app.go
+++ b/internal/view/app.go
@@ -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
diff --git a/internal/view/browser.go b/internal/view/browser.go
index ab9f46f8c7..0678bfacd3 100644
--- a/internal/view/browser.go
+++ b/internal/view/browser.go
@@ -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() {})
}
diff --git a/internal/view/command.go b/internal/view/command.go
index 55dae27be9..1cc473fb5c 100644
--- a/internal/view/command.go
+++ b/internal/view/command.go
@@ -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 {
diff --git a/internal/view/container.go b/internal/view/container.go
index 1ccad276c8..9d39eda48e 100644
--- a/internal/view/container.go
+++ b/internal/view/container.go
@@ -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
}
diff --git a/internal/view/context.go b/internal/view/context.go
index fd57b741e4..4896d06a29 100644
--- a/internal/view/context.go
+++ b/internal/view/context.go
@@ -118,12 +118,12 @@ 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")
return err
}
-
+
return app.switchContext(name)
}
diff --git a/internal/view/dir.go b/internal/view/dir.go
index a16f91372e..38700a9345 100644
--- a/internal/view/dir.go
+++ b/internal/view/dir.go
@@ -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")
diff --git a/internal/view/exec.go b/internal/view/exec.go
index 95a95035c0..67c2366a86 100644
--- a/internal/view/exec.go
+++ b/internal/view/exec.go
@@ -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()
}
diff --git a/internal/view/help.go b/internal/view/help.go
index d9ce5085a8..fb95384c5e 100644
--- a/internal/view/help.go
+++ b/internal/view/help.go
@@ -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())
diff --git a/internal/view/help_test.go b/internal/view/help_test.go
index adeedac632..962109e072 100644
--- a/internal/view/help_test.go
+++ b/internal/view/help_test.go
@@ -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, "", strings.TrimSpace(v.GetCell(1, 0).Text))
assert.Equal(t, "Attach", strings.TrimSpace(v.GetCell(1, 1).Text))
diff --git a/internal/view/helpers.go b/internal/view/helpers.go
index 129d0740ea..da65c794e8 100644
--- a/internal/view/helpers.go
+++ b/internal/view/helpers.go
@@ -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.
diff --git a/internal/view/helpers_test.go b/internal/view/helpers_test.go
index 385cca50af..a2c182e202 100644
--- a/internal/view/helpers_test.go
+++ b/internal/view/helpers_test.go
@@ -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 {
diff --git a/internal/view/pf.go b/internal/view/pf.go
index 0d268089b1..c4063b9674 100644
--- a/internal/view/pf.go
+++ b/internal/view/pf.go
@@ -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
diff --git a/internal/view/pf_extender.go b/internal/view/pf_extender.go
index 4347c74327..42120c127a 100644
--- a/internal/view/pf_extender.go
+++ b/internal/view/pf_extender.go
@@ -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)
diff --git a/internal/view/pod.go b/internal/view/pod.go
index 38fb80bec8..bb1574ce35 100644
--- a/internal/view/pod.go
+++ b/internal/view/pod.go
@@ -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)
}
}
diff --git a/internal/view/pod_test.go b/internal/view/pod_test.go
index 22a3f7ae3b..6db6f4d00f 100644
--- a/internal/view/pod_test.go
+++ b/internal/view/pod_test.go
@@ -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...
diff --git a/internal/view/screen_dump.go b/internal/view/screen_dump.go
index 940588cb97..5f02fd39a9 100644
--- a/internal/view/screen_dump.go
+++ b/internal/view/screen_dump.go
@@ -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")
}
}
diff --git a/internal/view/svc.go b/internal/view/svc.go
index 292bee97d1..82a18d900b 100644
--- a/internal/view/svc.go
+++ b/internal/view/svc.go
@@ -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
diff --git a/internal/view/xray.go b/internal/view/xray.go
index 87a05b38c5..74fe8f8c54 100644
--- a/internal/view/xray.go
+++ b/internal/view/xray.go
@@ -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)
}
}
diff --git a/internal/watch/forwarders.go b/internal/watch/forwarders.go
index c69f8b9825..517a1ad4a9 100644
--- a/internal/watch/forwarders.go
+++ b/internal/watch/forwarders.go
@@ -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)
}
}