Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kubectl-view-secret for openSUSE:Factory checked in at 2024-08-17 12:41:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kubectl-view-secret (Old) and /work/SRC/openSUSE:Factory/.kubectl-view-secret.new.2698 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kubectl-view-secret" Sat Aug 17 12:41:42 2024 rev:4 rq:1194373 version:0.13.0 Changes: -------- --- /work/SRC/openSUSE:Factory/kubectl-view-secret/kubectl-view-secret.changes 2024-08-02 17:27:56.398795630 +0200 +++ /work/SRC/openSUSE:Factory/.kubectl-view-secret.new.2698/kubectl-view-secret.changes 2024-08-17 12:42:00.185404657 +0200 @@ -1,0 +2,9 @@ +Fri Aug 16 18:13:25 UTC 2024 - opensuse_buildserv...@ojkastl.de + +- Update to version 0.13.0: + * Update changelog for v0.13.0 release + * Integrate with huh for better interface (#47) + * Add instructions for nixpkgs to README (#49) + * Add test coverage via codecov.io (#48) + +------------------------------------------------------------------- @@ -4,2 +13,2 @@ -- make kubectl recognize this as a plugin by having an executable with the - right name +- make kubectl recognize this as a plugin by having an executable + with the right name Old: ---- kubectl-view-secret-0.12.1.obscpio New: ---- kubectl-view-secret-0.13.0.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kubectl-view-secret.spec ++++++ --- /var/tmp/diff_new_pack.YZuwSe/_old 2024-08-17 12:42:00.689425616 +0200 +++ /var/tmp/diff_new_pack.YZuwSe/_new 2024-08-17 12:42:00.693425782 +0200 @@ -23,7 +23,7 @@ %define executable_name kubectl-view_secret Name: kubectl-view-secret -Version: 0.12.1 +Version: 0.13.0 Release: 0 Summary: Kubernetes CLI plugin to decode Kubernetes secrets License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.YZuwSe/_old 2024-08-17 12:42:00.721426947 +0200 +++ /var/tmp/diff_new_pack.YZuwSe/_new 2024-08-17 12:42:00.725427113 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/elsesiy/kubectl-view-secret</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.12.1</param> + <param name="revision">v0.13.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">enable</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.YZuwSe/_old 2024-08-17 12:42:00.745427945 +0200 +++ /var/tmp/diff_new_pack.YZuwSe/_new 2024-08-17 12:42:00.749428111 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/elsesiy/kubectl-view-secret</param> - <param name="changesrevision">122b2e639fd48b6bd2655957e82c539d83ced5df</param></service></servicedata> + <param name="changesrevision">9599a2e2e9bf4ecc14d6b4fb52230c10195ecb4e</param></service></servicedata> (No newline at EOF) ++++++ kubectl-view-secret-0.12.1.obscpio -> kubectl-view-secret-0.13.0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/.github/workflows/ci.yml new/kubectl-view-secret-0.13.0/.github/workflows/ci.yml --- old/kubectl-view-secret-0.12.1/.github/workflows/ci.yml 2024-08-02 06:26:00.000000000 +0200 +++ new/kubectl-view-secret-0.13.0/.github/workflows/ci.yml 2024-08-14 22:05:48.000000000 +0200 @@ -1,15 +1,25 @@ --- name: ci -on: - pull_request: +on: [push, pull_request] jobs: ci_job: name: test runs-on: ubuntu-22.04 - container: - image: golang:1.22 steps: - name: Checkout uses: actions/checkout@v4 + - name: Create kind cluster + uses: helm/kind-action@v1 + with: + cluster_name: kvs-test + install_only: true + - name: Prepare env + run: make bootstrap - name: Test run: make test + - name: Generate coverage report + run: make test-cov + - name: Upload results to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/.gitignore new/kubectl-view-secret-0.13.0/.gitignore --- old/kubectl-view-secret-0.12.1/.gitignore 2024-08-02 06:26:00.000000000 +0200 +++ new/kubectl-view-secret-0.13.0/.gitignore 2024-08-14 22:05:48.000000000 +0200 @@ -4,4 +4,5 @@ .idea .vscode -kubectl-view-secret \ No newline at end of file +kubectl-view-secret +coverage.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/CHANGELOG.md new/kubectl-view-secret-0.13.0/CHANGELOG.md --- old/kubectl-view-secret-0.12.1/CHANGELOG.md 2024-08-02 06:26:00.000000000 +0200 +++ new/kubectl-view-secret-0.13.0/CHANGELOG.md 2024-08-14 22:05:48.000000000 +0200 @@ -1,8 +1,18 @@ # Changelog -## [0.12.1](https://github.com/elsesiy/kubectl-view-secret/tree/0.12.1) (2024-08-02) +## [0.13.0](https://github.com/elsesiy/kubectl-view-secret/tree/0.13.0) (2024-08-14) -[Full Changelog](https://github.com/elsesiy/kubectl-view-secret/compare/v0.12.0...0.12.1) +[Full Changelog](https://github.com/elsesiy/kubectl-view-secret/compare/v0.12.1...0.13.0) + +**Merged pull requests:** + +- Add instructions for nixpkgs to README [\#49](https://github.com/elsesiy/kubectl-view-secret/pull/49) ([elsesiy](https://github.com/elsesiy)) +- Add test coverage via codecov.io [\#48](https://github.com/elsesiy/kubectl-view-secret/pull/48) ([elsesiy](https://github.com/elsesiy)) +- Integrate with huh for better interface [\#47](https://github.com/elsesiy/kubectl-view-secret/pull/47) ([elsesiy](https://github.com/elsesiy)) + +## [v0.12.1](https://github.com/elsesiy/kubectl-view-secret/tree/v0.12.1) (2024-08-02) + +[Full Changelog](https://github.com/elsesiy/kubectl-view-secret/compare/v0.12.0...v0.12.1) **Closed issues:** diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/Makefile new/kubectl-view-secret-0.13.0/Makefile --- old/kubectl-view-secret-0.12.1/Makefile 2024-08-02 06:26:00.000000000 +0200 +++ new/kubectl-view-secret-0.13.0/Makefile 2024-08-14 22:05:48.000000000 +0200 @@ -1,13 +1,22 @@ SOURCES := $(shell find . -name '*.go') BINARY := kubectl-view-secret +COV_REPORT := "coverage.txt" build: kubectl-view-secret +bootstrap: + ./hack/kind-bootstrap.sh + test: $(SOURCES) go test -v -short -race -timeout 30s ./... +test-cov: + go test ./... -coverprofile=$(COV_REPORT) + go tool cover -html=$(COV_REPORT) + clean: @rm -rf $(BINARY) + @kind delete cluster --name kvs-test $(BINARY): $(SOURCES) CGO_ENABLED=0 go build -o $(BINARY) -ldflags="-s -w" ./cmd/$(BINARY).go diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/README.md new/kubectl-view-secret-0.13.0/README.md --- old/kubectl-view-secret-0.12.1/README.md 2024-08-02 06:26:00.000000000 +0200 +++ new/kubectl-view-secret-0.13.0/README.md 2024-08-14 22:05:48.000000000 +0200 @@ -2,9 +2,12 @@ [](https://goreportcard.com/report/github.com/elsesiy/kubectl-view-secret)  +[](https://codecov.io/github/elsesiy/kubectl-view-secret) [](http://twitter.com/elsesiy) [](https://github.com/elsesiy/kubectl-view-secret/releases) + + This plugin allows for easy secret decoding. Useful if you want to see what's inside of a secret without always go through the following: 1. `kubectl get secret <secret> -o yaml` @@ -58,6 +61,13 @@ Contribution by [@jocelynthode](https://github.com/jocelynthode) +#### Nix +You can install the latest version from Nixpkgs ([24.05](https://search.nixos.org/packages?channel=24.05&from=0&size=50&sort=relevance&type=packages&query=kubectl-view-secret), [unstable](https://search.nixos.org/packages?channel=unstable&show=kubectl-view-secret&from=0&size=50&sort=relevance&type=packages&query=kubectl-view-secret)) or try it via a temporary nix-shell: + +``` +nix-shell -p kubectl-view-secret +``` + ### Build from source # Clone this repository (or your fork) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/go.mod new/kubectl-view-secret-0.13.0/go.mod --- old/kubectl-view-secret-0.12.1/go.mod 2024-08-02 06:26:00.000000000 +0200 +++ new/kubectl-view-secret-0.13.0/go.mod 2024-08-14 22:05:48.000000000 +0200 @@ -3,12 +3,43 @@ go 1.22 require ( + github.com/charmbracelet/bubbletea v0.26.6 + github.com/charmbracelet/huh v0.5.2 github.com/goccy/go-json v0.10.3 - github.com/magiconair/properties v1.8.7 github.com/spf13/cobra v1.8.1 + github.com/stretchr/testify v1.9.0 ) require ( + github.com/atotto/clipboard v0.1.4 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/catppuccin/go v0.2.0 // indirect + github.com/charmbracelet/bubbles v0.18.0 // indirect + github.com/charmbracelet/lipgloss v0.12.1 // indirect + github.com/charmbracelet/x/ansi v0.1.4 // indirect + github.com/charmbracelet/x/exp/strings v0.0.0-20240806224806-c5da01e9b4d1 // indirect + github.com/charmbracelet/x/exp/term v0.0.0-20240725160154-f9f6568126ec // indirect + github.com/charmbracelet/x/input v0.1.3 // indirect + github.com/charmbracelet/x/term v0.1.1 // indirect + github.com/charmbracelet/x/windows v0.1.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/go.sum new/kubectl-view-secret-0.13.0/go.sum --- old/kubectl-view-secret-0.12.1/go.sum 2024-08-02 06:26:00.000000000 +0200 +++ new/kubectl-view-secret-0.13.0/go.sum 2024-08-14 22:05:48.000000000 +0200 @@ -1,19 +1,81 @@ -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA= +github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= +github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= +github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= +github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s= +github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk= +github.com/charmbracelet/huh v0.5.2 h1:ofeNkJ4iaFnzv46Njhx896DzLUe/j0L2QAf8znwzX4c= +github.com/charmbracelet/huh v0.5.2/go.mod h1:Sf7dY0oAn6N/e3sXJFtFX9hdQLrUdO3z7AYollG9bAM= +github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs= +github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8= +github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= +github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/exp/strings v0.0.0-20240806224806-c5da01e9b4d1 h1:KuM2G5oWPAd0FlLh+mSl5p6bLVKK52KEmSik5z6UNbE= +github.com/charmbracelet/x/exp/strings v0.0.0-20240806224806-c5da01e9b4d1/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= +github.com/charmbracelet/x/exp/term v0.0.0-20240725160154-f9f6568126ec h1:tg3MAnVjISHLuysTCGGtsjkuoBzPzL9OrWvxs5Rd29I= +github.com/charmbracelet/x/exp/term v0.0.0-20240725160154-f9f6568126ec/go.mod h1:YG0dzr31td4jqC86wFlB8fJGgPwqPgTSa8VC0Hf7u+k= +github.com/charmbracelet/x/input v0.1.3 h1:oy4TMhyGQsYs/WWJwu1ELUMFnjiUAXwtDf048fHbCkg= +github.com/charmbracelet/x/input v0.1.3/go.mod h1:1gaCOyw1KI9e2j00j/BBZ4ErzRZqa05w0Ghn83yIhKU= +github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= +github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= +github.com/charmbracelet/x/windows v0.1.2 h1:Iumiwq2G+BRmgoayww/qfcvof7W/3uLoelhxojXlRWg= +github.com/charmbracelet/x/windows v0.1.2/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +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/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-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/hack/kind-bootstrap.sh new/kubectl-view-secret-0.13.0/hack/kind-bootstrap.sh --- old/kubectl-view-secret-0.12.1/hack/kind-bootstrap.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-view-secret-0.13.0/hack/kind-bootstrap.sh 2024-08-14 22:05:48.000000000 +0200 @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +set -euo pipefail + +CLUSTER_NAME="kvs-test" + +# Check if kind & kubectl are installed +which kind &>/dev/null || { echo "failed to find 'kind' binary, please install it" && exit 1; } +which kubectl &>/dev/null || { echo "failed to find 'kubectl' binary, please install it" && exit 1; } + +# Ensure the cluster exists +[[ $(kind get clusters) == *$CLUSTER_NAME* ]] || kind create cluster --name $CLUSTER_NAME + +# Set context in case there are mulitple +kubectl config set-context kind-${CLUSTER_NAME} + +# Seed test secrets + +## secret 'test' in namespace 'default' (multiple keys) +kubectl apply -f - <<EOF +apiVersion: v1 +data: + key1: dmFsdWUxCg== + key2: dmFsdWUyCg== +kind: Secret +metadata: + name: test + namespace: default +type: Opaque +EOF + +## secret 'test2' in namespace 'default' (single key) +kubectl apply -f - <<EOF +apiVersion: v1 +data: + key1: dmFsdWUx +kind: Secret +metadata: + name: test2 + namespace: default +type: Opaque +EOF + +## 'another' namespace +kubectl apply -f - <<EOF +apiVersion: v1 +kind: Namespace +metadata: + name: another +EOF + +## secret 'gopher' in namespace 'another' (single key) +kubectl apply -f - <<EOF +apiVersion: v1 +data: + foo: YmFy +kind: Secret +metadata: + name: gopher + namespace: another +type: Opaque +EOF + +## 'empty' namespace +kubectl apply -f - <<EOF +apiVersion: v1 +kind: Namespace +metadata: + name: empty +EOF Binary files old/kubectl-view-secret-0.12.1/media/view-secret.gif and new/kubectl-view-secret-0.13.0/media/view-secret.gif differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/media/view-secret.tape new/kubectl-view-secret-0.13.0/media/view-secret.tape --- old/kubectl-view-secret-0.12.1/media/view-secret.tape 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-view-secret-0.13.0/media/view-secret.tape 2024-08-14 22:05:48.000000000 +0200 @@ -0,0 +1,88 @@ +Output media/view-secret.gif +Set Theme "Catppuccin Mocha" +Set CursorBlink false +Set Width 1400 + +Hide +Type "# Secrets in current namespace" +Enter 2 +Show +Sleep 2 +Type "kubectl get secret" +Enter +Sleep 2 +Type "clear" +Enter + +Hide +Type "# Showing all secrets in current namespace with interactive selection" +Enter 2 +Show +Sleep 2 +Type "kubectl view-secret" +Enter +Sleep 2 +Down +Sleep 1 +Enter +Sleep 1 +Type "clear" +Enter + +Hide +Type "# View 'test' secret in current namespace with interactive selection for a single key" +Enter 2 +Show +Sleep 2 +Type "kubectl view-secret test" +Enter +Sleep 1 +Type "/key2" +Sleep 1 +Enter +Sleep 1 +Type "clear" +Enter + +Hide +Type "# View 'test' secret in current namespace and show all keys" +Enter 2 +Show +Sleep 2 +Type "kubectl view-secret test -a" +Enter +Sleep 1 +Type "clear" +Enter + +Hide +Type "# View 'test2' secret in current namespace immediately shows the secret contents (only one key exists)" +Enter 2 +Show +Sleep 2 +Type "kubectl view-secret test2" +Enter +Sleep 1 +Type "clear" +Enter + +Hide +Type "# Secrets in 'another' namespace" +Enter 2 +Show +Sleep 2 +Type "kubectl get secret -n another" +Enter +Sleep 2 +Type "clear" +Enter + +Hide +Type "# View 'gopher' secret in 'another' namespace and silence info about single key" +Enter 2 +Show +Sleep 2 +Type "kubectl view-secret gopher -n another -q" +Enter + +Sleep 5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/pkg/cmd/types.go new/kubectl-view-secret-0.13.0/pkg/cmd/types.go --- old/kubectl-view-secret-0.12.1/pkg/cmd/types.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-view-secret-0.13.0/pkg/cmd/types.go 2024-08-14 22:05:48.000000000 +0200 @@ -0,0 +1,17 @@ +package cmd + +type SecretList struct { + Items []Secret `json:"items"` +} + +type Secret struct { + Data SecretData `json:"data"` + Metadata Metadata `json:"metadata"` +} + +type SecretData map[string]string + +type Metadata struct { + Name string `json:"name"` + Namespace string `json:"namespace"` +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/pkg/cmd/types_test.go new/kubectl-view-secret-0.13.0/pkg/cmd/types_test.go --- old/kubectl-view-secret-0.12.1/pkg/cmd/types_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-view-secret-0.13.0/pkg/cmd/types_test.go 2024-08-14 22:05:48.000000000 +0200 @@ -0,0 +1,96 @@ +package cmd + +import ( + "encoding/json" + "errors" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +var ( + validSecretJson = `{ + "apiVersion": "v1", + "data": { + "key1": "dmFsdWUxCg==", + "key2": "dmFsdWUyCg==" + }, + "kind": "Secret", + "metadata": { + "creationTimestamp": "2024-08-02T21:25:40Z", + "name": "test", + "namespace": "default", + "resourceVersion": "715", + "uid": "0027fdc9-5371-4715-a0a8-61f3f78fdd36" + }, + "type": "Opaque" +}` + + emptySecretJson = `{ + "apiVersion": "v1", + "data": {}, + "kind": "Secret", + "metadata": { + "name": "test-empty", + "namespace": "default", + }, + "type": "Opaque" +}` +) + +func TestSerialize(t *testing.T) { + tests := map[string]struct { + input string + want Secret + wantErr error + }{ + "empty secret": { + input: emptySecretJson, + want: Secret{ + Metadata: Metadata{ + Name: "test", + Namespace: "default", + }, + }, + wantErr: errors.New("invalid character '}' looking for beginning of object key string"), + }, + "valid secret": { + input: validSecretJson, + want: Secret{ + Data: SecretData{ + "key1": "dmFsdWUxCg==", + "key2": "dmFsdWUyCg==", + }, + Metadata: Metadata{ + Name: "test", + Namespace: "default", + }, + }, + }, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + var got Secret + err := json.Unmarshal([]byte(tt.input), &got) + if err != nil { + if tt.wantErr == nil { + assert.Fail(t, "unexpected error", err) + } else if err.Error() != tt.wantErr.Error() { + assert.Equal(t, tt.wantErr, err) + } + return + } else if tt.wantErr != nil { + assert.Fail(t, "expected error, got nil", tt.wantErr) + return + } + + if !reflect.DeepEqual(got, tt.want) { + t.Fatal(err) + } + }) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/pkg/cmd/view-secret.go new/kubectl-view-secret-0.13.0/pkg/cmd/view-secret.go --- old/kubectl-view-secret-0.12.1/pkg/cmd/view-secret.go 2024-08-02 06:26:00.000000000 +0200 +++ new/kubectl-view-secret-0.13.0/pkg/cmd/view-secret.go 2024-08-14 22:05:48.000000000 +0200 @@ -11,6 +11,8 @@ "sort" "strings" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/huh" "github.com/goccy/go-json" "github.com/spf13/cobra" ) @@ -39,19 +41,23 @@ %[1]s view-secret <secret> -q/--quiet ` - singleKeyDescription = "Choosing key: %[1]s" - listDescription = "Multiple sub keys found. Specify another argument, one of:" - listPrefix = "->" + secretDescription = "Found %d keys in secret %q. Choose one or select 'all' to view." + secretListDescription = "Found %d secrets. Choose one." + secretListTitle = "Available Secrets" + secretTitle = "Secret Data" + singleKeyDescription = "Viewing only available key: %[1]s" ) -// ErrSecretKeyNotFound is thrown if the key doesn't exist in the secret -var ErrSecretKeyNotFound = errors.New("provided key not found in secret") +var ( + // ErrNoSecretFound is thrown when no secret name was provided but we didn't find any secrets + ErrNoSecretFound = errors.New("no secrets found") -// ErrSecretEmpty is thrown when there's no data in the secret -var ErrSecretEmpty = errors.New("secret is empty") + // ErrSecretEmpty is thrown when there's no data in the secret + ErrSecretEmpty = errors.New("secret is empty") -// ErrInsufficientArgs is thrown if arg len <1 or >2 -var ErrInsufficientArgs = fmt.Errorf("\nincorrect number or arguments, see --help for usage instructions") + // ErrSecretKeyNotFound is thrown if the key doesn't exist in the secret + ErrSecretKeyNotFound = errors.New("provided key not found in secret") +) // CommandOpts is the struct holding common properties type CommandOpts struct { @@ -71,14 +77,13 @@ res := &CommandOpts{} cmd := &cobra.Command{ - Use: "view-secret [secret-name] [secret-key]", - Short: "Decode a kubernetes secret by name & key in the current context/cluster/namespace", + Args: cobra.RangeArgs(0, 2), Example: fmt.Sprintf(example, "kubectl"), + Short: "Decode a kubernetes secret by name & key in the current context/cluster/namespace", SilenceUsage: true, + Use: "view-secret [secret-name] [secret-key]", RunE: func(c *cobra.Command, args []string) error { - if err := res.Validate(args); err != nil { - return err - } + res.ParseArgs(args) if err := res.Retrieve(c); err != nil { return err } @@ -100,19 +105,16 @@ return cmd } -// Validate ensures proper command usage -func (c *CommandOpts) Validate(args []string) error { +// ParseArgs serializes the user supplied program arguments +func (c *CommandOpts) ParseArgs(args []string) { argLen := len(args) - if argLen < 1 || argLen > 2 { - return ErrInsufficientArgs - } + if argLen >= 1 { + c.secretName = args[0] - c.secretName = args[0] - if argLen == 2 { - c.secretKey = args[1] + if argLen == 2 { + c.secretKey = args[1] + } } - - return nil } // Retrieve reads the kubeconfig and decodes the secret @@ -124,7 +126,12 @@ impersonateGroupOverride, _ := cmd.Flags().GetString("as-group") var res, cmdErr bytes.Buffer - commandArgs := []string{"get", "secret", c.secretName, "-o", "json"} + + commandArgs := []string{"get", "secret", "-o", "json"} + if c.secretName != "" { + commandArgs = []string{"get", "secret", c.secretName, "-o", "json"} + } + if nsOverride != "" { commandArgs = append(commandArgs, "-n", nsOverride) } @@ -154,54 +161,108 @@ return nil } - var secret map[string]interface{} - if err := json.Unmarshal(res.Bytes(), &secret); err != nil { - return err + var secret Secret + if c.secretName == "" { + var secretList SecretList + if err := json.Unmarshal(res.Bytes(), &secretList); err != nil { + return err + } + + // Since we don't query valid namespaces, we'll avoid prompting the user to select a secret if we didn't retrieve any secrets + if len(secretList.Items) == 0 { + return ErrNoSecretFound + } + + opts := []string{} + secretMap := map[string]Secret{} + for _, v := range secretList.Items { + opts = append(opts, v.Metadata.Name) + secretMap[v.Metadata.Name] = v + } + + err := huh.NewForm( + huh.NewGroup( + huh.NewSelect[string](). + Title(secretListTitle). + Description(fmt.Sprintf(secretListDescription, len(secretList.Items))). + Options(huh.NewOptions(opts...)...). + Value(&c.secretName), + ), + ).WithProgramOptions(tea.WithInput(cmd.InOrStdin()), tea.WithOutput(cmd.OutOrStdout())).Run() + if err != nil { + return err + } + + secret = secretMap[c.secretName] + } else { + if err := json.Unmarshal(res.Bytes(), &secret); err != nil { + return err + } } if c.quiet { - return ProcessSecret(os.Stdout, io.Discard, secret, c.secretKey, c.decodeAll) + return ProcessSecret(cmd.OutOrStdout(), io.Discard, cmd.InOrStdin(), secret, c.secretKey, c.decodeAll) } - return ProcessSecret(os.Stdout, os.Stderr, secret, c.secretKey, c.decodeAll) + return ProcessSecret(cmd.OutOrStdout(), cmd.OutOrStderr(), cmd.InOrStdin(), secret, c.secretKey, c.decodeAll) } // ProcessSecret takes the secret and user input to determine the output -func ProcessSecret(outWriter, errWriter io.Writer, secret map[string]interface{}, secretKey string, decodeAll bool) error { - data, ok := secret["data"].(map[string]interface{}) - if !ok { +func ProcessSecret(outWriter, errWriter io.Writer, inputReader io.Reader, secret Secret, secretKey string, decodeAll bool) error { + data := secret.Data + if len(data) == 0 { return ErrSecretEmpty } var keys []string - for k := range data { + for k := range secret.Data { keys = append(keys, k) } sort.Strings(keys) if decodeAll { for _, k := range keys { - b64d, _ := base64.StdEncoding.DecodeString(data[k].(string)) + b64d, _ := base64.StdEncoding.DecodeString(data[k]) _, _ = fmt.Fprintf(outWriter, "%s='%s'\n", k, strings.TrimSpace(string(b64d))) } } else if len(data) == 1 { for k, v := range data { _, _ = fmt.Fprintf(errWriter, singleKeyDescription+"\n", k) - b64d, _ := base64.StdEncoding.DecodeString(v.(string)) - _, _ = fmt.Fprint(outWriter, string(b64d)) + b64d, _ := base64.StdEncoding.DecodeString(v) + _, _ = fmt.Fprintf(outWriter, "%s\n", strings.TrimSpace(string(b64d))) } } else if secretKey != "" { if v, ok := data[secretKey]; ok { - b64d, _ := base64.StdEncoding.DecodeString(v.(string)) - _, _ = fmt.Fprint(outWriter, string(b64d)) + b64d, _ := base64.StdEncoding.DecodeString(v) + _, _ = fmt.Fprintf(outWriter, "%s\n", strings.TrimSpace(string(b64d))) } else { return ErrSecretKeyNotFound } } else { - _, _ = fmt.Fprintln(errWriter, listDescription) + opts := []string{"all"} for k := range data { - _, _ = fmt.Fprintf(outWriter, "%s %s\n", listPrefix, k) + opts = append(opts, k) + } + + var selection string + err := huh.NewForm( + huh.NewGroup( + huh.NewSelect[string](). + Title(secretTitle). + Description(fmt.Sprintf(secretDescription, len(data), secret.Metadata.Name)). + Options(huh.NewOptions(opts...)...). + Value(&selection), + ), + ).WithProgramOptions(tea.WithInput(inputReader), tea.WithOutput(outWriter)).Run() + if err != nil { + return err } + + if selection == "all" { + decodeAll = true + } + + return ProcessSecret(outWriter, errWriter, inputReader, secret, selection, decodeAll) } return nil diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-view-secret-0.12.1/pkg/cmd/view-secret_test.go new/kubectl-view-secret-0.13.0/pkg/cmd/view-secret_test.go --- old/kubectl-view-secret-0.12.1/pkg/cmd/view-secret_test.go 2024-08-02 06:26:00.000000000 +0200 +++ new/kubectl-view-secret-0.13.0/pkg/cmd/view-secret_test.go 2024-08-14 22:05:48.000000000 +0200 @@ -1,81 +1,135 @@ package cmd import ( - "bufio" "bytes" + "errors" "fmt" - "reflect" - "sort" + "io" "strings" "testing" - "github.com/goccy/go-json" - "github.com/magiconair/properties/assert" + "github.com/stretchr/testify/assert" ) -const ( - testSecret = ` -{ - "data": { - "TEST_CONN_STR": "bW9uZ29kYjovL215REJSZWFkZXI6RDFmZmljdWx0UCU0MHNzdzByZEBtb25nb2RiMC5leGFtcGxlLmNvbToyNzAxNy8/YXV0aFNvdXJjZT1hZG1pbg==", - "TEST_PASSWORD": "c2VjcmV0Cg==", - "TEST_PASSWORD_2": "dmVyeXNlY3JldAo=" - } -} -` - testSecretSingle = ` -{ - "data": { - "SINGLE_PASSWORD": "c2VjcmV0Cg==" - } -} -` - testSecretEmpty = "{}" +var ( + secret = SecretData{ + "TEST_CONN_STR": "bW9uZ29kYjovL215REJSZWFkZXI6RDFmZmljdWx0UCU0MHNzdzByZEBtb25nb2RiMC5leGFtcGxlLmNvbToyNzAxNy8/YXV0aFNvdXJjZT1hZG1pbg==", + "TEST_PASSWORD": "c2VjcmV0Cg==", + "TEST_PASSWORD_2": "dmVyeXNlY3JldAo=", + } + + secretSingle = SecretData{ + "SINGLE_PASSWORD": "c2VjcmV0Cg==", + } + + secretEmpty = SecretData{} ) -func TestValidate(t *testing.T) { +func TestParseArgs(t *testing.T) { opts := CommandOpts{} tests := map[string]struct { - opts CommandOpts - args []string - err error + opts CommandOpts + args []string + wantOpts CommandOpts }{ - "args insufficient length": {opts, []string{}, ErrInsufficientArgs}, - "valid args": {opts, []string{"test", "key"}, nil}, + "one arg": {opts, []string{"test"}, CommandOpts{secretName: "test"}}, + "two args": {opts, []string{"test", "key"}, CommandOpts{secretName: "test", secretKey: "key"}}, } for name, test := range tests { t.Run(name, func(t *testing.T) { - got := test.opts.Validate(test.args) - want := test.err - if got != want { - t.Errorf("got %v, want %v", got, want) + t.Parallel() + + test.opts.ParseArgs(test.args) + got := test.opts + if got != test.wantOpts { + t.Errorf("got %v, want %v", got, test.wantOpts) } }) } } -func TestProcessSecret(t *testing.T) { - var secret, secretSingle, secretEmpty map[string]interface{} - _ = json.Unmarshal([]byte(testSecret), &secret) - _ = json.Unmarshal([]byte(testSecretSingle), &secretSingle) - _ = json.Unmarshal([]byte(testSecretEmpty), &secretEmpty) +func TestNewCmdViewSecret(t *testing.T) { + tests := map[string]struct { + args []string + feedkeys string + want string + wantErr error + }{ + "all": {args: []string{"test", "--all"}, want: `key1='value1'\nkey2='value2'`}, + "custom ctx": {args: []string{"test", "--context", "gotest"}}, + "custom kubecfg": {args: []string{"test", "--kubeconfig", "cfg"}}, + "custom ns (does not exist)": {args: []string{"test", "--namespace", "bob"}, want: `Error from server (NotFound): namespaces "bob" not found`}, + "custom ns (no secret)": {args: []string{"test", "--namespace", "another"}, want: `Error from server (NotFound): secrets "test" not found`}, + "custom ns (valid secret)": {args: []string{"gopher", "--namespace", "another"}, want: `Viewing only available key: foo\nbar`}, + "impersonate group": {args: []string{"test", "--as", "gopher"}}, + "impersonate user & group": {args: []string{"test", "--as", "gopher", "--as-group", "golovers"}}, + // make bootstrap sources 2 test secrets in the default namespace, select the first one and print all values + "interactive": {args: []string{"--all"}, feedkeys: "\r", want: `key1='value1'\nkey2='value2'`}, + "interactive custom ns (no secret)": {args: []string{"--namespace", "empty"}, wantErr: ErrNoSecretFound}, + "invalid arg count": {args: []string{"a", "b", "c"}, wantErr: errors.New("accepts between 0 and 2 arg(s), received 3")}, + "quiet": {args: []string{"test2", "--quiet"}, want: `value1`}, + "unknown flag": {args: []string{"--version"}, wantErr: errors.New("unknown flag: --version")}, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + cmd := NewCmdViewSecret() + outBuf := bytes.NewBufferString("") + readBuf := &strings.Reader{} + if tt.feedkeys != "" { + readBuf = strings.NewReader(tt.feedkeys) + } + cmd.SetOut(outBuf) + cmd.SetIn(readBuf) + cmd.SetArgs(tt.args) + + err := cmd.Execute() + if err != nil { + if tt.wantErr == nil { + assert.Fail(t, "unexpected error", err) + } else if err.Error() != tt.wantErr.Error() { + assert.Equal(t, tt.wantErr, err) + } + return + } else if tt.wantErr != nil { + assert.Fail(t, "expected error, got nil", tt.wantErr) + return + } + + _, err = io.ReadAll(outBuf) + if err != nil { + t.Fatal(err) + } + }) + } +} + +func TestProcessSecret(t *testing.T) { tests := map[string]struct { - secretData map[string]interface{} + secretData SecretData wantStdOut []string wantStdErr []string secretKey string decodeAll bool err error + feedkeys string }{ "view-secret <secret>": { secret, - []string{"-> TEST_CONN_STR", "-> TEST_PASSWORD", "-> TEST_PASSWORD_2"}, - []string{listDescription}, + []string{ + "TEST_CONN_STR='mongodb://myDBReader:d1fficultp%40ssw...@mongodb0.example.com:27017/?authSource=admin'", + "TEST_PASSWORD='secret'", + "TEST_PASSWORD_2='verysecret'", + }, + []string{}, "", false, nil, + "\r", // selects 'all' as it's the default selection }, "view-secret <secret-single-key>": { secretSingle, @@ -84,8 +138,9 @@ "", false, nil, + "", }, - "view-secret test TEST_PASSWORD": {secret, []string{"secret"}, nil, "TEST_PASSWORD", false, nil}, + "view-secret test TEST_PASSWORD": {secret, []string{"secret"}, nil, "TEST_PASSWORD", false, nil, ""}, "view-secret test -a": { secret, []string{ @@ -97,40 +152,42 @@ "", true, nil, + "", }, - "view-secret test NONE": {secret, nil, nil, "NONE", false, ErrSecretKeyNotFound}, - "view-secret <secret-empty>": {secretEmpty, nil, nil, "", false, ErrSecretEmpty}, + "view-secret test NONE": {secret, nil, nil, "NONE", false, ErrSecretKeyNotFound, ""}, + "view-secret <secret-empty>": {secretEmpty, nil, nil, "", false, ErrSecretEmpty, ""}, } for name, test := range tests { t.Run(name, func(t *testing.T) { - gotStdOut := bytes.Buffer{} - gotStdErr := bytes.Buffer{} - err := ProcessSecret(&gotStdOut, &gotStdErr, test.secretData, test.secretKey, test.decodeAll) + t.Parallel() + + stdOutBuf := bytes.Buffer{} + stdErrBuf := bytes.Buffer{} + readBuf := strings.Reader{} + + if test.feedkeys != "" { + readBuf = *strings.NewReader(test.feedkeys) + } + + err := ProcessSecret(&stdOutBuf, &stdErrBuf, &readBuf, Secret{Data: test.secretData}, test.secretKey, test.decodeAll) if test.err != nil { assert.Equal(t, err, test.err) } else { - var gotStdOutArr, gotStdErrArr []string - scanner := bufio.NewScanner(strings.NewReader(gotStdOut.String())) - for scanner.Scan() { - gotStdOutArr = append(gotStdOutArr, scanner.Text()) - } - - scanner = bufio.NewScanner(strings.NewReader(gotStdErr.String())) - for scanner.Scan() { - gotStdErrArr = append(gotStdErrArr, scanner.Text()) - } - - sort.Strings(gotStdOutArr) - sort.Strings(gotStdErrArr) + gotStdOut := stdOutBuf.String() + gotStdErr := stdErrBuf.String() - if !reflect.DeepEqual(gotStdOutArr, test.wantStdOut) { - t.Errorf("got %v, want %v", gotStdOutArr, test.wantStdOut) + for _, s := range test.wantStdOut { + if !assert.Contains(t, gotStdOut, s) { + t.Errorf("got %v, want %v", gotStdOut, s) + } } - if !reflect.DeepEqual(gotStdErrArr, test.wantStdErr) { - t.Errorf("got %v, want %v", gotStdErrArr, test.wantStdErr) + for _, s := range test.wantStdErr { + if !assert.Contains(t, gotStdErr, s) { + t.Errorf("got %v, want %v", gotStdErr, s) + } } } }) ++++++ kubectl-view-secret.obsinfo ++++++ --- /var/tmp/diff_new_pack.YZuwSe/_old 2024-08-17 12:42:00.861432768 +0200 +++ /var/tmp/diff_new_pack.YZuwSe/_new 2024-08-17 12:42:00.865432935 +0200 @@ -1,5 +1,5 @@ name: kubectl-view-secret -version: 0.12.1 -mtime: 1722572760 -commit: 122b2e639fd48b6bd2655957e82c539d83ced5df +version: 0.13.0 +mtime: 1723665948 +commit: 9599a2e2e9bf4ecc14d6b4fb52230c10195ecb4e ++++++ vendor.tar.gz ++++++ ++++ 302658 lines of diff (skipped)