Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kubectl-gather for openSUSE:Factory checked in at 2026-03-26 21:10:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kubectl-gather (Old) and /work/SRC/openSUSE:Factory/.kubectl-gather.new.8177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kubectl-gather" Thu Mar 26 21:10:08 2026 rev:5 rq:1342840 version:0.12.0 Changes: -------- --- /work/SRC/openSUSE:Factory/kubectl-gather/kubectl-gather.changes 2025-10-23 16:38:29.617045800 +0200 +++ /work/SRC/openSUSE:Factory/.kubectl-gather.new.8177/kubectl-gather.changes 2026-03-27 06:42:34.532808665 +0100 @@ -1,0 +2,15 @@ +Thu Mar 26 11:51:24 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 0.12.0: + * doc: Update readme with secret sanitization support + * e2e: Add e2e tests for secret sanitization + * gather: Add unit tests for secret sanitization + * gather: Sanitize secret data with PBKDF2 hashes + * Update actions for node 24 (#121) + * deps: Update k8s.io dependencies to oldest supported k8s + version + * Update zap to v1.27.1 + * Update cobra to latest version + * Update go to 1.25.0 + +------------------------------------------------------------------- Old: ---- kubectl-gather-0.11.0.obscpio New: ---- kubectl-gather-0.12.0.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kubectl-gather.spec ++++++ --- /var/tmp/diff_new_pack.fjC22s/_old 2026-03-27 06:42:35.660855271 +0100 +++ /var/tmp/diff_new_pack.fjC22s/_new 2026-03-27 06:42:35.668855601 +0100 @@ -1,7 +1,7 @@ # # spec file for package kubectl-gather # -# Copyright (c) 2025 SUSE LLC and contributors +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: kubectl-gather -Version: 0.11.0 +Version: 0.12.0 Release: 0 Summary: Kubectl plugin to gather data about your cluster License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.fjC22s/_old 2026-03-27 06:42:35.708857254 +0100 +++ /var/tmp/diff_new_pack.fjC22s/_new 2026-03-27 06:42:35.708857254 +0100 @@ -3,7 +3,7 @@ <param name="url">https://github.com/nirs/kubectl-gather</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.11.0</param> + <param name="revision">v0.12.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.fjC22s/_old 2026-03-27 06:42:35.732858246 +0100 +++ /var/tmp/diff_new_pack.fjC22s/_new 2026-03-27 06:42:35.732858246 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/nirs/kubectl-gather</param> - <param name="changesrevision">c42a9ee16b240236e0cb67809e810e028176d0ad</param></service></servicedata> + <param name="changesrevision">a0e7607071c92e7d7c8b1902eafb0dcd39f392dc</param></service></servicedata> (No newline at EOF) ++++++ kubectl-gather-0.11.0.obscpio -> kubectl-gather-0.12.0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/README.md new/kubectl-gather-0.12.0/README.md --- old/kubectl-gather-0.11.0/README.md 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/README.md 2026-03-26 11:26:48.000000000 +0100 @@ -416,11 +416,15 @@ > [!IMPORTANT] > Gathering remotely require the "oc" command. +Use `--salt` to ensure all remote clusters use the same salt for +consistent secret hashing. See [Secret +sanitization](#secret-sanitization) for more info. + In this example we gather data from OpenShift Data Foundation clusters configured for disaster recovery. Gathering everything takes more than 6 minutes: - $ kubectl gather --contexts kevin-rdr-hub,kevin-rdr-c1,kevin-rdr-c2 --remote --directory gather.remote + $ kubectl gather --contexts kevin-rdr-hub,kevin-rdr-c1,kevin-rdr-c2 --remote --salt "$(openssl rand -base64 16)" --directory gather.remote 2024-05-28T20:57:32.684+0300 INFO gather Using kubeconfig "/home/nsoffer/.kube/config" 2024-05-28T20:57:32.686+0300 INFO gather Gathering from all namespaces 2024-05-28T20:57:32.686+0300 INFO gather Gathering on remote cluster "kevin-rdr-c2" @@ -496,7 +500,7 @@ In this example we gather data related to single DR protected VM: ``` -$ kubectl gather --contexts kevin-rdr-hub,kevin-rdr-c1,kevin-rdr-c2 --namespaces openshift-dr-ops,ui-vms3 --remote -d gather.remote.app +$ kubectl gather --contexts kevin-rdr-hub,kevin-rdr-c1,kevin-rdr-c2 --namespaces openshift-dr-ops,ui-vms3 --remote --salt "$(openssl rand -base64 16)" -d gather.remote.app 2024-05-28T21:14:15.883+0300 INFO gather Using kubeconfig "/home/nsoffer/.kube/config" 2024-05-28T21:14:15.884+0300 INFO gather Gathering from namespaces [openshift-dr-ops ui-vms3] 2024-05-28T21:14:15.884+0300 INFO gather Gathering on remote cluster "kevin-rdr-c2" @@ -573,6 +577,42 @@ 8.8M gather.resources ``` +## Secret sanitization + +All Secret resources are automatically sanitized before writing. Each +data value is replaced with a deterministic PBKDF2-HMAC-SHA256 hash, +and the `kubectl.kubernetes.io/last-applied-configuration` annotation +is stripped to prevent plaintext leaks. + +A `kubectl-gather.nirs.github.com/sanitized` annotation is added to +each sanitized secret with the base64-encoded salt. The salt can be +used to verify the original secret hash value. + +By default a random salt is generated. To use a specific salt, use the +`--salt` flag with `--remote` to ensure all remote clusters use the +same salt. + +``` +$ kubectl gather --contexts dr1,dr2,hub --remote --salt "$(openssl rand -base64 16)" -d gather.remote +``` + +Specifying a salt is also useful when comparing changes between two +gather runs: + +``` +salt=$(openssl rand -base64 16) +kubectl gather --contexts hub,dr1,dr2 -d gather.a --salt "$salt" +sleep 60 +kubectl gather --contexts hub,dr1,dr2 -d gather.b --salt "$salt" +diff -ur gather.a gather.b +``` + +Without a specified salt, the diff would include unwanted changes in +secret hashes and annotations. + +Gathering secrets can be slower than other resources. The impact is +small when gathering only a few namespaces. + ## Integrating with other programs When running the *kubectl gather* from another program you may want to diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/cmd/local.go new/kubectl-gather-0.12.0/cmd/local.go --- old/kubectl-gather-0.11.0/cmd/local.go 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/cmd/local.go 2026-03-26 11:26:48.000000000 +0100 @@ -40,6 +40,7 @@ Namespaces: namespaces, Addons: addons, Cluster: cluster, + Salt: parsedSalt, Log: log.Named(clusterConfig.Context), } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/cmd/remote.go new/kubectl-gather-0.12.0/cmd/remote.go --- old/kubectl-gather-0.11.0/cmd/remote.go 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/cmd/remote.go 2026-03-26 11:26:48.000000000 +0100 @@ -113,6 +113,10 @@ remoteArgs = append(remoteArgs, "--addons="+strings.Join(addons, ",")) } + // Always pass the salt so all remote clusters use the same salt value, + // ensuring consistent hashes for comparing secrets across clusters. + remoteArgs = append(remoteArgs, "--salt="+salt) + if len(remoteArgs) > 0 { args = append(args, "--", "/usr/bin/gather") args = append(args, remoteArgs...) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/cmd/root.go new/kubectl-gather-0.12.0/cmd/root.go --- old/kubectl-gather-0.11.0/cmd/root.go 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/cmd/root.go 2026-03-26 11:26:48.000000000 +0100 @@ -4,6 +4,7 @@ package cmd import ( + "encoding/base64" "fmt" stdlog "log" "os" @@ -26,6 +27,8 @@ var addons []string var cluster bool var remote bool +var salt string +var parsedSalt gather.Salt var verbose bool var logFormat string var log *zap.SugaredLogger @@ -43,8 +46,9 @@ kubectl gather --contexts dr1,dr2,hub --namespaces my-ns,other-ns --directory gather.ns # Gather data on the remote clusters "dr1", "dr2" and "hub" and download it to - # "gather.remote/". Requires the "oc" command. - kubectl gather --contexts dr1,dr2,hub --remote --directory gather.remote + # "gather.remote/". Requires the "oc" command. Use --salt to ensure all remote + # clusters use the same salt for consistent secret hashing. + kubectl gather --contexts dr1,dr2,hub --remote --salt "$(openssl rand -base64 16)" --directory gather.remote # Enable only the "logs" addon, gathering all resources and pod logs. Use # --addons= to disable all addons. @@ -97,6 +101,8 @@ "specified, gather all resources") rootCmd.Flags().BoolVarP(&remote, "remote", "r", false, "run on the remote clusters (requires the \"oc\" command)") + rootCmd.Flags().StringVar(&salt, "salt", "", + "base64-encoded 16-byte salt for secret hashing (default: randomly generated)") rootCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "be more verbose") rootCmd.Flags().StringVar(&logFormat, "log-format", "text", "Set the logging format [text, json]") @@ -124,6 +130,12 @@ log.Fatal(err) } + if cmd.Flags().Changed("salt") { + log.Infof("Using user-provided salt %q", salt) + } else { + log.Infof("Using generated salt %q", salt) + } + if namespaces == nil { log.Infof("Gathering from all namespaces") } else if len(namespaces) > 0 { @@ -152,6 +164,18 @@ } func validateOptions(cmd *cobra.Command) error { + if salt != "" { + var err error + parsedSalt, err = validateSalt(salt) + if err != nil { + return err + } + } else { + parsedSalt = gather.RandomSalt() + // Keep the base64 salt string for logging and passing to remote clusters. + salt = base64.StdEncoding.EncodeToString(parsedSalt[:]) + } + // --namespaces="" // --namespaces="" --cluster=false if namespaces != nil && len(namespaces) == 0 && !cluster { @@ -223,6 +247,21 @@ return zap.New(core).Named("gather").Sugar() } +func validateSalt(saltFlag string) (gather.Salt, error) { + var salt gather.Salt + + decodedSalt, err := base64.StdEncoding.DecodeString(saltFlag) + if err != nil { + return salt, fmt.Errorf("invalid --salt value: must be base64-encoded: %w", err) + } + if len(decodedSalt) != 16 { + return salt, fmt.Errorf("invalid --salt value: must be 16 bytes, got %d", len(decodedSalt)) + } + + copy(salt[:], decodedSalt) + return salt, nil +} + func defaultGatherDirectory() string { return time.Now().Format("gather.20060102150405") } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/e2e/gather_test.go new/kubectl-gather-0.12.0/e2e/gather_test.go --- old/kubectl-gather-0.11.0/e2e/gather_test.go 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/e2e/gather_test.go 2026-03-26 11:26:48.000000000 +0100 @@ -23,6 +23,7 @@ "namespaces/test-common/apps/replicasets/common-busybox-*.yaml", "namespaces/test-common/configmaps/kube-root-ca.crt.yaml", "namespaces/test-common/serviceaccounts/default.yaml", + "namespaces/test-common/secrets/common-secret1.yaml", } commonLogResources = []string{ @@ -48,6 +49,7 @@ "namespaces/test-c1/apps/replicasets/c1-busybox-*.yaml", "namespaces/test-c1/configmaps/kube-root-ca.crt.yaml", "namespaces/test-c1/serviceaccounts/default.yaml", + "namespaces/test-c1/secrets/c1-secret1.yaml", } c1LogResources = []string{ @@ -73,6 +75,7 @@ "namespaces/test-c2/apps/replicasets/c2-busybox-*.yaml", "namespaces/test-c2/configmaps/kube-root-ca.crt.yaml", "namespaces/test-c2/serviceaccounts/default.yaml", + "namespaces/test-c2/secrets/c2-secret1.yaml", } c2LogResources = []string{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/e2e/go.mod new/kubectl-gather-0.12.0/e2e/go.mod --- old/kubectl-gather-0.11.0/e2e/go.mod 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/e2e/go.mod 2026-03-26 11:26:48.000000000 +0100 @@ -1,15 +1,22 @@ module github.com/nirs/kubectl-gather/e2e -go 1.24.0 +go 1.25.0 -toolchain go1.24.5 +toolchain go1.26.1 require ( github.com/nirs/kubectl-gather v0.8.0 - github.com/spf13/cobra v1.9.1 + github.com/spf13/cobra v1.10.2 ) require ( + github.com/pkg/errors v0.9.1 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect +) + +require golang.org/x/crypto v0.49.0 // indirect + +require ( github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect @@ -19,10 +26,8 @@ github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -33,26 +38,25 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/spf13/pflag v1.0.6 // indirect + github.com/spf13/pflag v1.0.9 // indirect github.com/x448/float16 v0.8.4 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - golang.org/x/net v0.38.0 // indirect + go.uber.org/zap v1.27.1 // indirect + golang.org/x/net v0.51.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.6.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/term v0.41.0 // indirect + golang.org/x/text v0.35.0 // indirect + golang.org/x/time v0.9.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.31.1 - k8s.io/apimachinery v0.31.1 // indirect - k8s.io/cli-runtime v0.31.1 // indirect - k8s.io/client-go v0.31.1 // indirect + k8s.io/api v0.33.10 + k8s.io/apimachinery v0.33.10 // indirect + k8s.io/cli-runtime v0.33.10 // indirect + k8s.io/client-go v0.33.10 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/randfill v1.0.0 // indirect diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/e2e/go.sum new/kubectl-gather-0.12.0/e2e/go.sum --- old/kubectl-gather-0.11.0/e2e/go.sum 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/e2e/go.sum 2026-03-26 11:26:48.000000000 +0100 @@ -23,18 +23,14 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -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/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 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/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -62,26 +58,27 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 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/onsi/ginkgo/v2 v2.20.0 h1:PE84V2mHqoT1sglvHc8ZdQtPcwmvvt29WLEEO3xmdZw= -github.com/onsi/ginkgo/v2 v2.20.0/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= -github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= -github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -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/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -90,21 +87,22 @@ go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= 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/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -114,28 +112,28 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= 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/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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= @@ -143,22 +141,20 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= -k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= -k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= -k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk= -k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U= -k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= -k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= +k8s.io/api v0.33.10 h1:8OHPP+ybXl9UKq1gpqvQPvfnwDtilBqfCY3+LQl4TK4= +k8s.io/api v0.33.10/go.mod h1:pVsaVIqAFpx/8NDmICPI0VOgnIc6oZJoGCpOgXrPGxY= +k8s.io/apimachinery v0.33.10 h1:XHdu/6MeJHzh7H2y31XVOL5GIYjP/tH5jcHofNYWaDc= +k8s.io/apimachinery v0.33.10/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/cli-runtime v0.33.10 h1:4V3wwsEhp+9edaXyO71PuQXpaIP5Xcrl3aLfk1nA9RE= +k8s.io/cli-runtime v0.33.10/go.mod h1:t3UsILXibAluoLrWc8PLr7trFoGsFI+Dq/3ZVAvgaPQ= +k8s.io/client-go v0.33.10 h1:Q3KkTB0zPnGlrs6mS2p6LaJ2zM5ehBVz0JO9ZKQied0= +k8s.io/client-go v0.33.10/go.mod h1:pEg4JnHgHR+m6tZ23SUWj+md2iPdszmVG8KD9/pHtec= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34 h1:/amS69DLm09mtbFtN3+LyygSFohnYGMseF8iv+2zulg= -k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34/go.mod h1:G0W3eI9gG219NHRq3h5uQaRBl4pj4ZpwzRP5ti8y770= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/e2e/output_test.go new/kubectl-gather-0.12.0/e2e/output_test.go --- old/kubectl-gather-0.11.0/e2e/output_test.go 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/e2e/output_test.go 2026-03-26 11:26:48.000000000 +0100 @@ -1,8 +1,11 @@ package e2e import ( + "encoding/base64" + "os" "os/exec" "path/filepath" + "strings" "testing" apps "k8s.io/api/apps/v1" @@ -118,3 +121,89 @@ } }) } + +func TestSecretSanitization(t *testing.T) { + outputDir := "out/test-secret-sanitization" + + salt := gather.RandomSalt() + saltB64 := base64.StdEncoding.EncodeToString(salt[:]) + + cmd := exec.Command( + kubectlGather, + "--contexts", strings.Join(clusters.Names, ","), + "--kubeconfig", clusters.Kubeconfig(), + "--salt", saltB64, + "--directory", outputDir, + ) + if err := commands.Run(cmd); err != nil { + t.Fatalf("kubectl-gather failed: %s", err) + } + + for _, cluster := range clusters.Names { + for _, secret := range gatheredSecrets(t, outputDir, cluster) { + salt, ok := secret.Annotations["kubectl-gather.nirs.github.com/sanitized"] + if !ok { + t.Fatalf("secret %s: sanitized annotation not found", secret.Name) + } + if salt != saltB64 { + t.Errorf("secret %s: expected salt %q, got %q", secret.Name, saltB64, salt) + } + } + } +} + +func TestSecretSanitizationRandomSalt(t *testing.T) { + outputDir := "out/test-secret-sanitization-random" + + cmd := exec.Command( + kubectlGather, + "--contexts", strings.Join(clusters.Names, ","), + "--kubeconfig", clusters.Kubeconfig(), + "--directory", outputDir, + ) + if err := commands.Run(cmd); err != nil { + t.Fatalf("kubectl-gather failed: %s", err) + } + + salts := map[string]struct{}{} + for _, cluster := range clusters.Names { + for _, secret := range gatheredSecrets(t, outputDir, cluster) { + salt, ok := secret.Annotations["kubectl-gather.nirs.github.com/sanitized"] + if !ok { + t.Fatalf("secret %s: sanitized annotation not found", secret.Name) + } + salts[salt] = struct{}{} + } + } + if len(salts) != 1 { + t.Fatalf("secrets hashed with multiple salts: %v", salts) + } +} + +// Test helpers + +// gatheredSecrets returns all secrets gathered for a cluster. +func gatheredSecrets(t *testing.T, outputDir, cluster string) []core.Secret { + t.Helper() + pattern := filepath.Join(outputDir, cluster, "namespaces", "*", "secrets", "*.yaml") + files, err := filepath.Glob(pattern) + if err != nil { + t.Fatal(err) + } + if len(files) == 0 { + t.Fatalf("no secrets found for cluster %s", cluster) + } + var secrets []core.Secret + for _, file := range files { + data, err := os.ReadFile(file) + if err != nil { + t.Fatal(err) + } + var secret core.Secret + if err := yaml.Unmarshal(data, &secret); err != nil { + t.Fatal(err) + } + secrets = append(secrets, secret) + } + return secrets +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/e2e/testdata/base/kustomization.yaml new/kubectl-gather-0.12.0/e2e/testdata/base/kustomization.yaml --- old/kubectl-gather-0.11.0/e2e/testdata/base/kustomization.yaml 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/e2e/testdata/base/kustomization.yaml 2026-03-26 11:26:48.000000000 +0100 @@ -4,3 +4,4 @@ - ns.yaml - pvc.yaml - deploy.yaml + - secret.yaml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/e2e/testdata/base/secret.yaml new/kubectl-gather-0.12.0/e2e/testdata/base/secret.yaml --- old/kubectl-gather-0.11.0/e2e/testdata/base/secret.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-gather-0.12.0/e2e/testdata/base/secret.yaml 2026-03-26 11:26:48.000000000 +0100 @@ -0,0 +1,9 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: secret1 +type: Opaque +data: + secret1: c2VjcmV0MQ== + secret2: c2VjcmV0Mg== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/go.mod new/kubectl-gather-0.12.0/go.mod --- old/kubectl-gather-0.11.0/go.mod 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/go.mod 2026-03-26 11:26:48.000000000 +0100 @@ -1,16 +1,19 @@ module github.com/nirs/kubectl-gather -go 1.24.0 +go 1.25.0 -toolchain go1.25.3 +toolchain go1.26.1 require ( - github.com/spf13/cobra v1.9.1 - go.uber.org/zap v1.27.0 - k8s.io/api v0.31.1 - k8s.io/apimachinery v0.31.1 - k8s.io/cli-runtime v0.31.1 - k8s.io/client-go v0.31.1 + github.com/aymanbagabas/go-udiff v0.4.1 + github.com/spf13/cobra v1.10.2 + go.uber.org/zap v1.27.1 + golang.org/x/crypto v0.49.0 + k8s.io/api v0.33.10 + k8s.io/apimachinery v0.33.10 + k8s.io/cli-runtime v0.33.10 + k8s.io/client-go v0.33.10 + sigs.k8s.io/yaml v1.4.0 ) require ( @@ -23,12 +26,9 @@ github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -38,23 +38,24 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/spf13/pflag v1.0.6 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/spf13/pflag v1.0.9 // indirect github.com/x448/float16 v0.8.4 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.38.0 // indirect + golang.org/x/net v0.51.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.6.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/term v0.41.0 // indirect + golang.org/x/text v0.35.0 // indirect + golang.org/x/time v0.9.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34 // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/go.sum new/kubectl-gather-0.12.0/go.sum --- old/kubectl-gather-0.11.0/go.sum 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/go.sum 2026-03-26 11:26:48.000000000 +0100 @@ -1,5 +1,7 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o= +github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -23,22 +25,16 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -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/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 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/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -64,26 +60,27 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 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/onsi/ginkgo/v2 v2.20.0 h1:PE84V2mHqoT1sglvHc8ZdQtPcwmvvt29WLEEO3xmdZw= -github.com/onsi/ginkgo/v2 v2.20.0/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= -github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= -github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -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/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -92,21 +89,22 @@ go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= 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/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -116,28 +114,28 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= 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/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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= @@ -145,28 +143,28 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.8/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= -k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= -k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= -k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk= -k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U= -k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= -k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= +k8s.io/api v0.33.10 h1:8OHPP+ybXl9UKq1gpqvQPvfnwDtilBqfCY3+LQl4TK4= +k8s.io/api v0.33.10/go.mod h1:pVsaVIqAFpx/8NDmICPI0VOgnIc6oZJoGCpOgXrPGxY= +k8s.io/apimachinery v0.33.10 h1:XHdu/6MeJHzh7H2y31XVOL5GIYjP/tH5jcHofNYWaDc= +k8s.io/apimachinery v0.33.10/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/cli-runtime v0.33.10 h1:4V3wwsEhp+9edaXyO71PuQXpaIP5Xcrl3aLfk1nA9RE= +k8s.io/cli-runtime v0.33.10/go.mod h1:t3UsILXibAluoLrWc8PLr7trFoGsFI+Dq/3ZVAvgaPQ= +k8s.io/client-go v0.33.10 h1:Q3KkTB0zPnGlrs6mS2p6LaJ2zM5ehBVz0JO9ZKQied0= +k8s.io/client-go v0.33.10/go.mod h1:pEg4JnHgHR+m6tZ23SUWj+md2iPdszmVG8KD9/pHtec= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34 h1:/amS69DLm09mtbFtN3+LyygSFohnYGMseF8iv+2zulg= -k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34/go.mod h1:G0W3eI9gG219NHRq3h5uQaRBl4pj4ZpwzRP5ti8y770= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -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/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/agent.go new/kubectl-gather-0.12.0/pkg/gather/agent.go --- old/kubectl-gather-0.11.0/pkg/gather/agent.go 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/pkg/gather/agent.go 2026-03-26 11:26:48.000000000 +0100 @@ -81,21 +81,20 @@ type agentWatcher struct { agent *AgentPod - ctx context.Context } -func (w *agentWatcher) Watch(opts metav1.ListOptions) (watch.Interface, error) { +func (w *agentWatcher) WatchWithContext(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { w.agent.Log.Debugf("Watching agent pod %q", w.agent) opts.FieldSelector = fields.OneTermEqualSelector(metav1.ObjectNameField, w.agent.Pod.Name).String() - return w.agent.Client.CoreV1().Pods(w.agent.Pod.Namespace).Watch(w.ctx, opts) + return w.agent.Client.CoreV1().Pods(w.agent.Pod.Namespace).Watch(ctx, opts) } func (a *AgentPod) WaitUntilRunning() error { ctx, cancel := context.WithTimeout(context.Background(), agentPodTimeoutSeconds*time.Second) defer cancel() - w := agentWatcher{agent: a, ctx: ctx} - watcher, err := toolswatch.NewRetryWatcher(a.Pod.ResourceVersion, &w) + w := agentWatcher{agent: a} + watcher, err := toolswatch.NewRetryWatcherWithContext(ctx, a.Pod.ResourceVersion, &w) if err != nil { return err } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/gather.go new/kubectl-gather-0.12.0/pkg/gather/gather.go --- old/kubectl-gather-0.11.0/pkg/gather/gather.go 2025-10-22 14:55:18.000000000 +0200 +++ new/kubectl-gather-0.12.0/pkg/gather/gather.go 2026-03-26 11:26:48.000000000 +0100 @@ -44,6 +44,7 @@ Namespaces []string Addons []string Cluster bool + Salt Salt Log *zap.SugaredLogger } @@ -103,6 +104,11 @@ return nil, err } + // Generate a random salt if the Salt option is not set. + if opts.Salt == (Salt{}) { + opts.Salt = RandomSalt() + } + g := &Gatherer{ config: config, httpClient: httpClient, @@ -450,6 +456,8 @@ } func (g *Gatherer) dumpResource(r *resourceInfo, item *unstructured.Unstructured) error { + g.sanitizeResource(item) + dst, err := g.createResource(r, item) if err != nil { return err diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/sanitize.go new/kubectl-gather-0.12.0/pkg/gather/sanitize.go --- old/kubectl-gather-0.11.0/pkg/gather/sanitize.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-gather-0.12.0/pkg/gather/sanitize.go 2026-03-26 11:26:48.000000000 +0100 @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: The kubectl-gather authors +// SPDX-License-Identifier: Apache-2.0 + +package gather + +import ( + "crypto/rand" + "crypto/sha256" + "encoding/base64" + + "golang.org/x/crypto/pbkdf2" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +// Salt is a 16-byte (128-bit) salt for PBKDF2 hashing. +type Salt [16]byte + +const ( + // OWASP recommended iterations for PBKDF2-HMAC-SHA256, needed to protect + // weak passwords in user applications. Could use fewer iterations if we + // know secrets are random keys (e.g. ceph keys). + // https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2 + pbkdf2Iterations = 600_000 + + // sanitizedAnnotation is added to sanitized secrets with the base64-encoded + // salt. Prevents double-hashing on repeated runs and makes the salt visible + // in the gathered output for verification. + sanitizedAnnotation = "kubectl-gather.nirs.github.com/sanitized" + + lastAppliedConfigAnnotation = "kubectl.kubernetes.io/last-applied-configuration" +) + +// RandomSalt generates a cryptographically secure random salt. +// It may panic if the system cannot provide random data. +func RandomSalt() Salt { + var salt Salt + // rand.Read panics if the underlying OS returns an error. + _, _ = rand.Read(salt[:]) + return salt +} + +// HashValue returns the PBKDF2-HMAC-SHA256 hash of data with the given salt. +func HashValue(data []byte, salt Salt) []byte { + return pbkdf2.Key(data, salt[:], pbkdf2Iterations, sha256.Size, sha256.New) +} + +// sanitizeResource modifies the resource in place, replacing sensitive data +// with deterministic hashes. Currently handles Secret resources, other resource +// types pass through unchanged. +func (g *Gatherer) sanitizeResource(item *unstructured.Unstructured) { + switch item.GetKind() { + case "Secret": + g.sanitizeSecret(item) + } +} + +// sanitizeSecret hashes secret data values and strips the +// last-applied-configuration annotation. +func (g *Gatherer) sanitizeSecret(item *unstructured.Unstructured) { + log := g.opts.Log + obj := item.Object + name := item.GetName() + salt := g.opts.Salt + saltB64 := base64.StdEncoding.EncodeToString(salt[:]) + + // If already sanitized, skip to avoid double hashing. + existing, found, _ := unstructured.NestedString(obj, "metadata", "annotations", sanitizedAnnotation) + if found { + if existing != saltB64 { + log.Warnf("Secret %q: already sanitized with different salt %q", name, existing) + } + return + } + + // Replace each secret value with a deterministic PBKDF2 hash. + data, found, err := unstructured.NestedStringMap(obj, "data") + if err != nil { + log.Warnf("Secret %q: failed to read data, removing data: %s", name, err) + unstructured.RemoveNestedField(obj, "data") + } + if found { + hashed := make(map[string]string, len(data)) + for key, value := range data { + raw, err := base64.StdEncoding.DecodeString(value) + if err != nil { + log.Warnf("Secret %q: dropping key %q: invalid base64: %s", name, key, err) + continue + } + h := HashValue(raw, salt) + hashed[key] = base64.StdEncoding.EncodeToString(h) + } + if err := unstructured.SetNestedStringMap(obj, hashed, "data"); err != nil { + log.Warnf("Secret %q: failed to set sanitized data: %s", name, err) + } + } + + // Remove last-applied-configuration which may contain plaintext secret data. + unstructured.RemoveNestedField(obj, "metadata", "annotations", lastAppliedConfigAnnotation) + + // Mark the secret as sanitized with the salt used. + if err := unstructured.SetNestedField(obj, saltB64, "metadata", "annotations", sanitizedAnnotation); err != nil { + log.Warnf("Secret %q: failed to set sanitized annotation: %s", name, err) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/sanitize_test.go new/kubectl-gather-0.12.0/pkg/gather/sanitize_test.go --- old/kubectl-gather-0.11.0/pkg/gather/sanitize_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-gather-0.12.0/pkg/gather/sanitize_test.go 2026-03-26 11:26:48.000000000 +0100 @@ -0,0 +1,149 @@ +// SPDX-FileCopyrightText: The kubectl-gather authors +// SPDX-License-Identifier: Apache-2.0 + +package gather + +import ( + "os" + "path/filepath" + "testing" + + "github.com/aymanbagabas/go-udiff" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/yaml" +) + +var testSalt = Salt{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + +func TestSanitizeResource(t *testing.T) { + tests := []string{ + "secret-with-data", + "secret-with-annotations", + "secret-empty", + "configmap", + } + + g := newTestGatherer(testSalt) + + for _, name := range tests { + t.Run(name, func(t *testing.T) { + obj := loadTestdata(t, name+".yaml") + g.sanitizeResource(obj) + actual := marshalYAML(t, obj.Object) + + golden := filepath.Join("testdata", "sanitize", name+".golden.yaml") + expected, err := os.ReadFile(golden) + if err != nil { + t.Fatal(err) + } + if actual != string(expected) { + t.Errorf("mismatch:\n%s", unifiedDiff(t, string(expected), actual)) + } + }) + } +} + +func TestSanitizeSecretIdempotent(t *testing.T) { + g := newTestGatherer(testSalt) + + obj := loadTestdata(t, "secret-with-data.yaml") + g.sanitizeResource(obj) + expected := marshalYAML(t, obj.Object) + + g.sanitizeResource(obj) + actual := marshalYAML(t, obj.Object) + + if expected != actual { + t.Fatalf("sanitizing twice changed the secret:\n%s", unifiedDiff(t, expected, actual)) + } +} + +func TestSanitizeSecretDifferentSalts(t *testing.T) { + g1 := newTestGatherer(RandomSalt()) + g2 := newTestGatherer(RandomSalt()) + + s1 := loadTestdata(t, "secret-with-data.yaml") + s2 := loadTestdata(t, "secret-with-data.yaml") + + g1.sanitizeResource(s1) + g2.sanitizeResource(s2) + + d1, found, err := unstructured.NestedStringMap(s1.Object, "data") + if err != nil || !found { + t.Fatalf("failed to get data from secret 1: err=%v, found=%v", err, found) + } + d2, found, err := unstructured.NestedStringMap(s2.Object, "data") + if err != nil || !found { + t.Fatalf("failed to get data from secret 2: err=%v, found=%v", err, found) + } + + if d1["secret1"] == d2["secret1"] { + t.Fatalf("different salts must produce different hashes, both got %q", d1["secret1"]) + } +} + +func TestRandomSalt(t *testing.T) { + values := map[Salt]struct{}{} + for range 1000 { + values[RandomSalt()] = struct{}{} + } + if len(values) != 1000 { + t.Fatalf("duplicate random salt: got %d unique out of 1000", len(values)) + } +} + +func BenchmarkHashValue(b *testing.B) { + data := []byte("random secure key, 32 bytes long") + for b.Loop() { + HashValue(data, testSalt) + } +} + +// Test helpers + +func newTestGatherer(salt Salt) *Gatherer { + return &Gatherer{ + opts: &Options{ + Salt: salt, + Log: zap.NewNop().Sugar(), + }, + } +} + +func loadTestdata(t *testing.T, name string) *unstructured.Unstructured { + t.Helper() + data, err := os.ReadFile(filepath.Join("testdata", "sanitize", name)) + if err != nil { + t.Fatal(err) + } + obj := &unstructured.Unstructured{} + if err := yaml.Unmarshal(data, &obj.Object); err != nil { + t.Fatal(err) + } + return obj +} + +func marshalYAML(t *testing.T, obj any) string { + t.Helper() + data, err := yaml.Marshal(obj) + if err != nil { + t.Fatal(err) + } + return string(data) +} + +func unifiedDiff(t *testing.T, expected, actual any) string { + t.Helper() + expectedString := marshal(t, expected) + actualString := marshal(t, actual) + return udiff.Unified("expected", "actual", expectedString, actualString) +} + +func marshal(t *testing.T, obj any) string { + t.Helper() + if s, ok := obj.(string); ok { + return s + } + return marshalYAML(t, obj) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/configmap.golden.yaml new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/configmap.golden.yaml --- old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/configmap.golden.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/configmap.golden.yaml 2026-03-26 11:26:48.000000000 +0100 @@ -0,0 +1,7 @@ +apiVersion: v1 +data: + config.yaml: 'key: value' +kind: ConfigMap +metadata: + name: test-configmap + namespace: default diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/configmap.yaml new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/configmap.yaml --- old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/configmap.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/configmap.yaml 2026-03-26 11:26:48.000000000 +0100 @@ -0,0 +1,7 @@ +apiVersion: v1 +data: + config.yaml: 'key: value' +kind: ConfigMap +metadata: + name: test-configmap + namespace: default diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-empty.golden.yaml new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-empty.golden.yaml --- old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-empty.golden.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-empty.golden.yaml 2026-03-26 11:26:48.000000000 +0100 @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + annotations: + kubectl-gather.nirs.github.com/sanitized: AQIDBAUGBwgJCgsMDQ4PEA== + name: test-secret + namespace: default +type: Opaque diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-empty.yaml new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-empty.yaml --- old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-empty.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-empty.yaml 2026-03-26 11:26:48.000000000 +0100 @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Secret +metadata: + name: test-secret + namespace: default +type: Opaque diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-annotations.golden.yaml new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-annotations.golden.yaml --- old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-annotations.golden.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-annotations.golden.yaml 2026-03-26 11:26:48.000000000 +0100 @@ -0,0 +1,11 @@ +apiVersion: v1 +data: + secret1: vfit80gH2RKCit7Rsa/lv6wGJa5cgA+4Q5WyyOQJIDI= +kind: Secret +metadata: + annotations: + app.kubernetes.io/name: my-app + kubectl-gather.nirs.github.com/sanitized: AQIDBAUGBwgJCgsMDQ4PEA== + name: test-secret + namespace: default +type: Opaque diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-annotations.yaml new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-annotations.yaml --- old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-annotations.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-annotations.yaml 2026-03-26 11:26:48.000000000 +0100 @@ -0,0 +1,11 @@ +apiVersion: v1 +data: + secret1: c2VjcmV0MQ== +kind: Secret +metadata: + annotations: + app.kubernetes.io/name: my-app + kubectl.kubernetes.io/last-applied-configuration: '{"apiVersion":"v1","data":{"secret1":"c2VjcmV0"},"kind":"Secret"}' + name: test-secret + namespace: default +type: Opaque diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-data.golden.yaml new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-data.golden.yaml --- old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-data.golden.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-data.golden.yaml 2026-03-26 11:26:48.000000000 +0100 @@ -0,0 +1,12 @@ +apiVersion: v1 +data: + duplicate: vfit80gH2RKCit7Rsa/lv6wGJa5cgA+4Q5WyyOQJIDI= + secret1: vfit80gH2RKCit7Rsa/lv6wGJa5cgA+4Q5WyyOQJIDI= + secret2: eSWghhz+7r7TPKmMN9Gv50RVTXd/HpoEjwQXcBDFEZA= +kind: Secret +metadata: + annotations: + kubectl-gather.nirs.github.com/sanitized: AQIDBAUGBwgJCgsMDQ4PEA== + name: test-secret + namespace: default +type: Opaque diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-data.yaml new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-data.yaml --- old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-data.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-data.yaml 2026-03-26 11:26:48.000000000 +0100 @@ -0,0 +1,10 @@ +apiVersion: v1 +data: + duplicate: c2VjcmV0MQ== + secret1: c2VjcmV0MQ== + secret2: c2VjcmV0Mg== +kind: Secret +metadata: + name: test-secret + namespace: default +type: Opaque ++++++ kubectl-gather.obsinfo ++++++ --- /var/tmp/diff_new_pack.fjC22s/_old 2026-03-27 06:42:35.876864195 +0100 +++ /var/tmp/diff_new_pack.fjC22s/_new 2026-03-27 06:42:35.876864195 +0100 @@ -1,5 +1,5 @@ name: kubectl-gather -version: 0.11.0 -mtime: 1761137718 -commit: c42a9ee16b240236e0cb67809e810e028176d0ad +version: 0.12.0 +mtime: 1774520808 +commit: a0e7607071c92e7d7c8b1902eafb0dcd39f392dc ++++++ vendor.tar.gz ++++++ ++++ 266676 lines of diff (skipped)
