diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e4e7901c86..6133c17697 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,17 +15,18 @@ jobs: build: runs-on: ubuntu-20.04 steps: + - name: Checkout Code + uses: actions/checkout@v4.1.1 + - name: Install Go - uses: actions/setup-go@v3.5.0 + uses: actions/setup-go@v4.1.0 with: - go-version: 1.19 + go-version-file: go.mod + cache-dependency-path: go.sum - name: Setup GO env run: go env -w CGO_ENABLED=0 - - name: Checkout Code - uses: actions/checkout@v3.3.0 - - name: Run Tests run: make test env: diff --git a/.golangci.yml b/.golangci.yml index 4837e925b5..a13a58d5c3 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -365,7 +365,7 @@ linters-settings: gosimple: # Select the Go version to target. The default is '1.13'. - go: "1.15" + go: "1.20" # https://staticcheck.io/docs/options#checks checks: ["all"] @@ -515,13 +515,13 @@ linters-settings: staticcheck: # Select the Go version to target. The default is '1.13'. - go: "1.15" + go: "1.20" # https://staticcheck.io/docs/options#checks checks: ["all"] stylecheck: # Select the Go version to target. The default is '1.13'. - go: "1.15" + go: "1.20" # https://staticcheck.io/docs/options#checks checks: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022"] @@ -616,7 +616,7 @@ linters-settings: unused: # Select the Go version to target. The default is '1.13'. - go: "1.15" + go: "1.20" whitespace: multi-if: false # Enforces newlines (or comments) after every multi-line if statement diff --git a/.goreleaser.yml b/.goreleaser.yml index 4c7089c780..e19e0d4d67 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -33,8 +33,11 @@ archives: bit: Arm bitv6: Arm6 bitv7: Arm7 + format_overrides: + - goos: windows + format: zip checksum: - name_template: "checksums.txt" + name_template: "checksums.sha256" snapshot: name_template: "{{ .Tag }}-next" changelog: diff --git a/Dockerfile b/Dockerfile index d77ed5b788..6018cbedde 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # The base image for building the k9s binary -FROM golang:1.20.0-alpine3.16 AS build +FROM golang:1.20.4-alpine3.16 AS build WORKDIR /k9s COPY go.mod go.sum main.go Makefile ./ @@ -12,13 +12,14 @@ RUN apk --no-cache add --update make libx11-dev git gcc libc-dev curl && make bu # ----------------------------------------------------------------------------- # Build the final Docker image -FROM alpine:3.17.1 +FROM alpine:3.18.4 ARG KUBECTL_VERSION="v1.25.2" COPY --from=build /k9s/execs/k9s /bin/k9s RUN apk add --update ca-certificates \ && apk add --update -t deps curl vim \ - && curl -L https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl -o /usr/local/bin/kubectl \ + && TARGET_ARCH=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) \ + && curl -L https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/${TARGET_ARCH}/kubectl -o /usr/local/bin/kubectl \ && chmod +x /usr/local/bin/kubectl \ && apk del --purge deps \ && rm /var/cache/apk/* diff --git a/Makefile b/Makefile index 3a6c7e0e8f..e59570082b 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.3 +VERSION ?= v0.28.2 IMG_NAME := derailed/k9s IMAGE := ${IMG_NAME}:${VERSION} diff --git a/README.md b/README.md index b439539b88..43ce1db346 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ K9s is available on Linux, macOS and Windows platforms. * Via [Homebrew](https://brew.sh/) for macOS or Linux ```shell - brew install derailed/k9s/k9s + brew install k9s ``` * Via [MacPorts](https://www.macports.org) @@ -56,6 +56,11 @@ K9s is available on Linux, macOS and Windows platforms. ```shell sudo port install k9s ``` +* Via [snap](https://snapcraft.io/k9s) for Linux + + ```shell + snap install k9s --devmode + ``` * On Arch Linux @@ -69,6 +74,17 @@ K9s is available on Linux, macOS and Windows platforms. zypper install k9s ``` +* On FreeBSD + + ```shell + pkg install k9s + ``` + +* Via [Winget](https://github.com/microsoft/winget-cli) for Windows + ```shell + winget install k9s + ``` + * Via [Scoop](https://scoop.sh) for Windows ```shell @@ -186,6 +202,7 @@ K9s is available on Linux, macOS and Windows platforms. | k9s | k8s client | | ------------------ | ---------- | +| >= v0.27.0 | 0.26.1 | | v0.26.7 - v0.26.6 | 0.25.3 | | v0.26.5 - v0.26.4 | 0.25.1 | | v0.26.3 - v0.26.1 | 0.24.3 | @@ -253,8 +270,8 @@ K9s uses aliases to navigate most K8s resources. | Fuzzy find a resource given a filter | `/`-f filter⏎ | | | Bails out of view/command/filter mode | `` | | | Key mapping to describe, view, edit, view logs,... | `d`,`v`, `e`, `l`,... | | -| To view and switch to another Kubernetes context | `:`ctx⏎ | | -| To view and switch to another Kubernetes context | `:`ctx context-name⏎ | | +| To view and switch to another Kubernetes context (Pod view) | `:`ctx⏎ | | +| To view and switch directly to another Kubernetes context (Last used view) | `:`ctx context-name⏎ | | | To view and switch to another Kubernetes namespace | `:`ns⏎ | | | To view all saved resources | `:`screendump or sd⏎ | | | To delete a resource (TAB and ENTER to confirm) | `ctrl-d` | | @@ -307,6 +324,8 @@ K9s uses aliases to navigate most K8s resources. ```yaml # $XDG_CONFIG_HOME/k9s/config.yml k9s: + # Enable periodic refresh of resource browser windows. Default false + liveViewAutoRefresh: false # Represents ui poll intervals. Default 2secs refreshRate: 2 # Number of retries once the connection to the api-server is lost. Default 15. @@ -345,6 +364,8 @@ K9s uses aliases to navigate most K8s resources. currentContext: minikube # Indicates the current kube cluster. Defaults to current context cluster currentCluster: minikube + # KeepMissingClusters will keep clusters in the config if they are missing from the current kubeconfig file. Default false + KeepMissingClusters: false # Persists per cluster preferences for favorite namespaces and view. clusters: coolio: @@ -861,8 +882,13 @@ k9s: valueColor: royalblue # Logs styles. logs: - fgColor: white + fgColor: lightskyblue bgColor: black + indicator: + fgColor: dodgerblue + bgColor: black + toggleOnColor: limegreen + toggleOffColor: gray ``` --- @@ -903,6 +929,8 @@ to make this project a reality! * email fernand@imhotep.io * twitter [@kitesurfer](https://twitter.com/kitesurfer?lang=en) +* [Aleksei Romanenko](https://github.com/slimus) + We always enjoy hearing from folks who benefit from our work! ## Contributions Guideline diff --git a/assets/k9s-xmas.png b/assets/k9s-xmas.png index 979cf02c8a..e85148777a 100644 Binary files a/assets/k9s-xmas.png and b/assets/k9s-xmas.png differ diff --git a/assets/k9s.png b/assets/k9s.png index 36753e26f0..9137cb2194 100644 Binary files a/assets/k9s.png and b/assets/k9s.png differ diff --git a/assets/k9s_err.png b/assets/k9s_err.png index 4553b2150f..7bc1c542d2 100644 Binary files a/assets/k9s_err.png and b/assets/k9s_err.png differ diff --git a/assets/k9s_helm.png b/assets/k9s_helm.png index 6a5a21b252..472e406063 100644 Binary files a/assets/k9s_helm.png and b/assets/k9s_helm.png differ diff --git a/assets/k9s_small.png b/assets/k9s_small.png index 7f827fb1ae..1ccfd3a6d8 100644 Binary files a/assets/k9s_small.png and b/assets/k9s_small.png differ diff --git a/assets/k9s_xray.png b/assets/k9s_xray.png index 6fc25dfabd..a28453526b 100644 Binary files a/assets/k9s_xray.png and b/assets/k9s_xray.png differ diff --git a/assets/k9salpha.png b/assets/k9salpha.png index bfcff2ba90..30196bd30e 100644 Binary files a/assets/k9salpha.png and b/assets/k9salpha.png differ diff --git a/change_logs/release_v0.27.4.md b/change_logs/release_v0.27.4.md new file mode 100644 index 0000000000..6d63b6c6e9 --- /dev/null +++ b/change_logs/release_v0.27.4.md @@ -0,0 +1,74 @@ + + +# Release v0.27.4 + +## 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) + +--- +## Core Team... + +Please help me welcome Aleksei Romanenko(https://github.com/slimus) to the K9s contributor team!! +Alex is very knowledgeable in this space, kind and a great human being! +He has been instrumental with issues, prs and fielding questions in forums and slack. + +🎉 Welcome Alex!!🎉 + +--- + +## 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!! + +* [Jon Walton](https://github.com/jon-walton) +* [gmbnomis](https://github.com/gmbnomis) +* [Alex Viscreanu](https://github.com/aexvir) +* [Björn Petersen](https://github.com/BjoernPetersen) +* [Tanner Watson](https://github.com/tannerwatson) +* [Jabunovoty](https://github.com/jabunovoty) +* [Joey Guerra](https://github.com/joeyguerra) +* [Materialize Inc](https://github.com/MaterializeInc) +* [Kijana Woodard](https://github.com/kijanawoodard) +* [Tom Saleeba](https://github.com/tomsaleeba) +* [William Alexander](https://github.com/carpetfuz) +* [Süddeutsche Zeitung](https://github.com/sueddeutsche) + +> Sponsorship cancellations since the last release: `12` ;( + +--- + +## Maintenance Release + +--- + +## Resolved Issues + +* [Issue #2072](https://github.com/derailed/k9s/issues/2072) Triggered Job from cronjob is missing annotations +* [Issue #2024](https://github.com/derailed/k9s/issues/2024) Allow customization of log indicators with skin theme +* [Issue #1971](https://github.com/derailed/k9s/issues/1971) Zip binary for windows + +--- + +## Contributed PRs + +Please give `Big Thanks!` and `ATTA Girls/Boys!` to all the fine contributors for making K9s better for all of us!! + +* [PR #2073](https://github.com/derailed/k9s/pull/2073) Fix for missing Job annotations created from Cronjob +* [PR #2069](https://github.com/derailed/k9s/pull/2069) Unify all go version to 1.20 +* [PR #2067](https://github.com/derailed/k9s/pull/2067) Create narsingh skin +* [PR #2054](https://github.com/derailed/k9s/pull/2054) Update setup-go action, with caching +* [PR #2045](https://github.com/derailed/k9s/pull/2045) Fix: (views) use saved context view when switching +* [PR #2041](https://github.com/derailed/k9s/pull/2041) Feat: allow customization of log indicator toggles +* [PR #2030](https://github.com/derailed/k9s/pull/2030) Updated monokai skin with help styles, and more monokai appropriate colors +* [PR #2027](https://github.com/derailed/k9s/pull/2027) Roles are rendered using same colorer function from skin +* [PR #2045](https://github.com/derailed/k9s/pull/2045) Fix: (views) use saved context view when switching\ +* [PR #2011](https://github.com/derailed/k9s/pull/2011) Fix #2007: Remove debug command + +--- + + © 2023 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 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/change_logs/release_v0.28.1.md b/change_logs/release_v0.28.1.md new file mode 100644 index 0000000000..bb7cd0774b --- /dev/null +++ b/change_logs/release_v0.28.1.md @@ -0,0 +1,63 @@ + + +# Release v0.28.1 + +## 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 ♭ + +* [If Trouble Was Money - Albert Collins](https://www.youtube.com/watch?v=cz6LbWWqX-g) +* [Old Love - Eric Clapton](https://www.youtube.com/watch?v=EklciRHZnUQ) +* [Touch And GO - The Cars](https://www.youtube.com/watch?v=L7Gpr_Auz8Y) + +--- + +## 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!! + +* [Bradley Heilbrun](https://github.com/bheilbrun) + +> Sponsorship cancellations since the last release: `2` ;( + +--- + +## Feature Release + +### Sanitize Me! + +Over time, you might end up with a lot of pod cruft on your cluster. Pods that might be completed, erroring out, etc... Once you've completed your pod analysis it could be useful to clear out these pods from your cluster. + +In this drop, we introduce a new command `sanitize` aka `z` available on pod views otherwise known as `The Axe!`. This command performs a clean up of all pods that are in either in completed, crashloopBackoff or failed state. This could be especially handy if you run workflows jobs or commands on your cluster that might leave lots of `turd` pods. Tho this has a `phat` fail safe dialog please be careful with this one as it is a blunt tool! + +--- + +## Resolved Issues + +* [Issue #2281](https://github.com/derailed/k9s/issues/2281) Can't run Node shell +* [Issue #2277](https://github.com/derailed/k9s/issues/2277) bulk actions applied to power filters +* [Issue #2273](https://github.com/derailed/k9s/issues/2273) Error when draining node that is cordoned bug +* [Issue #2233](https://github.com/derailed/k9s/issues/2233) Invalid port-forwarding status displayed over the k9s UI + +--- + +## Contributed PRs + +Please be sure to give `Big Thanks!` and `ATTA Girls/Boys!` to all the fine contributors for making K9s better for all of us!! + +* [PR #2280](https://github.com/derailed/k9s/pull/2280) chore: replace github.com/ghodss/yaml with sigs.k8s. +* [PR #2278](https://github.com/derailed/k9s/pull/2278) README.md: fix typo in netshoot URL +* [PR #2275](https://github.com/derailed/k9s/pull/2275) check if the Node already cordoned when executing Drain +* [PR #2247](https://github.com/derailed/k9s/pull/2247) Delete port forwards when pods get deleted + +--- + + © 2023 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) diff --git a/change_logs/release_v0.28.2.md b/change_logs/release_v0.28.2.md new file mode 100644 index 0000000000..94514d8638 --- /dev/null +++ b/change_logs/release_v0.28.2.md @@ -0,0 +1,63 @@ + + +# Release v0.28.2 + +## 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 ♭ + +* [If Trouble Was Money - Albert Collins](https://www.youtube.com/watch?v=cz6LbWWqX-g) +* [Old Love - Eric Clapton](https://www.youtube.com/watch?v=EklciRHZnUQ) +* [Touch And GO - The Cars](https://www.youtube.com/watch?v=L7Gpr_Auz8Y) + +--- + +## 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!! + +* [Bradley Heilbrun](https://github.com/bheilbrun) + +> Sponsorship cancellations since the last release: `2` ;( + +--- + +## Feature Release + +### Sanitize Me! + +Over time, you might end up with a lot of pod cruft on your cluster. Pods that might be completed, erroring out, etc... Once you've completed your pod analysis it could be useful to clear out these pods from your cluster. + +In this drop, we introduce a new command `sanitize` aka `z` available on pod views otherwise known as `The Axe!`. This command performs a clean up of all pods that are in either in completed, crashloopBackoff or failed state. This could be especially handy if you run workflows jobs or commands on your cluster that might leave lots of `turd` pods. Tho this has a `phat` fail safe dialog please be careful with this one as it is a blunt tool! + +--- + +## Resolved Issues + +* [Issue #2281](https://github.com/derailed/k9s/issues/2281) Can't run Node shell +* [Issue #2277](https://github.com/derailed/k9s/issues/2277) bulk actions applied to power filters +* [Issue #2273](https://github.com/derailed/k9s/issues/2273) Error when draining node that is cordoned bug +* [Issue #2233](https://github.com/derailed/k9s/issues/2233) Invalid port-forwarding status displayed over the k9s UI + +--- + +## Contributed PRs + +Please be sure to give `Big Thanks!` and `ATTA Girls/Boys!` to all the fine contributors for making K9s better for all of us!! + +* [PR #2280](https://github.com/derailed/k9s/pull/2280) chore: replace github.com/ghodss/yaml with sigs.k8s. +* [PR #2278](https://github.com/derailed/k9s/pull/2278) README.md: fix typo in netshoot URL +* [PR #2275](https://github.com/derailed/k9s/pull/2275) check if the Node already cordoned when executing Drain +* [PR #2247](https://github.com/derailed/k9s/pull/2247) Delete port forwards when pods get deleted + +--- + + © 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 fd65e65cbc..b244e8b4ff 100644 --- a/go.mod +++ b/go.mod @@ -1,159 +1,165 @@ module github.com/derailed/k9s -go 1.19 +go 1.21 require ( github.com/adrg/xdg v0.4.0 github.com/atotto/clipboard v0.1.4 - github.com/cenkalti/backoff/v4 v4.2.0 - github.com/derailed/popeye v0.10.1 + github.com/cenkalti/backoff/v4 v4.2.1 + github.com/derailed/popeye v0.11.1 github.com/derailed/tcell/v2 v2.3.1-rc.3 github.com/derailed/tview v0.8.1 - github.com/fatih/color v1.14.1 - github.com/fsnotify/fsnotify v1.6.0 - github.com/fvbommel/sortorder v1.0.2 - github.com/ghodss/yaml v1.0.0 + github.com/fatih/color v1.16.0 + github.com/fsnotify/fsnotify v1.7.0 + github.com/fvbommel/sortorder v1.1.0 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-runewidth v0.0.14 github.com/petergtz/pegomock v2.9.0+incompatible github.com/rakyll/hey v0.1.4 - github.com/rs/zerolog v1.29.0 + github.com/rs/zerolog v1.31.0 github.com/sahilm/fuzzy v0.1.0 - github.com/spf13/cobra v1.6.1 - github.com/stretchr/testify v1.8.1 - golang.org/x/text v0.6.0 + github.com/spf13/cobra v1.8.0 + github.com/stretchr/testify v1.8.4 + golang.org/x/text v0.13.0 gopkg.in/yaml.v2 v2.4.0 - helm.sh/helm/v3 v3.11.1 - k8s.io/api v0.26.1 - k8s.io/apiextensions-apiserver v0.26.1 - k8s.io/apimachinery v0.26.1 - k8s.io/cli-runtime v0.26.1 - k8s.io/client-go v0.26.1 - k8s.io/klog/v2 v2.90.0 - k8s.io/kubectl v0.26.1 - k8s.io/metrics v0.26.1 + helm.sh/helm/v3 v3.13.1 + k8s.io/api v0.28.3 + k8s.io/apiextensions-apiserver v0.28.3 + k8s.io/apimachinery v0.28.3 + k8s.io/cli-runtime v0.28.3 + k8s.io/client-go v0.28.3 + k8s.io/klog/v2 v2.100.1 + k8s.io/kubectl v0.28.3 + k8s.io/kubernetes v1.28.3 + k8s.io/metrics v0.28.3 sigs.k8s.io/yaml v1.3.0 ) require ( + github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/BurntSushi/toml v1.2.1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect - github.com/Masterminds/squirrel v1.5.3 // indirect + github.com/Masterminds/squirrel v1.5.4 // indirect + github.com/Microsoft/hcsshim v0.11.0 // indirect github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect github.com/aws/aws-sdk-go v1.38.49 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/containerd/containerd v1.6.15 // indirect - github.com/cyphar/filepath-securejoin v0.2.3 // indirect + github.com/containerd/containerd v1.7.6 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.21+incompatible // indirect - github.com/docker/distribution v2.8.1+incompatible // indirect - github.com/docker/docker v20.10.21+incompatible // indirect + github.com/docker/cli v24.0.6+incompatible // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker v24.0.7+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect - github.com/docker/go-units v0.4.0 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/emicklei/go-restful/v3 v3.10.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/gdamore/encoding v1.0.0 // indirect - github.com/go-errors/errors v1.0.1 // indirect - github.com/go-gorp/gorp/v3 v3.0.2 // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect - github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-gorp/gorp/v3 v3.1.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.0.1 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect - github.com/huandu/xstrings v1.3.3 // indirect - github.com/imdario/mergo v0.3.12 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/huandu/xstrings v1.4.0 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.13.6 // indirect + github.com/klauspost/compress v1.16.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/lib/pq v1.10.7 // indirect + github.com/lib/pq v1.10.9 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-wordwrap v1.0.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/sys/mountinfo v0.6.0 // indirect - github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/onsi/gomega v1.23.0 // indirect + github.com/onsi/gomega v1.27.6 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc2 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect github.com/rivo/uniseg v0.4.3 // indirect github.com/rm-hull/colorjson v0.0.0-20220923220430-b50ee91dc6f6 // indirect - github.com/rubenv/sql-migrate v1.2.0 // indirect + github.com/rubenv/sql-migrate v1.5.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/shopspring/decimal v1.2.0 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/spf13/cast v1.4.1 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spf13/cast v1.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/xlab/treeprint v1.1.0 // indirect - go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect - golang.org/x/crypto v0.5.0 // indirect - golang.org/x/net v0.5.0 // indirect - golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/term v0.4.0 // indirect - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + github.com/xlab/treeprint v1.2.0 // indirect + go.opentelemetry.io/otel v1.14.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect + go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect - google.golang.org/grpc v1.49.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect + google.golang.org/grpc v1.56.3 // indirect + 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.26.1 // indirect - k8s.io/component-base v0.26.1 // indirect - k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect - k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect - oras.land/oras-go v1.2.2 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/kustomize/api v0.12.1 // indirect - sigs.k8s.io/kustomize/kyaml v0.13.9 // 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 + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect + sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect ) diff --git a/go.sum b/go.sum index 2f99bc7efb..4a42b0625c 100644 --- a/go.sum +++ b/go.sum @@ -1,77 +1,36 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +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.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= -github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= -github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= -github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= +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/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 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= @@ -82,81 +41,70 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= 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/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= -github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +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= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= -github.com/containerd/containerd v1.6.15 h1:4wWexxzLNHNE46aIETc6ge4TofO550v+BlLoANrbses= -github.com/containerd/containerd v1.6.15/go.mod h1:U2NnBPIhzJDm59xF7xB2MMHnKtggpZ+phKg8o2TKj2c= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +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/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +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= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/derailed/popeye v0.10.1 h1:+lyLDGUyKANfMiSHV8X1pHD1HhcxEXCa8ple0ZvyRqc= -github.com/derailed/popeye v0.10.1/go.mod h1:ChD8GPNvgsgjLG5fQBZIxm3VOhFrqd10Xn8IxfuGn4w= +github.com/derailed/popeye v0.11.1 h1:bjt5mXkcXY696ipuJqwY1sa5s3i431L9BlkQc6EuaqE= +github.com/derailed/popeye v0.11.1/go.mod h1:NkvjHH1F94tE7Ui17PlYiagQcFt7yXUV2hIhPzSK+0w= github.com/derailed/tcell/v2 v2.3.1-rc.3 h1:9s1fmyRcSPRlwr/C9tcpJKCujbrtmPpST6dcMUD2piY= github.com/derailed/tcell/v2 v2.3.1-rc.3/go.mod h1:nf68BEL8fjmXQHJT3xZjoZFs2uXOzyJcNAQqGUEMrFY= 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/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= -github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog= -github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +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= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= 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.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +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= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -164,52 +112,47 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwC github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= -github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +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.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo= -github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= +github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4= -github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= +github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 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= @@ -219,30 +162,16 @@ github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXs 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= -github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 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= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -250,60 +179,36 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +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 v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +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= @@ -311,41 +216,22 @@ github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +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/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +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.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -354,33 +240,22 @@ github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= -github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI= +github.com/karrick/godirwalk v1.17.0/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.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 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= @@ -392,60 +267,43 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtB github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= 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.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +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.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 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.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw= -github.com/mattn/go-sqlite3 v1.14.14/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.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= +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= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -453,10 +311,10 @@ github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= 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.0 h1:gUDhXQx58YNrpHlK4nSL+7y2pxFZkUcXqzFDKWdC0Oo= -github.com/moby/sys/mountinfo v0.6.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +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= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -471,72 +329,55 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 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.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= +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.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= -github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= -github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +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.8.1/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/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg= -github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= +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= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rakyll/hey v0.1.4 h1:hhc8GIqHN4+rPFZvkM9lkCQGi7da0sINM83xxpFkbPA= github.com/rakyll/hey v0.1.4/go.mod h1:nAOTOo+L52KB9SZq/M6J18kxjto4yVtXQDjU2HgjUPI= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -544,45 +385,32 @@ github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rm-hull/colorjson v0.0.0-20220923220430-b50ee91dc6f6 h1:avrA8y9AJF9WtGipEvrM8I/7XoKcxEk30659rPHJlnM= github.com/rm-hull/colorjson v0.0.0-20220923220430-b50ee91dc6f6/go.mod h1:tJTNxpJk1e15vd8WY5lsj9Tq5vjdnNz3YAbCwxYskBs= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= -github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/rubenv/sql-migrate v1.2.0 h1:fOXMPLMd41sK7Tg75SXDec15k3zg5WNV6SjuDRiNfcU= -github.com/rubenv/sql-migrate v1.2.0/go.mod h1:Z5uVnq7vrIrPmHbVFfR4YLHRZquxeHpckCnRq0P/K9Y= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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= +github.com/rubenv/sql-migrate v1.5.2 h1:bMDqOnrJVV/6JQgQ/MxOpU+AdO8uzYYA/TxFUBzFtS0= +github.com/rubenv/sql-migrate v1.5.2/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWxs+kmzlg0Is= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +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/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -590,431 +418,159 @@ github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= -github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 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/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= -github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +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= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/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= golang.org/x/net v0.0.0-20181017193950-04a2e542c03f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= 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= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= +google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1023,87 +579,71 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -helm.sh/helm/v3 v3.11.1 h1:cmL9fFohOoNQf+wnp2Wa0OhNFH0KFnSzEkVxi3fcc3I= -helm.sh/helm/v3 v3.11.1/go.mod h1:z/Bu/BylToGno/6dtNGuSmjRqxKq5gaH+FU0BPO+AQ8= +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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= -k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= -k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= -k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= -k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= -k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= -k8s.io/apiserver v0.26.1 h1:6vmnAqCDO194SVCPU3MU8NcDgSqsUA62tBUSWrFXhsc= -k8s.io/apiserver v0.26.1/go.mod h1:wr75z634Cv+sifswE9HlAo5FQ7UoUauIICRlOE+5dCg= -k8s.io/cli-runtime v0.26.1 h1:f9+bRQ1V3elQsx37KmZy5fRAh56mVLbE9A7EMdlqVdI= -k8s.io/cli-runtime v0.26.1/go.mod h1:+e5Ym/ARySKscUhZ8K3hZ+ZBo/wYPIcg+7b5sFYi6Gg= -k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= -k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= -k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= -k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= -k8s.io/klog/v2 v2.90.0 h1:VkTxIV/FjRXn1fgNNcKGM8cfmL1Z33ZjXRTVxKCoF5M= -k8s.io/klog/v2 v2.90.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= -k8s.io/kubectl v0.26.1 h1:K8A0Jjlwg8GqrxOXxAbjY5xtmXYeYjLU96cHp2WMQ7s= -k8s.io/kubectl v0.26.1/go.mod h1:miYFVzldVbdIiXMrHZYmL/EDWwJKM+F0sSsdxsATFPo= -k8s.io/metrics v0.26.1 h1:iB+QdMLa2V70a7zb0XYEcaUpPM0y+p4fZN0UtxcPHLk= -k8s.io/metrics v0.26.1/go.mod h1:fMeLXmK/xgvckFG63GJ0kDjFiQH7P0Dpi5Lvhlo5DXE= -k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= -k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -oras.land/oras-go v1.2.2 h1:0E9tOHUfrNH7TCDk5KU0jVBEzCqbfdyuVfGmJ7ZeRPE= -oras.land/oras-go v1.2.2/go.mod h1:Apa81sKoZPpP7CDciE006tSZ0x3Q3+dOoBcMZ/aNxvw= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= -sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= -sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= -sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= +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.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.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.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.3 h1:H1Peu1O3EbN9zHkJCcvhiJ4NUj6lb88sGPO5wrWIM6k= +k8s.io/kubectl v0.28.3/go.mod h1:RDAudrth/2wQ3Sg46fbKKl4/g+XImzvbsSRZdP2RiyE= +k8s.io/kubernetes v1.28.3 h1:XTci6gzk+JR51UZuZQCFJ4CsyUkfivSjLI4O1P9z6LY= +k8s.io/kubernetes v1.28.3/go.mod h1:NhAysZWvHtNcJFFHic87ofxQN7loylCQwg3ZvXVDbag= +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= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +oras.land/oras-go v1.2.4 h1:djpBY2/2Cs1PV87GSJlxv4voajVOMZxqqtq9AB8YNvY= +oras.land/oras-go v1.2.4/go.mod h1:DYcGfb3YF1nKjcezfX2SNlDAeQFKSXmf+qrFmrh4324= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM7vh3b7HvGNfXrJ/xL6BDMS0v1V/HHg5U= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= 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 7fa4321869..6086ac3159 100644 --- a/internal/client/config.go +++ b/internal/client/config.go @@ -94,6 +94,19 @@ func (c *Config) CurrentContextName() (string, error) { return cfg.CurrentContext, nil } +func (c *Config) CurrentContextNamespace() (string, error) { + name, err := c.CurrentContextName() + if err != nil { + return "", err + } + context, err := c.GetContext(name) + if err != nil { + return "", err + } + + return context.Namespace, nil +} + // GetContext fetch a given context or error if it does not exists. func (c *Config) GetContext(n string) (*clientcmdapi.Context, error) { cfg, err := c.RawConfig() @@ -133,6 +146,36 @@ func (c *Config) DelContext(n string) error { return clientcmd.ModifyConfig(acc, cfg, true) } +// RenameContext renames a context. +func (c *Config) RenameContext(old string, new string) error { + cfg, err := c.RawConfig() + if err != nil { + return err + } + + if _, ok := cfg.Contexts[new]; ok { + return fmt.Errorf("context with name %s already exists", new) + } + cfg.Contexts[new] = cfg.Contexts[old] + delete(cfg.Contexts, old) + acc, err := c.ConfigAccess() + if err != nil { + return err + } + if e := clientcmd.ModifyConfig(acc, cfg, true); e != nil { + return e + } + current, err := c.CurrentContextName() + if err != nil { + return err + } + if current == old { + return c.SwitchContext(new) + } + + return nil +} + // ContextNames fetch all available contexts. func (c *Config) ContextNames() ([]string, error) { cfg, err := c.RawConfig() @@ -253,6 +296,13 @@ func (c *Config) CurrentUserName() (string, error) { func (c *Config) CurrentNamespaceName() (string, error) { ns, _, err := c.clientConfig().Namespace() + if ns == "default" { + ns, err = c.CurrentContextNamespace() + if ns == "" && err == nil { + return "", errors.New("No namespace specified in context") + } + } + return ns, err } diff --git a/internal/client/config_test.go b/internal/client/config_test.go index eb02cc530e..cf2e854945 100644 --- a/internal/client/config_test.go +++ b/internal/client/config_test.go @@ -111,7 +111,7 @@ func TestConfigCurrentNamespace(t *testing.T) { }{ "default": { flags: &genericclioptions.ConfigFlags{KubeConfig: &kubeConfig}, - namespace: "default", + namespace: "", }, "withContext": { flags: &genericclioptions.ConfigFlags{KubeConfig: &kubeConfig, Context: &bleeCTX}, @@ -128,7 +128,9 @@ func TestConfigCurrentNamespace(t *testing.T) { t.Run(k, func(t *testing.T) { cfg := client.NewConfig(u.flags) ns, err := cfg.CurrentNamespaceName() - assert.Nil(t, err) + if ns != "" { + assert.Nil(t, err) + } assert.Equal(t, u.namespace, ns) }) } diff --git a/internal/config/alias.go b/internal/config/alias.go index 626ee19172..8dddf782be 100644 --- a/internal/config/alias.go +++ b/internal/config/alias.go @@ -143,7 +143,7 @@ func (a *Aliases) loadDefaultAliases() { a.Alias["np"] = "networking.k8s.io/v1/networkpolicies" a.declare("help", "h", "?") - a.declare("quit", "q", "q!", "Q") + a.declare("quit", "q", "q!", "qa", "Q") a.declare("aliases", "alias", "a") a.declare("popeye", "pop") a.declare("helm", "charts", "chart", "hm") diff --git a/internal/config/config.go b/internal/config/config.go index 8c13c6fd8c..3a10544cfa 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -87,18 +87,23 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c } context, ok := cc[c.K9s.CurrentContext] if !ok { - return fmt.Errorf("The specified context %q does not exists in kubeconfig", c.K9s.CurrentContext) + return fmt.Errorf("the specified context %q does not exists in kubeconfig", c.K9s.CurrentContext) } c.K9s.CurrentCluster = context.Cluster c.K9s.ActivateCluster(context.Namespace) var ns = client.DefaultNamespace - if k9sFlags != nil && IsBoolSet(k9sFlags.AllNamespaces) { + switch { + case k9sFlags != nil && IsBoolSet(k9sFlags.AllNamespaces): ns = client.NamespaceAll - } else if isSet(flags.Namespace) { + case isSet(flags.Namespace): ns = *flags.Namespace - } else { - ns = context.Namespace + default: + if nss := context.Namespace; nss != "" { + ns = nss + } else if nss == "" { + ns = c.K9s.ActiveCluster().Namespace.Active + } } if err := c.SetActiveNamespace(ns); err != nil { @@ -186,6 +191,10 @@ func (c *Config) ActiveView() string { cmd := cl.View.Active if c.K9s.manualCommand != nil && *c.K9s.manualCommand != "" { cmd = *c.K9s.manualCommand + // We reset the manualCommand property because + // the command-line switch should only be considered once, + // on startup. + *c.K9s.manualCommand = "" } return cmd diff --git a/internal/config/config_test.go b/internal/config/config_test.go index b66daad14c..149ecdaf41 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -277,6 +277,7 @@ func (m *mockSettings) ClusterNames() (map[string]struct{}, error) { return nil, // Test Data... var expectedConfig = `k9s: + liveViewAutoRefresh: true refreshRate: 100 maxConnRetry: 5 enableMouse: false @@ -297,6 +298,7 @@ var expectedConfig = `k9s: showJSON: false currentContext: blee currentCluster: blee + keepMissingClusters: false clusters: blee: namespace: @@ -374,9 +376,11 @@ var expectedConfig = `k9s: critical: 90 warn: 70 screenDumpDir: /tmp + disablePodCounting: false ` var resetConfig = `k9s: + liveViewAutoRefresh: true refreshRate: 2 maxConnRetry: 5 enableMouse: false @@ -397,6 +401,7 @@ var resetConfig = `k9s: showJSON: false currentContext: blee currentCluster: blee + keepMissingClusters: false clusters: blee: namespace: @@ -426,4 +431,5 @@ var resetConfig = `k9s: critical: 90 warn: 70 screenDumpDir: /tmp + disablePodCounting: false ` diff --git a/internal/config/k9s.go b/internal/config/k9s.go index 2358d9aba6..4afbb2b111 100644 --- a/internal/config/k9s.go +++ b/internal/config/k9s.go @@ -11,6 +11,7 @@ const ( // K9s tracks K9s configuration options. type K9s struct { + LiveViewAutoRefresh bool `yaml:"liveViewAutoRefresh"` RefreshRate int `yaml:"refreshRate"` MaxConnRetry int `yaml:"maxConnRetry"` EnableMouse bool `yaml:"enableMouse"` @@ -24,9 +25,11 @@ type K9s struct { Logger *Logger `yaml:"logger"` CurrentContext string `yaml:"currentContext"` CurrentCluster string `yaml:"currentCluster"` + KeepMissingClusters bool `yaml:"keepMissingClusters"` Clusters map[string]*Cluster `yaml:"clusters,omitempty"` Thresholds Threshold `yaml:"thresholds"` ScreenDumpDir string `yaml:"screenDumpDir"` + DisablePodCounting bool `yaml:"disablePodCounting"` manualRefreshRate int manualHeadless *bool manualLogoless *bool @@ -54,6 +57,9 @@ func (k *K9s) CurrentContextDir() string { // ActivateCluster initializes the active cluster is not present. func (k *K9s) ActivateCluster(ns string) { + if k.Clusters == nil { + k.Clusters = map[string]*Cluster{} + } if _, ok := k.Clusters[k.CurrentCluster]; ok { return } @@ -202,9 +208,17 @@ func (k *K9s) validateClusters(c client.Connection, ks KubeSettings) { } for key, cluster := range k.Clusters { cluster.Validate(c, ks) + // if the cluster is defined in the $KUBECONFIG file, keep it in the k9s config file if _, ok := cc[key]; ok { continue } + + // if we asked to keep the clusters in the config file + if k.KeepMissingClusters { + continue + } + + // else remove it from the k9s config file if k.CurrentCluster == key { k.CurrentCluster = "" } diff --git a/internal/config/plugin.go b/internal/config/plugin.go index 2e1fbe3941..029bda8995 100644 --- a/internal/config/plugin.go +++ b/internal/config/plugin.go @@ -6,11 +6,14 @@ import ( "path/filepath" "strings" + "github.com/adrg/xdg" + "github.com/rs/zerolog/log" "gopkg.in/yaml.v2" ) -// K9sPlugins manages K9s plugins. -var K9sPlugins = filepath.Join(K9sHome(), "plugin.yml") +// K9sPluginsFilePath manages K9s plugins. +var K9sPluginsFilePath = filepath.Join(K9sHome(), "plugin.yml") +var K9sPluginDirectory = filepath.Join("k9s", "plugins") // Plugins represents a collection of plugins. type Plugins struct { @@ -42,23 +45,54 @@ func NewPlugins() Plugins { // Load K9s plugins. func (p Plugins) Load() error { - return p.LoadPlugins(K9sPlugins) + var pluginDirs []string + for _, dataDir := range xdg.DataDirs { + pluginDirs = append(pluginDirs, filepath.Join(dataDir, K9sPluginDirectory)) + } + return p.LoadPlugins(K9sPluginsFilePath, pluginDirs) } -// LoadPlugins loads plugins from a given file. -func (p Plugins) LoadPlugins(path string) error { +// LoadPlugins loads plugins from a given file and a set of plugin directories. +func (p Plugins) LoadPlugins(path string, pluginDirs []string) error { f, err := os.ReadFile(path) if err != nil { return err } - var pp Plugins if err := yaml.Unmarshal(f, &pp); err != nil { return err } + + for _, pluginDir := range pluginDirs { + pluginFiles, err := os.ReadDir(pluginDir) + if err != nil { + log.Warn().Msgf("Failed reading plugin path %s; %s", pluginDir, err) + continue + } + for _, file := range pluginFiles { + if file.IsDir() || !isYamlFile(file) { + continue + } + pluginFile, err := os.ReadFile(filepath.Join(pluginDir, file.Name())) + if err != nil { + return err + } + var plugin Plugin + if err = yaml.Unmarshal(pluginFile, &plugin); err != nil { + return err + } + p.Plugin[strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))] = plugin + } + } + for k, v := range pp.Plugin { p.Plugin[k] = v } return nil } + +func isYamlFile(file os.DirEntry) bool { + ext := filepath.Ext(file.Name()) + return ext == ".yml" || ext == ".yaml" +} diff --git a/internal/config/plugin_test.go b/internal/config/plugin_test.go index 961118fcc5..8350625e67 100644 --- a/internal/config/plugin_test.go +++ b/internal/config/plugin_test.go @@ -7,18 +7,61 @@ import ( "github.com/stretchr/testify/assert" ) -func TestPluginLoad(t *testing.T) { +var pluginYmlTestData = config.Plugin{ + Scopes: []string{"po", "dp"}, + Args: []string{"-n", "$NAMESPACE", "-boolean"}, + ShortCut: "shift-s", + Description: "blee", + Command: "duh", + Confirm: true, + Background: false, +} + +var test1YmlTestData = config.Plugin{ + Scopes: []string{"po", "dp"}, + Args: []string{"-n", "$NAMESPACE", "-boolean"}, + ShortCut: "shift-s", + Description: "blee", + Command: "duh", + Confirm: true, + Background: false, +} + +var test2YmlTestData = config.Plugin{ + Scopes: []string{"svc", "ing"}, + Args: []string{"-n", "$NAMESPACE", "-oyaml"}, + ShortCut: "shift-r", + Description: "bla", + Command: "duha", + Confirm: false, + Background: true, +} + +func TestSinglePluginFileLoad(t *testing.T) { p := config.NewPlugins() - assert.Nil(t, p.LoadPlugins("testdata/plugin.yml")) + assert.Nil(t, p.LoadPlugins("testdata/plugin.yml", []string{"/random/dir/not/exist"})) assert.Equal(t, 1, len(p.Plugin)) k, ok := p.Plugin["blah"] assert.True(t, ok) - assert.Equal(t, "shift-s", k.ShortCut) - assert.True(t, k.Confirm) - assert.Equal(t, "blee", k.Description) - assert.Equal(t, []string{"po", "dp"}, k.Scopes) - assert.Equal(t, "duh", k.Command) - assert.False(t, k.Background) - assert.Equal(t, []string{"-n", "$NAMESPACE", "-boolean"}, k.Args) + + assert.ObjectsAreEqual(pluginYmlTestData, k) +} + +func TestMultiplePluginFilesLoad(t *testing.T) { + p := config.NewPlugins() + assert.Nil(t, p.LoadPlugins("testdata/plugin.yml", []string{"testdata/plugins"})) + + testPlugins := map[string]config.Plugin{ + "blah": pluginYmlTestData, + "test1": test1YmlTestData, + "test2": test2YmlTestData, + } + + assert.Equal(t, len(testPlugins), len(p.Plugin)) + for name, expectedPlugin := range testPlugins { + k, ok := p.Plugin[name] + assert.True(t, ok) + assert.ObjectsAreEqual(expectedPlugin, k) + } } diff --git a/internal/config/styles.go b/internal/config/styles.go index 5ae172e3e8..9e1194165c 100644 --- a/internal/config/styles.go +++ b/internal/config/styles.go @@ -45,9 +45,16 @@ type ( // Prompt tracks command styles Prompt struct { - FgColor Color `yaml:"fgColor"` - BgColor Color `yaml:"bgColor"` - SuggestColor Color `yaml:"suggestColor"` + FgColor Color `yaml:"fgColor"` + BgColor Color `yaml:"bgColor"` + SuggestColor Color `yaml:"suggestColor"` + Border PromptBorder `yaml:"border"` + } + + // PromptBorder tracks the color of the prompt depending on its kind (e.g., command or filter) + PromptBorder struct { + CommandColor Color `yaml:"command"` + DefaultColor Color `yaml:"default"` } // Help tracks help styles. @@ -61,9 +68,13 @@ type ( // Body tracks body styles. Body struct { - FgColor Color `yaml:"fgColor"` - BgColor Color `yaml:"bgColor"` - LogoColor Color `yaml:"logoColor"` + FgColor Color `yaml:"fgColor"` + BgColor Color `yaml:"bgColor"` + LogoColor Color `yaml:"logoColor"` + LogoColorMsg Color `yaml:"logoColorMsg"` + LogoColorInfo Color `yaml:"logoColorInfo"` + LogoColorWarn Color `yaml:"logoColorWarn"` + LogoColorError Color `yaml:"logoColorError"` } // Dialog tracks dialog styles. @@ -93,6 +104,7 @@ type ( Xray Xray `yaml:"xray"` Charts Charts `yaml:"charts"` Yaml Yaml `yaml:"yaml"` + Picker Picker `yaml:"picker"` Log Log `yaml:"logs"` } @@ -115,10 +127,19 @@ type ( Indicator LogIndicator `yaml:"indicator"` } + // Picker tracks color when selecting containers + Picker struct { + MainColor Color `yaml:"mainColor"` + FocusColor Color `yaml:"focusColor"` + ShortcutColor Color `yaml:"shortcutColor"` + } + // LogIndicator tracks log view indicator. LogIndicator struct { - FgColor Color `yaml:"fgColor"` - BgColor Color `yaml:"bgColor"` + FgColor Color `yaml:"fgColor"` + BgColor Color `yaml:"bgColor"` + ToggleOnColor Color `yaml:"toggleOnColor"` + ToggleOffColor Color `yaml:"toggleOffColor"` } // Yaml tracks yaml styles. @@ -281,6 +302,10 @@ func newPrompt() Prompt { FgColor: "cadetblue", BgColor: "black", SuggestColor: "dodgerblue", + Border: PromptBorder{ + DefaultColor: "seagreen", + CommandColor: "aqua", + }, } } @@ -304,6 +329,7 @@ func newViews() Views { Xray: newXray(), Charts: newCharts(), Yaml: newYaml(), + Picker: newPicker(), Log: newLog(), } } @@ -330,9 +356,13 @@ func newHelp() Help { func newBody() Body { return Body{ - FgColor: "cadetblue", - BgColor: "black", - LogoColor: "orange", + FgColor: "cadetblue", + BgColor: "black", + LogoColor: "orange", + LogoColorMsg: "white", + LogoColorInfo: "green", + LogoColorWarn: "mediumvioletred", + LogoColorError: "red", } } @@ -349,6 +379,14 @@ func newStatus() Status { } } +func newPicker() Picker { + return Picker{ + MainColor: "white", + FocusColor: "aqua", + ShortcutColor: "aqua", + } +} + func newLog() Log { return Log{ FgColor: "lightskyblue", @@ -359,8 +397,10 @@ func newLog() Log { func newLogIndicator() LogIndicator { return LogIndicator{ - FgColor: "dodgerblue", - BgColor: "black", + FgColor: "dodgerblue", + BgColor: "black", + ToggleOnColor: "limegreen", + ToggleOffColor: "gray", } } @@ -499,6 +539,11 @@ func (s *Styles) Body() Body { return s.K9s.Body } +// Prompt returns prompt styles. +func (s *Styles) Prompt() Prompt { + return s.K9s.Prompt +} + // Frame returns frame styles. func (s *Styles) Frame() Frame { return s.K9s.Frame diff --git a/internal/config/testdata/k9s.yml b/internal/config/testdata/k9s.yml index 0587cd29bd..e98a2180cf 100644 --- a/internal/config/testdata/k9s.yml +++ b/internal/config/testdata/k9s.yml @@ -1,4 +1,5 @@ k9s: + liveViewAutoRefresh: true refreshRate: 2 readOnly: false logger: @@ -30,3 +31,4 @@ k9s: view: active: po screenDumpDir: /tmp + disablePodCounting: false diff --git a/internal/config/testdata/plugins/test1.yml b/internal/config/testdata/plugins/test1.yml new file mode 100644 index 0000000000..6d8eec1363 --- /dev/null +++ b/internal/config/testdata/plugins/test1.yml @@ -0,0 +1,12 @@ +shortCut: shift-s +confirm: true +description: blee +scopes: + - po + - dp +command: duh +background: false +args: + - -n + - $NAMESPACE + - -boolean diff --git a/internal/config/testdata/plugins/test2.yml b/internal/config/testdata/plugins/test2.yml new file mode 100644 index 0000000000..379dee9151 --- /dev/null +++ b/internal/config/testdata/plugins/test2.yml @@ -0,0 +1,12 @@ +shortCut: shift-r +confirm: false +description: bla +scopes: + - svc + - ing +command: duha +background: true +args: + - -n + - $NAMESPACE + - -oyaml diff --git a/internal/dao/container_test.go b/internal/dao/container_test.go index 1bf5896b22..61df494829 100644 --- a/internal/dao/container_test.go +++ b/internal/dao/container_test.go @@ -8,7 +8,6 @@ import ( "github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/dao" "github.com/derailed/k9s/internal/watch" - "github.com/ghodss/yaml" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -21,6 +20,7 @@ import ( "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" versioned "k8s.io/metrics/pkg/client/clientset/versioned" + "sigs.k8s.io/yaml" ) func TestContainerList(t *testing.T) { diff --git a/internal/dao/cronjob.go b/internal/dao/cronjob.go index b3faf365c3..9c5d5011f9 100644 --- a/internal/dao/cronjob.go +++ b/internal/dao/cronjob.go @@ -60,6 +60,7 @@ func (c *CronJob) Run(path string) error { Name: jobName + "-manual-" + rand.String(3), Namespace: ns, Labels: cj.Spec.JobTemplate.Labels, + Annotations: cj.Spec.JobTemplate.Annotations, OwnerReferences: []metav1.OwnerReference{ { APIVersion: c.gvr.GV().String(), diff --git a/internal/dao/dp.go b/internal/dao/dp.go index 3c426e7f5f..8e21d0c46f 100644 --- a/internal/dao/dp.go +++ b/internal/dao/dp.go @@ -115,7 +115,7 @@ func (d *Deployment) Restart(ctx context.Context, path string) error { // TailLogs tail logs for all pods represented by this Deployment. func (d *Deployment) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) { - dp, err := d.Load(d.Factory, opts.Path) + dp, err := d.GetInstance(d.Factory, opts.Path) if err != nil { return nil, err } @@ -128,7 +128,7 @@ func (d *Deployment) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, // Pod returns a pod victim by name. func (d *Deployment) Pod(fqn string) (string, error) { - dp, err := d.Load(d.Factory, fqn) + dp, err := d.GetInstance(d.Factory, fqn) if err != nil { return "", err } @@ -136,8 +136,8 @@ func (d *Deployment) Pod(fqn string) (string, error) { return podFromSelector(d.Factory, dp.Namespace, dp.Spec.Selector.MatchLabels) } -// Load returns a deployment instance. -func (*Deployment) Load(f Factory, fqn string) (*appsv1.Deployment, error) { +// GetInstance fetch a matching deployment. +func (*Deployment) GetInstance(f Factory, fqn string) (*appsv1.Deployment, error) { o, err := f.Get("apps/v1/deployments", fqn, true, labels.Everything()) if err != nil { return nil, err @@ -240,7 +240,7 @@ func (d *Deployment) Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs // GetPodSpec returns a pod spec given a resource. func (d *Deployment) GetPodSpec(path string) (*v1.PodSpec, error) { - dp, err := d.Load(d.Factory, path) + dp, err := d.GetInstance(d.Factory, path) if err != nil { return nil, err } diff --git a/internal/dao/log_item.go b/internal/dao/log_item.go index ddf7cc7cb9..efea5ff70c 100644 --- a/internal/dao/log_item.go +++ b/internal/dao/log_item.go @@ -73,10 +73,10 @@ func (l *LogItem) Render(paint string, showTime bool, showJson bool, bb *bytes.B 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("[-::]") + bb.WriteString("[-::-]") } if l.Pod != "" { diff --git a/internal/dao/log_item_test.go b/internal/dao/log_item_test.go index a8a81bea20..edfb7e15ec 100644 --- a/internal/dao/log_item_test.go +++ b/internal/dao/log_item_test.go @@ -63,7 +63,7 @@ func TestLogItemRender(t *testing.T) { ShowTimestamp: true, }, log: fmt.Sprintf("%s %s\n", "2018-12-14T10:36:43.326972-07:00", "Testing 1,2,3..."), - e: "[gray::b]2018-12-14T10:36:43.326972-07:00 [-::][yellow::]fred [yellow::b]blee[-::-] Testing 1,2,3...\n", + e: "[gray::b]2018-12-14T10:36:43.326972-07:00 [-::-][yellow::]fred [yellow::b]blee[-::-] Testing 1,2,3...\n", }, "log-level": { opts: dao.LogOptions{ @@ -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/log_items_test.go b/internal/dao/log_items_test.go index 63839893b4..71fa733dc2 100644 --- a/internal/dao/log_items_test.go +++ b/internal/dao/log_items_test.go @@ -108,7 +108,7 @@ func TestLogItemsRender(t *testing.T) { Container: "blee", ShowTimestamp: true, }, - e: "[gray::b]2018-12-14T10:36:43.326972-07:00 [-::][teal::]fred [teal::b]blee[-::-] Testing 1,2,3...\n", + e: "[gray::b]2018-12-14T10:36:43.326972-07:00 [-::-][teal::]fred [teal::b]blee[-::-] Testing 1,2,3...\n", }, } diff --git a/internal/dao/node.go b/internal/dao/node.go index 09540d865e..9504f110be 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,16 @@ 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) + cordoned, err := n.ensureCordoned(path) + if err != nil { + return err + } + + if !cordoned { + if err = n.ToggleCordon(path, true); err != nil { + return err + } + } dial, err := n.GetFactory().Client().Dial() if err != nil { @@ -97,7 +107,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 { @@ -145,6 +155,8 @@ func (n *Node) List(ctx context.Context, ns string) ([]runtime.Object, error) { nmx, _ = client.DialMetrics(n.Client()).FetchNodesMetricsMap(ctx) } + shouldCountPods, _ := ctx.Value(internal.KeyPodCounting).(bool) + res := make([]runtime.Object, 0, len(oo)) for _, o := range oo { u, ok := o.(*unstructured.Unstructured) @@ -154,9 +166,12 @@ func (n *Node) List(ctx context.Context, ns string) ([]runtime.Object, error) { fqn := extractFQN(o) _, name := client.Namespaced(fqn) - podCount, err := n.CountPods(name) - if err != nil { - log.Error().Err(err).Msgf("unable to get pods count for %s", name) + podCount := -1 + if shouldCountPods { + podCount, err = n.CountPods(name) + if err != nil { + log.Error().Err(err).Msgf("unable to get pods count for %s", name) + } } res = append(res, &render.NodeWithMetrics{ Raw: u, @@ -214,6 +229,16 @@ func (n *Node) GetPods(nodeName string) ([]*v1.Pod, error) { return pp, nil } +// ensureCordoned returns whether the given node has been cordoned +func (n *Node) ensureCordoned(path string) (bool, error) { + o, err := FetchNode(context.Background(), n.Factory, path) + if err != nil { + return false, err + } + + return o.Spec.Unschedulable, nil +} + // ---------------------------------------------------------------------------- // Helpers... diff --git a/internal/dao/pod.go b/internal/dao/pod.go index 6b616347a0..a65e63d39f 100644 --- a/internal/dao/pod.go +++ b/internal/dao/pod.go @@ -33,9 +33,9 @@ var ( ) const ( - logRetryCount = 20 - logRetryWait = 1 * time.Second - defaultLogContainerAnnotation = "kubectl.kubernetes.io/default-logs-container" + logRetryCount = 20 + logRetryWait = 1 * time.Second + defaultContainerAnnotation = "kubectl.kubernetes.io/default-container" ) // Pod represents a pod resource. @@ -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 { @@ -197,7 +197,7 @@ func (p *Pod) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) } outs := make([]LogChan, 0, coCounts) - if co, ok := GetDefaultLogContainer(po.ObjectMeta, po.Spec); ok && !opts.AllContainers { + if co, ok := GetDefaultContainer(po.ObjectMeta, po.Spec); ok && !opts.AllContainers { opts.DefaultContainer = co return append(outs, tailLogs(ctx, p, opts)), nil } @@ -486,17 +486,51 @@ func (p *Pod) isControlled(path string) (string, bool, error) { return "", false, nil } -// GetDefaultLogContainer returns a container name if specified in an annotation. -func GetDefaultLogContainer(m metav1.ObjectMeta, spec v1.PodSpec) (string, bool) { - defaultContainer, ok := m.Annotations[defaultLogContainerAnnotation] +// GetDefaultContainer returns a container name if specified in an annotation. +func GetDefaultContainer(m metav1.ObjectMeta, spec v1.PodSpec) (string, bool) { + defaultContainer, ok := m.Annotations[defaultContainerAnnotation] if ok { for _, container := range spec.Containers { if container.Name == defaultContainer { return defaultContainer, true } } - log.Warn().Msg(defaultContainer + " container not found. " + defaultLogContainerAnnotation + " annotation will be ignored") + log.Warn().Msg(defaultContainer + " container not found. " + defaultContainerAnnotation + " annotation will be ignored") } return "", false } + +func (p *Pod) Sanitize(ctx context.Context, ns string) (int, error) { + oo, err := p.Resource.List(ctx, ns) + if err != nil { + return 0, err + } + + var count int + for _, o := range oo { + u, ok := o.(*unstructured.Unstructured) + if !ok { + continue + } + var pod v1.Pod + err = runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &pod) + if err != nil { + continue + } + log.Debug().Msgf("Pod status: %q", render.PodStatus(&pod)) + switch render.PodStatus(&pod) { + case render.PhaseCompleted, render.PhaseCrashLoop, render.PhaseError, render.PhaseImagePullBackOff, render.PhaseOOMKilled: + log.Debug().Msgf("Sanitizing %s:%s", pod.Namespace, pod.Name) + fqn := client.FQN(pod.Namespace, pod.Name) + if err := p.Resource.Delete(ctx, fqn, nil, NowGrace); err != nil { + log.Warn().Err(err).Msgf("Pod %s deletion failed", fqn) + continue + } + count++ + } + } + log.Debug().Msgf("Sanitizer deleted %d pods", count) + + return count, nil +} diff --git a/internal/dao/pod_test.go b/internal/dao/pod_test.go index 9bb0d76edf..cec9b794b9 100644 --- a/internal/dao/pod_test.go +++ b/internal/dao/pod_test.go @@ -8,7 +8,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func TestGetDefaultLogContainer(t *testing.T) { +func TestGetDefaultContainer(t *testing.T) { uu := map[string]struct { po *v1.Pod wantContainer string @@ -26,7 +26,7 @@ func TestGetDefaultLogContainer(t *testing.T) { "container_not_present": { po: &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{"kubectl.kubernetes.io/default-logs-container": "container1"}, + Annotations: map[string]string{"kubectl.kubernetes.io/default-container": "container1"}, }, }, wantContainer: "", @@ -35,7 +35,7 @@ func TestGetDefaultLogContainer(t *testing.T) { "container_found": { po: &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{"kubectl.kubernetes.io/default-logs-container": "container1"}, + Annotations: map[string]string{"kubectl.kubernetes.io/default-container": "container1"}, }, Spec: v1.PodSpec{ Containers: []v1.Container{{Name: "container1"}}, @@ -48,7 +48,7 @@ func TestGetDefaultLogContainer(t *testing.T) { for k := range uu { u := uu[k] t.Run(k, func(t *testing.T) { - container, ok := GetDefaultLogContainer(u.po.ObjectMeta, u.po.Spec) + container, ok := GetDefaultContainer(u.po.ObjectMeta, u.po.Spec) assert.Equal(t, u.wantContainer, container) assert.Equal(t, u.wantOk, ok) }) diff --git a/internal/dao/port_forwarder.go b/internal/dao/port_forwarder.go index 5940eb4550..37812215c2 100644 --- a/internal/dao/port_forwarder.go +++ b/internal/dao/port_forwarder.go @@ -43,6 +43,11 @@ func NewPortForwarder(f Factory) *PortForwarder { } } +// String dumps as string. +func (p *PortForwarder) String() string { + return fmt.Sprintf("%s|%s", p.path, p.tunnel) +} + // Age returns the port forward age. func (p *PortForwarder) Age() string { return time.Since(p.age).String() diff --git a/internal/dao/rbac_policy.go b/internal/dao/rbac_policy.go index c88da803cb..a7f86543e2 100644 --- a/internal/dao/rbac_policy.go +++ b/internal/dao/rbac_policy.go @@ -88,21 +88,20 @@ func (p *Policy) loadClusterRoleBinding(kind, name string) (render.Policies, err } func (p *Policy) loadRoleBinding(kind, name string) (render.Policies, error) { - ss, err := p.fetchRoleBindingSubjects(kind, name) + rbsMap, err := p.fetchRoleBindingNamespaces(kind, name) if err != nil { return nil, err } - crs, err := p.fetchClusterRoles() if err != nil { return nil, err } rows := make(render.Policies, 0, len(crs)) for _, cr := range crs { - if !inList(ss, "ClusterRole:"+cr.Name) { - continue + if rbNs, ok := rbsMap["ClusterRole:"+cr.Name]; ok { + log.Debug().Msgf("Loading rules for clusterrole %q:%q", rbNs, cr.Name) + rows = append(rows, parseRules(rbNs, "CR:"+cr.Name, cr.Rules)...) } - rows = append(rows, parseRules("*", "CR:"+cr.Name, cr.Rules)...) } ros, err := p.fetchRoles() @@ -110,7 +109,7 @@ func (p *Policy) loadRoleBinding(kind, name string) (render.Policies, error) { return nil, err } for _, ro := range ros { - if !inList(ss, "Role:"+ro.Name) { + if _, ok := rbsMap["Role:"+ro.Name]; !ok { continue } log.Debug().Msgf("Loading rules for role %q:%q", ro.Namespace, ro.Name) @@ -156,19 +155,19 @@ func fetchRoleBindings(f Factory) ([]rbacv1.RoleBinding, error) { return rbs, nil } -func (p *Policy) fetchRoleBindingSubjects(kind, name string) ([]string, error) { +func (p *Policy) fetchRoleBindingNamespaces(kind, name string) (map[string]string, error) { rbs, err := fetchRoleBindings(p.Factory) if err != nil { return nil, err } ns, n := client.Namespaced(name) - ss := make([]string, 0, len(rbs)) + ss := make(map[string]string, len(rbs)) for _, rb := range rbs { for _, s := range rb.Subjects { s := s if isSameSubject(kind, ns, n, &s) { - ss = append(ss, rb.RoleRef.Kind+":"+rb.RoleRef.Name) + ss[rb.RoleRef.Kind+":"+rb.RoleRef.Name] = rb.Namespace } } } diff --git a/internal/dao/registry.go b/internal/dao/registry.go index f8bfc7c262..64638842c2 100644 --- a/internal/dao/registry.go +++ b/internal/dao/registry.go @@ -9,7 +9,6 @@ import ( "github.com/derailed/k9s/internal/client" "github.com/rs/zerolog/log" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -134,7 +133,7 @@ func (m *Meta) AllGVRs() client.GVRs { } // IsCRD checks if resource represents a CRD -func IsCRD(r v1.APIResource) bool { +func IsCRD(r metav1.APIResource) bool { for _, c := range r.Categories { if c == CRD { return true diff --git a/internal/dao/rs.go b/internal/dao/rs.go index 9da0a84fe0..7e7ae755b5 100644 --- a/internal/dao/rs.go +++ b/internal/dao/rs.go @@ -8,7 +8,6 @@ import ( "github.com/derailed/k9s/internal/client" appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -23,7 +22,7 @@ type ReplicaSet struct { } // Load returns a given instance. -func (r *ReplicaSet) Load(f Factory, path string) (*v1.ReplicaSet, error) { +func (r *ReplicaSet) Load(f Factory, path string) (*appsv1.ReplicaSet, error) { o, err := f.Get("apps/v1/replicasets", path, true, labels.Everything()) if err != nil { return nil, err @@ -38,7 +37,7 @@ func (r *ReplicaSet) Load(f Factory, path string) (*v1.ReplicaSet, error) { return &rs, nil } -func getRSRevision(rs *v1.ReplicaSet) (int64, error) { +func getRSRevision(rs *appsv1.ReplicaSet) (int64, error) { revision := rs.ObjectMeta.Annotations["deployment.kubernetes.io/revision"] if rs.Status.Replicas != 0 { return 0, errors.New("can not rollback current replica") @@ -51,7 +50,7 @@ func getRSRevision(rs *v1.ReplicaSet) (int64, error) { return int64(vers), nil } -func controllerInfo(rs *v1.ReplicaSet) (string, string, string, error) { +func controllerInfo(rs *appsv1.ReplicaSet) (string, string, string, error) { for _, ref := range rs.ObjectMeta.OwnerReferences { if ref.Controller == nil { continue @@ -96,7 +95,7 @@ func (r *ReplicaSet) Rollback(fqn string) error { } var ddp Deployment - dp, err := ddp.Load(r.Factory, client.FQN(rs.Namespace, name)) + dp, err := ddp.GetInstance(r.Factory, client.FQN(rs.Namespace, name)) if err != nil { return err } diff --git a/internal/dao/sts.go b/internal/dao/sts.go index d15822032f..8e346233b1 100644 --- a/internal/dao/sts.go +++ b/internal/dao/sts.go @@ -67,15 +67,19 @@ func (s *StatefulSet) Scale(ctx context.Context, path string, replicas int32) er // Restart a StatefulSet rollout. func (s *StatefulSet) Restart(ctx context.Context, path string) error { - o, err := s.GetFactory().Get("apps/v1/statefulsets", path, true, labels.Everything()) + sts, err := s.GetInstance(s.Factory, path) if err != nil { return err } - var sts appsv1.StatefulSet - err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &sts) + + ns, _ := client.Namespaced(path) + pp, err := podsFromSelector(s.Factory, ns, sts.Spec.Selector.MatchLabels) if err != nil { return err } + for _, p := range pp { + s.Forwarders().Kill(client.FQN(p.Namespace, p.Name)) + } auth, err := s.Client().CanI(sts.Namespace, "apps/v1/statefulsets", []string{client.PatchVerb}) if err != nil { @@ -90,12 +94,12 @@ func (s *StatefulSet) Restart(ctx context.Context, path string) error { return err } - before, err := runtime.Encode(scheme.Codecs.LegacyCodec(appsv1.SchemeGroupVersion), &sts) + before, err := runtime.Encode(scheme.Codecs.LegacyCodec(appsv1.SchemeGroupVersion), sts) if err != nil { return err } - after, err := polymorphichelpers.ObjectRestarterFn(&sts) + after, err := polymorphichelpers.ObjectRestarterFn(sts) if err != nil { return err } @@ -115,8 +119,8 @@ func (s *StatefulSet) Restart(ctx context.Context, path string) error { } -// Load returns a statefulset instance. -func (*StatefulSet) Load(f Factory, fqn string) (*appsv1.StatefulSet, error) { +// GetInstance returns a statefulset instance. +func (*StatefulSet) GetInstance(f Factory, fqn string) (*appsv1.StatefulSet, error) { o, err := f.Get("apps/v1/statefulsets", fqn, true, labels.Everything()) if err != nil { return nil, err @@ -301,3 +305,26 @@ func (s *StatefulSet) SetImages(ctx context.Context, path string, imageSpecs Ima ) return err } + +func podsFromSelector(f Factory, ns string, sel map[string]string) ([]*v1.Pod, error) { + oo, err := f.List("v1/pods", ns, true, labels.Set(sel).AsSelector()) + if err != nil { + return nil, err + } + + if len(oo) == 0 { + return nil, fmt.Errorf("no matching pods for %v", sel) + } + + pp := make([]*v1.Pod, 0, len(oo)) + for _, o := range oo { + pod := new(v1.Pod) + err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, pod) + if err != nil { + return nil, err + } + pp = append(pp, pod) + } + + return pp, nil +} diff --git a/internal/dao/testdata/config b/internal/dao/testdata/config index 5541a687e2..74d10be07c 100644 --- a/internal/dao/testdata/config +++ b/internal/dao/testdata/config @@ -21,6 +21,7 @@ contexts: name: fred - context: cluster: blee + namespace: zorg user: blee name: blee - context: diff --git a/internal/dao/types.go b/internal/dao/types.go index aa1f49e27b..23ac9e7221 100644 --- a/internal/dao/types.go +++ b/internal/dao/types.go @@ -155,3 +155,9 @@ type ContainsPodSpec interface { // Set Images for a resource SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error } + +// Sanitizer represents a resource sanitizer. +type Sanitizer interface { + // Sanitize nukes all resources in unhappy state. + Sanitize(context.Context, string) (int, error) +} diff --git a/internal/keys.go b/internal/keys.go index 522d0f9bdb..f184b49d5f 100644 --- a/internal/keys.go +++ b/internal/keys.go @@ -30,4 +30,5 @@ const ( KeyWithMetrics ContextKey = "withMetrics" KeyViewConfig ContextKey = "viewConfig" KeyWait ContextKey = "wait" + KeyPodCounting ContextKey = "podCounting" ) diff --git a/internal/model/cmd_buff.go b/internal/model/cmd_buff.go index 06b59d0080..ba4db4b392 100644 --- a/internal/model/cmd_buff.go +++ b/internal/model/cmd_buff.go @@ -38,7 +38,7 @@ type ( type CmdBuff struct { buff []rune suggestion string - listeners []BuffWatcher + listeners map[BuffWatcher]struct{} hotKey rune kind BufferKind active bool @@ -52,7 +52,7 @@ func NewCmdBuff(key rune, kind BufferKind) *CmdBuff { hotKey: key, kind: kind, buff: make([]rune, 0, maxBuff), - listeners: []BuffWatcher{}, + listeners: make(map[BuffWatcher]struct{}), } } @@ -61,7 +61,11 @@ func (c *CmdBuff) InCmdMode() bool { c.mx.RLock() defer c.mx.RUnlock() - return c.active || len(c.buff) > 0 + if !c.active { + return false + } + + return len(c.buff) > 0 } // IsActive checks if command buffer is active. @@ -217,7 +221,7 @@ func (c *CmdBuff) Empty() bool { func (c *CmdBuff) AddListener(w BuffWatcher) { c.mx.Lock() { - c.listeners = append(c.listeners, w) + c.listeners[w] = struct{}{} } c.mx.Unlock() } @@ -225,36 +229,24 @@ func (c *CmdBuff) AddListener(w BuffWatcher) { // RemoveListener removes a listener. func (c *CmdBuff) RemoveListener(l BuffWatcher) { c.mx.Lock() - defer c.mx.Unlock() - - victim := -1 - for i, lis := range c.listeners { - if l == lis { - victim = i - break - } - } - if victim == -1 { - return - } - - c.listeners = append(c.listeners[:victim], c.listeners[victim+1:]...) + delete(c.listeners, l) + c.mx.Unlock() } func (c *CmdBuff) fireBufferCompleted(t, s string) { - for _, l := range c.listeners { + for l := range c.listeners { l.BufferCompleted(t, s) } } func (c *CmdBuff) fireBufferChanged(t, s string) { - for _, l := range c.listeners { + for l := range c.listeners { l.BufferChanged(t, s) } } func (c *CmdBuff) fireActive(b bool) { - for _, l := range c.listeners { + for l := range c.listeners { l.BufferActive(b, c.GetKind()) } } diff --git a/internal/model/fish_buff.go b/internal/model/fish_buff.go index ab92f4de4b..77098df946 100644 --- a/internal/model/fish_buff.go +++ b/internal/model/fish_buff.go @@ -70,6 +70,9 @@ func (f *FishBuff) NextSuggestion() (string, bool) { // ClearSuggestions clear out all suggestions. func (f *FishBuff) ClearSuggestions() { + if len(f.suggestions) > 0 { + f.suggestions = f.suggestions[:0] + } f.suggestionIndex = -1 } diff --git a/internal/model/yaml.go b/internal/model/yaml.go index df4f7b51d4..e919b26fce 100644 --- a/internal/model/yaml.go +++ b/internal/model/yaml.go @@ -84,11 +84,11 @@ func (*YAML) rxFilter(q string, lines []string) fuzzy.Matches { } matches := make(fuzzy.Matches, 0, len(lines)) for i, l := range lines { - if loc := rx.FindStringIndex(l); len(loc) == 2 { + locs := rx.FindAllStringIndex(l, -1) + for _, loc := range locs { matches = append(matches, fuzzy.Match{Str: q, Index: i, MatchedIndexes: loc}) } } - return matches } diff --git a/internal/model/yaml_test.go b/internal/model/yaml_test.go new file mode 100644 index 0000000000..9b595134e0 --- /dev/null +++ b/internal/model/yaml_test.go @@ -0,0 +1,65 @@ +package model + +import ( + "testing" + + "github.com/sahilm/fuzzy" + "github.com/stretchr/testify/assert" +) + +func TestYAML_rxFilter(t *testing.T) { + uu := map[string]struct { + q string + lines []string + e fuzzy.Matches + }{ + "empty-lines": { + q: "foo", + e: fuzzy.Matches{}, + }, + "no-match": { + q: "foo", + lines: []string{"bar"}, + e: fuzzy.Matches{}, + }, + "single-match": { + q: "foo", + lines: []string{"foo", "bar", "baz"}, + e: fuzzy.Matches{ + { + Str: "foo", + Index: 0, + MatchedIndexes: []int{0, 3}, + }, + }, + }, + "multiple-matches": { + q: "foo", + lines: []string{"foo", "bar", "foo bar foo", "baz"}, + e: fuzzy.Matches{ + { + Str: "foo", + Index: 0, + MatchedIndexes: []int{0, 3}, + }, + { + Str: "foo", + Index: 2, + MatchedIndexes: []int{0, 3}, + }, + { + Str: "foo", + Index: 2, + MatchedIndexes: []int{8, 11}, + }, + }, + }, + } + var y YAML + for k := range uu { + u := uu[k] + t.Run(k, func(t *testing.T) { + assert.Equal(t, u.e, y.rxFilter(u.q, u.lines)) + }) + } +} diff --git a/internal/perf/benchmark.go b/internal/perf/benchmark.go index 9ed0af01c6..8cd529372e 100644 --- a/internal/perf/benchmark.go +++ b/internal/perf/benchmark.go @@ -25,7 +25,6 @@ const ( k9sUA = "k9s/" ) - var ( // K9sBenchDir directory to store K9s Benchmark files. K9sBenchDir = filepath.Join(os.TempDir(), fmt.Sprintf("k9s-bench-%s", config.MustK9sUser())) @@ -107,7 +106,7 @@ func (b *Benchmark) Canceled() bool { return b.canceled } -// Run starts a benchmark,. +// Run starts a benchmark. func (b *Benchmark) Run(cluster string, done func()) { log.Debug().Msgf("Running benchmark on cluster %s", cluster) buff := new(bytes.Buffer) @@ -115,7 +114,7 @@ func (b *Benchmark) Run(cluster string, done func()) { // this call will block until the benchmark is complete or times out. b.worker.Run() b.worker.Stop() - if len(buff.Bytes()) > 0 { + if buff.Len() > 0 { if err := b.save(cluster, buff); err != nil { log.Error().Err(err).Msg("Saving Benchmark") } @@ -141,11 +140,7 @@ func (b *Benchmark) save(cluster string, r io.Reader) error { } }() - bb, err := io.ReadAll(r) - if err != nil { - return err - } - if _, err := f.Write(bb); err != nil { + if _, err = io.Copy(f, r); err != nil { return err } diff --git a/internal/port/tunnel.go b/internal/port/tunnel.go index 99b5628155..8074372abb 100644 --- a/internal/port/tunnel.go +++ b/internal/port/tunnel.go @@ -32,6 +32,11 @@ func NewPortTunnel(a, co, lp, cp string) PortTunnel { } } +// String dumps as string. +func (t PortTunnel) String() string { + return fmt.Sprintf("%s|%s|%s:%s", t.Address, t.Container, t.LocalPort, t.ContainerPort) +} + // PortMap returns a port mapping. func (t PortTunnel) PortMap() string { if t.LocalPort == "" { diff --git a/internal/render/header.go b/internal/render/header.go index 91eab1aedc..253189cf29 100644 --- a/internal/render/header.go +++ b/internal/render/header.go @@ -17,6 +17,7 @@ type HeaderColumn struct { Wide bool MX bool Time bool + Capacity bool } // Clone copies a header. @@ -163,6 +164,15 @@ func (h Header) IsTimeCol(col int) bool { return h[col].Time } +// IsCapacityCol checks if given column index represents a capacity. +func (h Header) IsCapacityCol(col int) bool { + if col < 0 || col >= len(h) { + return false + } + + return h[col].Capacity +} + // ValidColIndex returns the valid col index or -1 if none. func (h Header) ValidColIndex() int { return h.IndexOf("VALID", true) diff --git a/internal/render/helpers.go b/internal/render/helpers.go index 1988633b09..95ed2cf6ef 100644 --- a/internal/render/helpers.go +++ b/internal/render/helpers.go @@ -12,6 +12,7 @@ import ( "github.com/rs/zerolog/log" "golang.org/x/text/language" "golang.org/x/text/message" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/duration" ) @@ -57,6 +58,11 @@ func durationToSeconds(duration string) int64 { return n } +func capacityToNumber(capacity string) int64 { + quantity := resource.MustParse(capacity) + return quantity.Value() +} + // AsThousands prints a number with thousand separator. func AsThousands(n int64) string { p := message.NewPrinter(language.English) @@ -298,6 +304,13 @@ func boolPtrToStr(b *bool) string { return boolToStr(*b) } +func strPtrToStr(s *string) string { + if s == nil { + return "" + } + return *s +} + // Check if string is in a string list. func in(ll []string, s string) bool { for _, l := range ll { diff --git a/internal/render/node.go b/internal/render/node.go index b5331a6f16..2e892b104b 100644 --- a/internal/render/node.go +++ b/internal/render/node.go @@ -53,11 +53,11 @@ func (Node) Header(_ string) Header { func (n Node) Render(o interface{}, ns string, r *Row) error { oo, ok := o.(*NodeWithMetrics) if !ok { - return fmt.Errorf("Expected *NodeAndMetrics, but got %T", o) + return fmt.Errorf("expected *NodeAndMetrics, but got %T", o) } meta, ok := oo.Raw.Object["metadata"].(map[string]interface{}) if !ok { - return fmt.Errorf("Unable to extract meta") + return fmt.Errorf("unable to extract meta") } na := extractMetaField(meta, "name") var no v1.Node @@ -77,6 +77,10 @@ func (n Node) Render(o interface{}, ns string, r *Row) error { nodeRoles(&no, roles) sort.Sort(roles) + podCount := strconv.Itoa(oo.PodCount) + if pc := oo.PodCount; pc == -1 { + podCount = NAValue + } r.ID = client.FQN("", na) r.Fields = Fields{ no.Name, @@ -86,7 +90,7 @@ func (n Node) Render(o interface{}, ns string, r *Row) error { no.Status.NodeInfo.KernelVersion, iIP, eIP, - strconv.Itoa(oo.PodCount), + podCount, toMc(c.cpu), toMi(c.mem), client.ToPercentageStr(c.cpu, a.cpu), diff --git a/internal/render/pdb.go b/internal/render/pdb.go index 71a306bd6e..24d4ccb9e5 100644 --- a/internal/render/pdb.go +++ b/internal/render/pdb.go @@ -23,7 +23,7 @@ func (PodDisruptionBudget) Header(ns string) Header { HeaderColumn{Name: "NAMESPACE"}, HeaderColumn{Name: "NAME"}, HeaderColumn{Name: "MIN AVAILABLE", Align: tview.AlignRight}, - HeaderColumn{Name: "MAX_ UNAVAILABLE", Align: tview.AlignRight}, + HeaderColumn{Name: "MAX UNAVAILABLE", Align: tview.AlignRight}, HeaderColumn{Name: "ALLOWED DISRUPTIONS", Align: tview.AlignRight}, HeaderColumn{Name: "CURRENT", Align: tview.AlignRight}, HeaderColumn{Name: "DESIRED", Align: tview.AlignRight}, diff --git a/internal/render/pod.go b/internal/render/pod.go index bfbb8c1759..afbcd7da81 100644 --- a/internal/render/pod.go +++ b/internal/render/pod.go @@ -5,7 +5,6 @@ import ( "strconv" "strings" - "github.com/derailed/k9s/internal/client" "github.com/derailed/tcell/v2" "github.com/derailed/tview" v1 "k8s.io/api/core/v1" @@ -13,7 +12,25 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/kubernetes/pkg/util/node" mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" + + "github.com/derailed/k9s/internal/client" +) + +const ( + PhaseTerminating = "Terminating" + PhaseInitialized = "Initialized" + PhaseRunning = "Running" + PhaseNotReady = "NoReady" + PhaseCompleted = "Completed" + PhaseContainerCreating = "ContainerCreating" + PhasePodInitializing = "PodInitializing" + PhaseUnknown = "Unknown" + PhaseCrashLoop = "CrashLoopBackOff" + PhaseError = "Error" + PhaseImagePullBackOff = "ImagePullBackOff" + PhaseOOMKilled = "OOMKilled" ) // Pod renders a K8s Pod to screen. @@ -63,8 +80,12 @@ func (Pod) Header(ns string) Header { HeaderColumn{Name: "NAME"}, HeaderColumn{Name: "PF"}, HeaderColumn{Name: "READY"}, - HeaderColumn{Name: "RESTARTS", Align: tview.AlignRight}, HeaderColumn{Name: "STATUS"}, + HeaderColumn{Name: "RESTARTS", Align: tview.AlignRight}, + HeaderColumn{Name: "IP"}, + HeaderColumn{Name: "NODE"}, + HeaderColumn{Name: "NOMINATED NODE", Wide: true}, + HeaderColumn{Name: "READINESS GATES", Wide: true}, HeaderColumn{Name: "CPU", Align: tview.AlignRight, MX: true}, HeaderColumn{Name: "MEM", Align: tview.AlignRight, MX: true}, HeaderColumn{Name: "CPU/R:L", Align: tview.AlignRight, Wide: true}, @@ -73,13 +94,9 @@ func (Pod) Header(ns string) Header { HeaderColumn{Name: "%CPU/L", Align: tview.AlignRight, MX: true}, HeaderColumn{Name: "%MEM/R", Align: tview.AlignRight, MX: true}, HeaderColumn{Name: "%MEM/L", Align: tview.AlignRight, MX: true}, - HeaderColumn{Name: "IP"}, - HeaderColumn{Name: "NODE"}, HeaderColumn{Name: "QOS", Wide: true}, HeaderColumn{Name: "LABELS", Wide: true}, HeaderColumn{Name: "VALID", Wide: true}, - HeaderColumn{Name: "NOMINATED NODE", Wide: true}, - HeaderColumn{Name: "READINESS GATES", Wide: true}, HeaderColumn{Name: "AGE", Time: true}, } } @@ -88,7 +105,7 @@ func (Pod) Header(ns string) Header { func (p Pod) Render(o interface{}, ns string, row *Row) error { pwm, ok := o.(*PodWithMetrics) if !ok { - return fmt.Errorf("Expected PodWithMetrics, but got %T", o) + return fmt.Errorf("expected PodWithMetrics, but got %T", o) } var po v1.Pod @@ -106,9 +123,13 @@ func (p Pod) Render(o interface{}, ns string, row *Row) error { po.Namespace, po.ObjectMeta.Name, "●", - strconv.Itoa(cr) + "/" + strconv.Itoa(len(ss)), - strconv.Itoa(rc), + strconv.Itoa(cr) + "/" + strconv.Itoa(len(po.Spec.Containers)), phase, + strconv.Itoa(rc), + na(po.Status.PodIP), + na(po.Spec.NodeName), + asNominated(po.Status.NominatedNodeName), + asReadinessGate(po), toMc(c.cpu), toMi(c.mem), toMc(r.cpu) + ":" + toMc(r.lcpu), @@ -117,13 +138,9 @@ func (p Pod) Render(o interface{}, ns string, row *Row) error { client.ToPercentageStr(c.cpu, r.lcpu), client.ToPercentageStr(c.mem, r.mem), client.ToPercentageStr(c.mem, r.lmem), - na(po.Status.PodIP), - na(po.Spec.NodeName), p.mapQOS(po.Status.QOSClass), mapToStr(po.Labels), asStatus(p.diagnose(phase, cr, len(ss))), - asNominated(po.Status.NominatedNodeName), - asReadinessGate(po), toAge(po.GetCreationTimestamp()), } @@ -368,3 +385,89 @@ func checkContainerStatus(cs v1.ContainerStatus, i, initCount int) string { return "Init:" + strconv.Itoa(i) + "/" + strconv.Itoa(initCount) } } + +// PosStatus computes pod status. +func PodStatus(pod *v1.Pod) string { + reason := string(pod.Status.Phase) + if pod.Status.Reason != "" { + reason = pod.Status.Reason + } + + for _, condition := range pod.Status.Conditions { + if condition.Type == v1.PodScheduled && condition.Reason == v1.PodReasonSchedulingGated { + reason = v1.PodReasonSchedulingGated + } + } + + var initializing bool + for i := range pod.Status.InitContainerStatuses { + container := pod.Status.InitContainerStatuses[i] + switch { + case container.State.Terminated != nil && container.State.Terminated.ExitCode == 0: + continue + case container.State.Terminated != nil: + if len(container.State.Terminated.Reason) == 0 { + if container.State.Terminated.Signal != 0 { + reason = fmt.Sprintf("Init:Signal:%d", container.State.Terminated.Signal) + } else { + reason = fmt.Sprintf("Init:ExitCode:%d", container.State.Terminated.ExitCode) + } + } else { + reason = "Init:" + container.State.Terminated.Reason + } + initializing = true + case container.State.Waiting != nil && len(container.State.Waiting.Reason) > 0 && container.State.Waiting.Reason != "PodInitializing": + reason = "Init:" + container.State.Waiting.Reason + initializing = true + default: + reason = fmt.Sprintf("Init:%d/%d", i, len(pod.Spec.InitContainers)) + initializing = true + } + break + } + if !initializing { + var hasRunning bool + for i := len(pod.Status.ContainerStatuses) - 1; i >= 0; i-- { + container := pod.Status.ContainerStatuses[i] + if container.State.Waiting != nil && container.State.Waiting.Reason != "" { + reason = container.State.Waiting.Reason + } else if container.State.Terminated != nil && container.State.Terminated.Reason != "" { + reason = container.State.Terminated.Reason + } else if container.State.Terminated != nil && container.State.Terminated.Reason == "" { + if container.State.Terminated.Signal != 0 { + reason = fmt.Sprintf("Signal:%d", container.State.Terminated.Signal) + } else { + reason = fmt.Sprintf("ExitCode:%d", container.State.Terminated.ExitCode) + } + } else if container.Ready && container.State.Running != nil { + hasRunning = true + } + } + + if reason == PhaseCompleted && hasRunning { + if hasPodReadyCondition(pod.Status.Conditions) { + reason = PhaseRunning + } else { + reason = PhaseNotReady + } + } + } + + if pod.DeletionTimestamp != nil && pod.Status.Reason == node.NodeUnreachablePodReason { + reason = PhaseUnknown + } else if pod.DeletionTimestamp != nil { + reason = PhaseTerminating + } + + return reason +} + +func hasPodReadyCondition(conditions []v1.PodCondition) bool { + for _, condition := range conditions { + if condition.Type == v1.PodReady && condition.Status == v1.ConditionTrue { + return true + } + } + + return false +} diff --git a/internal/render/pod_test.go b/internal/render/pod_test.go index d65ff66748..e439b6caf2 100644 --- a/internal/render/pod_test.go +++ b/internal/render/pod_test.go @@ -159,7 +159,7 @@ func TestPodRender(t *testing.T) { assert.Nil(t, err) assert.Equal(t, "default/nginx", r.ID) - e := render.Fields{"default", "nginx", "●", "1/1", "0", "Running", "100", "50", "100:0", "70:170", "100", "n/a", "71", "29", "172.17.0.6", "minikube", "BE"} + e := render.Fields{"default", "nginx", "●", "1/1", "Running", "0", "172.17.0.6", "minikube", "", "", "100", "50", "100:0", "70:170", "100", "n/a", "71"} assert.Equal(t, e, r.Fields[:17]) } @@ -190,10 +190,70 @@ func TestPodInitRender(t *testing.T) { assert.Nil(t, err) assert.Equal(t, "default/nginx", r.ID) - e := render.Fields{"default", "nginx", "●", "1/1", "0", "Init:0/1", "10", "10", "100:0", "70:170", "10", "n/a", "14", "5", "172.17.0.6", "minikube", "BE"} + e := render.Fields{"default", "nginx", "●", "1/1", "Init:0/1", "0", "172.17.0.6", "minikube", "", "", "10", "10", "100:0", "70:170", "10", "n/a", "14"} assert.Equal(t, e, r.Fields[:17]) } +func TestCheckPodStatus(t *testing.T) { + uu := map[string]struct { + pod v1.Pod + e string + }{ + "unknown": { + pod: v1.Pod{ + Status: v1.PodStatus{ + Phase: render.PhaseUnknown, + }, + }, + e: render.PhaseUnknown, + }, + "running": { + pod: v1.Pod{ + Status: v1.PodStatus{ + Phase: v1.PodRunning, + InitContainerStatuses: []v1.ContainerStatus{}, + ContainerStatuses: []v1.ContainerStatus{ + { + Name: "c1", + State: v1.ContainerState{ + Running: &v1.ContainerStateRunning{}, + }, + }, + }, + }, + }, + e: render.PhaseRunning, + }, + "backoff": { + pod: v1.Pod{ + Status: v1.PodStatus{ + Phase: v1.PodRunning, + InitContainerStatuses: []v1.ContainerStatus{}, + ContainerStatuses: []v1.ContainerStatus{ + { + Name: "c1", + State: v1.ContainerState{ + Waiting: &v1.ContainerStateWaiting{ + Reason: render.PhaseImagePullBackOff, + }, + }, + }, + }, + }, + }, + e: render.PhaseImagePullBackOff, + }, + } + + for k := range uu { + u := uu[k] + t.Run(k, func(t *testing.T) { + assert.Equal(t, u.e, render.PodStatus(&u.pod)) + }) + } + +} + // ---------------------------------------------------------------------------- // Helpers... @@ -218,3 +278,123 @@ func makeRes(c, m string) v1.ResourceList { v1.ResourceMemory: mem, } } + +// apiVersion: v1 +// kind: Pod +// metadata: +// creationTimestamp: "2023-11-11T17:01:40Z" +// finalizers: +// - batch.kubernetes.io/job-tracking +// generateName: hello-28328646- +// labels: +// batch.kubernetes.io/controller-uid: 35cf5552-7180-48c1-b7b2-8b6e630a7860 +// batch.kubernetes.io/job-name: hello-28328646 +// controller-uid: 35cf5552-7180-48c1-b7b2-8b6e630a7860 +// job-name: hello-28328646 +// name: hello-28328646-h9fnh +// namespace: fred +// ownerReferences: +// - apiVersion: batch/v1 +// blockOwnerDeletion: true +// controller: true +// kind: Job +// name: hello-28328646 +// uid: 35cf5552-7180-48c1-b7b2-8b6e630a7860 +// resourceVersion: "381637" +// uid: ea77c360-6375-459b-8b30-2ac0c59404cd +// spec: +// containers: +// - args: +// - /bin/bash +// - -c +// - for i in {1..5}; do echo "hello";sleep 1; done +// image: blang/busybox-bash +// imagePullPolicy: Always +// name: c1 +// resources: {} +// terminationMessagePath: /dev/termination-log +// terminationMessagePolicy: File +// volumeMounts: +// - mountPath: /var/run/secrets/kubernetes.io/serviceaccount +// name: kube-api-access-7sztm +// readOnly: true +// dnsPolicy: ClusterFirst +// enableServiceLinks: true +// nodeName: kind-worker +// preemptionPolicy: PreemptLowerPriority +// priority: 0 +// restartPolicy: OnFailure +// schedulerName: default-scheduler +// securityContext: {} +// serviceAccount: default +// serviceAccountName: default +// terminationGracePeriodSeconds: 30 +// tolerations: +// - effect: NoExecute +// key: node.kubernetes.io/not-ready +// operator: Exists +// tolerationSeconds: 300 +// - effect: NoExecute +// key: node.kubernetes.io/unreachable +// operator: Exists +// tolerationSeconds: 300 +// volumes: +// - name: kube-api-access-7sztm +// projected: +// defaultMode: 420 +// sources: +// - serviceAccountToken: +// expirationSeconds: 3607 +// path: token +// - configMap: +// items: +// - key: ca.crt +// path: ca.crt +// name: kube-root-ca.crt +// - downwardAPI: +// items: +// - fieldRef: +// apiVersion: v1 +// fieldPath: metadata.namespace +// path: namespace +// status: +// conditions: +// - lastProbeTime: null +// lastTransitionTime: "2023-11-11T17:01:40Z" +// status: "True" +// type: Initialized +// - lastProbeTime: null +// lastTransitionTime: "2023-11-11T17:01:40Z" +// message: 'containers with unready status: [c1[]' +// reason: ContainersNotReady +// status: "False" +// type: Ready +// - lastProbeTime: null +// lastTransitionTime: "2023-11-11T17:01:40Z" +// message: 'containers with unready status: [c1[]' +// reason: ContainersNotReady +// status: "False" +// type: ContainersReady +// - lastProbeTime: null +// lastTransitionTime: "2023-11-11T17:01:40Z" +// status: "True" +// type: PodScheduled +// containerStatuses: +// - image: blang/busybox-bash +// imageID: "" +// lastState: {} +// name: c1 +// ready: false +// restartCount: 0 +// started: false +// state: +// waiting: +// message: Back-off pulling image "blang/busybox-bash" +// reason: ImagePullBackOff +// hostIP: 172.18.0.3 +// phase: Pending +// podIP: 10.244.1.59 +// podIPs: +// - ip: 10.244.1.59 +// qosClass: BestEffort +// startTime: "2023-11-11T17:01:40Z" diff --git a/internal/render/pv.go b/internal/render/pv.go index eecf98a7e8..630c2e77ac 100644 --- a/internal/render/pv.go +++ b/internal/render/pv.go @@ -49,7 +49,7 @@ func (p PersistentVolume) ColorerFunc() ColorerFunc { func (PersistentVolume) Header(string) Header { return Header{ HeaderColumn{Name: "NAME"}, - HeaderColumn{Name: "CAPACITY"}, + HeaderColumn{Name: "CAPACITY", Capacity: true}, HeaderColumn{Name: "ACCESS MODES"}, HeaderColumn{Name: "RECLAIM POLICY"}, HeaderColumn{Name: "STATUS"}, diff --git a/internal/render/pvc.go b/internal/render/pvc.go index 809ae1c612..f5514d5b58 100644 --- a/internal/render/pvc.go +++ b/internal/render/pvc.go @@ -21,7 +21,7 @@ func (PersistentVolumeClaim) Header(ns string) Header { HeaderColumn{Name: "NAME"}, HeaderColumn{Name: "STATUS"}, HeaderColumn{Name: "VOLUME"}, - HeaderColumn{Name: "CAPACITY"}, + HeaderColumn{Name: "CAPACITY", Capacity: true}, HeaderColumn{Name: "ACCESS MODES"}, HeaderColumn{Name: "STORAGECLASS"}, HeaderColumn{Name: "LABELS", Wide: true}, diff --git a/internal/render/rbac.go b/internal/render/rbac.go index ddf2c790af..5d0f7a3cd7 100644 --- a/internal/render/rbac.go +++ b/internal/render/rbac.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "github.com/derailed/tcell/v2" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -36,9 +35,7 @@ type Rbac struct { // ColorerFunc colors a resource row. func (Rbac) ColorerFunc() ColorerFunc { - return func(_ string, _ Header, _re RowEvent) tcell.Color { - return tcell.ColorMediumSpringGreen - } + return DefaultColorer } // Header returns a header row. diff --git a/internal/render/row.go b/internal/render/row.go index 5962c24aab..1bd5e3872f 100644 --- a/internal/render/row.go +++ b/internal/render/row.go @@ -145,12 +145,13 @@ func (rr Rows) Find(id string) (int, bool) { } // Sort rows based on column index and order. -func (rr Rows) Sort(col int, asc, isNum, isDur bool) { +func (rr Rows) Sort(col int, asc, isNum, isDur, isCapacity bool) { t := RowSorter{ Rows: rr, Index: col, IsNumber: isNum, IsDuration: isDur, + IsCapacity: isCapacity, Asc: asc, } sort.Sort(t) @@ -160,10 +161,12 @@ func (rr Rows) Sort(col int, asc, isNum, isDur bool) { // RowSorter sorts rows. type RowSorter struct { - Rows Rows - Index int - IsNumber, IsDuration bool - Asc bool + Rows Rows + Index int + IsNumber bool + IsDuration bool + IsCapacity bool + Asc bool } func (s RowSorter) Len() int { @@ -177,7 +180,7 @@ func (s RowSorter) Swap(i, j int) { func (s RowSorter) Less(i, j int) bool { v1, v2 := s.Rows[i].Fields[s.Index], s.Rows[j].Fields[s.Index] id1, id2 := s.Rows[i].ID, s.Rows[j].ID - less := Less(s.IsNumber, s.IsDuration, id1, id2, v1, v2) + less := Less(s.IsNumber, s.IsDuration, s.IsCapacity, id1, id2, v1, v2) if s.Asc { return less } @@ -188,7 +191,7 @@ func (s RowSorter) Less(i, j int) bool { // Helpers... // Less return true if c1 < c2. -func Less(isNumber, isDuration bool, id1, id2, v1, v2 string) bool { +func Less(isNumber, isDuration, isCapacity bool, id1, id2, v1, v2 string) bool { var less bool switch { case isNumber: @@ -197,6 +200,9 @@ func Less(isNumber, isDuration bool, id1, id2, v1, v2 string) bool { case isDuration: d1, d2 := durationToSeconds(v1), durationToSeconds(v2) less = d1 <= d2 + case isCapacity: + c1, c2 := capacityToNumber(v1), capacityToNumber(v2) + less = c1 <= c2 default: less = sortorder.NaturalLess(v1, v2) } diff --git a/internal/render/row_event.go b/internal/render/row_event.go index 795006c4bc..83c7c44022 100644 --- a/internal/render/row_event.go +++ b/internal/render/row_event.go @@ -193,7 +193,7 @@ func (r RowEvents) FindIndex(id string) (int, bool) { } // Sort rows based on column index and order. -func (r RowEvents) Sort(ns string, sortCol int, isDuration, numCol, asc bool) { +func (r RowEvents) Sort(ns string, sortCol int, isDuration, numCol, isCapacity, asc bool) { if sortCol == -1 { return } @@ -205,6 +205,7 @@ func (r RowEvents) Sort(ns string, sortCol int, isDuration, numCol, asc bool) { Asc: asc, IsNumber: numCol, IsDuration: isDuration, + IsCapacity: isCapacity, } sort.Sort(t) } @@ -218,6 +219,7 @@ type RowEventSorter struct { NS string IsNumber bool IsDuration bool + IsCapacity bool Asc bool } @@ -232,7 +234,7 @@ func (r RowEventSorter) Swap(i, j int) { func (r RowEventSorter) Less(i, j int) bool { f1, f2 := r.Events[i].Row.Fields, r.Events[j].Row.Fields id1, id2 := r.Events[i].Row.ID, r.Events[j].Row.ID - less := Less(r.IsNumber, r.IsDuration, id1, id2, f1[r.Index], f2[r.Index]) + less := Less(r.IsNumber, r.IsDuration, r.IsCapacity, id1, id2, f1[r.Index], f2[r.Index]) if r.Asc { return less } diff --git a/internal/render/row_event_test.go b/internal/render/row_event_test.go index 975815e5a2..c106673098 100644 --- a/internal/render/row_event_test.go +++ b/internal/render/row_event_test.go @@ -413,6 +413,7 @@ func TestRowEventsSort(t *testing.T) { re render.RowEvents col int duration, num, asc bool + capacity bool e render.RowEvents }{ "age_time": { @@ -464,12 +465,33 @@ func TestRowEventsSort(t *testing.T) { {Row: render.Row{ID: "ns2/C", Fields: render.Fields{"C", "2", "3"}}}, }, }, + "capacity": { + re: render.RowEvents{ + {Row: render.Row{ID: "ns1/B", Fields: render.Fields{"B", "2", "3", "1Gi"}}}, + {Row: render.Row{ID: "ns1/A", Fields: render.Fields{"A", "2", "3", "1.1G"}}}, + {Row: render.Row{ID: "ns1/C", Fields: render.Fields{"C", "2", "3", "0.5Ti"}}}, + {Row: render.Row{ID: "ns2/B", Fields: render.Fields{"B", "2", "3", "12e6"}}}, + {Row: render.Row{ID: "ns2/A", Fields: render.Fields{"A", "2", "3", "1234"}}}, + {Row: render.Row{ID: "ns2/C", Fields: render.Fields{"C", "2", "3", "0.1Ei"}}}, + }, + col: 3, + asc: true, + capacity: true, + e: render.RowEvents{ + {Row: render.Row{ID: "ns2/A", Fields: render.Fields{"A", "2", "3", "1234"}}}, + {Row: render.Row{ID: "ns2/B", Fields: render.Fields{"B", "2", "3", "12e6"}}}, + {Row: render.Row{ID: "ns1/B", Fields: render.Fields{"B", "2", "3", "1Gi"}}}, + {Row: render.Row{ID: "ns1/A", Fields: render.Fields{"A", "2", "3", "1.1G"}}}, + {Row: render.Row{ID: "ns1/C", Fields: render.Fields{"C", "2", "3", "0.5Ti"}}}, + {Row: render.Row{ID: "ns2/C", Fields: render.Fields{"C", "2", "3", "0.1Ei"}}}, + }, + }, } for k := range uu { u := uu[k] t.Run(k, func(t *testing.T) { - u.re.Sort("", u.col, u.duration, u.num, u.asc) + u.re.Sort("", u.col, u.duration, u.num, u.capacity, u.asc) assert.Equal(t, u.e, u.re) }) } diff --git a/internal/render/row_test.go b/internal/render/row_test.go index 185eb7000b..82e4728f44 100644 --- a/internal/render/row_test.go +++ b/internal/render/row_test.go @@ -304,7 +304,7 @@ func TestRowsSortText(t *testing.T) { for k := range uu { u := uu[k] t.Run(k, func(t *testing.T) { - u.rows.Sort(u.col, u.asc, u.num, false) + u.rows.Sort(u.col, u.asc, u.num, false, false) assert.Equal(t, u.e, u.rows) }) } @@ -369,7 +369,7 @@ func TestRowsSortDuration(t *testing.T) { for k := range uu { u := uu[k] t.Run(k, func(t *testing.T) { - u.rows.Sort(u.col, u.asc, false, true) + u.rows.Sort(u.col, u.asc, false, true, false) assert.Equal(t, u.e, u.rows) }) } @@ -411,7 +411,49 @@ func TestRowsSortMetrics(t *testing.T) { for k := range uu { u := uu[k] t.Run(k, func(t *testing.T) { - u.rows.Sort(u.col, u.asc, true, false) + u.rows.Sort(u.col, u.asc, true, false, false) + assert.Equal(t, u.e, u.rows) + }) + } +} + +func TestRowsSortCapacity(t *testing.T) { + uu := map[string]struct { + rows render.Rows + col int + asc bool + e render.Rows + }{ + "capacityAsc": { + rows: render.Rows{ + {Fields: []string{"10Gi", "duh"}}, + {Fields: []string{"10G", "blee"}}, + }, + col: 0, + asc: true, + e: render.Rows{ + {Fields: []string{"10G", "blee"}}, + {Fields: []string{"10Gi", "duh"}}, + }, + }, + "capacityDesc": { + rows: render.Rows{ + {Fields: []string{"10000m", "1000Mi"}}, + {Fields: []string{"1m", "50Mi"}}, + }, + col: 1, + asc: false, + e: render.Rows{ + {Fields: []string{"10000m", "1000Mi"}}, + {Fields: []string{"1m", "50Mi"}}, + }, + }, + } + + for k := range uu { + u := uu[k] + t.Run(k, func(t *testing.T) { + u.rows.Sort(u.col, u.asc, false, false, true) assert.Equal(t, u.e, u.rows) }) } @@ -419,14 +461,17 @@ func TestRowsSortMetrics(t *testing.T) { func TestLess(t *testing.T) { uu := map[string]struct { - isNumber, isDuration bool - id1, id2 string - v1, v2 string - e bool + isNumber bool + isDuration bool + isCapacity bool + id1, id2 string + v1, v2 string + e bool }{ "years": { isNumber: false, isDuration: true, + isCapacity: false, id1: "id1", id2: "id2", v1: "2y263d", @@ -435,17 +480,38 @@ func TestLess(t *testing.T) { "hours": { isNumber: false, isDuration: true, + isCapacity: false, id1: "id1", id2: "id2", v1: "2y263d", v2: "19h", }, + "capacity1": { + isNumber: false, + isDuration: false, + isCapacity: true, + id1: "id1", + id2: "id2", + v1: "1Gi", + v2: "1G", + e: false, + }, + "capacity2": { + isNumber: false, + isDuration: false, + isCapacity: true, + id1: "id1", + id2: "id2", + v1: "1Gi", + v2: "1Ti", + e: true, + }, } for k := range uu { u := uu[k] t.Run(k, func(t *testing.T) { - assert.Equal(t, u.e, render.Less(u.isNumber, u.isDuration, u.id1, u.id2, u.v1, u.v2)) + assert.Equal(t, u.e, render.Less(u.isNumber, u.isDuration, u.isCapacity, u.id1, u.id2, u.v1, u.v2)) }) } } diff --git a/internal/render/sc.go b/internal/render/sc.go index dc6de66c7c..a2995eab7e 100644 --- a/internal/render/sc.go +++ b/internal/render/sc.go @@ -5,8 +5,10 @@ import ( "github.com/derailed/k9s/internal/client" storagev1 "k8s.io/api/storage/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubectl/pkg/util/storage" ) // StorageClass renders a K8s StorageClass to screen. @@ -19,6 +21,9 @@ func (StorageClass) Header(ns string) Header { return Header{ HeaderColumn{Name: "NAME"}, HeaderColumn{Name: "PROVISIONER"}, + HeaderColumn{Name: "RECLAIMPOLICY"}, + HeaderColumn{Name: "VOLUMEBINDINGMODE"}, + HeaderColumn{Name: "ALLOWVOLUMEEXPANSION"}, HeaderColumn{Name: "LABELS", Wide: true}, HeaderColumn{Name: "VALID", Wide: true}, HeaderColumn{Name: "AGE", Time: true}, @@ -26,7 +31,7 @@ func (StorageClass) Header(ns string) Header { } // Render renders a K8s resource to screen. -func (StorageClass) Render(o interface{}, ns string, r *Row) error { +func (s StorageClass) Render(o interface{}, ns string, r *Row) error { raw, ok := o.(*unstructured.Unstructured) if !ok { return fmt.Errorf("Expected StorageClass, but got %T", o) @@ -39,8 +44,11 @@ func (StorageClass) Render(o interface{}, ns string, r *Row) error { r.ID = client.FQN(client.ClusterScope, sc.ObjectMeta.Name) r.Fields = Fields{ - sc.Name, - string(sc.Provisioner), + s.nameWithDefault(sc.ObjectMeta), + sc.Provisioner, + strPtrToStr((*string)(sc.ReclaimPolicy)), + strPtrToStr((*string)(sc.VolumeBindingMode)), + boolPtrToStr(sc.AllowVolumeExpansion), mapToStr(sc.Labels), "", toAge(sc.GetCreationTimestamp()), @@ -48,3 +56,10 @@ func (StorageClass) Render(o interface{}, ns string, r *Row) error { return nil } + +func (StorageClass) nameWithDefault(meta metav1.ObjectMeta) string { + if storage.IsDefaultAnnotationText(meta) == "Yes" { + return meta.Name + " (default)" + } + return meta.Name +} diff --git a/internal/render/sc_test.go b/internal/render/sc_test.go index 80e13393d0..60cd56f242 100644 --- a/internal/render/sc_test.go +++ b/internal/render/sc_test.go @@ -13,5 +13,5 @@ func TestStorageClassRender(t *testing.T) { assert.NoError(t, c.Render(load(t, "sc"), "", &r)) assert.Equal(t, "-/standard", r.ID) - assert.Equal(t, render.Fields{"standard", "kubernetes.io/gce-pd"}, r.Fields[:2]) + assert.Equal(t, render.Fields{"standard (default)", "kubernetes.io/gce-pd", "Delete", "Immediate", "true"}, r.Fields[:5]) } diff --git a/internal/render/testdata/sc.json b/internal/render/testdata/sc.json index afd1d892c3..c8ad58efe6 100644 --- a/internal/render/testdata/sc.json +++ b/internal/render/testdata/sc.json @@ -20,5 +20,6 @@ }, "provisioner": "kubernetes.io/gce-pd", "reclaimPolicy": "Delete", - "volumeBindingMode": "Immediate" + "volumeBindingMode": "Immediate", + "allowVolumeExpansion": true } \ No newline at end of file diff --git a/internal/ui/config.go b/internal/ui/config.go index 23a6c195ca..4a95e10151 100644 --- a/internal/ui/config.go +++ b/internal/ui/config.go @@ -2,7 +2,9 @@ package ui import ( "context" + "errors" "fmt" + "os" "path/filepath" "github.com/derailed/k9s/internal/config" @@ -42,10 +44,11 @@ func (c *Configurator) CustomViewsWatcher(ctx context.Context, s synchronizer) e for { select { case evt := <-w.Events: - _ = evt - s.QueueUpdateDraw(func() { - c.RefreshCustomViews() - }) + if evt.Name == config.K9sViewConfigFile { + s.QueueUpdateDraw(func() { + c.RefreshCustomViews() + }) + } case err := <-w.Errors: log.Warn().Err(err).Msg("CustomView watcher failed") return @@ -61,7 +64,7 @@ func (c *Configurator) CustomViewsWatcher(ctx context.Context, s synchronizer) e log.Debug().Msgf("CustomView watching `%s", config.K9sViewConfigFile) c.RefreshCustomViews() - return w.Add(config.K9sViewConfigFile) + return w.Add(config.K9sHome()) } // RefreshCustomViews load view configuration changes. @@ -93,7 +96,7 @@ func (c *Configurator) StylesWatcher(ctx context.Context, s synchronizer) error for { select { case evt := <-w.Events: - if evt.Op != fsnotify.Chmod { + if evt.Name == c.skinFile && evt.Op != fsnotify.Chmod { s.QueueUpdateDraw(func() { c.RefreshStyles(c.Config.K9s.CurrentCluster) }) @@ -112,7 +115,7 @@ func (c *Configurator) StylesWatcher(ctx context.Context, s synchronizer) error }() log.Debug().Msgf("SkinWatcher watching `%s", c.skinFile) - return w.Add(c.skinFile) + return w.Add(config.K9sHome()) } // BenchConfig location of the benchmarks configuration file. @@ -131,14 +134,22 @@ func (c *Configurator) RefreshStyles(context string) { c.Styles.Reset() } if err := c.Styles.Load(clusterSkins); err != nil { - log.Warn().Msgf("No context specific skin file found -- %s", clusterSkins) + if errors.Is(err, os.ErrNotExist) { + log.Warn().Msgf("No context specific skin file found -- %s", clusterSkins) + } else { + log.Error().Msgf("Failed to parse context specific skin file -- %s. %s.", clusterSkins, err) + } } else { c.updateStyles(clusterSkins) return } if err := c.Styles.Load(config.K9sStylesFile); err != nil { - log.Warn().Msgf("No skin file found -- %s. Loading stock skins.", config.K9sStylesFile) + if errors.Is(err, os.ErrNotExist) { + log.Warn().Msgf("No skin file found -- %s. Loading stock skins.", config.K9sStylesFile) + } else { + log.Error().Msgf("Failed to parse skin file -- %s. %s. Loading stock skins.", config.K9sStylesFile, err) + } c.updateStyles("") return } diff --git a/internal/ui/dialog/confirm.go b/internal/ui/dialog/confirm.go index e7278e3441..fec298763e 100644 --- a/internal/ui/dialog/confirm.go +++ b/internal/ui/dialog/confirm.go @@ -10,6 +10,59 @@ const dialogKey = "dialog" type confirmFunc func() +func ShowConfirmAck(app *ui.App, pages *ui.Pages, acceptStr string, override bool, title, msg string, ack confirmFunc, cancel cancelFunc) { + styles := app.Styles.Dialog() + + 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) + cancel() + }) + + var accept bool + if override { + changedFn := func(t string) { + accept = (t == acceptStr) + } + f.AddInputField("Confirm:", "", 30, nil, changedFn) + } else { + accept = true + } + + f.AddButton("OK", func() { + if !accept { + return + } + ack() + dismissConfirm(pages) + 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) + modal := tview.NewModalForm("<"+title+">", f) + modal.SetText(msg) + modal.SetTextColor(styles.FgColor.Color()) + modal.SetDoneFunc(func(int, string) { + dismissConfirm(pages) + cancel() + }) + pages.AddPage(confirmKey, modal, false, false) + pages.ShowPage(confirmKey) +} + // ShowConfirm pops a confirmation dialog. func ShowConfirm(styles config.Dialog, pages *ui.Pages, title, msg string, ack confirmFunc, cancel cancelFunc) { f := tview.NewForm() 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/logo.go b/internal/ui/logo.go index 80c63199d6..a8cddad04c 100644 --- a/internal/ui/logo.go +++ b/internal/ui/logo.go @@ -67,17 +67,17 @@ func (l *Logo) Reset() { // Err displays a log error state. func (l *Logo) Err(msg string) { - l.update(msg, config.NewColor("red")) + l.update(msg, l.styles.Body().LogoColorError) } // Warn displays a log warning state. func (l *Logo) Warn(msg string) { - l.update(msg, config.NewColor("mediumvioletred")) + l.update(msg, l.styles.Body().LogoColorWarn) } // Info displays a log info state. func (l *Logo) Info(msg string) { - l.update(msg, config.NewColor("green")) + l.update(msg, l.styles.Body().LogoColorInfo) } func (l *Logo) update(msg string, c config.Color) { @@ -87,7 +87,9 @@ func (l *Logo) update(msg string, c config.Color) { func (l *Logo) refreshStatus(msg string, c config.Color) { l.status.SetBackgroundColor(c.Color()) - l.status.SetText(fmt.Sprintf("[white::b]%s", msg)) + l.status.SetText( + fmt.Sprintf("[%s::b]%s", l.styles.Body().LogoColorMsg, msg), + ) } func (l *Logo) refreshLogo(c config.Color) { diff --git a/internal/ui/logo_test.go b/internal/ui/logo_test.go index 7a42c1d3be..2e3c053c69 100644 --- a/internal/ui/logo_test.go +++ b/internal/ui/logo_test.go @@ -24,17 +24,17 @@ func TestLogoStatus(t *testing.T) { "info": { "[#008000::b] ____ __.________ \n[#008000::b]| |/ _/ __ \\______\n[#008000::b]| < \\____ / ___/\n[#008000::b]| | \\ / /\\___ \\ \n[#008000::b]|____|__ \\ /____//____ >\n[#008000::b] \\/ \\/ \n", "blee", - "[white::b]blee\n", + "[#ffffff::b]blee\n", }, "warn": { "[#c71585::b] ____ __.________ \n[#c71585::b]| |/ _/ __ \\______\n[#c71585::b]| < \\____ / ___/\n[#c71585::b]| | \\ / /\\___ \\ \n[#c71585::b]|____|__ \\ /____//____ >\n[#c71585::b] \\/ \\/ \n", "blee", - "[white::b]blee\n", + "[#ffffff::b]blee\n", }, "err": { "[#ff0000::b] ____ __.________ \n[#ff0000::b]| |/ _/ __ \\______\n[#ff0000::b]| < \\____ / ___/\n[#ff0000::b]| | \\ / /\\___ \\ \n[#ff0000::b]|____|__ \\ /____//____ >\n[#ff0000::b] \\/ \\/ \n", "blee", - "[white::b]blee\n", + "[#ffffff::b]blee\n", }, } diff --git a/internal/ui/menu.go b/internal/ui/menu.go index 4e2082f097..02cbedbe79 100644 --- a/internal/ui/menu.go +++ b/internal/ui/menu.go @@ -148,7 +148,7 @@ func (m *Menu) buildMenuTable(hh model.MenuHints, table []model.MenuHints, colCo func (m *Menu) layout(table []model.MenuHints, mm []int, out [][]string) { for r := range table { for c := range table[r] { - out[r][c] = keyConv(m.formatMenu(table[r][c], mm[c])) + out[r][c] = m.formatMenu(table[r][c], mm[c]) } } } @@ -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/prompt.go b/internal/ui/prompt.go index 4283793cb9..117dd10017 100644 --- a/internal/ui/prompt.go +++ b/internal/ui/prompt.go @@ -182,6 +182,7 @@ func (p *Prompt) InCmdMode() bool { } func (p *Prompt) activate() { + p.Clear() p.SetCursorIndex(len(p.model.GetText())) p.write(p.model.GetText(), p.model.GetSuggestion()) p.model.Notify(false) @@ -201,7 +202,7 @@ func (p *Prompt) write(text, suggest string) { p.SetCursorIndex(p.spacer + len(text)) txt := text if suggest != "" { - txt += fmt.Sprintf("[%s::-]%s", p.styles.K9s.Prompt.SuggestColor, suggest) + txt += fmt.Sprintf("[%s::-]%s", p.styles.Prompt().SuggestColor, suggest) } fmt.Fprintf(p, defaultPrompt, p.icon, txt) } @@ -230,7 +231,7 @@ func (p *Prompt) BufferActive(activate bool, kind model.BufferKind) { p.ShowCursor(true) p.SetBorder(true) p.SetTextColor(p.styles.FgColor()) - p.SetBorderColor(colorFor(kind)) + p.SetBorderColor(p.colorFor(kind)) p.icon = p.iconFor(kind) p.activate() return @@ -259,12 +260,12 @@ func (p *Prompt) iconFor(k model.BufferKind) rune { // ---------------------------------------------------------------------------- // Helpers... -func colorFor(k model.BufferKind) tcell.Color { +func (p *Prompt) colorFor(k model.BufferKind) tcell.Color { // nolint:exhaustive switch k { case model.CommandBuffer: - return tcell.ColorAqua + return p.styles.Prompt().Border.CommandColor.Color() default: - return tcell.ColorSeaGreen + return p.styles.Prompt().Border.DefaultColor.Color() } } diff --git a/internal/ui/prompt_test.go b/internal/ui/prompt_test.go index b41c7ec232..41b1c96497 100644 --- a/internal/ui/prompt_test.go +++ b/internal/ui/prompt_test.go @@ -1,6 +1,7 @@ package ui_test import ( + "github.com/derailed/tcell/v2" "testing" "github.com/derailed/k9s/internal/config" @@ -45,3 +46,87 @@ func TestCmdMode(t *testing.T) { assert.Equal(t, f, v.InCmdMode()) } } + +// Tests that, when active, the prompt has the appropriate color +func TestPromptColor(t *testing.T) { + styles := config.NewStyles() + app := ui.App{} + + // Make sure to have different values to be sure that the prompt color actually changes depending on its type + assert.NotEqual(t, styles.Prompt().Border.DefaultColor.Color(), styles.Prompt().Border.CommandColor.Color()) + + testCases := []struct { + kind model.BufferKind + expectedColor tcell.Color + }{ + // Command prompt case + { + kind: model.CommandBuffer, + expectedColor: styles.Prompt().Border.CommandColor.Color(), + }, + // Any other prompt type case + { + // Simulate a different type of prompt since no particular constant exists + kind: model.CommandBuffer + 1, + expectedColor: styles.Prompt().Border.DefaultColor.Color(), + }, + } + + for _, testCase := range testCases { + model := model.NewFishBuff(':', testCase.kind) + prompt := ui.NewPrompt(&app, true, styles) + + prompt.SetModel(model) + model.AddListener(prompt) + + model.SetActive(true) + assert.Equal(t, prompt.GetBorderColor(), testCase.expectedColor) + } +} + +// Tests that, when a change of style occurs, the prompt will have the appropriate color when active +func TestPromptStyleChanged(t *testing.T) { + app := ui.App{} + styles := config.NewStyles() + newStyles := config.NewStyles() + newStyles.K9s.Prompt.Border = config.PromptBorder{ + DefaultColor: "green", + CommandColor: "yellow", + } + + // Check that the prompt won't change the border into the same style + assert.NotEqual(t, styles.Prompt().Border.CommandColor.Color(), newStyles.Prompt().Border.CommandColor.Color()) + assert.NotEqual(t, styles.Prompt().Border.DefaultColor.Color(), newStyles.Prompt().Border.DefaultColor.Color()) + + testCases := []struct { + kind model.BufferKind + expectedColor tcell.Color + }{ + // Command prompt case + { + kind: model.CommandBuffer, + expectedColor: newStyles.Prompt().Border.CommandColor.Color(), + }, + // Any other prompt type case + { + // Simulate a different type of prompt since no particular constant exists + kind: model.CommandBuffer + 1, + expectedColor: newStyles.Prompt().Border.DefaultColor.Color(), + }, + } + + for _, testCase := range testCases { + model := model.NewFishBuff(':', testCase.kind) + prompt := ui.NewPrompt(&app, true, styles) + + model.SetActive(true) + + prompt.SetModel(model) + model.AddListener(prompt) + + prompt.StylesChanged(newStyles) + + model.SetActive(true) + assert.Equal(t, prompt.GetBorderColor(), testCase.expectedColor) + } +} diff --git a/internal/ui/table.go b/internal/ui/table.go index 511cefa6bf..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 @@ -85,7 +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.viewSetting = &settings + t.viewSetting, t.manualSort = &settings, false t.Refresh() } @@ -202,9 +203,11 @@ func (t *Table) doUpdate(data *render.TableData) { cols = t.viewSetting.Columns } custData := data.Customize(cols, t.wide) - if t.viewSetting != nil && t.viewSetting.SortColumn != "" { + // The sortColumn settings in the configuration file are only used + // 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 @@ -247,6 +250,7 @@ func (t *Table) doUpdate(data *render.TableData) { colIndex, custData.Header.IsTimeCol(colIndex), custData.Header.IsMetricsCol(colIndex), + custData.Header.IsCapacityCol(colIndex), t.sortCol.asc, ) @@ -310,11 +314,13 @@ 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 } t.sortCol.name = name + t.manualSort = true t.Refresh() return nil } 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 da4cab6c2e..a921d443d0 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 @@ -398,7 +397,7 @@ func (a *App) isValidNS(ns string) (bool, error) { return true, nil } -func (a *App) switchContext(name string, loadPods bool) error { +func (a *App) switchContext(name string) error { log.Debug().Msgf("--> Switching Context %q--%q", name, a.Config.ActiveView()) a.Halt() defer a.Resume() @@ -406,16 +405,15 @@ func (a *App) switchContext(name string, loadPods bool) error { ns, err := a.Conn().Config().CurrentNamespaceName() if err != nil { log.Warn().Msg("No namespace specified in context. Using K9s config") + ns = a.Config.ActiveNamespace() } a.initFactory(ns) if e := a.command.Reset(true); e != nil { return e } - v := a.Config.ActiveView() - if v == "" || isContextCmd(v) || loadPods { - v = "pod" - a.Config.SetActiveView(v) + if a.Config.ActiveView() == "" || isContextCmd(a.Config.ActiveView()) { + a.Config.SetActiveView("pod") } a.Config.Reset() a.Config.K9s.CurrentContext = name @@ -433,7 +431,7 @@ func (a *App) switchContext(name string, loadPods bool) error { a.Flash().Infof("Switching context to %s", name) a.ReloadStyles(name) - a.gotoResource(v, "", true) + a.gotoResource(a.Config.ActiveView(), "", true) a.clusterModel.Reset(a.factory) } @@ -468,6 +466,10 @@ func (a *App) Run() error { <-time.After(splashDelay) a.QueueUpdateDraw(func() { a.Main.SwitchToPage("main") + // if command bar is already active, focus it + if a.CmdBuff().IsActive() { + a.SetFocus(a.Prompt()) + } }) }() diff --git a/internal/view/benchmark.go b/internal/view/benchmark.go index bd6a8b900b..5601c42a18 100644 --- a/internal/view/benchmark.go +++ b/internal/view/benchmark.go @@ -65,7 +65,7 @@ func fileToSubject(path string) string { } func benchDir(cfg *config.Config) string { - return filepath.Join(perf.K9sBenchDir, cfg.K9s.CurrentContextDir()) + return filepath.Join(perf.K9sBenchDir, cfg.K9s.CurrentCluster) } func readBenchFile(cfg *config.Config, n string) (string, error) { diff --git a/internal/view/browser.go b/internal/view/browser.go index 3d833dd815..0678bfacd3 100644 --- a/internal/view/browser.go +++ b/internal/view/browser.go @@ -2,7 +2,6 @@ package view import ( "context" - "errors" "fmt" "sort" "strconv" @@ -121,7 +120,6 @@ func (b *Browser) bindKeys(aa ui.KeyActions) { tcell.KeyEscape: ui.NewSharedKeyAction("Filter Reset", b.resetCmd, false), tcell.KeyEnter: ui.NewSharedKeyAction("Filter", b.filterCmd, false), tcell.KeyHelp: ui.NewSharedKeyAction("Help", b.helpCmd, false), - ui.KeyV: ui.NewSharedKeyAction("Zob", b.blahCmd, true), }) } @@ -146,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) } } @@ -351,28 +349,6 @@ func (b *Browser) deleteCmd(evt *tcell.EventKey) *tcell.EventKey { return nil } -func (b *Browser) blahCmd(evt *tcell.EventKey) *tcell.EventKey { - b.Stop() - defer b.Start() - { - v := NewDetails(b.app, "Results", "Blee", true) - if err := v.app.inject(v, false); err != nil { - v.app.Flash().Err(err) - } - - for i := 0; i < 10; i++ { - j := i - b.app.QueueUpdateDraw(func() { - log.Debug().Msgf("YO %d", j) - fmt.Fprintf(v.GetWriter(), "Yo %d\n", j) - time.Sleep(1 * time.Second) - }) - } - } - - return nil -} - func (b *Browser) describeCmd(evt *tcell.EventKey) *tcell.EventKey { path := b.GetSelectedItem() if path == "" { @@ -396,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 } @@ -409,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) } } @@ -566,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()) @@ -586,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 c1d0da035a..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,18 +108,24 @@ 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 } cmds := strings.Split(cmd, " ") - gvr, v, err := c.viewMetaFor(cmds[0]) + command := strings.ToLower(cmds[0]) + gvr, v, err := c.viewMetaFor(command) if err != nil { return err } + var cns string + tt := strings.Split(gvr, " ") + if len(tt) == 2 { + gvr, cns = tt[0], tt[1] + } - switch cmds[0] { + switch command { case "ctx", "context", "contexts": if len(cmds) == 2 { return useContext(c.app, cmds[1]) @@ -127,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: @@ -136,10 +142,13 @@ 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 } - if !c.alias.Check(cmds[0]) { + if !c.alias.Check(command) { return fmt.Errorf("`%s` Command not found", cmd) } return c.exec(cmd, gvr, c.componentFor(gvr, path, v), clearStack) @@ -179,7 +188,7 @@ func (c *Command) specialCmd(cmd, path string) bool { case "cow": c.app.cowCmd(path) return true - case "q", "q!", "Q", "quit": + case "q", "q!", "qa", "Q", "quit": c.app.BailOut() return true case "?", "h", "help": @@ -253,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 f285bd41b9..7bc0501dbe 100644 --- a/internal/view/container.go +++ b/internal/view/container.go @@ -171,7 +171,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 8a9a19b866..4896d06a29 100644 --- a/internal/view/context.go +++ b/internal/view/context.go @@ -2,14 +2,21 @@ package view import ( "errors" + "fmt" "github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/dao" "github.com/derailed/k9s/internal/ui" "github.com/derailed/tcell/v2" + "github.com/derailed/tview" "github.com/rs/zerolog/log" ) +const ( + renamePage = "rename" + inputField = "New name:" +) + // Context presents a context viewer. type Context struct { ResourceViewer @@ -28,6 +35,67 @@ func NewContext(gvr client.GVR) ResourceViewer { func (c *Context) bindKeys(aa ui.KeyActions) { aa.Delete(ui.KeyShiftA, tcell.KeyCtrlSpace, ui.KeySpace) + aa.Add(ui.KeyActions{ + ui.KeyR: ui.NewKeyAction("Rename", c.renameCmd, true), + }) +} + +func (c *Context) renameCmd(evt *tcell.EventKey) *tcell.EventKey { + contextName := c.GetTable().GetSelectedItem() + if contextName == "" { + return evt + } + + app := c.App() + c.showRenameModal(app, contextName, c.renameDialogCallback) + + return nil +} + +func (c *Context) renameDialogCallback(form *tview.Form, contextName string) error { + app := c.App() + input := form.GetFormItemByLabel(inputField).(*tview.InputField) + if err := app.factory.Client().Config().RenameContext(contextName, input.GetText()); err != nil { + c.App().Flash().Err(err) + return nil + } + c.Refresh() + return nil +} + +func (c *Context) showRenameModal(a *App, name string, ok func(form *tview.Form, contextName string) error) { + p := a.Content.Pages + f := c.makeStyledForm() + f.AddInputField(inputField, name, 0, nil, nil). + AddButton("OK", func() { + if err := ok(f, name); err != nil { + c.App().Flash().Err(err) + return + } + p.RemovePage(renamePage) + }). + AddButton("Cancel", func() { + p.RemovePage(renamePage) + }) + m := tview.NewModalForm("", f) + m.SetText(fmt.Sprintf("Rename context %q?", name)) + m.SetDoneFunc(func(int, string) { + p.RemovePage(renamePage) + }) + p.AddPage(renamePage, m, false, false) + p.ShowPage(renamePage) +} + +func (c *Context) makeStyledForm() *tview.Form { + f := tview.NewForm() + f.SetItemPadding(0) + f.SetButtonsAlign(tview.AlignCenter). + SetButtonBackgroundColor(tview.Styles.PrimitiveBackgroundColor). + SetButtonTextColor(tview.Styles.PrimaryTextColor). + SetLabelColor(tcell.ColorAqua). + SetFieldTextColor(tcell.ColorOrange) + + return f } func (c *Context) useCtx(app *App, model ui.Tabular, gvr, path string) { @@ -50,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, true) + return app.switchContext(name) } diff --git a/internal/view/context_test.go b/internal/view/context_test.go index dc0f693ea0..e6ce542a74 100644 --- a/internal/view/context_test.go +++ b/internal/view/context_test.go @@ -13,5 +13,5 @@ func TestContext(t *testing.T) { assert.Nil(t, ctx.Init(makeCtx())) assert.Equal(t, "Contexts", ctx.Name()) - assert.Equal(t, 4, len(ctx.Hints())) + assert.Equal(t, 5, len(ctx.Hints())) } diff --git a/internal/view/dir.go b/internal/view/dir.go index ce3a18858c..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 @@ -229,13 +228,23 @@ func (d *Dir) delCmd(evt *tcell.EventKey) *tcell.EventKey { return evt } + opts := []string{"-f"} + msgResource := "manifest" + if containsDir(sel) { + opts = append(opts, "-R") + } + if isKustomized(sel) { + opts = []string{"-k"} + msgResource = "kustomization" + } + d.Stop() defer d.Start() - msg := fmt.Sprintf("Delete resource(s) in manifest %s", 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") - args = append(args, "-f") + args = append(args, opts...) args = append(args, sel) res, err := runKu(d.App(), shellOpts{clear: false, args: args}) if err != nil { diff --git a/internal/view/dp.go b/internal/view/dp.go index 2c135ef4a3..4c81caf94d 100644 --- a/internal/view/dp.go +++ b/internal/view/dp.go @@ -59,7 +59,7 @@ func (d *Deploy) logOptions(prev bool) (*dao.LogOptions, error) { co, dco string allCos bool ) - if c, ok := dao.GetDefaultLogContainer(sts.Spec.Template.ObjectMeta, sts.Spec.Template.Spec); ok { + if c, ok := dao.GetDefaultContainer(sts.Spec.Template.ObjectMeta, sts.Spec.Template.Spec); ok { co, dco = c, c } else if len(cc) == 1 { co = cc[0].Name @@ -89,7 +89,7 @@ func (d *Deploy) logOptions(prev bool) (*dao.LogOptions, error) { func (d *Deploy) showPods(app *App, model ui.Tabular, gvr, path string) { var ddp dao.Deployment - dp, err := ddp.Load(app.factory, path) + dp, err := ddp.GetInstance(app.factory, path) if err != nil { app.Flash().Err(err) return @@ -100,7 +100,7 @@ func (d *Deploy) showPods(app *App, model ui.Tabular, gvr, path string) { func (d *Deploy) dp(path string) (*appsv1.Deployment, error) { var dp dao.Deployment - return dp.Load(d.App().factory, path) + return dp.GetInstance(d.App().factory, path) } // ---------------------------------------------------------------------------- diff --git a/internal/view/drain_dialog.go b/internal/view/drain_dialog.go index 76e0f1bf71..ed56a99e2d 100644 --- a/internal/view/drain_dialog.go +++ b/internal/view/drain_dialog.go @@ -15,7 +15,7 @@ const drainKey = "drain" type DrainFunc func(v ResourceViewer, path string, opts dao.DrainOptions) // ShowDrain pops a node drain dialog. -func ShowDrain(view ResourceViewer, path string, defaults dao.DrainOptions, okFn DrainFunc) { +func ShowDrain(view ResourceViewer, path string, opts dao.DrainOptions, okFn DrainFunc) { styles := view.App().Styles f := tview.NewForm() @@ -26,8 +26,7 @@ func ShowDrain(view ResourceViewer, path string, defaults dao.DrainOptions, okFn SetLabelColor(styles.K9s.Info.FgColor.Color()). SetFieldTextColor(styles.K9s.Info.SectionColor.Color()) - var opts dao.DrainOptions - f.AddInputField("GracePeriod:", strconv.Itoa(defaults.GracePeriodSeconds), 0, nil, func(v string) { + f.AddInputField("GracePeriod:", strconv.Itoa(opts.GracePeriodSeconds), 0, nil, func(v string) { a, err := asIntOpt(v) if err != nil { view.App().Flash().Err(err) @@ -36,7 +35,7 @@ func ShowDrain(view ResourceViewer, path string, defaults dao.DrainOptions, okFn view.App().Flash().Clear() opts.GracePeriodSeconds = a }) - f.AddInputField("Timeout:", defaults.Timeout.String(), 0, nil, func(v string) { + f.AddInputField("Timeout:", opts.Timeout.String(), 0, nil, func(v string) { a, err := asDurOpt(v) if err != nil { view.App().Flash().Err(err) @@ -45,13 +44,13 @@ func ShowDrain(view ResourceViewer, path string, defaults dao.DrainOptions, okFn view.App().Flash().Clear() opts.Timeout = a }) - f.AddCheckbox("Ignore DaemonSets:", defaults.IgnoreAllDaemonSets, func(_ string, v bool) { + f.AddCheckbox("Ignore DaemonSets:", opts.IgnoreAllDaemonSets, func(_ string, v bool) { opts.IgnoreAllDaemonSets = v }) - f.AddCheckbox("Delete Local Data:", defaults.DeleteEmptyDirData, func(_ string, v bool) { + f.AddCheckbox("Delete Local Data:", opts.DeleteEmptyDirData, func(_ string, v bool) { opts.DeleteEmptyDirData = v }) - f.AddCheckbox("Force:", defaults.Force, func(_ string, v bool) { + f.AddCheckbox("Force:", opts.Force, func(_ string, v bool) { opts.Force = v }) diff --git a/internal/view/exec.go b/internal/view/exec.go index 95a95035c0..e80dca0a05 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 { @@ -279,7 +323,7 @@ func launchShellPod(a *App, node string) error { return err } conn := dial.CoreV1().Pods(ns) - if _, err := conn.Create(ctx, &spec, metav1.CreateOptions{}); err != nil { + if _, err := conn.Create(ctx, spec, metav1.CreateOptions{}); err != nil { return err } @@ -300,14 +344,14 @@ 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 { return fmt.Sprintf("%s-%d", k9sShell, os.Getpid()) } -func k9sShellPod(node string, cfg *config.ShellPod) v1.Pod { +func k9sShellPod(node string, cfg *config.ShellPod) *v1.Pod { var grace int64 var priv bool = true @@ -335,7 +379,7 @@ func k9sShellPod(node string, cfg *config.ShellPod) v1.Pod { c.Args = cfg.Args } - return v1.Pod{ + return &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: k9sShellPodName(), Namespace: cfg.Namespace, @@ -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..2f29a6b765 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, 28, 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/live_view.go b/internal/view/live_view.go index 35d1730b39..d9d6af892b 100644 --- a/internal/view/live_view.go +++ b/internal/view/live_view.go @@ -48,6 +48,7 @@ func NewLiveView(app *App, title string, m model.ResourceViewer) *LiveView { maxRegions: 0, cmdBuff: model.NewFishBuff('/', model.FilterBuffer), model: m, + autoRefresh: app.Config.K9s.LiveViewAutoRefresh, } v.AddItem(v.text, 0, 1, true) @@ -92,6 +93,21 @@ func (v *LiveView) ResourceFailed(err error) { v.text.SetText(cowTalk(err.Error(), x+w)) } +func (*LiveView) linesWithRegions(lines []string, matches fuzzy.Matches) []string { + ll := make([]string, len(lines)) + copy(ll, lines) + offsetForLine := make(map[int]int) + for i, m := range matches { + loc, line := m.MatchedIndexes, ll[m.Index] + offset := offsetForLine[m.Index] + loc[0], loc[1] = loc[0]+offset, loc[1]+offset + regionStr := `<<<"search_` + strconv.Itoa(i) + `">>>` + line[loc[0]:loc[1]] + `<<<"">>>` + ll[m.Index] = line[:loc[0]] + regionStr + line[loc[1]:] + offsetForLine[m.Index] += len(regionStr) - (loc[1] - loc[0]) + } + return ll +} + // ResourceChanged notifies when the filter changes. func (v *LiveView) ResourceChanged(lines []string, matches fuzzy.Matches) { v.app.QueueUpdateDraw(func() { @@ -101,23 +117,14 @@ func (v *LiveView) ResourceChanged(lines []string, matches fuzzy.Matches) { v.text.SetTextAlign(tview.AlignLeft) v.maxRegions = len(matches) - var ll []string - if len(matches) == 0 { - ll = lines - } else { - ll = make([]string, len(lines)) - copy(ll, lines) - for i, m := range matches { - loc, line := m.MatchedIndexes, ll[m.Index] - ll[m.Index] = line[:loc[0]] + `<<<"search_` + strconv.Itoa(i) + `">>>` + line[loc[0]:loc[1]] + `<<<"">>>` + line[loc[1]:] - } - } if v.text.GetText(true) == "" { v.text.ScrollToBeginning() } - v.text.SetText(colorizeYAML(v.app.Styles.Views().Yaml, strings.Join(ll, "\n"))) + lines = v.linesWithRegions(lines, matches) + + v.text.SetText(colorizeYAML(v.app.Styles.Views().Yaml, strings.Join(lines, "\n"))) v.text.Highlight() if v.currentRegion < v.maxRegions { v.text.Highlight("search_" + strconv.Itoa(v.currentRegion)) diff --git a/internal/view/live_view_test.go b/internal/view/live_view_test.go new file mode 100644 index 0000000000..d3b801cc3e --- /dev/null +++ b/internal/view/live_view_test.go @@ -0,0 +1,64 @@ +package view + +import ( + "strconv" + "testing" + + "github.com/sahilm/fuzzy" + "github.com/stretchr/testify/assert" +) + +func matchTag(i int, s string) string { + return `<<<"search_` + strconv.Itoa(i) + `">>>` + s + `<<<"">>>` +} + +func TestLiveView_linesWithRegions(t *testing.T) { + uu := map[string]struct { + lines []string + matches fuzzy.Matches + e []string + }{ + "empty-lines": { + e: []string{}, + }, + "no-match": { + lines: []string{"bar"}, + e: []string{"bar"}, + }, + "single-match": { + lines: []string{"foo", "bar", "baz"}, + matches: fuzzy.Matches{ + {Index: 1, MatchedIndexes: []int{0, 3}}, + }, + e: []string{"foo", matchTag(0, "bar"), "baz"}, + }, + "multiple-matches": { + lines: []string{"foo", "bar", "baz"}, + matches: fuzzy.Matches{ + {Index: 1, MatchedIndexes: []int{0, 3}}, + {Index: 2, MatchedIndexes: []int{0, 3}}, + }, + e: []string{"foo", matchTag(0, "bar"), matchTag(1, "baz")}, + }, + "multiple-matches-same-line": { + lines: []string{"foosfoo baz", "dfbarfoos bar"}, + matches: fuzzy.Matches{ + {Index: 0, MatchedIndexes: []int{0, 3}}, + {Index: 0, MatchedIndexes: []int{4, 7}}, + {Index: 1, MatchedIndexes: []int{5, 8}}, + }, + e: []string{ + matchTag(0, "foo") + "s" + matchTag(1, "foo") + " baz", + "dfbar" + matchTag(2, "foo") + "s bar", + }, + }, + } + var v LiveView + for k := range uu { + u := uu[k] + t.Run(k, func(t *testing.T) { + t.Parallel() + assert.Equal(t, u.e, v.linesWithRegions(u.lines, u.matches)) + }) + } +} diff --git a/internal/view/log.go b/internal/view/log.go index c70c2afdf9..3057c1490d 100644 --- a/internal/view/log.go +++ b/internal/view/log.go @@ -33,15 +33,16 @@ const ( type Log struct { *tview.Flex - app *App - logs *Logger - indicator *LogIndicator - ansiWriter io.Writer - model *model.Log - cancelFn context.CancelFunc - cancelUpdates bool - mx sync.Mutex - follow bool + app *App + logs *Logger + indicator *LogIndicator + ansiWriter io.Writer + model *model.Log + cancelFn context.CancelFunc + cancelUpdates bool + mx sync.Mutex + follow bool + requestOneRefresh bool } var _ model.Component = (*Log)(nil) @@ -340,9 +341,12 @@ func (l *Log) Flush(lines [][]byte) { } }() - if len(lines) == 0 || !l.indicator.AutoScroll() || l.cancelUpdates { + if len(lines) == 0 || (!l.requestOneRefresh && !l.indicator.AutoScroll()) || l.cancelUpdates { return } + if l.requestOneRefresh { + l.requestOneRefresh = false + } for i := 0; i < len(lines); i++ { if l.cancelUpdates { break @@ -367,6 +371,7 @@ func (l *Log) sinceCmd(n int) func(evt *tcell.EventKey) *tcell.EventKey { } else { l.model.SetSinceSeconds(ctx, int64(n)) } + l.requestOneRefresh = true l.updateTitle() return nil diff --git a/internal/view/log_indicator.go b/internal/view/log_indicator.go index c36ac14c92..43aacff782 100644 --- a/internal/view/log_indicator.go +++ b/internal/view/log_indicator.go @@ -1,10 +1,12 @@ package view import ( + "fmt" "sync/atomic" "github.com/derailed/k9s/internal/config" "github.com/derailed/tview" + "github.com/rs/zerolog/log" ) const spacer = " " @@ -49,6 +51,7 @@ func NewLogIndicator(cfg *config.Config, styles *config.Styles, allContainers bo func (l *LogIndicator) StylesChanged(styles *config.Styles) { l.SetBackgroundColor(styles.K9s.Views.Log.Indicator.BgColor.Color()) l.SetTextColor(styles.K9s.Views.Log.Indicator.FgColor.Color()) + l.Refresh() } // AutoScroll reports the current scrolling status. @@ -124,24 +127,30 @@ func (l *LogIndicator) reset() { func (l *LogIndicator) Refresh() { l.reset() + var ( + toggleFmt = "[::b]%s:[" + toggleOnFmt = toggleFmt + string(l.styles.K9s.Views.Log.Indicator.ToggleOnColor) + "::b]On[-::] %s" + toggleOffFmt = toggleFmt + string(l.styles.K9s.Views.Log.Indicator.ToggleOffColor) + "::d]Off[-::]%s" + ) + if l.shouldDisplayAllContainers { if l.allContainers { - l.indicator = append(l.indicator, "[::b]AllContainers:[limegreen::b]On[-::] "+spacer...) + l.indicator = append(l.indicator, fmt.Sprintf(toggleOnFmt, "AllContainers", spacer)...) } else { - l.indicator = append(l.indicator, "[::b]AllContainers:[gray::d]Off[-::]"+spacer...) + l.indicator = append(l.indicator, fmt.Sprintf(toggleOffFmt, "AllContainers", spacer)...) } } if l.AutoScroll() { - l.indicator = append(l.indicator, "[::b]Autoscroll:[limegreen::b]On[-::] "+spacer...) + l.indicator = append(l.indicator, fmt.Sprintf(toggleOnFmt, "Autoscroll", spacer)...) } else { - l.indicator = append(l.indicator, "[::b]Autoscroll:[gray::d]Off[-::]"+spacer...) + l.indicator = append(l.indicator, fmt.Sprintf(toggleOffFmt, "Autoscroll", spacer)...) } if l.FullScreen() { - l.indicator = append(l.indicator, "[::b]FullScreen:[limegreen::b]On[-::] "+spacer...) + l.indicator = append(l.indicator, fmt.Sprintf(toggleOnFmt, "FullScreen", spacer)...) } else { - l.indicator = append(l.indicator, "[::b]FullScreen:[gray::d]Off[-::]"+spacer...) + l.indicator = append(l.indicator, fmt.Sprintf(toggleOffFmt, "FullScreen", spacer)...) } if l.Json() { @@ -151,16 +160,18 @@ func (l *LogIndicator) Refresh() { } if l.Timestamp() { - l.indicator = append(l.indicator, "[::b]Timestamps:[limegreen::b]On[-::] "+spacer...) + l.indicator = append(l.indicator, fmt.Sprintf(toggleOnFmt, "Timestamps", spacer)...) } else { - l.indicator = append(l.indicator, "[::b]Timestamps:[gray::d]Off[-::]"+spacer...) + l.indicator = append(l.indicator, fmt.Sprintf(toggleOffFmt, "Timestamps", spacer)...) } if l.TextWrap() { - l.indicator = append(l.indicator, "[::b]Wrap:[limegreen::b]On[-::] "...) + l.indicator = append(l.indicator, fmt.Sprintf(toggleOnFmt, "Wrap", "")...) } else { - l.indicator = append(l.indicator, "[::b]Wrap:[gray::d]Off[-::]"...) + l.indicator = append(l.indicator, fmt.Sprintf(toggleOffFmt, "Wrap", "")...) } + log.Debug().Msgf("INDICATOR: %q", l.indicator) + _, _ = l.Write(l.indicator) } diff --git a/internal/view/node.go b/internal/view/node.go index 9cce2783b7..d70b2b517b 100644 --- a/internal/view/node.go +++ b/internal/view/node.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/derailed/k9s/internal" "github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/dao" "github.com/derailed/k9s/internal/ui" @@ -26,10 +27,15 @@ func NewNode(gvr client.GVR) ResourceViewer { } n.AddBindKeysFn(n.bindKeys) n.GetTable().SetEnterFn(n.showPods) + n.SetContextFn(n.nodeContext) return &n } +func (n *Node) nodeContext(ctx context.Context) context.Context { + return context.WithValue(ctx, internal.KeyPodCounting, !n.App().Config.K9s.DisablePodCounting) +} + func (n *Node) bindDangerousKeys(aa ui.KeyActions) { aa.Add(ui.KeyActions{ ui.KeyC: ui.NewKeyAction("Cordon", n.toggleCordonCmd(true), true), @@ -151,7 +157,7 @@ func (n *Node) sshCmd(evt *tcell.EventKey) *tcell.EventKey { defer n.Start() _, node := client.Namespaced(path) if err := ssh(n.App(), node); err != nil { - log.Error().Err(err).Msgf("SSH Failed") + log.Error().Err(err).Msgf("Node Shell Failed") } return nil 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/picker.go b/internal/view/picker.go index 4ead1fcaf1..c789a37037 100644 --- a/internal/view/picker.go +++ b/internal/view/picker.go @@ -30,14 +30,17 @@ func (p *Picker) Init(ctx context.Context) error { if err != nil { return err } + + pickerView := app.Styles.Views().Picker p.actions[tcell.KeyEscape] = ui.NewKeyAction("Back", app.PrevCmd, true) p.SetBorder(true) - p.SetMainTextColor(tcell.ColorWhite) + p.SetMainTextColor(pickerView.MainColor.Color()) p.ShowSecondaryText(false) - p.SetShortcutColor(tcell.ColorAqua) - p.SetSelectedBackgroundColor(tcell.ColorAqua) + p.SetShortcutColor(pickerView.ShortcutColor.Color()) + p.SetSelectedBackgroundColor(pickerView.FocusColor.Color()) p.SetTitle(" [aqua::b]Containers Picker ") + p.SetInputCapture(func(evt *tcell.EventKey) *tcell.EventKey { if a, ok := p.actions[evt.Key()]; ok { a.Action(evt) diff --git a/internal/view/pod.go b/internal/view/pod.go index 120dd56f65..f1d7f38825 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,10 +13,12 @@ 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" v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -23,8 +27,10 @@ import ( const ( windowsOS = "windows" powerShell = "powershell" - osBetaSelector = "beta.kubernetes.io/os" osSelector = "kubernetes.io/os" + osBetaSelector = "beta." + osSelector + trUpload = "Upload" + trDownload = "Download" ) // Pod represents a pod viewer. @@ -64,6 +70,8 @@ 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), + ui.KeyZ: ui.NewKeyAction("Sanitize", p.sanitizeCmd, true), }) } @@ -95,7 +103,7 @@ func (p *Pod) logOptions(prev bool) (*dao.LogOptions, error) { return nil, err } - cc, cfg := fetchContainers(pod.Spec, true), p.App().Config.K9s.Logger + cc, cfg := fetchContainers(pod.ObjectMeta, pod.Spec, true), p.App().Config.K9s.Logger opts := dao.LogOptions{ Path: path, Lines: int64(cfg.TailCount), @@ -105,7 +113,7 @@ func (p *Pod) logOptions(prev bool) (*dao.LogOptions, error) { ShowJSON: cfg.ShowJSON, Previous: prev, } - if c, ok := dao.GetDefaultLogContainer(pod.ObjectMeta, pod.Spec); ok { + if c, ok := dao.GetDefaultContainer(pod.ObjectMeta, pod.Spec); ok { opts.Container, opts.DefaultContainer = c, c } else if len(cc) == 1 { opts.Container = cc[0] @@ -249,6 +257,98 @@ func (p *Pod) attachCmd(evt *tcell.EventKey) *tcell.EventKey { return nil } +func (p *Pod) sanitizeCmd(evt *tcell.EventKey) *tcell.EventKey { + res, err := dao.AccessorFor(p.App().factory, p.GVR()) + if err != nil { + p.App().Flash().Err(err) + return nil + } + s, ok := res.(dao.Sanitizer) + if !ok { + p.App().Flash().Err(fmt.Errorf("expecting a sanitizer for %q", p.GVR())) + return nil + } + + ack := "sanitize me pods!" + msg := fmt.Sprintf("Sanitize deletes all pods in completed/error state\nPlease enter [orange::b]%s[-::-] to proceed.", ack) + dialog.ShowConfirmAck(p.App().App, p.App().Content.Pages, ack, true, "Sanitize", msg, func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*p.App().Conn().Config().CallTimeout()) + defer cancel() + total, err := s.Sanitize(ctx, p.GetTable().GetModel().GetNamespace()) + if err != nil { + p.App().Flash().Err(err) + return + } + p.App().Flash().Infof("Sanitized %d %s", total, p.GVR()) + p.Refresh() + }, func() {}) + + 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... @@ -262,7 +362,7 @@ func containerShellin(a *App, comp model.Component, path, co string) error { if err != nil { return err } - cc := fetchContainers(pod.Spec, false) + cc := fetchContainers(pod.ObjectMeta, pod.Spec, false) if len(cc) == 1 { resumeShellIn(a, comp, path, cc[0]) return nil @@ -291,8 +391,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) } } @@ -306,7 +407,7 @@ func containerAttachIn(a *App, comp model.Component, path, co string) error { if err != nil { return err } - cc := fetchContainers(pod.Spec, false) + cc := fetchContainers(pod.ObjectMeta, pod.Spec, false) if len(cc) == 1 { resumeAttachIn(a, comp, path, cc[0]) return nil @@ -333,8 +434,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) } } @@ -364,10 +465,19 @@ func buildShellArgs(cmd, path, co string, kcfg *string) []string { return args } -func fetchContainers(spec v1.PodSpec, allContainers bool) []string { +func fetchContainers(meta metav1.ObjectMeta, spec v1.PodSpec, allContainers bool) []string { nn := make([]string, 0, len(spec.Containers)+len(spec.InitContainers)) + + // put the default container as the first entry + defaultContainer, hasDefaultContainer := dao.GetDefaultContainer(meta, spec) + if hasDefaultContainer { + nn = append(nn, defaultContainer) + } + for _, c := range spec.Containers { - nn = append(nn, c.Name) + if !hasDefaultContainer || c.Name != defaultContainer { + nn = append(nn, c.Name) + } } if !allContainers { return nn @@ -413,15 +523,27 @@ func getPodOS(f dao.Factory, fqn string) (string, error) { if err != nil { return "", err } - if os, ok := po.Spec.NodeSelector[osBetaSelector]; ok { - return os, nil + if podOS, ok := osFromSelector(po.Spec.NodeSelector); ok { + return podOS, nil } - os, ok := po.Spec.NodeSelector[osSelector] - if !ok { - return "", fmt.Errorf("no os information available") + + node, err := dao.FetchNode(context.Background(), f, po.Spec.NodeName) + if err == nil { + if nodeOS, ok := osFromSelector(node.Labels); ok { + return nodeOS, nil + } + } + + return "", errors.New("no os information available") +} + +func osFromSelector(s map[string]string) (string, bool) { + if os, ok := s[osBetaSelector]; ok { + return os, ok } - return os, nil + os, ok := s[osSelector] + return os, ok } func resourceSorters(t *Table) ui.KeyActions { diff --git a/internal/view/pod_test.go b/internal/view/pod_test.go index 22a3f7ae3b..1f942588d7 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, 27, 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/sts.go b/internal/view/sts.go index 2f779b01bb..d18f5ad49c 100644 --- a/internal/view/sts.go +++ b/internal/view/sts.go @@ -38,7 +38,7 @@ func (s *StatefulSet) logOptions(prev bool) (*dao.LogOptions, error) { return nil, errors.New("you must provide a selection") } - sts, err := s.sts(path) + sts, err := s.getInstance(path) if err != nil { return nil, err } @@ -48,7 +48,7 @@ func (s *StatefulSet) logOptions(prev bool) (*dao.LogOptions, error) { co, dco string allCos bool ) - if c, ok := dao.GetDefaultLogContainer(sts.Spec.Template.ObjectMeta, sts.Spec.Template.Spec); ok { + if c, ok := dao.GetDefaultContainer(sts.Spec.Template.ObjectMeta, sts.Spec.Template.Spec); ok { co, dco = c, c } else if len(cc) == 1 { co = cc[0].Name @@ -83,16 +83,16 @@ func (s *StatefulSet) bindKeys(aa ui.KeyActions) { } func (s *StatefulSet) showPods(app *App, _ ui.Tabular, _, path string) { - sts, err := s.sts(path) + i, err := s.getInstance(path) if err != nil { app.Flash().Err(err) return } - showPodsFromSelector(app, path, sts.Spec.Selector) + showPodsFromSelector(app, path, i.Spec.Selector) } -func (s *StatefulSet) sts(path string) (*appsv1.StatefulSet, error) { +func (s *StatefulSet) getInstance(path string) (*appsv1.StatefulSet, error) { var sts dao.StatefulSet - return sts.Load(s.App().factory, path) + return sts.GetInstance(s.App().factory, path) } 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/user.go b/internal/view/user.go index aaa06a32ee..ab7d9bea50 100644 --- a/internal/view/user.go +++ b/internal/view/user.go @@ -3,10 +3,11 @@ package view import ( "context" + "github.com/derailed/tcell/v2" + "github.com/derailed/k9s/internal" "github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/ui" - "github.com/derailed/tcell/v2" ) // User presents a user viewer. @@ -24,7 +25,7 @@ func NewUser(gvr client.GVR) ResourceViewer { } func (u *User) bindKeys(aa ui.KeyActions) { - aa.Delete(ui.KeyShiftA, ui.KeyShiftP, tcell.KeyCtrlSpace, ui.KeySpace) + aa.Delete(ui.KeyShiftA, ui.KeyShiftP, tcell.KeyCtrlSpace, ui.KeySpace, tcell.KeyCtrlD, ui.KeyE) aa.Add(ui.KeyActions{ tcell.KeyEnter: ui.NewKeyAction("Rules", u.policyCmd, true), ui.KeyShiftK: ui.NewKeyAction("Sort Kind", u.GetTable().SortColCmd("KIND", true), false), 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..453df6054b 100644 --- a/internal/watch/forwarders.go +++ b/internal/watch/forwarders.go @@ -22,7 +22,7 @@ type Forwarder interface { // Container returns a container name. Container() string - // Ports returns the port mapping. + // Port returns the port mapping. Port() string // FQN returns the full port-forward name. @@ -49,9 +49,9 @@ func NewForwarders() Forwarders { return make(map[string]Forwarder) } -// BOZO!! Review!!! // IsPodForwarded checks if pod has a forward. func (ff Forwarders) IsPodForwarded(fqn string) bool { + fqn += "|" for k := range ff { if strings.HasPrefix(k, fqn) { return true @@ -63,9 +63,9 @@ 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 + fqn += "|" + co for k := range ff { - if strings.HasPrefix(k, prefix) { + if strings.HasPrefix(k, fqn) { return true } } @@ -85,13 +85,18 @@ func (ff Forwarders) DeleteAll() { // Kill stops and delete a port-forwards associated with pod. func (ff Forwarders) Kill(path string) int { var stats int + + // The way port forwards are stored is `pod_fqn|container|local_port:container_port` + // The '|' is added to make sure we do not delete port forwards from other pods that have the same prefix + // Without the `|` port forwards for pods, default/web-0 and default/web-0-bla would be both deleted + // even if we want only port forwards for default/web-0 to be deleted + prefix := path + "|" for k, f := range ff { - victim := k - if victim == path { + if k == path || strings.HasPrefix(k, prefix) { 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) } } @@ -102,6 +107,6 @@ func (ff Forwarders) Kill(path string) int { func (ff Forwarders) Dump() { log.Debug().Msgf("----------- PORT-FORWARDS --------------") for k, f := range ff { - log.Debug().Msgf(" %s -- %#v", k, f) + log.Debug().Msgf(" %s -- %s", k, f) } } diff --git a/internal/watch/forwarders_test.go b/internal/watch/forwarders_test.go new file mode 100644 index 0000000000..aa061ba066 --- /dev/null +++ b/internal/watch/forwarders_test.go @@ -0,0 +1,183 @@ +package watch_test + +import ( + "testing" + + "github.com/derailed/k9s/internal/port" + "github.com/derailed/k9s/internal/watch" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "k8s.io/client-go/tools/portforward" +) + +func init() { + zerolog.SetGlobalLevel(zerolog.FatalLevel) +} + +func TestIsPodForwarded(t *testing.T) { + uu := map[string]struct { + ff watch.Forwarders + fqn string + e bool + }{ + "happy": { + ff: watch.Forwarders{ + "ns1/p1||8080:8080": newNoOpForwarder(), + }, + fqn: "ns1/p1", + e: true, + }, + "dud": { + ff: watch.Forwarders{ + "ns1/p1||8080:8080": newNoOpForwarder(), + }, + fqn: "ns1/p2", + }, + "sub": { + ff: watch.Forwarders{ + "ns1/freddy||8080:8080": newNoOpForwarder(), + }, + fqn: "ns1/fred", + }, + } + + for k := range uu { + u := uu[k] + t.Run(k, func(t *testing.T) { + assert.Equal(t, u.e, u.ff.IsPodForwarded(u.fqn)) + }) + } +} + +func TestIsContainerForwarded(t *testing.T) { + uu := map[string]struct { + ff watch.Forwarders + fqn, co string + e bool + }{ + "happy": { + ff: watch.Forwarders{ + "ns1/p1|c1|8080:8080": newNoOpForwarder(), + }, + fqn: "ns1/p1", + co: "c1", + e: true, + }, + "dud": { + ff: watch.Forwarders{ + "ns1/p1|c1|8080:8080": newNoOpForwarder(), + }, + fqn: "ns1/p1", + co: "c2", + }, + "sub": { + ff: watch.Forwarders{ + "ns1/freddy|c1|8080:8080": newNoOpForwarder(), + }, + fqn: "ns1/fred", + co: "c1", + }, + } + + for k := range uu { + u := uu[k] + t.Run(k, func(t *testing.T) { + assert.Equal(t, u.e, u.ff.IsContainerForwarded(u.fqn, u.co)) + }) + } +} + +func TestKill(t *testing.T) { + uu := map[string]struct { + ff watch.Forwarders + path string + kills int + }{ + "partial_match": { + ff: watch.Forwarders{ + "ns1/p1|c1|8080:8080": newNoOpForwarder(), + "ns1/p1_1|c1|8080:8080": newNoOpForwarder(), + "ns1/p2|c1|8080:8080": newNoOpForwarder(), + }, + path: "ns1/p1", + kills: 1, + }, + "partial_no_match": { + ff: watch.Forwarders{ + "ns1/p1|c1|8080:8080": newNoOpForwarder(), + "ns1/p1_1|c1|8080:8080": newNoOpForwarder(), + "ns1/p2|c1|8080:8080": newNoOpForwarder(), + }, + path: "ns1/p", + }, + "path_sub": { + ff: watch.Forwarders{ + "ns1/p1|c1|8080:8080": newNoOpForwarder(), + "ns1/p1_1|c1|8080:8080": newNoOpForwarder(), + "ns1/p2|c1|8080:8080": newNoOpForwarder(), + }, + path: "ns1/p1", + kills: 1, + }, + "partial_multi": { + ff: watch.Forwarders{ + "ns1/p1|c1|8080:8080": newNoOpForwarder(), + "ns1/p1|c2|8081:8081": newNoOpForwarder(), + "ns1/p2|c1|8080:8080": newNoOpForwarder(), + }, + path: "ns1/p1", + kills: 2, + }, + "full_match": { + ff: watch.Forwarders{ + "ns1/p1|c1|8080:8080": newNoOpForwarder(), + "ns1/p1_1|c1|8080:8080": newNoOpForwarder(), + "ns1/p2|c1|8080:8080": newNoOpForwarder(), + }, + path: "ns1/p1|c1|8080:8080", + kills: 1, + }, + "full_no_match_co": { + ff: watch.Forwarders{ + "ns1/p1|c1|8080:8080": newNoOpForwarder(), + "ns1/p1_1|c1|8080:8080": newNoOpForwarder(), + "ns1/p2|c1|8080:8080": newNoOpForwarder(), + }, + path: "ns1/p1|c2|8080:8080", + }, + "full_no_match_ports": { + ff: watch.Forwarders{ + "ns1/p1|c1|8080:8080": newNoOpForwarder(), + "ns1/p1_1|c1|8080:8080": newNoOpForwarder(), + "ns1/p2|c1|8080:8080": newNoOpForwarder(), + }, + path: "ns1/p1|c1|8081:8080", + }, + } + + for k := range uu { + u := uu[k] + t.Run(k, func(t *testing.T) { + assert.Equal(t, u.kills, u.ff.Kill(u.path)) + }) + } +} + +type noOpForwarder struct{} + +func newNoOpForwarder() noOpForwarder { + return noOpForwarder{} +} + +func (m noOpForwarder) Start(path string, tunnel port.PortTunnel) (*portforward.PortForwarder, error) { + return nil, nil +} +func (m noOpForwarder) Stop() {} +func (m noOpForwarder) ID() string { return "" } +func (m noOpForwarder) Container() string { return "" } +func (m noOpForwarder) Port() string { return "" } +func (m noOpForwarder) FQN() string { return "" } +func (m noOpForwarder) Active() bool { return false } +func (m noOpForwarder) SetActive(bool) {} +func (m noOpForwarder) Age() string { return "" } +func (m noOpForwarder) HasPortMapping(string) bool { return false } diff --git a/plugins/README.md b/plugins/README.md index 78b3f17085..f49134e2e4 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -2,11 +2,18 @@ K9s plugins extend the tool to provide additional functionality via actions to further help you observe or administer your Kubernetes clusters. -| Plugin-Name | Description | Available on Views | Shortcut | Kubectl plugin, external dependencies | -|--------------------|----------------------------------|--------------------|----------|---------------------------------------------------------------------------------------| -| log_stern.yml | View resource logs using stern | pods | Ctrl-l | | -| log_jq.yml | View resource logs using jq | pods | Ctrl-j | kubectl-plugins/kubectl-jq | -| job_suspend.yml | Suspends a running cronjob | cronjobs | Ctrl-s | | -| dive.yml | Dive image layers | containers | d | [Dive](https://github.com/wagoodman/dive) | -| k3d_root_shell.yml | Root shell to k3d container | containers | Shift-s | [jq](https://stedolan.github.io/jq/) | -| get-all.yml | get all resources in a namespace | all | g | [Krew](https://krew.sigs.k8s.io/), [ketall](https://github.com/corneliusweig/ketall/) | +Following is an example of some of plugin files in this directory. Other files are not listed in this table. + +| Plugin-Name | Description | Available on Views | Shortcut | Kubectl plugin, external dependencies | +|--------------------|------------------------------------------------------------------|--------------------|----------|---------------------------------------------------------------------------------------| +| debug-container.yml| Add [ephemeral debug container][1]
([nicolaka/netshoot][2]) | containers | Shift-d | | +| dive.yml | Dive image layers | containers | d | [Dive](https://github.com/wagoodman/dive) | +| get-all.yml | get all resources in a namespace | all | g | [Krew](https://krew.sigs.k8s.io/), [ketall](https://github.com/corneliusweig/ketall/) | +| job_suspend.yml | Suspends a running cronjob | cronjobs | Ctrl-s | | +| k3d_root_shell.yml | Root shell to k3d container | containers | Shift-s | [jq](https://stedolan.github.io/jq/) | +| log_stern.yml | View resource logs using stern | pods | Ctrl-l | | +| log_jq.yml | View resource logs using jq | pods | Ctrl-j | kubectl-plugins/kubectl-jq | +| log_full.yml | get full logs from pod/container | pods/containers | Ctrl-l | | + +[1]: https://kubernetes.io/docs/tasks/debug/debug-application/debug-running-pod/#ephemeral-container +[2]: https://github.com/nicolaka/netshoot diff --git a/plugins/carvel.yml b/plugins/carvel.yml index 4dd172dc7f..02c6478ee9 100644 --- a/plugins/carvel.yml +++ b/plugins/carvel.yml @@ -10,7 +10,7 @@ plugin: background: false args: - -c - - "export FORCE_COLOR=1;kapp inspect -a $NAME.app --namespace $NAMESPACE --color --tty |& less -R" + - "export FORCE_COLOR=1;kapp inspect -a $NAME.app --namespace $NAMESPACE --kubeconfig-context $CONTEXT --color --tty |& less -R" kctrl-app-status: shortCut: Shift-Q confirm: false @@ -21,7 +21,7 @@ plugin: background: false args: - -c - - "export FORCE_COLOR=1;kctrl app status -a $NAME --namespace $NAMESPACE --color --tty |& less -R" + - "export FORCE_COLOR=1;kctrl app status -a $NAME --namespace $NAMESPACE --kubeconfig-context $CONTEXT --color --tty |& less -R" kctrl-app-pause: shortCut: Shift-T confirm: false @@ -32,7 +32,7 @@ plugin: background: false args: - -c - - "export FORCE_COLOR=1;kctrl app pause -a $NAME --namespace $NAMESPACE --yes --color --tty |& less -R" + - "export FORCE_COLOR=1;kctrl app pause -a $NAME --namespace $NAMESPACE --kubeconfig-context $CONTEXT --yes --color --tty |& less -R" kctrl-app-kick: shortCut: Shift-Z confirm: false @@ -43,5 +43,5 @@ plugin: background: false args: - -c - - "export FORCE_COLOR=1;kctrl app kick -a $NAME --namespace $NAMESPACE --yes --color --tty |& less -R" + - "export FORCE_COLOR=1;kctrl app kick -a $NAME --namespace $NAMESPACE --kubeconfig-context $CONTEXT --yes --color --tty |& less -R" diff --git a/plugins/crossplane.yml b/plugins/crossplane.yml new file mode 100644 index 0000000000..8b16340dce --- /dev/null +++ b/plugins/crossplane.yml @@ -0,0 +1,13 @@ +plugin: + # List all the resources managed by a Composite Resource + kube-lineage: + shortCut: Ctrl-X + confirm: false + description: "Kube Lineage" + scopes: + - all + command: sh + background: false + args: + - -c + - "kubectl lineage -d 6 --exclude-types Event,ProviderConfigUsage.aws.upbound.io,ProviderConfigUsage.kubernetes.crossplane.io --show-group --context $CONTEXT $RESOURCE_NAME $NAME | less" \ No newline at end of file diff --git a/plugins/debug-container.yml b/plugins/debug-container.yml new file mode 100644 index 0000000000..2040cefcf3 --- /dev/null +++ b/plugins/debug-container.yml @@ -0,0 +1,14 @@ +plugin: + #--- Create debug container for selected pod in current namespace + # See https://kubernetes.io/docs/tasks/debug/debug-application/debug-running-pod/#ephemeral-container + debug: + shortCut: Shift-D + description: Add debug container + scopes: + - containers + command: bash + background: false + confirm: true + args: + - -c + - "kubectl debug -it -n=$NAMESPACE $POD --target=$NAME --image=nicolaka/netshoot:v0.11 --share-processes -- bash" \ No newline at end of file diff --git a/plugins/flux.yml b/plugins/flux.yml index 454069a613..d997e387f6 100644 --- a/plugins/flux.yml +++ b/plugins/flux.yml @@ -47,6 +47,28 @@ plugin: args: - -c - "flux --context $CONTEXT reconcile helmrelease -n $NAMESPACE $NAME |& less" + reconcile-helm-repo: + shortCut: Shift-Z + description: Flux reconcile + scopes: + - helmrepositories + command: bash + background: false + confirm: false + args: + - -c + - "flux reconcile source helm --context $CONTEXT -n $NAMESPACE $NAME |& less" + reconcile-oci-repo: + shortCut: Shift-Z + description: Flux reconcile + scopes: + - ocirepositories + command: bash + background: false + confirm: false + args: + - -c + - "flux reconcile source oci --context $CONTEXT -n $NAMESPACE $NAME |& less" reconcile-ks: shortCut: Shift-R confirm: false diff --git a/plugins/get-all.yml b/plugins/get-all.yml index 20001d0eea..1e1d871945 100644 --- a/plugins/get-all.yml +++ b/plugins/get-all.yml @@ -1,6 +1,17 @@ plugin: #get all resources in a namespace using the krew get-all plugin - get-all: + get-all-namespace: + shortCut: g + confirm: false + description: get-all + scopes: + - namespaces + command: sh + background: false + args: + - -c + - "kubectl get-all --context $CONTEXT -n $NAME | less" + get-all-other: shortCut: g confirm: false description: get-all @@ -10,4 +21,4 @@ plugin: background: false args: - -c - - "kubectl get-all -n $NAMESPACE | less" \ No newline at end of file + - "kubectl get-all --context $CONTEXT -n $NAMESPACE | less" diff --git a/plugins/get_suspended.yml b/plugins/get_suspended.yml new file mode 100644 index 0000000000..a5270a27b9 --- /dev/null +++ b/plugins/get_suspended.yml @@ -0,0 +1,23 @@ +# credits: https://github.com/fluxcd/flux2/discussions/2494 + get-suspended-helmreleases: + shortCut: Shift-S + confirm: false + description: Suspended Helm Releases + scopes: + - helmrelease + command: sh + background: false + args: + - -c + - "kubectl get --all-namespaces helmreleases.helm.toolkit.fluxcd.io -o json | jq -r '.items[] | select(.spec.suspend==true) | [.metadata.namespace,.metadata.name,.spec.suspend] | @tsv' | less" + get-suspended-kustomizations: + shortCut: Shift-S + confirm: false + description: Suspended Kustomizations + scopes: + - kustomizations + command: sh + background: false + args: + - -c + - "kubectl get --all-namespaces kustomizations.kustomize.toolkit.fluxcd.io -o json | jq -r '.items[] | select(.spec.suspend==true) | [.metadata.name,.spec.suspend] | @tsv' | less" diff --git a/plugins/log_full.yml b/plugins/log_full.yml new file mode 100644 index 0000000000..304a86d384 --- /dev/null +++ b/plugins/log_full.yml @@ -0,0 +1,61 @@ +plugin: + # See https://k9scli.io/topics/plugins/ + raw-logs-follow: + shortCut: Ctrl-L + description: logs -f + scopes: + - po + command: kubectl + background: false + args: + - logs + - -f + - $NAME + - -n + - $NAMESPACE + - --context + - $CONTEXT + - --kubeconfig + - $KUBECONFIG + log-less: + shortCut: Shift-L + description: "logs|less" + scopes: + - po + command: bash + background: false + args: + - -c + - '"$@" | less' + - dummy-arg + - kubectl + - logs + - $NAME + - -n + - $NAMESPACE + - --context + - $CONTEXT + - --kubeconfig + - $KUBECONFIG + log-less-container: + shortCut: Shift-L + description: "logs|less" + scopes: + - containers + command: bash + background: false + args: + - -c + - '"$@" | less' + - dummy-arg + - kubectl + - logs + - -c + - $NAME + - $POD + - -n + - $NAMESPACE + - --context + - $CONTEXT + - --kubeconfig + - $KUBECONFIG diff --git a/plugins/log_stern.yml b/plugins/log_stern.yml index c3c281195d..9f7b9ed079 100644 --- a/plugins/log_stern.yml +++ b/plugins/log_stern.yml @@ -1,5 +1,5 @@ plugin: - # Leverage stern (https://github.com/wercker/stern) to output logs. + # Leverage stern (https://github.com/stern/stern) to output logs. stern: shortCut: Ctrl-L confirm: false diff --git a/plugins/rm-ns.yml b/plugins/rm-ns.yml new file mode 100644 index 0000000000..14a2a251d8 --- /dev/null +++ b/plugins/rm-ns.yml @@ -0,0 +1,13 @@ +plugin: + # remove finalizers from a stuck namespace + rm-ns: + shortCut: n + confirm: true + description: Remove NS Finalizers + scopes: + - namespace + command: sh + background: false + args: + - -c + - "kubectl get namespace $NAME -o json | jq '.spec.finalizers=[]' | kubectl replace --raw /api/v1/namespaces/$NAME/finalize -f - > /dev/null" diff --git a/plugins/schema.json b/plugins/schema.json new file mode 100644 index 0000000000..3dd50016e9 --- /dev/null +++ b/plugins/schema.json @@ -0,0 +1,54 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Schema for k9s CLI plugin.yml file : https://k9scli.io/topics/plugins", + "type": "object", + "additionalProperties": false, + "properties": { + "plugin": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "shortCut": { + "description": "Define a mnemonic to invoke the plugin", + "type": "string" + }, + "description": { + "description": "What will be shown on the K9s menu", + "type": "string" + }, + "confirm": { + "description": "See the command that is going to be executed and gives you an option to confirm", + "type": "boolean" + }, + "scopes": { + "type": "array", + "description": "Collections of views that support this shortcut. (You can use `all`)", + "items": { + "type": "string" + } + }, + "command": { + "description": "The command to run upon invocation. Can use Krew plugins here too!", + "type": "string" + }, + "background": { + "description": "Whether or not to run the command in background mode", + "type": "boolean" + }, + "args": { + "description": "Defines the command arguments", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["shortCut", "description", "scopes", "command"] + }, + "required": [] + } + }, + "required": ["plugin"] +} diff --git a/skins/axual.yml b/skins/axual.yml index 16c50f4710..bc7b8701df 100644 --- a/skins/axual.yml +++ b/skins/axual.yml @@ -1,5 +1,8 @@ +# ----------------------------------------------------------------------------- # Axual Skin contributed by [@JayKus](jimmy@axual.com) -# Style +# ----------------------------------------------------------------------------- + +# Styles... blue: &blue "#113851" red: &red "#D7595F" yellow: &yellow "#F2BF40" @@ -7,7 +10,7 @@ cyan: &cyan "#47B0AB" grey: &grey "#A99688" light: &light "#F1EFE4" -# Skin +# Skin... k9s: # General K9s styles body: @@ -108,6 +111,8 @@ k9s: indicator: fgColor: *red bgColor: *blue + toggleOnColor: *green + toggleOffColor: *grey # Chart drawing charts: diff --git a/skins/black_and_wtf.yml b/skins/black_and_wtf.yml index f117a24881..2b903ad78a 100644 --- a/skins/black_and_wtf.yml +++ b/skins/black_and_wtf.yml @@ -1,3 +1,7 @@ +# ----------------------------------------------------------------------------- +# BlackAndWtf skin +# ----------------------------------------------------------------------------- + # Styles... fg: &fg "white" bg: &bg "black" @@ -11,7 +15,7 @@ err: &err "pink" slate: &slate "slategray" gray: &gray "gray" -# Skin +# Skin... k9s: body: fgColor: *fg @@ -85,6 +89,8 @@ k9s: indicator: fgColor: *ghost bgColor: *bg + toggleOnColor: *mark + toggleOffColor: gray charts: bgColor: default defaultDialColors: diff --git a/skins/dracula.yml b/skins/dracula.yml index 4bc48b0074..6df70e8a2f 100644 --- a/skins/dracula.yml +++ b/skins/dracula.yml @@ -1,3 +1,7 @@ +# ----------------------------------------------------------------------------- +# Dracula skin +# ----------------------------------------------------------------------------- + # Styles... foreground: &foreground "#f8f8f2" background: &background "#282a36" @@ -107,3 +111,5 @@ k9s: indicator: fgColor: *foreground bgColor: *purple + toggleOnColor: *green + toggleOffColor: *cyan \ No newline at end of file diff --git a/skins/gruvbox-dark.yml b/skins/gruvbox-dark.yml index 99d6961593..d3bd8e4c92 100644 --- a/skins/gruvbox-dark.yml +++ b/skins/gruvbox-dark.yml @@ -1,4 +1,9 @@ -# K9s Gruvbox Dark Skin Contributed by [@indiebrain](https://github.com/indiebrain) +# ----------------------------------------------------------------------------- +# K9s Gruvbox Dark Skin +# Author: [@indiebrain](https://github.com/indiebrain) +# ----------------------------------------------------------------------------- + +# Styles... foreground: &foreground "#ebdbb2" background: &background "#272727" current_line: ¤t_line "#ebdbb2" diff --git a/skins/gruvbox-light.yml b/skins/gruvbox-light.yml index 216b70497b..65bc8c308a 100644 --- a/skins/gruvbox-light.yml +++ b/skins/gruvbox-light.yml @@ -1,4 +1,9 @@ -# K9s Gruvbox Light Skin Contributed by [@indiebrain](https://github.com/indiebrain) +# ----------------------------------------------------------------------------- +# K9s Gruvbox Light Skin +# Author: [@indiebrain](https://github.com/indiebrain) +# ----------------------------------------------------------------------------- + +# Styles... foreground: &foreground "#3c3735" background: &background "#fbf1c7" current_line: ¤t_line "#ebdbb2" @@ -98,3 +103,5 @@ k9s: indicator: fgColor: *foreground bgColor: *background + toggleOnColor: *magenta + toggleOffColor: *blue diff --git a/skins/in_the_navy.yml b/skins/in_the_navy.yml index 7a3dbba882..2b6f4ad452 100644 --- a/skins/in_the_navy.yml +++ b/skins/in_the_navy.yml @@ -1,5 +1,8 @@ -# Styles... +# ----------------------------------------------------------------------------- +# In the Navy +# ----------------------------------------------------------------------------- +# Styles... fg: &fg "dodgerblue" bg: &bg "white" blue: &blue "blue" @@ -101,3 +104,5 @@ k9s: indicator: fgColor: *dark bgColor: *bg + toggleOnColor: *steel + toggleOffColor: *blue diff --git a/skins/kiss.yml b/skins/kiss.yml index 771ca9e0c9..f8ba7a7d0d 100644 --- a/skins/kiss.yml +++ b/skins/kiss.yml @@ -1,4 +1,9 @@ -# K9s Kiss Skin Contributed by [@beejeebus](justin.p.randell@gmail.com) +# ----------------------------------------------------------------------------- +# K9s Kiss Skin +# Author: [@beejeebus](justin.p.randell@gmail.com) +# ----------------------------------------------------------------------------- + +# Skin... k9s: body: fgColor: default @@ -66,3 +71,5 @@ k9s: indicator: fgColor: default bgColor: default + toggleOnColor: default + toggleOffColor: default diff --git a/skins/monokai.yml b/skins/monokai.yml index a93825085b..91aaf50281 100644 --- a/skins/monokai.yml +++ b/skins/monokai.yml @@ -1,4 +1,7 @@ -# monokai.yml +# ----------------------------------------------------------------------------- +# Monokai skin +# ----------------------------------------------------------------------------- + # Styles... foreground: &foreground "#ffffff" background: &background "default" @@ -10,6 +13,8 @@ blue: &blue "#69d9ed" darkBlue: &darkBlue "#3174a2" green: &green "#a7e24c" purple: &purple "#856cc4" +yellow: &yellow "#e1df8f" +darkGray: &darkGray "#666666" # Skin... k9s: @@ -17,41 +22,60 @@ k9s: body: fgColor: *foreground bgColor: *background - logoColor: *magenta + logoColor: *purple + logoColorMsg: *foreground + logoColorInfo: *lightBlue + logoColorWarn: *orange + logoColorError: *magenta + # Command prompt styles prompt: fgColor: *foreground bgColor: *background - suggestColor: *orange + suggestColor: *darkGray + # ClusterInfoView styles. info: - fgColor: *blue - sectionColor: *lightBlue + fgColor: *magenta + sectionColor: *yellow + + # Help Menu styles + help: + fgColor: *foreground + bgColor: *background + keyColor: *green + numKeyColor: *green + sectionColor: *blue + # Dialog styles. dialog: - fgColor: *foreground + fgColor: *yellow bgColor: *background buttonFgColor: *foreground - buttonBgColor: *green + buttonBgColor: *background buttonFocusFgColor: *foreground - buttonFocusBgColor: *darkBlue - labelFgColor: *orange - fieldFgColor: *blue + buttonFocusBgColor: *purple + labelFgColor: *magenta + fieldFgColor: *darkBlue + frame: # Borders styles. border: - fgColor: *blue - focusColor: *darkBlue + fgColor: *darkGray + focusColor: *darkGray + menu: - fgColor: *lightBlue - keyColor: *green + fgColor: *foreground + keyColor: *magenta # Used for favorite namespaces numKeyColor: *green + # CrumbView attributes for history navigation. crumbs: - fgColor: *foreground + fgColor: *yellow bgColor: *backgroundOpaque - activeColor: *blue + activeColor: *purple + # Resource status and update styles status: newColor: *blue @@ -60,15 +84,18 @@ k9s: pendingColor: *orange errorColor: *magenta highlightColor: *blue - killColor: *green + killColor: *magenta completedColor: *darkBlue + # Border title styles. title: - fgColor: *foreground + fgColor: *purple bgColor: *background - highlightColor: *blue - counterColor: *lightBlue + highlightColor: *yellow + counterColor: *green filterColor: *orange + + # Specific views styles views: # Charts skins... charts: @@ -100,16 +127,20 @@ k9s: v1/pods: - *blue - *magenta + # TableView attributes. table: fgColor: *foreground bgColor: *background + cursorFgColor: *foreground + cursorBgColor: *backgroundOpaque markColor: *magenta # Header row styles. header: fgColor: *foreground bgColor: *backgroundOpaque - sorterColor: *blue + sorterColor: *magenta + # Xray view attributes. xray: fgColor: *foreground @@ -117,15 +148,19 @@ k9s: cursorColor: *blue cursorTextColor: *foreground graphicColor: *blue + # YAML info styles. yaml: - keyColor: *orange - colonColor: *orange + keyColor: *green + colonColor: *magenta valueColor: *foreground + # Logs styles. logs: fgColor: *foreground - bgColor: *backgroundOpaque + bgColor: *background indicator: fgColor: *foreground - bgColor: *darkBlue + bgColor: *backgroundOpaque + toggleOnColor: *green + toggleOffColor: *magenta diff --git a/skins/narsingh.yml b/skins/narsingh.yml new file mode 100644 index 0000000000..d2aa74d877 --- /dev/null +++ b/skins/narsingh.yml @@ -0,0 +1,89 @@ +# ----------------------------------------------------------------------------- +# Narsingh skin +# ----------------------------------------------------------------------------- + +# Skin... +k9s: + # General K9s styles + body: + fgColor: "#97979b" + bgColor: "#282a36" + logoColor: "#5af78e" + prompt: + fgColor: "#97979b" + bgColor: "#282a36" + suggestColor: "#5af78e" + info: + fgColor: white + sectionColor: "#5af78e" + dialog: + fgColor: "#97979b" + bgColor: "#282a36" + buttonFgColor: "#97979b" + buttonBgColor: "#282a36" + buttonFocusFgColor: "#97979b" + buttonFocusBgColor: "#5af78e" + labelFgColor: "#97979b" + fieldFgColor: "#5af78e" + frame: + border: + fgColor: "#5af78e" + focusColor: "#5af78e" + menu: + fgColor: white + keyColor: "#57c7ff" + numKeyColor: "#ff6ac1" + crumbs: + fgColor: "#282a36" + bgColor: white + activeColor: "#f3f99d" + status: + newColor: "#eff0eb" + modifyColor: "#5af78e" + addColor: "#57c7ff" + errorColor: "#ff5c57" + highlightColor: "#f3f99d" + killColor: mediumpurple + completedColor: gray + title: + fgColor: "#5af78e" + bgColor: "#282a36" + highlightColor: white + counterColor: white + filterColor: "#57c7ff" + views: + # Charts skins... + charts: + bgColor: default + defaultDialColors: + - "#57c7ff" + - "#ff5c57" + defaultChartColors: + - "#57c7ff" + - "#ff5c57" + table: + fgColor: "#57c7ff" + bgColor: "#282a36" + markColor: darkgoldenrod + header: + fgColor: white + bgColor: "#282a36" + sorterColor: orange + xray: + fgColor: "#57c7ff" + bgColor: "#282a36" + cursorColor: "#5af78e" + graphicColor: darkgoldenrod + showIcons: false + yaml: + keyColor: "#ff5c57" + colonColor: white + valueColor: "#f3f99d" + logs: + fgColor: white + bgColor: "#282a36" + indicator: + fgColor: white + bgColor: "#282a36" + toggleOnColor: "#ff5c57" + toggleOffColor: "#f3f99d" diff --git a/skins/nightfox.yml b/skins/nightfox.yml index f072b47b41..6d144c69b6 100644 --- a/skins/nightfox.yml +++ b/skins/nightfox.yml @@ -1,6 +1,10 @@ +# ----------------------------------------------------------------------------- # K9s Nightfox Theme # Based on the Nightfox.nvim color scheme: # https://github.com/EdenEast/nightfox.nvim +# ----------------------------------------------------------------------------- + +# Styles... foreground: &foreground "#cdcecf" background: &background "#192330" current_line: ¤t_line "#2b3b51" @@ -13,6 +17,7 @@ magenta: &magenta "#9d79d6" blue: &blue "#719cd6" red: &red "#c94f6d" +# Skin... k9s: body: fgColor: *foreground @@ -94,3 +99,5 @@ k9s: indicator: fgColor: *foreground bgColor: *selection + toggleOnColor: *margenta + toggleOffColor: *blue diff --git a/skins/nord.yml b/skins/nord.yml index 9092f42d9c..eb498b59db 100644 --- a/skins/nord.yml +++ b/skins/nord.yml @@ -1,3 +1,8 @@ +# ----------------------------------------------------------------------------- +# Nord skin +# ----------------------------------------------------------------------------- + +# Styles... foreground: &foreground "#DADEE8" background: &background "#30343F" current_line: ¤t_line "#383D4A" @@ -11,6 +16,7 @@ magenta: &magenta "#B48EAD" red: &red "#BF616A" yellow: &yellow "#EBCB8B" +# Skin... k9s: # General K9s styles body: @@ -105,6 +111,8 @@ k9s: indicator: fgColor: *foreground bgColor: *magenta + toggleOnColor: *magenta + toggleOffColor: *blue help: fgColor: *foreground bgColor: *background diff --git a/skins/one_dark.yml b/skins/one_dark.yml index 498ae80744..fc9b096410 100644 --- a/skins/one_dark.yml +++ b/skins/one_dark.yml @@ -1,4 +1,8 @@ -# OneDark presets +# ----------------------------------------------------------------------------- +# OneDark Skin +# ----------------------------------------------------------------------------- + +# Styles... foreground: &foreground "#abb2bf" background: &background "#282c34" black: &black "#080808" @@ -11,6 +15,7 @@ red: &red "#e06370" yellow: &yellow "#e5c07b" yellow_bright: &yellow_bright "#d19a66" +# Skin... k9s: body: fgColor: *foreground @@ -23,6 +28,12 @@ k9s: info: fgColor: *grey sectionColor: *green + help: + fgColor: *foreground + bgColor: *background + keyColor: *yellow + numKeyColor: *blue + sectionColor: *purple dialog: fgColor: *black bgColor: *background @@ -94,6 +105,8 @@ k9s: indicator: fgColor: *blue bgColor: *background + toggleOnColor: *red + toggleOffColor: *grey help: fgColor: *grey bgColor: *background diff --git a/skins/red.yml b/skins/red.yml index 7b977cff37..19605231be 100644 --- a/skins/red.yml +++ b/skins/red.yml @@ -1,3 +1,8 @@ +# ----------------------------------------------------------------------------- +# Red skin +# ----------------------------------------------------------------------------- + +# Skin... k9s: body: fgColor: red @@ -80,3 +85,5 @@ k9s: indicator: fgColor: red bgColor: black + toggleOnColor: red + toggleOffColor: white diff --git a/skins/rose_pine.yml b/skins/rose_pine.yml index 51b1171142..f66aa81dad 100644 --- a/skins/rose_pine.yml +++ b/skins/rose_pine.yml @@ -1,3 +1,7 @@ +# ----------------------------------------------------------------------------- +# Rose Pine skin +# ----------------------------------------------------------------------------- + # Styles... foreground: &foreground "#e0def4" background: &background "#191724" @@ -107,3 +111,5 @@ k9s: indicator: fgColor: *foreground bgColor: *purple + toggleOnColor: *pink + toggleOffColor: *purple diff --git a/skins/snazzy.yml b/skins/snazzy.yml index 03decefa35..826d5e594e 100644 --- a/skins/snazzy.yml +++ b/skins/snazzy.yml @@ -1,3 +1,8 @@ +# ----------------------------------------------------------------------------- +# Snazzy skin +# ----------------------------------------------------------------------------- + +# Skin... k9s: body: fgColor: "#97979b" @@ -79,3 +84,5 @@ k9s: indicator: fgColor: white bgColor: "#282a36" + toggleOnColor: "#ff5c57" + toggleOffColor: white diff --git a/skins/solarized-16.yml b/skins/solarized-16.yml new file mode 100644 index 0000000000..fa758dad37 --- /dev/null +++ b/skins/solarized-16.yml @@ -0,0 +1,128 @@ +# K9s Solarized Skin Contributed by [@graelo](graelo@grael.cc) +# +# The table below is extracted from +# and joined with both the ascii standard names from +# and the solarized color names from +# +# "black": ColorBlack, black base02 +# "maroon": ColorMaroon, red red +# "green": ColorGreen, green green +# "olive": ColorOlive, yellow yellow +# "navy": ColorNavy, blue blue +# "purple": ColorPurple, magenta magenta +# "teal": ColorTeal, cyan cyan +# "silver": ColorSilver, white base2 +# "gray": ColorGray, brightblack base03 +# "red": ColorRed, brightred orange +# "lime": ColorLime, brightgreen base01 +# "yellow": ColorYellow, brightyellow base00 +# "blue": ColorBlue, brightblue base0 +# "fuchsia": ColorFuchsia, brightmagenta violet +# "aqua": ColorAqua, brightcyan base1 +# "white": ColorWhite, brightwhite base3 + +base03: &base03 gray # base03 brightblack +base02: &base02 black # base02 black +base01: &base01 lime # base01 brightgreen +base00: &base00 yellow # base00 brightyellow +base0: &base0 blue # base0 brightblue +base1: &base1 aqua # base1 brightcyan +base2: &base2 silver # base2 white +base3: &base3 white # base3 brightwhite +yellow: &yellow olive # accent yellow #b58900 +orange: &orange red # accent orange #cb4b16 +red: &red maroon # accent red #dc322f +magenta: &magenta purple # accent magenta #d33682 +violet: &violet fuchsia # accent violet #6c71c4 +blue: &blue navy # accent blue #268bd2 +cyan: &cyan teal # accent cyan #2aa198 +green: &green green # accent green #859900 + +background: &background default # transparent +foreground: &foreground yellow # base00 +current_line: ¤t_line white # base2 +selection: &selection silver # base2 +comment: &comment aqua # base1 + +k9s: + body: + fgColor: *foreground + bgColor: *background + logoColor: *magenta + prompt: + fgColor: *foreground + bgColor: *background + suggestColor: *orange + info: + fgColor: *blue + sectionColor: *foreground + dialog: + fgColor: *foreground + bgColor: *background + buttonFgColor: *foreground + buttonBgColor: *magenta + buttonFocusFgColor: *base2 + buttonFocusBgColor: *cyan + labelFgColor: *orange + fieldFgColor: *foreground + frame: + border: + fgColor: *selection + focusColor: *foreground + menu: + fgColor: *foreground + keyColor: *blue + numKeyColor: *green + crumbs: + fgColor: *base2 + bgColor: *base0 + activeColor: *blue + status: + newColor: *base00 + modifyColor: *blue + addColor: *yellow + errorColor: *red + highlightColor: *orange + killColor: *violet + completedColor: *green + title: + fgColor: *foreground + bgColor: *background + highlightColor: *blue + counterColor: *magenta + filterColor: *magenta + views: + charts: + bgColor: default + defaultDialColors: + - *blue + - *red + defaultChartColors: + - *blue + - *red + table: + fgColor: *foreground + bgColor: *background + cursorFgColor: *base2 + cursorBgColor: *background + markColor: *magenta + header: + fgColor: *foreground + bgColor: *background + sorterColor: *magenta + xray: + fgColor: *foreground + bgColor: *background + cursorColor: *current_line + graphicColor: *blue + showIcons: false + yaml: + keyColor: *green + colonColor: *base02 + valueColor: *foreground + logs: + fgColor: *foreground + bgColor: *background + indicator: + fgColor: *foreground + bgColor: *selection diff --git a/skins/solarized_dark.yml b/skins/solarized_dark.yml index e249c16a5f..575406d8ad 100644 --- a/skins/solarized_dark.yml +++ b/skins/solarized_dark.yml @@ -1,4 +1,10 @@ -# Based on K9s Solarized Dark Skin Contributed by [@danmikita](danmikita@gmail.com) +# ----------------------------------------------------------------------------- +# Solarized Dark Skin +# Based on: K9s Solarized Dark Skin +# Author: [@danmikita](danmikita@gmail.com) +# ----------------------------------------------------------------------------- + +# Styles... foreground: &foreground "#839495" background: &background "#002833" current_line: ¤t_line "#003440" @@ -11,6 +17,7 @@ magenta: &magenta "#d33582" blue: &blue "#2aa198" red: &red "#dc312e" +# Skin... k9s: body: fgColor: *foreground @@ -92,3 +99,5 @@ k9s: indicator: fgColor: *foreground bgColor: *selection + toggleOnColor: *magenta + toggleOffColor: *blue diff --git a/skins/solarized_light.yml b/skins/solarized_light.yml index f20d3c6518..95a18117cb 100644 --- a/skins/solarized_light.yml +++ b/skins/solarized_light.yml @@ -1,4 +1,9 @@ -# K9s Solarized Light Skin Contributed by [@leg100](louisgarman@gmail.com) +# ----------------------------------------------------------------------------- +# Solarized Light Skin +# Author: [@leg100](louisgarman@gmail.com) +# ----------------------------------------------------------------------------- + +# Styles... foreground: &foreground "#657b83" background: &background "#fdf6e3" current_line: ¤t_line "#eee8d5" @@ -12,6 +17,7 @@ magenta: &magenta "#d33682" blue: &blue "#268bd2" red: &red "#dc322f" +# Skin... k9s: body: fgColor: *foreground @@ -94,3 +100,5 @@ k9s: indicator: fgColor: *foreground bgColor: *selection + toggleOnColor: *magenta + toggleOffColor: *blue diff --git a/skins/stock.yml b/skins/stock.yml index a89df7557b..89b8b9d51a 100644 --- a/skins/stock.yml +++ b/skins/stock.yml @@ -1,3 +1,8 @@ +# ----------------------------------------------------------------------------- +# Stock skin +# ----------------------------------------------------------------------------- + +# Skin... k9s: body: fgColor: dodgerblue @@ -81,3 +86,5 @@ k9s: indicator: fgColor: dodgerblue bgColor: black + toggleOnColor: papayawhip + toggleOffColor: steelblue diff --git a/skins/transparent.yml b/skins/transparent.yml index 917dbc24eb..be0a72e081 100644 --- a/skins/transparent.yml +++ b/skins/transparent.yml @@ -1,4 +1,9 @@ +# ----------------------------------------------------------------------------- +# Transparent skin # Preserve your terminal session background color +# ----------------------------------------------------------------------------- + +# Skin... k9s: body: bgColor: default @@ -32,6 +37,8 @@ k9s: bgColor: default indicator: bgColor: default + toggleOnColor: default + toggleOffColor: default yaml: colonColor: default valueColor: default diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml new file mode 100644 index 0000000000..682f054b40 --- /dev/null +++ b/snap/snapcraft.yaml @@ -0,0 +1,42 @@ +name: k9s +base: core20 +version: 'v0.27.4' +summary: K9s is a CLI to view and manage your Kubernetes clusters. +description: | + K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of you clusters in a single powerful session. + +grade: stable +confinement: classic + +architectures: + - amd64 + - arm64 + - armhf + - i386 + +plugs: + kube-config: + interface: personal-files + read: + - $HOME/.kube/config + +apps: + k9s: + command: bin/k9s + plugs: + - network + - network-bind + - home + - kube-config + +parts: + build: + plugin: go + source: https://github.com/derailed/k9s.git + source-tag: $SNAPCRAFT_PROJECT_VERSION + override-build: | + make test + make build + install $SNAPCRAFT_PART_BUILD/execs/k9s -D $SNAPCRAFT_PART_INSTALL/bin/k9s + build-packages: + - build-essential