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-10-15 12:21:41
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/kubectl-gather (Old)
and /work/SRC/openSUSE:Factory/.kubectl-gather.new.3698736 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kubectl-gather"
Thu Oct 15 12:21:41 2026 rev:9 rq:1384909 version:0.15.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/kubectl-gather/kubectl-gather.changes
2026-07-31 16:08:40.239274641 +0200
+++
/work/SRC/openSUSE:Factory/.kubectl-gather.new.3698736/kubectl-gather.changes
2026-10-15 12:21:44.090071200 +0200
@@ -1,0 +2,19 @@
+Thu Oct 15 04:55:58 UTC 2026 - Johannes Kastl
<[email protected]>
+
+- Update to version 0.15.0:
+ * build: drop version from release asset name
+ * docs: document --insecure-secrets flag
+ * test: verify InsecureSecrets skips sanitization
+ * e2e: test --insecure-secrets disables sanitization
+ * gather: add --insecure-secrets flag
+ * go: Update go to 1.26.0 and toolchain to 1.27.2
+ * e2e: make commands.Run() more useful
+ * Use --output-dir in gather script
+ * cmd: make output directory flags mutually exclusive
+ * cmd: replace directory flag with output-dir
+ * README:Harden Linux/macOS install snippet in README
+ * go: Update toolchain to go1.26.5
+ * lint: Add golines formatter and make fmt target
+ * Add spell checking using codespell
+
+-------------------------------------------------------------------
Old:
----
kubectl-gather-0.14.0.obscpio
New:
----
kubectl-gather-0.15.0.obscpio
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ kubectl-gather.spec ++++++
--- /var/tmp/diff_new_pack.oTlxMN/_old 2026-10-15 12:21:45.151115598 +0200
+++ /var/tmp/diff_new_pack.oTlxMN/_new 2026-10-15 12:21:45.154115723 +0200
@@ -17,14 +17,14 @@
Name: kubectl-gather
-Version: 0.14.0
+Version: 0.15.0
Release: 0
Summary: Kubectl plugin to gather data about your cluster
License: Apache-2.0
URL: https://github.com/nirs/kubectl-gather
Source: %{name}-%{version}.tar.gz
Source1: vendor.tar.gz
-BuildRequires: golang(API) >= 1.25
+BuildRequires: go1.27 >= 1.27.2
%description
This is a kubectl plugin for gathering data about your cluster that may help to
++++++ _service ++++++
--- /var/tmp/diff_new_pack.oTlxMN/_old 2026-10-15 12:21:45.194117397 +0200
+++ /var/tmp/diff_new_pack.oTlxMN/_new 2026-10-15 12:21:45.196117481 +0200
@@ -3,7 +3,7 @@
<param name="url">https://github.com/nirs/kubectl-gather.git</param>
<param name="scm">git</param>
<param name="exclude">.git</param>
- <param name="revision">refs/tags/v0.14.0</param>
+ <param name="revision">refs/tags/v0.15.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.oTlxMN/_old 2026-10-15 12:21:45.220118485 +0200
+++ /var/tmp/diff_new_pack.oTlxMN/_new 2026-10-15 12:21:45.224118652 +0200
@@ -3,6 +3,6 @@
<param
name="url">https://github.com/nirs/kubectl-gather</param>
<param
name="changesrevision">62c6089cb7cb3552a81b6f74b8394d4b1433fd70</param></service><service
name="tar_scm">
<param
name="url">https://github.com/nirs/kubectl-gather.git</param>
- <param
name="changesrevision">8e26ef87c72fc49d9120a1b1eacc88500928f877</param></service></servicedata>
+ <param
name="changesrevision">f1707eecd630c2c0653800f7ad6d91b31f965382</param></service></servicedata>
(No newline at EOF)
++++++ kubectl-gather-0.14.0.obscpio -> kubectl-gather-0.15.0.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/.golangci.yaml
new/kubectl-gather-0.15.0/.golangci.yaml
--- old/kubectl-gather-0.14.0/.golangci.yaml 2026-07-29 15:34:14.000000000
+0200
+++ new/kubectl-gather-0.15.0/.golangci.yaml 2026-10-14 14:18:42.000000000
+0200
@@ -27,9 +27,13 @@
enable:
- gofmt
- goimports
+ - golines
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
+ settings:
+ golines:
+ shorten-comments: true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/Makefile
new/kubectl-gather-0.15.0/Makefile
--- old/kubectl-gather-0.14.0/Makefile 2026-07-29 15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/Makefile 2026-10-14 14:18:42.000000000 +0200
@@ -37,6 +37,8 @@
all \
kubectl-gather \
lint \
+ fmt \
+ spell \
test \
unit-tests \
e2e-tests \
@@ -57,6 +59,12 @@
lint:
golangci-lint run ./...
+fmt:
+ golangci-lint fmt
+
+spell:
+ codespell -w --skip="go.sum,out,e2e/out"
+
test: unit-tests e2e-tests
unit-tests:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/README.md
new/kubectl-gather-0.15.0/README.md
--- old/kubectl-gather-0.14.0/README.md 2026-07-29 15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/README.md 2026-10-14 14:18:42.000000000 +0200
@@ -32,14 +32,17 @@
To install the latest version on Linux and macOS, run:
```
-tag="$(curl -fsSL
https://api.github.com/repos/nirs/kubectl-gather/releases/latest | jq -r
.tag_name)"
+bash -o pipefail <<'EOF'
os="$(uname | tr '[:upper:]' '[:lower:]')"
machine="$(uname -m)"
if [ "$machine" = "aarch64" ]; then machine="arm64"; fi
if [ "$machine" = "x86_64" ]; then machine="amd64"; fi
-curl -L -o kubectl-gather
https://github.com/nirs/kubectl-gather/releases/download/$tag/kubectl-gather-$tag-$os-$machine
+curl --fail --silent --show-error --location --output kubectl-gather \
+
https://github.com/nirs/kubectl-gather/releases/latest/download/kubectl-gather-$os-$machine
sudo install kubectl-gather /usr/local/bin
rm kubectl-gather
+EOF
+kubectl gather --version
```
## Shell completion
@@ -63,7 +66,7 @@
"hub":
```
-$ kubectl gather -d gather.one
+$ kubectl gather -o gather.one
2024-05-27T23:03:58.838+0300 INFO gather Using kubeconfig
"/home/nsoffer/.kube/config"
2024-05-27T23:03:58.840+0300 INFO gather Using current context "hub"
2024-05-27T23:03:58.841+0300 INFO gather Gathering from all namespaces
@@ -129,7 +132,7 @@
To gather data from all clusters run:
```
-$ kubectl gather --contexts hub,dr1,dr2 -d gather.all
+$ kubectl gather --contexts hub,dr1,dr2 -o gather.all
2024-05-27T23:16:16.459+0300 INFO gather Using kubeconfig
"/home/nsoffer/.kube/config"
2024-05-27T23:16:16.460+0300 INFO gather Gathering from all namespaces
2024-05-27T23:16:16.460+0300 INFO gather Gathering from cluster "hub"
@@ -212,7 +215,7 @@
`--kubeconfig`:
```
-$ kubectl gather --kubeconfig ocp/hub.yaml,ocp/c1.yaml,ocp/c2.yaml -d gather.dr
+$ kubectl gather --kubeconfig ocp/hub.yaml,ocp/c1.yaml,ocp/c2.yaml -o gather.dr
2026-05-06T00:15:32.100+0300 INFO gather Using cluster "hub" from
kubeconfig "ocp/hub.yaml"
2026-05-06T00:15:32.101+0300 INFO gather Using cluster "c1" from
kubeconfig "ocp/c1.yaml"
2026-05-06T00:15:32.102+0300 INFO gather Using cluster "c2" from
kubeconfig "ocp/c2.yaml"
@@ -251,7 +254,7 @@
file:
```
-$ kubectl gather --kubeconfig hub.yaml -d gather.one
+$ kubectl gather --kubeconfig hub.yaml -o gather.one
2026-05-06T00:15:32.100+0300 INFO gather Using cluster "hub" from
kubeconfig "hub.yaml"
2026-05-06T00:15:32.100+0300 INFO gather Gathering from all namespaces
2026-05-06T00:15:32.100+0300 INFO gather Gathering from cluster "hub"
@@ -275,7 +278,7 @@
To gather the "deployment-rbd" namespace from all clusters use:
```
-$ kubectl gather --contexts hub,dr1,dr2 -n deployment-rbd -d gather.before
+$ kubectl gather --contexts hub,dr1,dr2 -n deployment-rbd -o gather.before
2024-05-27T23:33:45.883+0300 INFO gather Using kubeconfig
"/home/nsoffer/.kube/config"
2024-05-27T23:33:45.888+0300 INFO gather Gathering from namespaces
[deployment-rbd]
2024-05-27T23:33:45.888+0300 INFO gather Gathering from cluster "hub"
@@ -329,7 +332,7 @@
failover:
```
-$ kubectl gather --contexts hub,dr1,dr2 -n deployment-rbd -d gather.after
+$ kubectl gather --contexts hub,dr1,dr2 -n deployment-rbd -o gather.after
2024-05-27T23:45:20.292+0300 INFO gather Using kubeconfig
"/home/nsoffer/.kube/config"
2024-05-27T23:45:20.297+0300 INFO gather Gathering from namespaces
[deployment-rbd]
2024-05-27T23:45:20.297+0300 INFO gather Gathering from cluster "hub"
@@ -402,7 +405,7 @@
To gather specific namespaces along with cluster scoped resources:
```
-$ kubectl-gather --contexts dr1,dr2 --namespaces=e2e-appset-deploy-cephfs
--cluster --directory gather.mixed
+$ kubectl-gather --contexts dr1,dr2 --namespaces=e2e-appset-deploy-cephfs
--cluster --output-dir gather.mixed
2025-08-01T14:25:15.029+0530 INFO gather Using kubeconfig
"/Users/pari/.kube/config"
2025-08-01T14:25:15.031+0530 INFO gather Gathering from namespaces
["e2e-appset-deploy-cephfs"]
2025-08-01T14:25:15.031+0530 INFO gather Gathering cluster scoped
resources
@@ -491,7 +494,7 @@
configured for disaster recovery. Gathering everything takes more than 6
minutes:
- $ kubectl gather --contexts kevin-rdr-hub,kevin-rdr-c1,kevin-rdr-c2
--remote --salt "$(openssl rand -base64 16)" --directory gather.remote
+ $ kubectl gather --contexts kevin-rdr-hub,kevin-rdr-c1,kevin-rdr-c2
--remote --salt "$(openssl rand -base64 16)" --output-dir 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"
@@ -567,7 +570,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 --salt "$(openssl rand -base64
16)" -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)" -o 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"
@@ -631,7 +634,7 @@
Gathering only resources:
```
-$ kubectl gather --contexts dr1,dr2 --addons= -d gather.resources
+$ kubectl gather --contexts dr1,dr2 --addons= -o gather.resources
2024-06-01T02:13:08.117+0300 INFO gather Using kubeconfig
"/home/nsoffer/.kube/config"
2024-06-01T02:13:08.118+0300 INFO gather Gathering from all namespaces
2024-06-01T02:13:08.119+0300 INFO gather Using addons []
@@ -645,7 +648,7 @@
Gathering resource and pod container logs:
```
-$ kubectl gather --contexts dr1,dr2 --addons logs -d gather.logs
+$ kubectl gather --contexts dr1,dr2 --addons logs -o gather.logs
2024-06-01T02:12:07.775+0300 INFO gather Using kubeconfig
"/home/nsoffer/.kube/config"
2024-06-01T02:12:07.776+0300 INFO gather Gathering from all namespaces
2024-06-01T02:12:07.777+0300 INFO gather Using addons ["logs"]
@@ -659,7 +662,7 @@
Gathering everything:
```
-$ kubectl gather --contexts dr1,dr2 -d gather.all
+$ kubectl gather --contexts dr1,dr2 -o gather.all
2024-06-01T02:11:46.490+0300 INFO gather Using kubeconfig
"/home/nsoffer/.kube/config"
2024-06-01T02:11:46.492+0300 INFO gather Gathering from all namespaces
2024-06-01T02:11:46.492+0300 INFO gather Using all addons
@@ -686,6 +689,10 @@
and the `kubectl.kubernetes.io/last-applied-configuration` annotation
is stripped to prevent plaintext leaks.
+Use `--insecure-secrets` to disable sanitization for debugging or in
+test environments where secrets are not sensitive.
+`--insecure-secrets` cannot be used with `--salt`.
+
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.
@@ -707,10 +714,10 @@
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.
+same salt. `--salt` cannot be used with `--insecure-secrets`.
```
-$ kubectl gather --contexts dr1,dr2,hub --remote --salt "$(openssl rand
-base64 16)" -d gather.remote
+$ kubectl gather --contexts dr1,dr2,hub --remote --salt "$(openssl rand
-base64 16)" -o gather.remote
```
Specifying a salt is also useful when comparing changes between two
@@ -718,9 +725,9 @@
```
salt=$(openssl rand -base64 16)
-kubectl gather --contexts hub,dr1,dr2 -d gather.a --salt "$salt"
+kubectl gather --contexts hub,dr1,dr2 -o gather.a --salt "$salt"
sleep 60
-kubectl gather --contexts hub,dr1,dr2 -d gather.b --salt "$salt"
+kubectl gather --contexts hub,dr1,dr2 -o gather.b --salt "$salt"
diff -ur gather.a gather.b
```
@@ -733,7 +740,7 @@
## Integrating with other programs
When running the *kubectl gather* from another program you may want to
-use JSON logs to extract certain fileds from the gather logs.
+use JSON logs to extract certain fields from the gather logs.
Example: extracting the "msg" field from gather JSON logs:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/cmd/local.go
new/kubectl-gather-0.15.0/cmd/local.go
--- old/kubectl-gather-0.14.0/cmd/local.go 2026-07-29 15:34:14.000000000
+0200
+++ new/kubectl-gather-0.15.0/cmd/local.go 2026-10-14 14:18:42.000000000
+0200
@@ -36,13 +36,14 @@
clusterDir := filepath.Join(directory, clusterConfig.Name)
options := gather.Options{
- Kubeconfig: clusterConfig.Kubeconfig,
- Context: clusterConfig.Context,
- Namespaces: namespaces,
- Addons: addons,
- Cluster: cluster,
- Salt: parsedSalt,
- Log: log.Named(clusterConfig.Name),
+ Kubeconfig: clusterConfig.Kubeconfig,
+ Context: clusterConfig.Context,
+ Namespaces: namespaces,
+ Addons: addons,
+ Cluster: cluster,
+ InsecureSecrets: insecureSecrets,
+ Salt: parsedSalt,
+ Log: log.Named(clusterConfig.Name),
}
wg.Add(1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/cmd/remote.go
new/kubectl-gather-0.15.0/cmd/remote.go
--- old/kubectl-gather-0.14.0/cmd/remote.go 2026-07-29 15:34:14.000000000
+0200
+++ new/kubectl-gather-0.15.0/cmd/remote.go 2026-10-14 14:18:42.000000000
+0200
@@ -99,9 +99,13 @@
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 insecureSecrets {
+ remoteArgs = append(remoteArgs, "--insecure-secrets")
+ } else {
+ // 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")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/cmd/root.go
new/kubectl-gather-0.15.0/cmd/root.go
--- old/kubectl-gather-0.14.0/cmd/root.go 2026-07-29 15:34:14.000000000
+0200
+++ new/kubectl-gather-0.15.0/cmd/root.go 2026-10-14 14:18:42.000000000
+0200
@@ -29,6 +29,7 @@
var addons []string
var cluster bool
var remote bool
+var insecureSecrets bool
var salt string
var parsedSalt gather.Salt
var verbose bool
@@ -45,27 +46,27 @@
# Gather data from all namespaces in clusters "dr1", "dr2" and "hub" and
store
# it in "gather.local/", using default kubeconfig (~/.kube/config).
- kubectl gather --contexts dr1,dr2,hub --directory gather.local
+ kubectl gather --contexts dr1,dr2,hub --output-dir gather.local
# Gather data from namespaces "my-ns" and "other-ns" in clusters "dr1",
"dr2",
# and "hub", and store it in "gather.ns/".
- kubectl gather --contexts dr1,dr2,hub --namespaces my-ns,other-ns
--directory gather.ns
+ kubectl gather --contexts dr1,dr2,hub --namespaces my-ns,other-ns
--output-dir gather.ns
# Gather data on the remote clusters "dr1", "dr2" and "hub" and download it
to
# "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
+ kubectl gather --contexts dr1,dr2,hub --remote --salt "$(openssl rand
-base64 16)" --output-dir gather.remote
# Enable only the "logs" addon, gathering all resources and pod logs. Use
# --addons= to disable all addons.
- kubectl gather --contexts dr1,dr2,hub --addons logs --directory
gather.resources+logs
+ kubectl gather --contexts dr1,dr2,hub --addons logs --output-dir
gather.resources+logs
# Gather both cluster and namespace resources from "my-ns" and "other-ns" in
clusters
# "dr1", "dr2", and "hub".
- kubectl gather --contexts dr1,dr2,hub --namespaces my-ns,other-ns --cluster
--directory gather.mixed
+ kubectl gather --contexts dr1,dr2,hub --namespaces my-ns,other-ns --cluster
--output-dir gather.mixed
# Gather only cluster resources from clusters "dr1", "dr2" and "hub".
- kubectl gather --contexts dr1,dr2,hub --namespace="" --cluster --directory
gather.cluster`
+ kubectl gather --contexts dr1,dr2,hub --namespace="" --cluster --output-dir
gather.cluster`
var rootCmd = &cobra.Command{
Use: "kubectl-gather",
@@ -85,8 +86,12 @@
}
func init() {
- rootCmd.Flags().StringVarP(&directory, "directory", "d", "",
- "directory for storing gathered data (default
\"gather.{timestamp}\")")
+ rootCmd.Flags().StringVarP(&directory, "output-dir", "o", "",
+ "output directory for storing gathered data (default
\"gather.{timestamp}\")")
+ // Keep the old flag for backward compatibility with existing scripts.
+ rootCmd.Flags().StringVarP(&directory, "directory", "d", "", "")
+ _ = rootCmd.Flags().MarkDeprecated("directory", "use -o, --output-dir")
+ rootCmd.MarkFlagsMutuallyExclusive("output-dir", "directory")
// Don't set default kubeconfig, so kubeconfig is empty unless the user
// specified the option. This is required to allow running remote
commands
@@ -106,11 +111,15 @@
"specified, gather all resources")
rootCmd.Flags().BoolVarP(&remote, "remote", "r", false,
"run on the remote clusters (requires the \"oc\" command)")
+ rootCmd.Flags().BoolVar(&insecureSecrets, "insecure-secrets", false,
+ "disable secret sanitization for debugging or testing")
rootCmd.Flags().StringVar(&salt, "salt", "",
"base64-encoded 16-byte salt for secret hashing (default:
randomly generated)")
+ rootCmd.MarkFlagsMutuallyExclusive("salt", "insecure-secrets")
rootCmd.Flags().BoolVarP(&verbose, "verbose", "v", false,
"be more verbose")
- rootCmd.Flags().StringVar(&logFormat, "log-format", "text", "Set the
logging format [text, json]")
+ rootCmd.Flags().StringVar(&logFormat, "log-format", "text",
+ "Set the logging format [text, json]")
rootCmd.Flags().BoolVar(&mustGatherVersion, "must-gather-version",
false,
"print must-gather version info and exit")
@@ -163,10 +172,14 @@
}
}
- if cmd.Flags().Changed("salt") {
- log.Infof("Using user-provided salt %q", salt)
+ if insecureSecrets {
+ log.Info("Using insecure secrets")
} else {
- log.Infof("Using generated salt %q", salt)
+ if cmd.Flags().Changed("salt") {
+ log.Infof("Using user-provided salt %q", salt)
+ } else {
+ log.Infof("Using generated salt %q", salt)
+ }
}
if namespaces == nil {
@@ -185,7 +198,7 @@
log.Infof("Using all addons")
}
- if !cmd.Flags().Changed("directory") {
+ if !cmd.Flags().Changed("output-dir") &&
!cmd.Flags().Changed("directory") {
log.Infof("Storing data in %q", directory)
}
@@ -201,16 +214,18 @@
return fmt.Errorf("--contexts cannot be used with multiple
--kubeconfig files")
}
- if salt != "" {
- var err error
- parsedSalt, err = validateSalt(salt)
- if err != nil {
- return err
+ if !insecureSecrets {
+ 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[:])
}
- } else {
- parsedSalt = gather.RandomSalt()
- // Keep the base64 salt string for logging and passing to
remote clusters.
- salt = base64.StdEncoding.EncodeToString(parsedSalt[:])
}
// --namespaces=""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/docs/internals.md
new/kubectl-gather-0.15.0/docs/internals.md
--- old/kubectl-gather-0.14.0/docs/internals.md 2026-07-29 15:34:14.000000000
+0200
+++ new/kubectl-gather-0.15.0/docs/internals.md 2026-10-14 14:18:42.000000000
+0200
@@ -5,7 +5,7 @@
kubectl-gather fetch and process data in a 3 steps pipeline:
```
-prepare --> gather --> inpsect
+prepare --> gather --> inspect
```
### Prepare step
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/e2e/commands/commands.go
new/kubectl-gather-0.15.0/e2e/commands/commands.go
--- old/kubectl-gather-0.14.0/e2e/commands/commands.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/e2e/commands/commands.go 2026-10-14
14:18:42.000000000 +0200
@@ -2,22 +2,45 @@
import (
"bufio"
+ "bytes"
"io"
"os/exec"
"go.uber.org/zap"
)
-// Run a command logging lines from stderr.
-func Run(cmd *exec.Cmd, log *zap.SugaredLogger) error {
+// FailedToStart is Result.ExitCode when the process failed to start.
+const FailedToStart = -2
+
+// Result holds the outcome of a command run by Run.
+// Stdout and Stderr are what the process wrote.
+// ExitCode == 0 and Err == nil means the process exited successfully.
+// ExitCode == -1 means the process was terminated by a signal.
+// ExitCode == FailedToStart means the process failed to start.
+type Result struct {
+ ExitCode int
+ Stdout []byte
+ Stderr []byte
+ Err error
+}
+
+// Run a command, logging lines from stderr and collecting output.
+func Run(cmd *exec.Cmd, log *zap.SugaredLogger) Result {
+ var stdout, stderr bytes.Buffer
+
log.Debugf("Running %v", cmd)
+
+ cmd.Stdout = &stdout
+
pipe, err := cmd.StderrPipe()
if err != nil {
- return err
+ return Result{Err: err, ExitCode: FailedToStart}
}
+
if err := cmd.Start(); err != nil {
- return err
+ return Result{Err: err, ExitCode: FailedToStart}
}
+
reader := bufio.NewReader(pipe)
for {
line, _, err := reader.ReadLine()
@@ -28,8 +51,21 @@
break
}
log.Debug(string(line))
+ stderr.Write(line)
+ stderr.WriteByte('\n')
+ }
+
+ err = cmd.Wait()
+ result := Result{
+ Stdout: stdout.Bytes(),
+ Stderr: stderr.Bytes(),
+ Err: err,
+ }
+ if ee, ok := err.(*exec.ExitError); ok {
+ result.ExitCode = ee.ExitCode()
}
- return cmd.Wait()
+
+ return result
}
func Stderr(err error) []byte {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/e2e/gather_test.go
new/kubectl-gather-0.15.0/e2e/gather_test.go
--- old/kubectl-gather-0.14.0/e2e/gather_test.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/e2e/gather_test.go 2026-10-14
14:18:42.000000000 +0200
@@ -102,10 +102,11 @@
cmd := exec.Command(
kubectlGather,
"--contexts", strings.Join(clusters.Names, ","),
+ // Use the legacy option to verify that it remains supported.
"--directory", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Errorf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Errorf("kubectl-gather failed: %s", r.Err)
}
validateGatherAll(t, validate.New(outputDir))
@@ -119,10 +120,10 @@
kubectlGather,
"--contexts", strings.Join(clusters.Names, ","),
"--cluster=true",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Errorf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Errorf("kubectl-gather failed: %s", r.Err)
}
validateGatherAll(t, validate.New(outputDir))
@@ -140,10 +141,10 @@
kubectlGather,
"--contexts", strings.Join(clusters.Names, ","),
"--remote",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Fatalf("kubectl-gather --remote failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Fatalf("kubectl-gather --remote failed: %s", r.Err)
}
dataRoot := findDataRoot(t, filepath.Join(outputDir, clusters.C1))
@@ -161,10 +162,10 @@
cmd := exec.Command(
kubectlGather,
"--kubeconfig", strings.Join(kubeconfigs, ","),
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Errorf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Errorf("kubectl-gather failed: %s", r.Err)
}
validateGatherAll(t, validate.New(outputDir))
@@ -184,10 +185,10 @@
kubectlGather,
"--kubeconfig", strings.Join(kubeconfigs, ","),
"--remote",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Fatalf("kubectl-gather --remote failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Fatalf("kubectl-gather --remote failed: %s", r.Err)
}
dataRoot := findDataRoot(t, filepath.Join(outputDir, clusters.C1))
@@ -204,10 +205,10 @@
kubectlGather,
"--contexts", strings.Join(clusters.Names, ","),
"--cluster=false",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Errorf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Errorf("kubectl-gather failed: %s", r.Err)
}
v := validate.New(outputDir)
@@ -254,9 +255,9 @@
kubectlGather,
"--contexts", strings.Join(clusters.Names, ","),
"--namespaces=", "",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err == nil {
+ if r := commands.Run(cmd, t.Log); r.Err == nil {
t.Errorf("kubectl-gather should fail, but it succeeded")
}
@@ -272,9 +273,9 @@
"--contexts", strings.Join(clusters.Names, ","),
"--namespaces=", "",
"--cluster=false",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err == nil {
+ if r := commands.Run(cmd, t.Log); r.Err == nil {
t.Errorf("kubectl-gather should fail, but it succeeded")
}
@@ -290,10 +291,10 @@
"--contexts", strings.Join(clusters.Names, ","),
"--namespaces=", "",
"--cluster=true",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Errorf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Errorf("kubectl-gather failed: %s", r.Err)
}
v := validate.New(outputDir)
@@ -340,10 +341,10 @@
kubectlGather,
"--contexts", strings.Join(clusters.Names, ","),
"--namespaces", "test-common,test-c1",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Errorf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Errorf("kubectl-gather failed: %s", r.Err)
}
validateSpecificNamespaces(t, validate.New(outputDir))
@@ -358,10 +359,10 @@
"--contexts", strings.Join(clusters.Names, ","),
"--namespaces", "test-common,test-c1",
"--cluster=false",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Errorf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Errorf("kubectl-gather failed: %s", r.Err)
}
validateSpecificNamespaces(t, validate.New(outputDir))
@@ -376,10 +377,10 @@
"--contexts", strings.Join(clusters.Names, ","),
"--namespaces", "test-common,test-c1",
"--cluster=true",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Errorf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Errorf("kubectl-gather failed: %s", r.Err)
}
v := validate.New(outputDir)
@@ -421,10 +422,10 @@
"--contexts", strings.Join(clusters.Names, ","),
"--namespaces", "test-common,test-c1,test-c2",
"--addons", "logs",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Errorf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Errorf("kubectl-gather failed: %s", r.Err)
}
v := validate.New(outputDir)
@@ -467,10 +468,10 @@
"--contexts", strings.Join(clusters.Names, ","),
"--namespaces", "test-common,test-c1,test-c2",
"--addons", "pvcs",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Errorf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Errorf("kubectl-gather failed: %s", r.Err)
}
v := validate.New(outputDir)
@@ -510,10 +511,10 @@
"--contexts", strings.Join(clusters.Names, ","),
"--namespaces", "test-common,test-c1,test-c2",
"--addons=",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Errorf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Errorf("kubectl-gather failed: %s", r.Err)
}
v := validate.New(outputDir)
@@ -558,11 +559,11 @@
cmd := exec.Command(
kubectlGather,
"--contexts", strings.Join(clusters.Names, ","),
- "--directory", outputDir,
+ "--output-dir", outputDir,
"--log-format", "json",
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Errorf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Errorf("kubectl-gather failed: %s", r.Err)
}
validate.JSONLog(t, logPath)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/e2e/output_test.go
new/kubectl-gather-0.15.0/e2e/output_test.go
--- old/kubectl-gather-0.14.0/e2e/output_test.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/e2e/output_test.go 2026-10-14
14:18:42.000000000 +0200
@@ -28,10 +28,10 @@
cmd := exec.Command(
kubectlGather,
"--contexts", clusters.C1,
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Fatal(err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Fatal(r.Err)
}
reader := gather.NewOutputReader(filepath.Join(outputDir,
clusters.C1))
@@ -51,10 +51,10 @@
kubectlGather,
"--contexts", clusters.C1,
"--remote",
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Fatal(err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Fatal(r.Err)
}
dataRoot := findDataRoot(t, filepath.Join(outputDir,
clusters.C1))
@@ -173,10 +173,10 @@
kubectlGather,
"--contexts", strings.Join(clusters.Names, ","),
"--salt", saltB64,
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Fatalf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Fatalf("kubectl-gather failed: %s", r.Err)
}
for _, cluster := range clusters.Names {
@@ -199,10 +199,10 @@
cmd := exec.Command(
kubectlGather,
"--contexts", strings.Join(clusters.Names, ","),
- "--directory", outputDir,
+ "--output-dir", outputDir,
)
- if err := commands.Run(cmd, t.Log); err != nil {
- t.Fatalf("kubectl-gather failed: %s", err)
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Fatalf("kubectl-gather failed: %s", r.Err)
}
salts := map[string]struct{}{}
@@ -220,6 +220,35 @@
}
}
+func TestSecretSanitizationDisabled(dt *testing.T) {
+ t := test.WithLog(dt)
+ outputDir := "out/test-secret-sanitization-disabled"
+
+ cmd := exec.Command(
+ kubectlGather,
+ "--contexts", strings.Join(clusters.Names, ","),
+ "--insecure-secrets",
+ "--output-dir", outputDir,
+ )
+ if r := commands.Run(cmd, t.Log); r.Err != nil {
+ t.Fatalf("kubectl-gather failed: %s", r.Err)
+ }
+
+ for _, cluster := range clusters.Names {
+ for _, secret := range gatheredSecrets(t, outputDir, cluster) {
+ _, ok :=
secret.Annotations["kubectl-gather.nirs.github.com/sanitized"]
+ if ok {
+ t.Errorf("secret %s: should not be sanitized
when --insecure-secrets is set", secret.Name)
+ }
+ for key, value := range secret.Data {
+ if len(value) == 0 {
+ t.Errorf("secret %s: key %q has empty
data", secret.Name, key)
+ }
+ }
+ }
+ }
+}
+
// Test helpers
// gatheredSecrets returns all secrets gathered for a cluster.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/gather
new/kubectl-gather-0.15.0/gather
--- old/kubectl-gather-0.14.0/gather 2026-07-29 15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/gather 2026-10-14 14:18:42.000000000 +0200
@@ -9,4 +9,4 @@
kubectl-gather --must-gather-version > "$base/version"
-kubectl-gather --directory "$base" "$@"
+kubectl-gather --output-dir "$base" "$@"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/go.mod
new/kubectl-gather-0.15.0/go.mod
--- old/kubectl-gather-0.14.0/go.mod 2026-07-29 15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/go.mod 2026-10-14 14:18:42.000000000 +0200
@@ -1,8 +1,8 @@
module github.com/nirs/kubectl-gather
-go 1.25.0
+go 1.26.0
-toolchain go1.26.2
+toolchain go1.27.2
require (
github.com/aymanbagabas/go-udiff v0.4.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/pkg/gather/addons.go
new/kubectl-gather-0.15.0/pkg/gather/addons.go
--- old/kubectl-gather-0.14.0/pkg/gather/addons.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/pkg/gather/addons.go 2026-10-14
14:18:42.000000000 +0200
@@ -69,7 +69,12 @@
}
for _, resource := range addonInfo.Resources {
if other, exists := registry[resource]; exists {
- return nil, fmt.Errorf("addon %q:
resource %q already registered by addon %q", name, resource, other.Name())
+ return nil, fmt.Errorf(
+ "addon %q: resource %q already
registered by addon %q",
+ name,
+ resource,
+ other.Name(),
+ )
}
registry[resource] = addon
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/pkg/gather/agent.go
new/kubectl-gather-0.15.0/pkg/gather/agent.go
--- old/kubectl-gather-0.14.0/pkg/gather/agent.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/pkg/gather/agent.go 2026-10-14
14:18:42.000000000 +0200
@@ -83,9 +83,13 @@
agent *AgentPod
}
-func (w *agentWatcher) WatchWithContext(ctx context.Context, 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()
+ opts.FieldSelector =
fields.OneTermEqualSelector(metav1.ObjectNameField, w.agent.Pod.Name).
+ String()
return w.agent.Client.CoreV1().Pods(w.agent.Pod.Namespace).Watch(ctx,
opts)
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/pkg/gather/commands.go
new/kubectl-gather-0.15.0/pkg/gather/commands.go
--- old/kubectl-gather-0.14.0/pkg/gather/commands.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/pkg/gather/commands.go 2026-10-14
14:18:42.000000000 +0200
@@ -24,7 +24,12 @@
var specialCharacters *regexp.Regexp
-func NewRemoteCommand(pod *corev1.Pod, opts *Options, log *zap.SugaredLogger,
directory string) *RemoteCommand {
+func NewRemoteCommand(
+ pod *corev1.Pod,
+ opts *Options,
+ log *zap.SugaredLogger,
+ directory string,
+) *RemoteCommand {
return &RemoteCommand{pod: pod, opts: opts, log: log, directory:
directory}
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/pkg/gather/gather.go
new/kubectl-gather-0.15.0/pkg/gather/gather.go
--- old/kubectl-gather-0.14.0/pkg/gather/gather.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/pkg/gather/gather.go 2026-10-14
14:18:42.000000000 +0200
@@ -52,8 +52,12 @@
Namespaces []string
Addons []string
Cluster bool
- Salt Salt
- Log *zap.SugaredLogger
+ // InsecureSecrets leaves Secret data unsanitized in the gathered
+ // output. By default (false), secret sanitization is always enabled
+ // regardless of Addons.
+ InsecureSecrets bool
+ Salt Salt
+ Log *zap.SugaredLogger
}
type Addon interface {
@@ -118,7 +122,7 @@
}
// Generate a random salt if the Salt option is not set.
- if opts.Salt == (Salt{}) {
+ if !opts.InsecureSecrets && opts.Salt == (Salt{}) {
opts.Salt = RandomSalt()
}
@@ -306,7 +310,11 @@
}
}
- g.log.Debugf("Listed %d api resources in %.3f seconds", len(resources),
time.Since(start).Seconds())
+ g.log.Debugf(
+ "Listed %d api resources in %.3f seconds",
+ len(resources),
+ time.Since(start).Seconds(),
+ )
return resources, nil
}
@@ -449,7 +457,11 @@
g.log.Debugf("Gathered %d %q in %.3f seconds", count, r.Name(),
time.Since(start).Seconds())
}
-func (g *Gatherer) listResources(r *resourceInfo, namespace string, opts
metav1.ListOptions) (*unstructured.UnstructuredList, error) {
+func (g *Gatherer) listResources(
+ r *resourceInfo,
+ namespace string,
+ opts metav1.ListOptions,
+) (*unstructured.UnstructuredList, error) {
start := time.Now()
ctx := context.TODO()
@@ -469,7 +481,12 @@
return nil, err
}
- g.log.Debugf("Listed %d %q in %.3f seconds", len(list.Items), r.Name(),
time.Since(start).Seconds())
+ g.log.Debugf(
+ "Listed %d %q in %.3f seconds",
+ len(list.Items),
+ r.Name(),
+ time.Since(start).Seconds(),
+ )
return list, nil
}
@@ -498,7 +515,10 @@
g.log.Debugf("Gathered %q in %.3f seconds", key,
time.Since(start).Seconds())
}
-func (g *Gatherer) getResource(r *resourceInfo, name types.NamespacedName)
(*unstructured.Unstructured, error) {
+func (g *Gatherer) getResource(
+ r *resourceInfo,
+ name types.NamespacedName,
+) (*unstructured.Unstructured, error) {
ctx := context.TODO()
var opts metav1.GetOptions
@@ -530,7 +550,10 @@
return writer.Flush()
}
-func (g *Gatherer) createResource(r *resourceInfo, item
*unstructured.Unstructured) (io.WriteCloser, error) {
+func (g *Gatherer) createResource(
+ r *resourceInfo,
+ item *unstructured.Unstructured,
+) (io.WriteCloser, error) {
if r.Namespaced {
return g.output.CreateNamespacedResource(item.GetNamespace(),
r.Name(), item.GetName())
} else {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/pkg/gather/logs.go
new/kubectl-gather-0.15.0/pkg/gather/logs.go
--- old/kubectl-gather-0.14.0/pkg/gather/logs.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/pkg/gather/logs.go 2026-10-14
14:18:42.000000000 +0200
@@ -183,7 +183,12 @@
//
// See also
https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/kubelet_pods.go#L1453
func containerHasPreviousLog(status map[string]interface{}) bool {
- containerID, found, err := unstructured.NestedString(status,
"lastState", "terminated", "containerID")
+ containerID, found, err := unstructured.NestedString(
+ status,
+ "lastState",
+ "terminated",
+ "containerID",
+ )
if err != nil || !found {
return false
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/pkg/gather/output.go
new/kubectl-gather-0.15.0/pkg/gather/output.go
--- old/kubectl-gather-0.14.0/pkg/gather/output.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/pkg/gather/output.go 2026-10-14
14:18:42.000000000 +0200
@@ -21,7 +21,12 @@
base string
}
-func (o *OutputDirectory) CreateContainerLog(namespace string, pod string,
container string, name string) (io.WriteCloser, error) {
+func (o *OutputDirectory) CreateContainerLog(
+ namespace string,
+ pod string,
+ container string,
+ name string,
+) (io.WriteCloser, error) {
dir, err := createDirectory(o.base, namespacesDir, namespace, "pods",
pod, container)
if err != nil {
return nil, err
@@ -29,7 +34,11 @@
return createFile(dir, name+logSuffix)
}
-func (o *OutputDirectory) CreateNamespacedResource(namespace string, resource
string, name string) (io.WriteCloser, error) {
+func (o *OutputDirectory) CreateNamespacedResource(
+ namespace string,
+ resource string,
+ name string,
+) (io.WriteCloser, error) {
dir, err := createDirectory(o.base, namespacesDir, namespace, resource)
if err != nil {
return nil, err
@@ -37,7 +46,10 @@
return createFile(dir, name+resourceSuffix)
}
-func (o *OutputDirectory) CreateClusterResource(resource string, name string)
(io.WriteCloser, error) {
+func (o *OutputDirectory) CreateClusterResource(
+ resource string,
+ name string,
+) (io.WriteCloser, error) {
dir, err := createDirectory(o.base, clusterDir, resource)
if err != nil {
return nil, err
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/pkg/gather/output_reader.go
new/kubectl-gather-0.15.0/pkg/gather/output_reader.go
--- old/kubectl-gather-0.14.0/pkg/gather/output_reader.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/pkg/gather/output_reader.go 2026-10-14
14:18:42.000000000 +0200
@@ -55,7 +55,13 @@
if namespace == "" {
resourcePath = filepath.Join(r.base, clusterDir, resource,
name+resourceSuffix)
} else {
- resourcePath = filepath.Join(r.base, namespacesDir, namespace,
resource, name+resourceSuffix)
+ resourcePath = filepath.Join(
+ r.base,
+ namespacesDir,
+ namespace,
+ resource,
+ name+resourceSuffix,
+ )
}
return os.ReadFile(resourcePath)
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/pkg/gather/rook.go
new/kubectl-gather-0.15.0/pkg/gather/rook.go
--- old/kubectl-gather-0.14.0/pkg/gather/rook.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/pkg/gather/rook.go 2026-10-14
14:18:42.000000000 +0200
@@ -108,7 +108,12 @@
}
func (a *RookAddon) logCollectorEnabled(cephcluster
*unstructured.Unstructured) bool {
- enabled, found, err := unstructured.NestedBool(cephcluster.Object,
"spec", "logCollector", "enabled")
+ enabled, found, err := unstructured.NestedBool(
+ cephcluster.Object,
+ "spec",
+ "logCollector",
+ "enabled",
+ )
if err != nil {
a.log.Warnf("Cannot get cephcluster .spec.logCollector.enabled:
%s", err)
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/pkg/gather/sanitize.go
new/kubectl-gather-0.15.0/pkg/gather/sanitize.go
--- old/kubectl-gather-0.14.0/pkg/gather/sanitize.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/pkg/gather/sanitize.go 2026-10-14
14:18:42.000000000 +0200
@@ -46,8 +46,11 @@
// sanitizeResource modifies the resource in place, replacing sensitive data
// with deterministic hashes. Currently handles Secret resources, other
resource
-// types pass through unchanged.
+// types pass through unchanged. Does nothing if InsecureSecrets is set.
func (g *Gatherer) sanitizeResource(item *unstructured.Unstructured) {
+ if g.opts.InsecureSecrets {
+ return
+ }
switch item.GetKind() {
case "Secret":
g.sanitizeSecret(item)
@@ -64,7 +67,12 @@
saltB64 := base64.StdEncoding.EncodeToString(salt[:])
// If already sanitized, skip to avoid double hashing.
- existing, found, _ := unstructured.NestedString(obj, "metadata",
"annotations", sanitizedAnnotation)
+ 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)
@@ -98,7 +106,13 @@
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 {
+ 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.14.0/pkg/gather/sanitize_test.go
new/kubectl-gather-0.15.0/pkg/gather/sanitize_test.go
--- old/kubectl-gather-0.14.0/pkg/gather/sanitize_test.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/pkg/gather/sanitize_test.go 2026-10-14
14:18:42.000000000 +0200
@@ -83,6 +83,47 @@
}
}
+func TestInsecureSecretsSkipsSanitization(t *testing.T) {
+ g := &Gatherer{
+ opts: &Options{
+ InsecureSecrets: true,
+ Salt: testSalt,
+ Log: zap.NewNop().Sugar(),
+ },
+ }
+
+ obj := loadTestdata(t, "secret-with-data.yaml")
+ before := marshalYAML(t, obj.Object)
+
+ g.sanitizeResource(obj)
+ after := marshalYAML(t, obj.Object)
+
+ if before != after {
+ t.Fatalf("InsecureSecrets should skip sanitization:\n%s",
unifiedDiff(t, before, after))
+ }
+}
+
+func TestSecureByDefault(t *testing.T) {
+ g := newTestGatherer(testSalt)
+
+ obj := loadTestdata(t, "secret-with-data.yaml")
+ before := marshalYAML(t, obj.Object)
+
+ g.sanitizeResource(obj)
+ after := marshalYAML(t, obj.Object)
+
+ if before == after {
+ t.Fatal("secret should be sanitized by default")
+ }
+
+ _, found, _ := unstructured.NestedString(
+ obj.Object, "metadata", "annotations",
"kubectl-gather.nirs.github.com/sanitized",
+ )
+ if !found {
+ t.Fatal("sanitized annotation not found")
+ }
+}
+
func TestRandomSalt(t *testing.T) {
values := map[Salt]struct{}{}
for range 1000 {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/kubectl-gather-0.14.0/pkg/kubeconfig/kubeconfig.go
new/kubectl-gather-0.15.0/pkg/kubeconfig/kubeconfig.go
--- old/kubectl-gather-0.14.0/pkg/kubeconfig/kubeconfig.go 2026-07-29
15:34:14.000000000 +0200
+++ new/kubectl-gather-0.15.0/pkg/kubeconfig/kubeconfig.go 2026-10-14
14:18:42.000000000 +0200
@@ -135,7 +135,12 @@
return nil, err
}
if prev, ok := hosts[restConfig.Host]; ok {
- return nil, fmt.Errorf("duplicate cluster %q from
contexts %q and %q", restConfig.Host, prev, context)
+ return nil, fmt.Errorf(
+ "duplicate cluster %q from contexts %q and %q",
+ restConfig.Host,
+ prev,
+ context,
+ )
}
hosts[restConfig.Host] = context
configs = append(configs, &Config{
@@ -190,7 +195,12 @@
name := nameFromFilename(path)
if prev, ok := names[name]; ok {
- return nil, fmt.Errorf("duplicate cluster name %q from
files %q and %q", name, prev, path)
+ return nil, fmt.Errorf(
+ "duplicate cluster name %q from files %q and
%q",
+ name,
+ prev,
+ path,
+ )
}
names[name] = path
@@ -211,7 +221,12 @@
}
if prev, ok := hosts[restConfig.Host]; ok {
- return nil, fmt.Errorf("duplicate cluster %q from files
%q and %q", restConfig.Host, prev, path)
+ return nil, fmt.Errorf(
+ "duplicate cluster %q from files %q and %q",
+ restConfig.Host,
+ prev,
+ path,
+ )
}
hosts[restConfig.Host] = path
++++++ kubectl-gather.obsinfo ++++++
--- /var/tmp/diff_new_pack.oTlxMN/_old 2026-10-15 12:21:45.379125138 +0200
+++ /var/tmp/diff_new_pack.oTlxMN/_new 2026-10-15 12:21:45.384125348 +0200
@@ -1,5 +1,5 @@
name: kubectl-gather
-version: 0.14.0
-mtime: 1785332054
-commit: 8e26ef87c72fc49d9120a1b1eacc88500928f877
+version: 0.15.0
+mtime: 1791980322
+commit: f1707eecd630c2c0653800f7ad6d91b31f965382
++++++ vendor.tar.gz ++++++