Compare commits

..
57 Commits
Author SHA1 Message Date
Kris ColemanandClaude Opus 4.7 2192aecf40 fix(analyze/secret): guard nil deref when spec omits fail: outcome (#2053) (#2054)
* fix(analyze/secret): guard nil deref when spec omits fail: outcome (#2053)

When a Preflight spec defines only warn: and/or pass: outcomes and the
target Secret is missing, analyzeSecret dereferenced a nil failOutcome and
panicked. Reproduction from replicatedhq/troubleshoot#2053:

  outcomes:
    - warn:
        when: "notFound"
        message: "secret missing (warn)"
    - pass:
        message: "secret found"

This change:
- Collects fail, warn, and warn-when-notFound outcomes up front.
- Routes a missing secret (or missing key) through a single resolver:
  prefer warn(when=notFound), fall back to fail, then any warn,
  then synthesize a benign warn result. Never panics.
- Adds table-driven tests covering the three new shapes.

Same defect shape as #263 (imagePullSecret). The sibling analyzers
configmap.go and image.go have the same pattern but are out of scope
for this PR.

Refs: replicatedhq/troubleshoot#2053

* refactor(analyze/secret): drop warn handling per analyzer contract

Per review feedback (banjoh): the secret analyzer only supports fail
(not found) and pass (found) outcomes — `warn:` and `when:` are not
part of the analyzer's contract (https://troubleshoot.sh/docs/analyze/secrets).

Drop the warn / notFoundWarn branches added in the previous commit and
collapse the nil-fail fallback onto IsFail with a default message. The
core fix — guarding the nil deref at the previous secret.go:72 — stays
in place.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(analyze/secret): mirror image_pull_secret pattern; return nil when spec has neither outcome

Per banjoh review: collect failOutcome and passOutcome with non-nil
checks; when the spec contains neither, return nil and let the framework
surface the missing-outcome error rather than fabricating a result.

Structure now mirrors pkg/analyze/image_pull_secret.go: default to
IsFail with fail-outcome message (if set), flip to IsPass with
pass-outcome message when the secret/key check succeeds, fill default
messages at the end only when none were configured. Analyze() now
forwards a nil result through cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(analyze/secret): assign outcome messages inside their own branch

Cursor bugbot caught: when the spec defined only a fail outcome and the
secret was found, the result showed IsPass=true with the fail outcome's
message. Pre-assigning the fail message at the top before flipping
IsPass meant the stale message leaked through whenever no pass outcome
was configured.

Move both message assignments into their respective branches so a pass
result never carries a fail message. Default messages still fill in
when no outcome is set on the active branch. Added a regression test
covering fail-only spec + secret found.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(analyze/secret): address Greptile P1s — combined outcome + empty configured messages

Two fixes flagged by review on #2054:

1. Combined pass outcome dropped: the outcome loop used 'else if' for pass, so a
   single outcome object containing both fail and pass silently dropped the pass.
   Capture fail and pass independently.

2. Empty configured messages overwritten: the default-message fallback fired
   whenever result.Message was empty, clobbering a configured outcome that
   intentionally has an empty message (e.g. URI-only). Track whether the matched
   branch had a configured outcome and only fall back to a default when none was
   supplied, preserving the configured message and URI verbatim.

Adds tests: combined fail+pass captured on both found/not-found paths, and
URI-only pass/fail outcomes preserved without default-message overwrite.

* fix(analyze/secret): drop fabricated default messages; error on missing outcome

Per maintainer review: analyzers do not fabricate default messages — an empty
outcome message is intentional (e.g. a URI-only outcome), so remove the default
message fallback and preserve the configured outcome verbatim. When a matched
branch has no configured outcome, the message stays empty.

Also address the missing-outcome case: when a spec defines neither a pass nor a
fail outcome, analyzeSecret returned (nil, nil), which the Analyze wrapper
swallowed into an empty result slice — the user saw neither a result nor a
config error. Return an explicit error so the framework surfaces the
misconfiguration.

Tests updated: the no-fail-outcome and only-fail-outcome cases now assert an
empty message rather than a fabricated one, and the neither-outcome case asserts
an error.

* fix(analyze/secret): default message only when no outcome is configured for the matched branch

Distinguish an absent matching outcome from an intentionally empty configured
message. A configured outcome with an empty message (e.g. a URI-only outcome) is
still preserved verbatim. But when the matched branch has no configured outcome at
all — a pass-only spec that took the fail path, or a fail-only spec that passed —
fall back to a default diagnostic instead of emitting an empty message.

Addresses the greptile P1 (endorsed by banjoh): dropping the default entirely
conflated the two cases and left users with a pass/fail result and no context.
Tests restore the default-message expectations for the no-configured-outcome
branches; URI-only (configured-empty) and neither-outcome (error) cases unchanged.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-08-24 12:16:33 -04:00
Dmitriy Ivolgin 5bf008f7d2 Use all tags when building with securebuild (#2119) 2026-08-21 11:27:04 -07:00
Xav PaiceandElasticClaw Factory 0c6fb2177b fix(cluster-resources): stop emitting duplicate unredacted YAML copy (#2117)
* fix(cluster-resources): stop emitting duplicate unredacted YAML copy

storeCustomResource was writing both a JSON and a YAML file for every
custom resource. The built-in redactors are authored for JSON, so the
YAML copy was left unredacted. JSON is valid YAML, so analyzers that
expect YAML can still parse the JSON file.

Also convert the built-in kurl installer redactors from YAML-specific
paths to scoped JSON regex redactors so they continue to apply after the
YAML copy is removed.

* fix(cluster-resources): add YAML symlinks and cover cluster-scoped kurl installer

- Add a .yaml symlink for every custom-resource JSON file so existing
  analyzers that look for the old YAML copy keep working. The symlink
  points at the JSON file, so redaction of the JSON also redacts the YAML
  view.
- Fix the built-in kurl installer redactor to match both the cluster-scoped
  file (installers.cluster.kurl.sh.json) and the namespaced file pattern
  (installers.cluster.kurl.sh/*.json). The previous selector only matched
  the namespaced form.

---------

Co-authored-by: ElasticClaw Factory <factory@replicated.com>
2026-08-21 09:29:20 +12:00
replicated-software-factory[bot]andElasticClaw Factory bea9c2220d [deps] Dependency update: troubleshoot (#2112)
deps: update Go dependencies

- github.com/miekg/dns v1.1.72 => v1.1.73
- github.com/stretchr/testify v1.11.1 => v1.12.1
- github.com/stretchr/objx v0.5.2 => v0.5.3 (indirect)

Co-authored-by: ElasticClaw Factory <factory@replicated.com>
2026-08-20 13:16:18 +12:00
Xav PaiceandElasticClaw Factory 207cb90b53 feat(supportbundle): support secret/... redactor URIs (#2111)
Co-authored-by: ElasticClaw Factory <factory@replicated.com>
2026-08-20 08:40:11 +12:00
Dmitriy Ivolgin 0bb4a21f22 Remove dockerfile based release (#2115) 2026-08-19 12:07:36 -07:00
Dmitriy Ivolgin fb82fef6e0 Add securebuild release jobs (#2114) 2026-08-19 09:38:49 -07:00
Dmitriy Ivolgin 3f3906fea1 Use latest go 1.26 in release (#2113) 2026-08-19 08:33:17 -07:00
Dmitriy Ivolgin 1f1a21b3cf Fix SBOM generation (#2109) 2026-08-17 12:48:56 -07:00
Dmitriy Ivolgin e828c21545 Generate and sign SBOM (#2108) 2026-08-17 11:26:40 -07:00
replicated-software-factory[bot]andlnx01 b158f7d79f [deps] Dependency update: troubleshoot (#2107)
chore(deps): update go dependencies for troubleshoot

- helm.sh/helm/v3 v3.21.3 => v3.21.4
- github.com/aws/aws-sdk-go-v2 v1.43.5 => v1.43.6
- github.com/aws/aws-sdk-go-v2/credentials v1.19.35 => v1.19.36
- github.com/aws/aws-sdk-go-v2/service/s3 v1.107.1 => v1.107.2
- golang.org/x/mod v0.39.0 => v0.40.0
- golang.org/x/tools v0.48.0 => v0.49.0 (via tidy)
- golang.org/x/crypto v0.54.0 => v0.55.0 (sdk example)
- golang.org/x/net v0.57.0 => v0.58.0 (sdk example)
- golang.org/x/text v0.40.0 => v0.41.0 (sdk example)

Co-authored-by: replicated-software-factory[bot] <282391587+replicated-software-factory[bot]@users.noreply.github.com>
2026-08-17 08:42:30 +12:00
replicated-software-factory[bot]andDependency Update Bot 8be420c42e [deps] Dependency update: troubleshoot (#2106)
chore(deps): update go dependencies

Co-authored-by: Dependency Update Bot <deps@replicated.com>
2026-08-14 09:46:11 +12:00
Xav Paice 0e2e0cd0ab fix(runpod): strip pod spec before saving collected output (#2105) 2026-08-12 09:13:40 +12:00
Xav Paice 651ec1bfdb ci: remove unmaintained upgrade-go workflow (#2104) 2026-08-11 06:56:48 +12:00
Xav Paice b9faedfdb0 fix(preflight): add tracing span and align error handling for host collector redaction (#2102)
* fix(preflight): add tracing span and align error handling for host collector redaction

Follow-up to #2101.

Adds an OpenTelemetry span around host collector redaction and returns the
unredacted collectResult on redaction failure, matching the behavior of
remote host collectors and in-cluster support-bundle collectors.

* fix(preflight): assign collected data before redaction so errors preserve output
2026-08-10 16:17:10 +01:00
3db6a99d3a feat(analyze): add countDistinct() aggregate to nodeResources (#2079)
Add a countDistinct(<labelKey>) aggregate to the nodeResources analyzer
"when" expression language. It counts distinct values of a node label
across the filtered nodes and returns an int the existing comparison
operators evaluate.

Enables the AIR-238 3-AZ preflight: warn when Keeper-eligible nodes do
not span 3 availability zones, e.g.
"countDistinct(topology.kubernetes.io/zone) < 3".

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Xav Paice <xavpaice@users.noreply.github.com>
2026-08-10 12:54:33 +12:00
Benjamin Reed 3c5bf74750 fix(preflight): redact local host preflight collector output (#2101)
CollectHostWithContext was the only collection path (in-cluster support
bundle, remote/SSH host collectors) that never ran collected data through
the redaction engine. A `run` collector's captured environment in
particular can carry credentials verbatim -- e.g. HTTPS_PROXY with
embedded Basic Auth -- straight into the bundle's <collectorName>-info.json
with no redaction applied.

Wire it through collect.RedactResult the same way CollectRemoteWithContext
and pkg/supportbundle/collect.go already do, so the built-in default
redactors apply to local host preflight output too.

Fixes #2100
2026-08-10 12:09:22 +12:00
95a4a81bce [deps] Dependency update: troubleshoot (#2096)
* chore(deps): update Go dependencies for 2026-08-05

Updates:

- github.com/ClickHouse/clickhouse-go/v2 v2.47.0 -> v2.48.0

- github.com/aws/aws-sdk-go-v2/service/s3 v1.106.3 -> v1.106.4

- go.opentelemetry.io/otel v1.44.0 -> v1.45.0

- go.opentelemetry.io/otel/sdk v1.44.0 -> v1.45.0

* chore(deps): update Go dependencies for 2026-08-07

---------

Co-authored-by: ElasticClaw <elasticclaw@replicated.com>
Co-authored-by: OpenClaw Bot <elasticclaw@openclaw.ai>
2026-08-09 11:28:54 +12:00
replicated-software-factory[bot]andReplicated Developer 5767e9b516 chore(deps): update Go dependencies for 2026-08-02 (#2093)
Updates top-level go.mod/go.sum with latest patch-level dependency
releases. Also refreshes examples/sdk/helm-template go.mod/go.sum
while preserving the local replace directive for troubleshoot.

Validated with:
- make build
- make test
- go build ./... in examples/sdk/helm-template

Co-authored-by: Replicated Developer <dev@replicated.com>
2026-08-03 18:28:31 +12:00
replicated-software-factory[bot]andElasticClaw 102843b433 chore(deps): update Go dependencies (#2091)
* chore(deps): update Go dependencies for top-level and helm-template example

* ci: re-trigger preflight e2e after timeout

* ci: add compile-preflight dependency for validate-supportbundle-e2e-go

---------

Co-authored-by: ElasticClaw <elasticclaw@replicated.com>
2026-07-31 16:43:46 +12:00
dependabot[bot]andlnx01 5f09d49e12 chore(deps): bump github.com/go-logr/logr from 1.4.3 to 1.4.4 in the security group across 1 directory (#2087)
chore(deps): bump github.com/go-logr/logr

Bumps the security group with 1 update in the / directory: [github.com/go-logr/logr](https://github.com/go-logr/logr).


Updates `github.com/go-logr/logr` from 1.4.3 to 1.4.4
- [Release notes](https://github.com/go-logr/logr/releases)
- [Changelog](https://github.com/go-logr/logr/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-logr/logr/compare/v1.4.3...v1.4.4)

---
updated-dependencies:
- dependency-name: github.com/go-logr/logr
  dependency-version: 1.4.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-25 15:15:11 +12:00
replicated-software-factory[bot]andTroubleshoot Dependency Bot 28d098815b build(deps): automated Go dependency update (#2084)
build(deps): bump github.com/longhorn/go-common-libs

Co-authored-by: Troubleshoot Dependency Bot <troubleshoot-bot@replicated.com>
2026-07-21 16:50:32 +12:00
dependabot[bot]andlnx01 55479d963a chore(deps): bump actions/setup-python from 6 to 7 (#2085)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6 to 7.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-21 16:50:05 +12:00
dependabot[bot]andlnx01 a75c227625 chore(deps): bump actions/setup-go from 6 to 7 (#2086)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 6 to 7.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-21 16:49:41 +12:00
Xav Paice 80845c8733 Xp/factory update (#2072)
* Revert "use CLI rather than action for factory workflow push (#2067)"

This reverts commit 65bd747c50.

* enable Github action for workflow updates

* update workflow
2026-07-21 11:34:18 +12:00
replicated-software-factory[bot]andElasticClaw Bot 46cb1b2206 chore: bump Go dependencies (#2083)
Grouped patch/minor updates from go get -u ./... and go mod tidy:
- github.com/aws/aws-sdk-go-v2/service/s3 v1.105.1 => v1.105.2
- github.com/aws/smithy-go v1.27.3 => v1.27.4
- github.com/docker/cli v29.6.1 => v29.6.2
- github.com/longhorn/go-common-libs v0.0.0-20260712050051 => v0.0.0-20260716070930
- github.com/mattn/go-isatty v0.0.22 => v0.0.23
- google.golang.org/api v0.288.0 => v0.289.0

examples/sdk/helm-template had no net changes (local replace preserved).

Co-authored-by: ElasticClaw Bot <elasticclaw@openclaw.ai>
2026-07-17 16:03:59 +12:00
replicated-software-factory[bot]andTroubleshoot Bot f81e4fae5a chore(deps): grouped Go dependency updates (#2082)
chore(deps): bump Go dependencies

Bumps indirect Google Cloud/GRPC dependencies to latest patch versions:
- google.golang.org/grpc v1.82.0 => v1.82.1
- google.golang.org/genproto v0.0.0-20260713224248-f5fc221cf8c4 => v0.0.0-20260715232425-e75dac1f907d
- google.golang.org/genproto/googleapis/api v0.0.0-20260713224248-f5fc221cf8c4 => v0.0.0-20260715232425-e75dac1f907d
- google.golang.org/genproto/googleapis/rpc v0.0.0-20260713224248-f5fc221cf8c4 => v0.0.0-20260715232425-e75dac1f907d
- cloud.google.com/go/logging v1.18.0 => v1.19.0

Validated with:
- make build
- make test

Co-authored-by: Troubleshoot Bot <troubleshoot-bot@replicated.com>
2026-07-16 15:20:45 +12:00
replicated-software-factory[bot]andElasticClaw b0c4a23a56 chore(deps): update Go dependencies for top-level and helm-template example (#2081)
Updates indirect deps pulled in via the top-level go.mod (notably Google
Cloud, AWS, Sentry, and genproto modules) and refreshes the corresponding
helm-template example sums. Verified with make build and make test.

Co-authored-by: ElasticClaw <elasticclaw@openclaw.ai>
2026-07-15 20:12:21 +12:00
dependabot[bot]andlnx01 bce58cfe56 chore(deps): bump the security group across 1 directory with 2 updates (#2078)
Bumps the security group with 2 updates in the / directory: [github.com/aws/aws-sdk-go-v2/credentials](https://github.com/aws/aws-sdk-go-v2) and [github.com/aws/aws-sdk-go-v2/service/s3](https://github.com/aws/aws-sdk-go-v2).


Updates `github.com/aws/aws-sdk-go-v2/credentials` from 1.19.28 to 1.19.29
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/credentials/v1.19.28...credentials/v1.19.29)

Updates `github.com/aws/aws-sdk-go-v2/service/s3` from 1.105.0 to 1.105.1
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.105.0...service/s3/v1.105.1)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/credentials
  dependency-version: 1.19.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/aws/aws-sdk-go-v2/service/s3
  dependency-version: 1.105.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-14 17:12:47 +12:00
dependabot[bot]andlnx01 10a2a82d32 chore(deps): bump actions/checkout from 4 to 7 (#2075)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-14 13:13:15 +12:00
replicated-software-factory[bot]andElasticClaw Bot 7fa4497b7f chore: update Go dependencies (#2074)
- github.com/longhorn/go-iscsi-helper: replaced by github.com/longhorn/go-common-libs
  and migrated pkg/longhorn/util/iscsi.go to the new namespace executor API
- helm.sh/helm/v3: v3.21.2 -> v3.21.3
- oras.land/oras-go/v2: v2.6.1 -> v2.6.2
- google.golang.org/api: v0.287.1 -> v0.288.0
- golang.org/x/tools: v0.47.0 -> v0.48.0
- github.com/GoogleCloudPlatform/opentelemetry-operations-go/*: v1.33.0/v0.57.0 -> v1.34.0/v0.58.0
- examples/sdk/helm-template: helm.sh/helm/v3 v3.21.2 -> v3.21.3

Fixes required by the update environment:
- Makefile: use $(shell go env GOPATH)/bin for controller-gen/client-gen so
  generate works when the tools are not on PATH
- pkg/collect/host_kernel_configs.go: only use /proc/config.gz when the
  requested kernel release matches the running kernel, making the collector
  robust to hosts that expose a generic /proc/config.gz

Co-authored-by: ElasticClaw Bot <elasticclaw@openclaw.ai>
2026-07-14 13:12:51 +12:00
Xav Paice b279b2bef9 chore: update Go to 1.26.5 (#2073) 2026-07-11 11:12:50 +12:00
replicated-software-factory[bot]andReplicated Dev 3c59305677 chore: update Go dependencies (#2070)
Updates:
- github.com/aws/aws-sdk-go-v2/credentials v1.19.27 => v1.19.28
- github.com/aws/aws-sdk-go-v2/config v1.32.28 => v1.32.29
- github.com/aws/aws-sdk-go-v2/service/signin v1.3.0 => v1.4.0
- golang.org/x/net v0.56.0 => v0.57.0
- examples/sdk/helm-template: various indirect dependency updates

Co-authored-by: Replicated Dev <dev@replicated.com>
2026-07-11 09:48:51 +12:00
Salah Al Saleh 2a87b280bf Add per-collector subcommands to the collect binary (#2071)
* Add per-collector subcommands to the collect binary

Add `collect http|postgres|mysql|mssql|redis` subcommands that run a single
collector and print its native result JSON to stdout, so a collector can run
inside a Pod via the troubleshoot image (e.g. as a runPod collector).

Each has its own flags mapping to the collector's spec fields and its own help.
The Kubernetes client (used only for TLS-from-Secret) is built to match the
production preflight/support-bundle path (QPS/Burst/UserAgent).

* Add clickhouse subcommand to the collect binary

Add `collect clickhouse`, matching the other per-collector subcommands, so the
clickhouse collector can run inside a Pod via the troubleshoot image (e.g. as a
runPod collector). Same flags as the other database collectors (--uri + --tls-*).

* Make global flags work on the collect subcommands

Register --debug and the Kubernetes flags (--kubeconfig, --context, etc.) as
persistent flags, and move the shared setup to PersistentPreRun/PersistentPostRun,
so the per-collector subcommands inherit the global flags and run the same
logger/profiling setup as `collect [url]`. Previously these were local flags with
setup in PreRun, so subcommands rejected --debug/--kubeconfig and ignored the setup.
2026-07-10 09:31:14 -07:00
replicated-software-factory[bot]andTroubleshoot Bot 5e6f3cdac1 chore: update Go dependencies (#2068)
Updated Go dependencies to latest patch/minor versions:
- cloud.google.com/go/auth v0.20.0 -> v0.21.0
- cloud.google.com/go/iam v1.5.3 -> v1.11.0
- cloud.google.com/go/monitoring v1.24.3 -> v1.29.0
- cloud.google.com/go/storage v1.61.3 -> v1.63.0
- github.com/andybalholm/brotli v1.2.1 -> v1.2.2
- github.com/aws/aws-sdk-go-v2/config v1.32.17 -> v1.32.28
- github.com/chai2010/gettext-go v1.0.2 -> v1.0.3
- github.com/cyphar/filepath-securejoin v0.6.1 -> v0.7.0
- github.com/docker/cli v29.5.3 -> v29.6.1
- github.com/fxamacker/cbor/v2 v2.9.0 -> v2.9.2
- github.com/go-viper/mapstructure/v2 v2.4.0 -> v2.5.0
- github.com/google/gnostic-models v0.7.0 -> v0.7.1
- github.com/googleapis/enterprise-certificate-proxy v0.3.16 -> v0.3.18
- github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72 -> v2.0.0-beta.73
- github.com/lufia/plan9stats v0.0.0-20260330125221 -> v0.0.0-20260627054121
- github.com/pelletier/go-toml/v2 v2.2.4 -> v2.4.3
- github.com/sagikazarmark/locafero v0.11.0 -> v0.12.0
- github.com/spiffe/go-spiffe/v2 v2.6.0 -> v2.8.1
- go.opentelemetry.io/contrib/detectors/gcp v1.42.0 -> v1.44.0
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 -> v0.69.0
- golang.org/x/crypto v0.53.0 -> v0.54.0
- golang.org/x/mod v0.37.0 -> v0.38.0
- golang.org/x/sync v0.21.0 -> v0.22.0
- golang.org/x/sys v0.46.0 -> v0.47.0
- golang.org/x/term v0.44.0 -> v0.45.0
- golang.org/x/text v0.39.0 -> v0.40.0
- google.golang.org/api v0.283.0 -> v0.287.1
- google.golang.org/genproto/googleapis/api v0.0.0-20260401024825 -> v0.0.0-20260706201446
- google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958 -> v0.0.0-20260706201446
- google.golang.org/grpc v1.81.1 -> v1.82.0
- k8s.io/kube-openapi v0.0.0-20260317180543 -> v0.0.0-20260706235625
- k8s.io/utils v0.0.0-20260210185600 -> v0.0.0-20260707023825
- sigs.k8s.io/structured-merge-diff/v6 v6.3.2 -> v6.4.2

Note: github.com/longhorn/go-iscsi-helper was kept at v0.0.0-20210330030558-49a327fb024e
due to breaking API changes in newer versions.

Build and tests pass successfully.

Co-authored-by: Troubleshoot Bot <troubleshoot-bot@replicated.com>
2026-07-09 09:36:56 +12:00
Xav Paice 65bd747c50 use CLI rather than action for factory workflow push (#2067)
* use CLI rather than action for factory workflow push

* update elasticclaw version

* replace inline elasticclaw token with env var
2026-07-09 07:45:29 +12:00
Xav Paice e0e45b0dc3 add factory workflow (#2066)
* add factory workflow

* add publish workflow for factory

* reduce duplication
2026-07-08 14:37:48 +12:00
dependabot[bot]andlnx01 eed103b6a5 chore(deps): bump the security group across 1 directory with 10 updates (#2065)
Bumps the security group with 10 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [github.com/ClickHouse/clickhouse-go/v2](https://github.com/ClickHouse/clickhouse-go) | `2.46.0` | `2.47.0` |
| [github.com/aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) | `1.42.0` | `1.42.1` |
| [github.com/aws/aws-sdk-go-v2/credentials](https://github.com/aws/aws-sdk-go-v2) | `1.19.24` | `1.19.27` |
| [github.com/aws/aws-sdk-go-v2/service/s3](https://github.com/aws/aws-sdk-go-v2) | `1.103.3` | `1.105.0` |
| [github.com/cilium/ebpf](https://github.com/cilium/ebpf) | `0.21.0` | `0.22.0` |
| [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) | `0.21.6` | `0.21.7` |
| [github.com/shirou/gopsutil/v4](https://github.com/shirou/gopsutil) | `4.26.5` | `4.26.6` |
| [github.com/vmware-tanzu/velero](https://github.com/vmware-tanzu/velero) | `1.18.1` | `1.18.2` |
| [golang.org/x/text](https://github.com/golang/text) | `0.38.0` | `0.39.0` |
| [helm.sh/helm/v3](https://github.com/helm/helm) | `3.21.1` | `3.21.2` |



Updates `github.com/ClickHouse/clickhouse-go/v2` from 2.46.0 to 2.47.0
- [Release notes](https://github.com/ClickHouse/clickhouse-go/releases)
- [Changelog](https://github.com/ClickHouse/clickhouse-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ClickHouse/clickhouse-go/compare/v2.46.0...v2.47.0)

Updates `github.com/aws/aws-sdk-go-v2` from 1.42.0 to 1.42.1
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.42.0...v1.42.1)

Updates `github.com/aws/aws-sdk-go-v2/credentials` from 1.19.24 to 1.19.27
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/credentials/v1.19.24...credentials/v1.19.27)

Updates `github.com/aws/aws-sdk-go-v2/service/s3` from 1.103.3 to 1.105.0
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.103.3...service/s3/v1.105.0)

Updates `github.com/cilium/ebpf` from 0.21.0 to 0.22.0
- [Release notes](https://github.com/cilium/ebpf/releases)
- [Commits](https://github.com/cilium/ebpf/compare/v0.21.0...v0.22.0)

Updates `github.com/google/go-containerregistry` from 0.21.6 to 0.21.7
- [Release notes](https://github.com/google/go-containerregistry/releases)
- [Commits](https://github.com/google/go-containerregistry/compare/v0.21.6...v0.21.7)

Updates `github.com/shirou/gopsutil/v4` from 4.26.5 to 4.26.6
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v4.26.5...v4.26.6)

Updates `github.com/vmware-tanzu/velero` from 1.18.1 to 1.18.2
- [Release notes](https://github.com/vmware-tanzu/velero/releases)
- [Changelog](https://github.com/velero-io/velero/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vmware-tanzu/velero/compare/v1.18.1...v1.18.2)

Updates `golang.org/x/text` from 0.38.0 to 0.39.0
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.38.0...v0.39.0)

Updates `helm.sh/helm/v3` from 3.21.1 to 3.21.2
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.21.1...v3.21.2)

---
updated-dependencies:
- dependency-name: github.com/ClickHouse/clickhouse-go/v2
  dependency-version: 2.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/aws/aws-sdk-go-v2
  dependency-version: 1.42.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/aws/aws-sdk-go-v2/credentials
  dependency-version: 1.19.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/aws/aws-sdk-go-v2/service/s3
  dependency-version: 1.105.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/cilium/ebpf
  dependency-version: 0.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/google/go-containerregistry
  dependency-version: 0.21.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/shirou/gopsutil/v4
  dependency-version: 4.26.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/vmware-tanzu/velero
  dependency-version: 1.18.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: golang.org/x/text
  dependency-version: 0.39.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: helm.sh/helm/v3
  dependency-version: 3.21.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-07 13:56:55 +12:00
dependabot[bot]andlnx01 d0c73d4723 chore(deps): bump actions/checkout from 6 to 7 (#2062)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 08:36:41 +12:00
dependabot[bot]andlnx01 19e7c9b9c8 chore(deps): bump helm.sh/helm/v3 from 3.21.0 to 3.21.2 in /examples/sdk/helm-template in the security group across 1 directory (#2059)
chore(deps): bump helm.sh/helm/v3

Bumps the security group with 1 update in the /examples/sdk/helm-template directory: [helm.sh/helm/v3](https://github.com/helm/helm).


Updates `helm.sh/helm/v3` from 3.21.0 to 3.21.2
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.21.0...v3.21.2)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  dependency-version: 3.21.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 08:36:12 +12:00
dependabot[bot]andlnx01 0edd588c3e chore(deps): bump github.com/containerd/containerd from 1.7.32 to 1.7.33 (#2061)
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.7.32 to 1.7.33.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.7.32...v1.7.33)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-version: 1.7.33
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-23 09:10:09 +12:00
dependabot[bot]andlnx01 78ac8f7dc9 chore(deps): bump the security group across 1 directory with 19 updates (#2060)
Bumps the security group with 13 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [github.com/aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) | `1.41.11` | `1.42.0` |
| [github.com/aws/aws-sdk-go-v2/credentials](https://github.com/aws/aws-sdk-go-v2) | `1.19.21` | `1.19.24` |
| [github.com/aws/aws-sdk-go-v2/service/s3](https://github.com/aws/aws-sdk-go-v2) | `1.103.1` | `1.103.3` |
| [golang.org/x/mod](https://github.com/golang/mod) | `0.36.0` | `0.37.0` |
| [golang.org/x/sync](https://github.com/golang/sync) | `0.20.0` | `0.21.0` |
| [k8s.io/api](https://github.com/kubernetes/api) | `0.36.1` | `0.36.2` |
| [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) | `0.36.1` | `0.36.2` |
| [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) | `0.36.1` | `0.36.2` |
| [oras.land/oras-go/v2](https://github.com/oras-project/oras-go) | `2.6.0` | `2.6.1` |
| [golang.org/x/net](https://github.com/golang/net) | `0.55.0` | `0.56.0` |
| [helm.sh/helm/v3](https://github.com/helm/helm) | `3.21.0` | `3.21.1` |
| [k8s.io/kubelet](https://github.com/kubernetes/kubelet) | `0.36.1` | `0.36.2` |
| [k8s.io/metrics](https://github.com/kubernetes/metrics) | `0.36.1` | `0.36.2` |



Updates `github.com/aws/aws-sdk-go-v2` from 1.41.11 to 1.42.0
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.41.11...v1.42.0)

Updates `github.com/aws/aws-sdk-go-v2/credentials` from 1.19.21 to 1.19.24
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/credentials/v1.19.21...credentials/v1.19.24)

Updates `github.com/aws/aws-sdk-go-v2/service/s3` from 1.103.1 to 1.103.3
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.103.1...service/s3/v1.103.3)

Updates `golang.org/x/mod` from 0.36.0 to 0.37.0
- [Commits](https://github.com/golang/mod/compare/v0.36.0...v0.37.0)

Updates `golang.org/x/sync` from 0.20.0 to 0.21.0
- [Commits](https://github.com/golang/sync/compare/v0.20.0...v0.21.0)

Updates `k8s.io/api` from 0.36.1 to 0.36.2
- [Commits](https://github.com/kubernetes/api/compare/v0.36.1...v0.36.2)

Updates `k8s.io/apiextensions-apiserver` from 0.36.1 to 0.36.2
- [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases)
- [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.36.1...v0.36.2)

Updates `k8s.io/apimachinery` from 0.36.1 to 0.36.2
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.36.1...v0.36.2)

Updates `k8s.io/apiserver` from 0.36.1 to 0.36.2
- [Commits](https://github.com/kubernetes/apiserver/compare/v0.36.1...v0.36.2)

Updates `k8s.io/cli-runtime` from 0.36.1 to 0.36.2
- [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.36.1...v0.36.2)

Updates `k8s.io/client-go` from 0.36.1 to 0.36.2
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.36.1...v0.36.2)

Updates `k8s.io/streaming` from 0.36.1 to 0.36.2
- [Commits](https://github.com/kubernetes/streaming/compare/v0.36.1...v0.36.2)

Updates `oras.land/oras-go/v2` from 2.6.0 to 2.6.1
- [Release notes](https://github.com/oras-project/oras-go/releases)
- [Commits](https://github.com/oras-project/oras-go/compare/v2.6.0...v2.6.1)

Updates `golang.org/x/net` from 0.55.0 to 0.56.0
- [Commits](https://github.com/golang/net/compare/v0.55.0...v0.56.0)

Updates `golang.org/x/sys` from 0.45.0 to 0.46.0
- [Commits](https://github.com/golang/sys/compare/v0.45.0...v0.46.0)

Updates `golang.org/x/text` from 0.37.0 to 0.38.0
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.37.0...v0.38.0)

Updates `helm.sh/helm/v3` from 3.21.0 to 3.21.1
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.21.0...v3.21.1)

Updates `k8s.io/kubelet` from 0.36.1 to 0.36.2
- [Commits](https://github.com/kubernetes/kubelet/compare/v0.36.1...v0.36.2)

Updates `k8s.io/metrics` from 0.36.1 to 0.36.2
- [Commits](https://github.com/kubernetes/metrics/compare/v0.36.1...v0.36.2)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2
  dependency-version: 1.42.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/aws/aws-sdk-go-v2/credentials
  dependency-version: 1.19.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/aws/aws-sdk-go-v2/service/s3
  dependency-version: 1.103.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: golang.org/x/mod
  dependency-version: 0.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/sync
  dependency-version: 0.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/api
  dependency-version: 0.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/apiextensions-apiserver
  dependency-version: 0.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/apimachinery
  dependency-version: 0.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/apiserver
  dependency-version: 0.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/cli-runtime
  dependency-version: 0.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/client-go
  dependency-version: 0.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/streaming
  dependency-version: 0.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: oras.land/oras-go/v2
  dependency-version: 2.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: golang.org/x/net
  dependency-version: 0.56.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/sys
  dependency-version: 0.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/text
  dependency-version: 0.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: helm.sh/helm/v3
  dependency-version: 3.21.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/kubelet
  dependency-version: 0.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/metrics
  dependency-version: 0.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 09:34:12 +12:00
Kris Coleman 22ab3cf551 feat(redact): make MAX_CONCURRENT_REDACTORS configurable via env var (sc-138321) (#2057)
Replace the hardcoded MAX_CONCURRENT_REDACTORS = 10 ceiling in
pkg/collect/redact.go with a runtime-resolved value driven by the
TROUBLESHOOT_MAX_CONCURRENT_REDACTORS env var. The default (10) is
unchanged, so behavior is identical unless an operator opts in.

- DefaultMaxConcurrentRedactors exported as the default
- MaxConcurrentRedactorsEnvVar exported as the env var name
- maxConcurrentRedactors() helper parses the env, logs on invalid input,
  and falls back to the default on missing/empty/non-numeric/<=0 values
- Table-driven tests in pkg/collect/redact_test.go cover unset, empty,
  positive override, default-equal, zero, negative, non-numeric, and
  whitespace-padded inputs

Unblocks Pixee's standalone support-bundle pipeline, which hits the
10-concurrent ceiling on large bundles.

Refs: sc-138321, replicated-collab/pixee-replicated#131
2026-06-10 16:07:27 -04:00
replicated-ciandlnx01 dcfddc78da Bump Go from 1.26.3 to 1.26.4 (#2055)
Bump Go to version from 1.26.3 to 1.26.4

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-08 16:38:50 +01:00
dependabot[bot]andlnx01 aad5f6ef06 chore(deps): bump the security group with 7 updates (#2052)
Bumps the security group with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) | `1.41.7` | `1.41.11` |
| [github.com/aws/aws-sdk-go-v2/credentials](https://github.com/aws/aws-sdk-go-v2) | `1.19.17` | `1.19.21` |
| [github.com/aws/aws-sdk-go-v2/service/s3](https://github.com/aws/aws-sdk-go-v2) | `1.101.0` | `1.103.1` |
| [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) | `5.9.2` | `5.10.0` |
| [github.com/shirou/gopsutil/v4](https://github.com/shirou/gopsutil) | `4.26.4` | `4.26.5` |
| [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.43.0` | `1.44.0` |
| [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) | `1.43.0` | `1.44.0` |


Updates `github.com/aws/aws-sdk-go-v2` from 1.41.7 to 1.41.11
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.41.7...v1.41.11)

Updates `github.com/aws/aws-sdk-go-v2/credentials` from 1.19.17 to 1.19.21
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/credentials/v1.19.17...credentials/v1.19.21)

Updates `github.com/aws/aws-sdk-go-v2/service/s3` from 1.101.0 to 1.103.1
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.101.0...service/s3/v1.103.1)

Updates `github.com/jackc/pgx/v5` from 5.9.2 to 5.10.0
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.9.2...v5.10.0)

Updates `github.com/shirou/gopsutil/v4` from 4.26.4 to 4.26.5
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v4.26.4...v4.26.5)

Updates `go.opentelemetry.io/otel` from 1.43.0 to 1.44.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.43.0 to 1.44.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2
  dependency-version: 1.41.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/aws/aws-sdk-go-v2/credentials
  dependency-version: 1.19.21
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/aws/aws-sdk-go-v2/service/s3
  dependency-version: 1.103.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/jackc/pgx/v5
  dependency-version: 5.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/shirou/gopsutil/v4
  dependency-version: 4.26.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: go.opentelemetry.io/otel
  dependency-version: 1.44.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-version: 1.44.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-04 17:46:47 +12:00
Kris Coleman 12b0e741d8 feat(ci): add Slack release notification via reusable workflow (#2051) 2026-05-29 14:25:15 -04:00
Kris ColemanandClaude Opus 4.6 baecd3c411 chore(deps): bump golang.org/x/net, x/crypto, containerd to patch CVEs (#2049) (#2050)
Bump golang.org/x/crypto v0.51.0 -> v0.52.0 in both root and examples modules.
Bump golang.org/x/net v0.54.0 -> v0.55.0 in examples module (root already at v0.55.0).
containerd already at v1.7.32 in root (no change needed).

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 13:39:20 -04:00
dependabot[bot]andlnx01 1cfa00ed9c chore(deps): bump the security group with 3 updates (#2048)
Bumps the security group with 3 updates: [github.com/aws/aws-sdk-go-v2/credentials](https://github.com/aws/aws-sdk-go-v2), [golang.org/x/net](https://github.com/golang/net) and [golang.org/x/sys](https://github.com/golang/sys).


Updates `github.com/aws/aws-sdk-go-v2/credentials` from 1.19.16 to 1.19.17
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/credentials/v1.19.16...credentials/v1.19.17)

Updates `golang.org/x/net` from 0.54.0 to 0.55.0
- [Commits](https://github.com/golang/net/compare/v0.54.0...v0.55.0)

Updates `golang.org/x/sys` from 0.44.0 to 0.45.0
- [Commits](https://github.com/golang/sys/compare/v0.44.0...v0.45.0)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/credentials
  dependency-version: 1.19.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: golang.org/x/net
  dependency-version: 0.55.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/sys
  dependency-version: 0.45.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-29 12:33:42 -04:00
dependabot[bot]andlnx01 9600c17454 chore(deps): bump github.com/containerd/containerd from 1.7.30 to 1.7.32 (#2046)
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.7.30 to 1.7.32.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.7.30...v1.7.32)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-version: 1.7.32
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-22 15:36:48 +12:00
Gerard NguyenandChuck D'Antonio 61ef1e1036 fix: TLS support in MySQL connector (#2045)
* fix(mysql): respect TLS configuration in MySQL collector

The MySQL collector was ignoring the TLS field in the Database spec,
which caused TLS connections to fail even when properly configured.

This change mirrors the PostgreSQL collector pattern:
- Parse the MySQL DSN using mysql.ParseDSN
- When TLS is configured, create a tls.Config and set cfg.TLS
- Use sql.OpenDB with mysql.NewConnector when TLS is active
- Keep the existing sql.Open path for backward compatibility

Fixes: MySQL preflight fails over TLS connections

* fix(mysql): address review feedback

- Fix gofmt indentation in mysql_test.go
- Remove duplicate DSN parsing in createConnectConfig
- Add CA-cert-only TLS test case

---------

Co-authored-by: Chuck D'Antonio <chuck@replicated.com>
2026-05-22 12:35:15 +10:00
dependabot[bot]andlnx01 77e39bc5ed chore(deps): bump helm.sh/helm/v3 from 3.20.2 to 3.21.0 in /examples/sdk/helm-template in the security group across 1 directory (#2044)
chore(deps): bump helm.sh/helm/v3

Bumps the security group with 1 update in the /examples/sdk/helm-template directory: [helm.sh/helm/v3](https://github.com/helm/helm).


Updates `helm.sh/helm/v3` from 3.20.2 to 3.21.0
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.20.2...v3.21.0)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  dependency-version: 3.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-20 16:13:24 +12:00
dependabot[bot]andlnx01 edba8f1ee0 chore(deps): bump the security group with 14 updates (#2043)
Bumps the security group with 14 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/ClickHouse/clickhouse-go/v2](https://github.com/ClickHouse/clickhouse-go) | `2.42.0` | `2.46.0` |
| [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) | `0.21.5` | `0.21.6` |
| [github.com/vmware-tanzu/velero](https://github.com/vmware-tanzu/velero) | `1.18.0` | `1.18.1` |
| [k8s.io/api](https://github.com/kubernetes/api) | `0.36.0` | `0.36.1` |
| [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) | `0.36.0` | `0.36.1` |
| [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) | `0.36.0` | `0.36.1` |
| [k8s.io/apiserver](https://github.com/kubernetes/apiserver) | `0.36.0` | `0.36.1` |
| [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) | `0.36.0` | `0.36.1` |
| [k8s.io/client-go](https://github.com/kubernetes/client-go) | `0.36.0` | `0.36.1` |
| [k8s.io/streaming](https://github.com/kubernetes/streaming) | `0.36.0` | `0.36.1` |
| [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) | `0.24.0` | `0.24.1` |
| [helm.sh/helm/v3](https://github.com/helm/helm) | `3.20.2` | `3.21.0` |
| [k8s.io/kubelet](https://github.com/kubernetes/kubelet) | `0.36.0` | `0.36.1` |
| [k8s.io/metrics](https://github.com/kubernetes/metrics) | `0.36.0` | `0.36.1` |


Updates `github.com/ClickHouse/clickhouse-go/v2` from 2.42.0 to 2.46.0
- [Release notes](https://github.com/ClickHouse/clickhouse-go/releases)
- [Changelog](https://github.com/ClickHouse/clickhouse-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ClickHouse/clickhouse-go/compare/v2.42.0...v2.46.0)

Updates `github.com/google/go-containerregistry` from 0.21.5 to 0.21.6
- [Release notes](https://github.com/google/go-containerregistry/releases)
- [Commits](https://github.com/google/go-containerregistry/compare/v0.21.5...v0.21.6)

Updates `github.com/vmware-tanzu/velero` from 1.18.0 to 1.18.1
- [Release notes](https://github.com/vmware-tanzu/velero/releases)
- [Changelog](https://github.com/velero-io/velero/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vmware-tanzu/velero/compare/v1.18.0...v1.18.1)

Updates `k8s.io/api` from 0.36.0 to 0.36.1
- [Commits](https://github.com/kubernetes/api/compare/v0.36.0...v0.36.1)

Updates `k8s.io/apiextensions-apiserver` from 0.36.0 to 0.36.1
- [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases)
- [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.36.0...v0.36.1)

Updates `k8s.io/apimachinery` from 0.36.0 to 0.36.1
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.36.0...v0.36.1)

Updates `k8s.io/apiserver` from 0.36.0 to 0.36.1
- [Commits](https://github.com/kubernetes/apiserver/compare/v0.36.0...v0.36.1)

Updates `k8s.io/cli-runtime` from 0.36.0 to 0.36.1
- [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.36.0...v0.36.1)

Updates `k8s.io/client-go` from 0.36.0 to 0.36.1
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.36.0...v0.36.1)

Updates `k8s.io/streaming` from 0.36.0 to 0.36.1
- [Commits](https://github.com/kubernetes/streaming/compare/v0.36.0...v0.36.1)

Updates `sigs.k8s.io/controller-runtime` from 0.24.0 to 0.24.1
- [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases)
- [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md)
- [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.24.0...v0.24.1)

Updates `helm.sh/helm/v3` from 3.20.2 to 3.21.0
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.20.2...v3.21.0)

Updates `k8s.io/kubelet` from 0.36.0 to 0.36.1
- [Commits](https://github.com/kubernetes/kubelet/compare/v0.36.0...v0.36.1)

Updates `k8s.io/metrics` from 0.36.0 to 0.36.1
- [Commits](https://github.com/kubernetes/metrics/compare/v0.36.0...v0.36.1)

---
updated-dependencies:
- dependency-name: github.com/ClickHouse/clickhouse-go/v2
  dependency-version: 2.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/google/go-containerregistry
  dependency-version: 0.21.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/vmware-tanzu/velero
  dependency-version: 1.18.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/api
  dependency-version: 0.36.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/apiextensions-apiserver
  dependency-version: 0.36.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/apimachinery
  dependency-version: 0.36.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/apiserver
  dependency-version: 0.36.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/cli-runtime
  dependency-version: 0.36.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/client-go
  dependency-version: 0.36.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/streaming
  dependency-version: 0.36.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: sigs.k8s.io/controller-runtime
  dependency-version: 0.24.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: helm.sh/helm/v3
  dependency-version: 3.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/kubelet
  dependency-version: 0.36.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/metrics
  dependency-version: 0.36.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-20 12:31:01 +12:00
replicated-ciandlnx01 894158779c Bump Go from 1.26.2 to 1.26.3 (#2040)
Bump Go to version from 1.26.2 to 1.26.3

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-15 08:19:00 -07:00
Lennard EijsackersandClaude Opus 4.6 1cd9b6103c feat: Add Clickhouse Support (#1967)
* feat: Add Clickhouse Support

* fix: missing case in GetCollector for ClickHouse

* chore: add analyzer

* fix; Findings

* fix: use go-version for ClickHouse version comparison

ClickHouse returns 4-part version numbers (e.g., 25.3.2.39) which
blang/semver.ParseTolerant cannot handle. Use hashicorp/go-version
(which supports arbitrary version parts) via a ClickHouse-specific
comparison function, matching the approach used by the MSSQL analyzer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* change icon

* chore: Resolve comments

- Change icon value
- Update converter to support ClickHouse
- Update loader to support ClickHouse
- Ensure IsConnected behaviour is similar to other db collectors

* fmt 🤦

* chore: Add Clickhouse case to convertAnalyzerToSpec

* resolve CollectorName comment

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 22:57:16 +01:00
dependabot[bot]andlnx01 ba33b743d5 chore(deps): bump the security group with 5 updates (#2042)
Bumps the security group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/aws/aws-sdk-go-v2/service/s3](https://github.com/aws/aws-sdk-go-v2) | `1.100.1` | `1.101.0` |
| [golang.org/x/mod](https://github.com/golang/mod) | `0.35.0` | `0.36.0` |
| [golang.org/x/net](https://github.com/golang/net) | `0.53.0` | `0.54.0` |
| [golang.org/x/sys](https://github.com/golang/sys) | `0.43.0` | `0.44.0` |
| [golang.org/x/text](https://github.com/golang/text) | `0.36.0` | `0.37.0` |


Updates `github.com/aws/aws-sdk-go-v2/service/s3` from 1.100.1 to 1.101.0
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.100.1...service/s3/v1.101.0)

Updates `golang.org/x/mod` from 0.35.0 to 0.36.0
- [Commits](https://github.com/golang/mod/compare/v0.35.0...v0.36.0)

Updates `golang.org/x/net` from 0.53.0 to 0.54.0
- [Commits](https://github.com/golang/net/compare/v0.53.0...v0.54.0)

Updates `golang.org/x/sys` from 0.43.0 to 0.44.0
- [Commits](https://github.com/golang/sys/compare/v0.43.0...v0.44.0)

Updates `golang.org/x/text` from 0.36.0 to 0.37.0
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.36.0...v0.37.0)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/service/s3
  dependency-version: 1.101.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/mod
  dependency-version: 0.36.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/net
  dependency-version: 0.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/sys
  dependency-version: 0.44.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/text
  dependency-version: 0.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-12 20:02:51 -07:00
dependabot[bot]andlnx01 f6c57607ea chore(deps): bump github.com/distribution/distribution/v3 from 3.1.0 to 3.1.1 (#2038)
chore(deps): bump github.com/distribution/distribution/v3

Bumps [github.com/distribution/distribution/v3](https://github.com/distribution/distribution) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/distribution/distribution/releases)
- [Commits](https://github.com/distribution/distribution/compare/v3.1.0...v3.1.1)

---
updated-dependencies:
- dependency-name: github.com/distribution/distribution/v3
  dependency-version: 3.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 16:06:49 -04:00
dependabot[bot]andlnx01 3779868880 chore(deps): bump the security group with 7 updates (#2039)
Bumps the security group with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) | `1.41.6` | `1.41.7` |
| [github.com/aws/aws-sdk-go-v2/credentials](https://github.com/aws/aws-sdk-go-v2) | `1.19.15` | `1.19.16` |
| [github.com/aws/aws-sdk-go-v2/service/s3](https://github.com/aws/aws-sdk-go-v2) | `1.100.0` | `1.100.1` |
| [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) | `1.9.3` | `1.10.0` |
| [github.com/microsoft/go-mssqldb](https://github.com/microsoft/go-mssqldb) | `1.9.8` | `1.10.0` |
| [github.com/shirou/gopsutil/v4](https://github.com/shirou/gopsutil) | `4.26.3` | `4.26.4` |
| [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) | `0.23.3` | `0.24.0` |


Updates `github.com/aws/aws-sdk-go-v2` from 1.41.6 to 1.41.7
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.41.6...v1.41.7)

Updates `github.com/aws/aws-sdk-go-v2/credentials` from 1.19.15 to 1.19.16
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/credentials/v1.19.15...credentials/v1.19.16)

Updates `github.com/aws/aws-sdk-go-v2/service/s3` from 1.100.0 to 1.100.1
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.100.0...service/s3/v1.100.1)

Updates `github.com/go-sql-driver/mysql` from 1.9.3 to 1.10.0
- [Release notes](https://github.com/go-sql-driver/mysql/releases)
- [Changelog](https://github.com/go-sql-driver/mysql/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-sql-driver/mysql/compare/v1.9.3...v1.10.0)

Updates `github.com/microsoft/go-mssqldb` from 1.9.8 to 1.10.0
- [Release notes](https://github.com/microsoft/go-mssqldb/releases)
- [Changelog](https://github.com/microsoft/go-mssqldb/blob/main/CHANGELOG.md)
- [Commits](https://github.com/microsoft/go-mssqldb/compare/v1.9.8...v1.10.0)

Updates `github.com/shirou/gopsutil/v4` from 4.26.3 to 4.26.4
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v4.26.3...v4.26.4)

Updates `sigs.k8s.io/controller-runtime` from 0.23.3 to 0.24.0
- [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases)
- [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md)
- [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.23.3...v0.24.0)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2
  dependency-version: 1.41.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/aws/aws-sdk-go-v2/credentials
  dependency-version: 1.19.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/aws/aws-sdk-go-v2/service/s3
  dependency-version: 1.100.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/go-sql-driver/mysql
  dependency-version: 1.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/microsoft/go-mssqldb
  dependency-version: 1.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/shirou/gopsutil/v4
  dependency-version: 4.26.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: sigs.k8s.io/controller-runtime
  dependency-version: 0.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 10:01:29 -04:00
61 changed files with 3368 additions and 846 deletions
+180
View File
@@ -0,0 +1,180 @@
schema_version: v1
name: dependency-update-troubleshoot
enabled: true
trigger:
cron:
schedule: "0 21 * * *"
timezone: "America/Chicago"
overlap_policy: skip
timeout: 2h
enable_manual_trigger: true
tags: ["cron", "dependencies"]
color: cyan
stages:
- id: start
label: Start
entry: true
on_enter:
inject: |
Workflow task: run the Go dependency update maintenance workflow for Troubleshoot.
This is an unattended dependency maintenance workflow. Scope: Go
dependency updates for the top-level go.mod/go.sum plus examples/sdk/helm-template. After updates
are applied, you will review the changes, fix any compatibility issues,
validate with make build and make test, and open one grouped PR.
Do not ask for permission to start. Immediately say exactly [PROCEED]
to run the dependency updater. Detailed post-update instructions will
follow.
- id: working
label: Working
triggers:
- message_contains: "[PROCEED]"
on_enter:
run:
command: |
bash -lc '
set -euo pipefail
if ! command -v go >/dev/null 2>&1; then
nix profile install nixpkgs#go_1_26 || nix-env -iA nixpkgs.go_1_26
fi
go_path="$(command -v go)"
go_version="$(go version)"
printf "{\"status\":\"ready\",\"go_path\":\"%s\",\"go_version\":\"%s\"}\n" "$go_path" "$go_version"
'
output: go_toolchain
timeout: 10m
dependency_updates:
ecosystems: [go]
paths: ["."]
output: dependency_updates
timeout: 30m
inject: |
Dependency updates were applied for the root Go module.
Scope this workflow to Go dependency updates only. Focus on the
top-level go.mod/go.sum. Exclude Node dependencies and any non-Go
modules.
Treat compatibility fixes as part of this workflow. If an updated
dependency causes build, lint, type, or test failures, first attempt
reasonable scoped code changes within the affected Go module to support
the new version. Do not downgrade, pin, or skip an update just because
it has breaking API changes unless the required changes are broad,
risky, unrelated to this workflow's scope, or cannot be completed within
reasonable effort. If you defer an update, explain exactly why.
Review {{ .Outputs.dependency_updates.files_changed }}.
Before opening a PR, validate using the repo make targets in the Nix
development environment (nix develop -c). Do not run ad-hoc go
commands; use exactly these targets:
- make build
- make test
Note: make test-integration requires a Kubernetes cluster. Only add it
to the validation if the workflow environment provides one.
Fix any failures and open one grouped PR.
Say [DONE] with the PR URL only when the PR is open.
If {{ .Outputs.dependency_updates.files_changed }} is empty and no PR is
needed, say [NO_CHANGES] instead of [DONE].
- id: no-changes
label: No Changes
triggers:
- message_contains: "[NO_CHANGES]"
- message_contains: "[DONE] No changes"
terminal: true
- id: validate
label: Validate
triggers:
- message_contains: "[DONE]"
on_enter:
run:
command: |
nix develop -c bash -lc '
set +e
tmpdir="$(mktemp -d)"
make build >"$tmpdir/build.log" 2>&1
build_status=$?
make test >"$tmpdir/test.log" 2>&1
test_status=$?
python3 - "$tmpdir" "$build_status" "$test_status" <<PY
import json
import pathlib
import sys
tmpdir = pathlib.Path(sys.argv[1])
statuses = {
"build": int(sys.argv[2]),
"test": int(sys.argv[3]),
}
checks = []
for name, code in statuses.items():
log = (tmpdir / f"{name}.log").read_text(errors="replace")
checks.append({
"name": name,
"status": "passed" if code == 0 else "failed",
"exit_code": code,
"log_tail": log[-12000:],
})
failed = [check for check in checks if check["exit_code"] != 0]
print(json.dumps({
"status": "passed" if not failed else "failed",
"reason": "all validation checks passed" if not failed else "one or more validation checks failed",
"checks": checks,
}))
PY
'
output: validation
timeout: 45m
gate:
output: validation
pass:
path: status
values: [passed]
fail:
path: status
values: [failed, error]
required: true
- id: fix-validation
label: Fix Validation
triggers:
- gate_result:
stage: validate
verdict: fail
on_enter:
inject: |
Validation failed before completion.
Status: {{ .Outputs.validation.status }}
Reason: {{ .Outputs.validation.reason }}
Checks:
{{ .Outputs.validation.checks }}
Fix the failures above, commit the changes to the same PR branch, and
say [DONE] with the PR URL again when the PR is ready for validation.
- id: complete
label: Complete
triggers:
- gate_result:
stage: validate
verdict: pass
terminal: true
+14 -14
View File
@@ -30,8 +30,8 @@ jobs:
tidy-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version-file: 'go.mod'
- run: make tidy-diff
@@ -39,8 +39,8 @@ jobs:
test-integration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version-file: 'go.mod'
- uses: replicatedhq/action-k3s@main
@@ -54,8 +54,8 @@ jobs:
compile-preflight:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version-file: 'go.mod'
- run: make generate preflight
@@ -68,7 +68,7 @@ jobs:
runs-on: ubuntu-latest
needs: compile-preflight
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: replicatedhq/action-k3s@main
id: k3s
with:
@@ -84,8 +84,8 @@ jobs:
compile-supportbundle:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version-file: 'go.mod'
- run: make generate support-bundle
@@ -97,8 +97,8 @@ jobs:
compile-collect:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version-file: 'go.mod'
- run: make generate collect
@@ -111,7 +111,7 @@ jobs:
runs-on: ubuntu-latest
needs: compile-supportbundle
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: replicatedhq/action-k3s@main
id: k3s
with:
@@ -127,9 +127,9 @@ jobs:
# Additional e2e tests for support bundle that run in Go, these create a Kind cluster
validate-supportbundle-e2e-go:
runs-on: ubuntu-latest
needs: [compile-supportbundle, compile-collect]
needs: [compile-supportbundle, compile-collect, compile-preflight]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Download support bundle binary
uses: actions/download-artifact@v8
with:
+10 -10
View File
@@ -21,7 +21,7 @@ jobs:
support-bundle: ${{ steps.filter.outputs.support-bundle }}
examples: ${{ steps.filter.outputs.examples }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: dorny/paths-filter@v4
id: filter
with:
@@ -44,8 +44,8 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version-file: 'go.mod'
@@ -73,8 +73,8 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version-file: 'go.mod'
@@ -93,8 +93,8 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version-file: 'go.mod'
- run: make build
@@ -128,8 +128,8 @@ jobs:
docker_platform: linux/riscv64
expected_machine: riscv64
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version-file: 'go.mod'
- name: Setup K3s
@@ -252,7 +252,7 @@ jobs:
target: support-bundle-e2e-go-test
needs-k3s: false
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Setup K3s
if: matrix.needs-k3s
+31
View File
@@ -0,0 +1,31 @@
name: Publish ElasticClaw Workflow
on:
push:
branches:
- main
paths:
- ".factory-workflows/dependency-update.yaml"
workflow_dispatch:
jobs:
publish:
name: Publish workflow (${{ matrix.workflow }})
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
workflow:
- .factory-workflows/dependency-update.yaml
steps:
- uses: actions/checkout@v7
- name: Publish workflow (dependency-update)
uses: elasticclaw/actions/publish-workflow@main
with:
hub-endpoint: https://factory.repldev.com
token: ${{ secrets.ELASTICCLAW_TOKEN }}
workspace: replicated-troubleshoot
path: ${{ matrix.workflow }}
+101
View File
@@ -0,0 +1,101 @@
name: publish-securebuild
env:
SECUREBUILD_CLI_VERSION: v0.0.498
SECUREBUILD_CLI_SHA256: d259aa99d85aef957fe1e40c6f26e1983cc7fd72277e08599a82adc6681d3b37
on:
workflow_call:
inputs:
version:
description: Stable version tag to build (for example, v0.130.0)
required: true
type: string
secrets:
SECUREBUILD_API_TOKEN:
required: true
workflow_dispatch:
inputs:
version:
description: Stable version tag to build (for example, v0.130.0)
required: true
type: string
jobs:
validate-version:
runs-on: ubuntu-22.04
steps:
- name: Require a stable release version
env:
VERSION: ${{ inputs.version }}
run: |
if ! [[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "SecureBuild only supports stable release versions (for example, v0.130.0): $VERSION"
exit 1
fi
build-package:
needs: validate-version
runs-on: ubuntu-22.04
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Set version
id: version
env:
VERSION: ${{ inputs.version }}
run: echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Install SecureBuild CLI
run: |
curl --fail --silent --show-error --location \
"https://github.com/securebuildhq/securebuild/releases/download/${SECUREBUILD_CLI_VERSION}/securebuild-cli-linux-amd64" \
--output securebuild
echo "${SECUREBUILD_CLI_SHA256} securebuild" | sha256sum --check --strict
chmod +x securebuild
- name: Build package
env:
SECUREBUILD_API_TOKEN: ${{ secrets.SECUREBUILD_API_TOKEN }}
VERSION: ${{ steps.version.outputs.version }}
run: |
./securebuild build package \
--package-family-name troubleshoot \
--tag "$VERSION" \
--api-token "$SECUREBUILD_API_TOKEN"
build-image:
needs: build-package
if: ${{ !cancelled() && needs.build-package.result == 'success' }}
runs-on: ubuntu-22.04
strategy:
matrix:
image-name:
- troubleshoot
- preflight
steps:
- name: Install SecureBuild CLI
run: |
curl --fail --silent --show-error --location \
"https://github.com/securebuildhq/securebuild/releases/download/${SECUREBUILD_CLI_VERSION}/securebuild-cli-linux-amd64" \
--output securebuild
echo "${SECUREBUILD_CLI_SHA256} securebuild" | sha256sum --check --strict
chmod +x securebuild
- name: Build image
env:
IMAGE_NAME: ${{ matrix.image-name }}
SECUREBUILD_API_TOKEN: ${{ secrets.SECUREBUILD_API_TOKEN }}
VERSION: ${{ needs.build-package.outputs.version }}
run: |
IMAGE_VERSION="${VERSION#v}"
MAJOR_MINOR_VERSION="${IMAGE_VERSION%.*}"
MAJOR_VERSION="${IMAGE_VERSION%%.*}"
./securebuild build image \
--image-name "$IMAGE_NAME" \
--tag "$VERSION" \
--image-tag "$MAJOR_MINOR_VERSION" \
--image-tag "$MAJOR_VERSION" \
--image-tag latest \
--api-token "$SECUREBUILD_API_TOKEN"
+10 -10
View File
@@ -21,12 +21,12 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v6
uses: actions/setup-go@v7
with:
go-version-file: 'go.mod'
@@ -55,7 +55,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Create output directory
run: mkdir -p test/output
@@ -87,7 +87,7 @@ jobs:
./bin/preflight version
- name: Setup Python for comparison
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.11'
@@ -159,7 +159,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Create output directory
run: mkdir -p test/output
@@ -191,7 +191,7 @@ jobs:
./bin/preflight version
- name: Setup Python for comparison
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.11'
@@ -262,7 +262,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Create output directory
run: mkdir -p test/output
@@ -294,7 +294,7 @@ jobs:
./bin/support-bundle version
- name: Setup Python for comparison
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.11'
@@ -361,10 +361,10 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Setup Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.11'
+29 -6
View File
@@ -14,18 +14,25 @@ jobs:
runs-on: troubleshoot_release
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: azure/docker-login@v2
- uses: actions/setup-go@v7
with:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
go-version: '1.26'
check-latest: true
- uses: actions/setup-go@v6
- name: Install Cosign
uses: sigstore/cosign-installer@v4.1.2
with:
go-version-file: 'go.mod'
cosign-release: 'v3.1.3'
- name: Generate and sign SBOM
run: make sbom
env:
COSIGN_KEY: ${{ secrets.COSIGN_KEY }}
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v7
@@ -46,3 +53,19 @@ jobs:
uses: rajatjindal/krew-release-bot@v0.0.51
with:
krew_template_file: deploy/krew/support-bundle.yaml
securebuild:
if: ${{ !contains(github.ref_name, '-') }}
uses: ./.github/workflows/publish-securebuild.yml
with:
version: ${{ github.ref_name }}
secrets:
SECUREBUILD_API_TOKEN: ${{ secrets.SECUREBUILD_API_TOKEN }}
notify:
if: ${{ !contains(github.ref_name, '-') }}
uses: replicatedhq/reusable-workflows/.github/workflows/notify-release.yml@0432fb838fc83852b78be97a150b35991a85d56f
with:
tag: ${{ github.ref_name }}
secrets:
slack_webhook: ${{ secrets.RELEASE_SLACK_WEBHOOK }}
-24
View File
@@ -1,24 +0,0 @@
name: Upgrade Go Version
on:
# Run manually when needed
workflow_dispatch:
# Run weekly on Mondays at 8am UTC
schedule:
- cron: "0 8 * * MON"
jobs:
upgrade-go:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Check for Go updates
uses: StefMa/Upgrade-Go-Action@v1
with:
base-branch: 'main'
gh-token: ${{ secrets.TROUBLESHOOT_GH_PAT }}
+6 -28
View File
@@ -152,17 +152,8 @@ vet:
.PHONY: generate
generate: controller-gen client-gen
$(CONTROLLER_GEN) \
object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/...
$(CLIENT_GEN) \
--output-dir=. \
--output-pkg=github.com/replicatedhq/troubleshoot/pkg/client \
--clientset-name troubleshootclientset \
--input-base github.com/replicatedhq/troubleshoot/pkg/apis \
--input troubleshoot/v1beta1 \
--input troubleshoot/v1beta2 \
--input troubleshoot/v1beta3 \
--go-header-file ./hack/boilerplate.go.txt
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/...
$(CLIENT_GEN) --output-dir=. --output-pkg=github.com/replicatedhq/troubleshoot/pkg/client --clientset-name troubleshootclientset --input-base github.com/replicatedhq/troubleshoot/pkg/apis --input troubleshoot/v1beta1 --input troubleshoot/v1beta2 --input troubleshoot/v1beta3 --go-header-file ./hack/boilerplate.go.txt
cp -r troubleshootclientset pkg/client
rm -rf troubleshootclientset
@@ -194,32 +185,18 @@ bin/docsgen:
controller-gen:
go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.19.0
CONTROLLER_GEN=$(shell which controller-gen)
CONTROLLER_GEN=$(shell go env GOPATH)/bin/controller-gen
.PHONY: client-gen
client-gen:
go install k8s.io/code-generator/cmd/client-gen@v0.34.0
CLIENT_GEN=$(shell which client-gen)
CLIENT_GEN=$(shell go env GOPATH)/bin/client-gen
.PHONY: release
release: export GITHUB_TOKEN = $(shell echo ${GITHUB_TOKEN_TROUBLESHOOT})
release:
curl -sL https://git.io/goreleaser | bash -s -- --rm-dist --config deploy/.goreleaser.yml
.PHONY: snapshot-release
snapshot-release:
curl -sL https://git.io/goreleaser | bash -s -- --rm-dist --snapshot --config deploy/.goreleaser.snapshot.yml
docker push replicated/troubleshoot:alpha
docker push replicated/preflight:alpha
.PHONY: local-release
local-release:
curl -sL https://git.io/goreleaser | bash -s -- --rm-dist --snapshot --config deploy/.goreleaser.yaml
docker tag replicated/troubleshoot:alpha localhost:32000/troubleshoot:alpha
docker tag replicated/preflight:alpha localhost:32000/preflight:alpha
docker push localhost:32000/troubleshoot:alpha
docker push localhost:32000/preflight:alpha
.PHONY: run-preflight
run-preflight: bin/preflight
./bin/preflight ./examples/preflight/sample-preflight.yaml
@@ -255,7 +232,8 @@ sbom: sbom/assets/troubleshoot-sbom.tgz
--tlog-upload \
--yes \
--rekor-url=https://rekor.sigstore.dev \
sbom/assets/troubleshoot-sbom.tgz > sbom/assets/troubleshoot-sbom.tgz.sig
--bundle sbom/assets/troubleshoot-sbom.tgz.bundle \
sbom/assets/troubleshoot-sbom.tgz
cosign public-key --key cosign.key --outfile sbom/assets/key.pub
.PHONY: get-govulncheck
+3 -10
View File
@@ -56,19 +56,12 @@ For questions about using Troubleshoot, how to contribute and engaging with the
# Software Bill of Materials
A signed SBOM that includes Troubleshoot dependencies is included in each release.
- **troubleshoot-sbom.tgz** contains a software bill of materials for Troubleshoot.
- **troubleshoot-sbom.tgz.sig** is the digital signature for troubleshoot-sbom.tgz
- **troubleshoot-sbom.tgz.bundle** contains the signature and transparency log material used by Cosign.
- **key.pub** is the public key from the key pair used to sign troubleshoot-sbom.tgz
The following example illustrates using [cosign](https://github.com/sigstore/cosign) to verify that **troubleshoot-sbom.tgz** has
not been tampered with.
not been tampered with. Install [Cosign v3](https://github.com/sigstore/cosign/releases).
```sh
$ cosign verify-blob --key key.pub --signature troubleshoot-sbom.tgz.sig troubleshoot-sbom.tgz
Verified OK
```
If you were to get an error similar to the one below, it means you are verifying an SBOM signed using cosign `v1` using a newer `v2` of the binary. This version introduced [breaking changes](https://github.com/sigstore/cosign/blob/main/CHANGELOG.md#breaking-changes) which require an additional flag `--insecure-ignore-tlog=true` to successfully verify SBOMs like so.
```sh
$ cosign verify-blob --key key.pub --signature troubleshoot-sbom.tgz.sig troubleshoot-sbom.tgz --insecure-ignore-tlog=true
WARNING: Skipping tlog verification is an insecure practice that lacks of transparency and auditability verification for the blob.
$ cosign verify-blob --key key.pub --bundle troubleshoot-sbom.tgz.bundle troubleshoot-sbom.tgz
Verified OK
```
+88
View File
@@ -0,0 +1,88 @@
package cli
import (
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
// ClickhouseCmd runs the clickhouse collector against a single database and
// prints the native result JSON ({"isConnected":..,"version":..,"error":..}).
func ClickhouseCmd() *cobra.Command {
var (
uri string
skipVerify bool
caCert string
clientCert string
clientKey string
secretName string
secretNamespace string
)
cmd := &cobra.Command{
Use: "clickhouse",
Short: "Run the clickhouse collector against a database",
Long: `Run the clickhouse collector: connect to a ClickHouse server, run "SELECT version()",
and print the collector result as JSON.
--uri is a clickhouse-go DSN:
clickhouse://user:password@host:9000/dbname
TLS material may be provided inline / by file path (--tls-cacert, --tls-client-cert,
--tls-client-key) or sourced from a Secret (--tls-secret-name), which requires
cluster access.
Example:
collect clickhouse --uri "clickhouse://default:@ch.default.svc:9000/default"`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
db := &troubleshootv1beta2.Database{
CollectorMeta: troubleshootv1beta2.CollectorMeta{CollectorName: "clickhouse"},
URI: uri,
}
var (
client kubernetes.Interface
cfg *rest.Config
)
if skipVerify || caCert != "" || clientCert != "" || clientKey != "" || secretName != "" {
tls := &troubleshootv1beta2.TLSParams{
SkipVerify: skipVerify,
CACert: caCert,
ClientCert: clientCert,
ClientKey: clientKey,
}
if secretName != "" {
tls.Secret = &troubleshootv1beta2.TLSSecret{Name: secretName, Namespace: secretNamespace}
var err error
client, cfg, err = k8sClientForCollectors()
if err != nil {
return err
}
}
db.TLS = tls
}
c := &collect.CollectClickhouse{Collector: db, ClientConfig: cfg, Client: client, Context: cmd.Context()}
res, err := c.Collect(nil)
if err != nil {
return err
}
return printCollectorResult(res)
},
}
f := cmd.Flags()
f.StringVar(&uri, "uri", "", "ClickHouse connection DSN, e.g. clickhouse://user:pass@host:9000/db (required)")
cmd.MarkFlagRequired("uri")
f.BoolVar(&skipVerify, "tls-skip-verify", false, "skip TLS certificate verification")
f.StringVar(&caCert, "tls-cacert", "", "CA certificate (PEM contents or a file path)")
f.StringVar(&clientCert, "tls-client-cert", "", "client certificate (PEM contents or a file path)")
f.StringVar(&clientKey, "tls-client-key", "", "client key (PEM contents or a file path)")
f.StringVar(&secretName, "tls-secret-name", "", "name of a Secret holding TLS material (requires cluster access)")
f.StringVar(&secretNamespace, "tls-secret-namespace", "default", "namespace of the TLS Secret")
return cmd
}
+47
View File
@@ -0,0 +1,47 @@
// Package cli implements the `collect` command and its subcommands.
//
// The per-collector subcommands (http, postgres, mysql, mssql, redis) run a
// single collector and print its native result JSON to stdout. This lets a
// collector run inside a Pod using the troubleshoot image — e.g. as a runPod
// collector — so the check executes from within the cluster instead of from
// wherever the CLI happens to be invoked.
package cli
import (
"fmt"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/version"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
// printCollectorResult writes the native collector result JSON to stdout.
// The single-collector subcommands run with an empty BundlePath, so the result
// is held in memory (map value = the JSON bytes) rather than written to disk.
func printCollectorResult(res collect.CollectorResult) error {
for _, b := range res {
fmt.Println(string(b))
}
return nil
}
// k8sClientForCollectors builds a Kubernetes client from the ambient kubeconfig
// or in-cluster config. Only the collectors that resolve TLS material from a
// Secret need this; plain connections and inline/file TLS do not.
func k8sClientForCollectors() (kubernetes.Interface, *rest.Config, error) {
cfg, err := k8sutil.GetRESTConfig()
if err != nil {
return nil, nil, err
}
cfg.QPS = constants.DEFAULT_CLIENT_QPS
cfg.Burst = constants.DEFAULT_CLIENT_BURST
cfg.UserAgent = fmt.Sprintf("%s/%s", constants.DEFAULT_CLIENT_USER_AGENT, version.Version())
client, err := kubernetes.NewForConfig(cfg)
if err != nil {
return nil, nil, err
}
return client, cfg, nil
}
+79
View File
@@ -0,0 +1,79 @@
package cli
import (
"fmt"
"strings"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/spf13/cobra"
)
// HTTPCmd runs the http collector against a single endpoint and prints the
// native result JSON ({"response":{"status":...}} or {"error":{...}}).
func HTTPCmd() *cobra.Command {
var (
method string
url string
headers map[string]string
body string
timeout string
proxy string
insecure bool
caCert string
)
cmd := &cobra.Command{
Use: "http",
Short: "Run the http collector against an endpoint",
Long: `Run the http collector: issue an HTTP request and print the collector result as JSON.
The result contains the response status, body and headers, or an error object.
Examples:
collect http --url http://myapp.default.svc:8080/healthz
collect http --method POST --url https://api.internal/ping \
--header 'Content-Type=application/json' --body '{"ping":true}'`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
var tls *troubleshootv1beta2.TLSParams
if caCert != "" {
tls = &troubleshootv1beta2.TLSParams{CACert: caCert}
}
spec := &troubleshootv1beta2.HTTP{
CollectorMeta: troubleshootv1beta2.CollectorMeta{CollectorName: "http"},
}
switch strings.ToUpper(method) {
case "GET":
spec.Get = &troubleshootv1beta2.Get{URL: url, Headers: headers, Timeout: timeout, Proxy: proxy, InsecureSkipVerify: insecure, TLS: tls}
case "POST":
spec.Post = &troubleshootv1beta2.Post{URL: url, Headers: headers, Body: body, Timeout: timeout, Proxy: proxy, InsecureSkipVerify: insecure, TLS: tls}
case "PUT":
spec.Put = &troubleshootv1beta2.Put{URL: url, Headers: headers, Body: body, Timeout: timeout, Proxy: proxy, InsecureSkipVerify: insecure, TLS: tls}
default:
return fmt.Errorf("unsupported --method %q (use GET, POST, or PUT)", method)
}
c := &collect.CollectHTTP{Collector: spec}
res, err := c.Collect(nil)
if err != nil {
return err
}
return printCollectorResult(res)
},
}
f := cmd.Flags()
f.StringVar(&url, "url", "", "request URL (required)")
cmd.MarkFlagRequired("url")
f.StringVar(&method, "method", "GET", "HTTP method: GET, POST, or PUT")
f.StringToStringVar(&headers, "header", nil, "request header as key=value (repeatable)")
f.StringVar(&body, "body", "", "request body (POST/PUT only)")
f.StringVar(&timeout, "timeout", "", "request timeout, e.g. 15s (empty = no timeout)")
f.StringVar(&proxy, "proxy", "", "proxy URL to use for the request")
f.BoolVar(&insecure, "insecure-skip-verify", false, "do not verify the server's TLS certificate")
f.StringVar(&caCert, "tls-cacert", "", "CA certificate to trust (PEM contents or a file path)")
return cmd
}
+49
View File
@@ -0,0 +1,49 @@
package cli
import (
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/spf13/cobra"
)
// MssqlCmd runs the mssql collector against a single database and prints the
// native result JSON ({"isConnected":..,"version":..,"error":..}).
//
// Unlike the other database collectors, the mssql collector does not honor a
// separate TLS field — TLS is configured through the connection URI query
// parameters (e.g. encrypt=true) — so this subcommand only takes --uri.
func MssqlCmd() *cobra.Command {
var uri string
cmd := &cobra.Command{
Use: "mssql",
Short: "Run the mssql collector against a database",
Long: `Run the mssql collector: connect to a Microsoft SQL Server, run "select @@VERSION",
and print the collector result as JSON.
--uri is a go-mssqldb connection URL; TLS options are passed as query parameters:
sqlserver://user:password@host:1433?database=app&encrypt=true
Example:
collect mssql --uri "sqlserver://sa:pass@mssql.default.svc:1433?encrypt=true"`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
db := &troubleshootv1beta2.Database{
CollectorMeta: troubleshootv1beta2.CollectorMeta{CollectorName: "mssql"},
URI: uri,
}
c := &collect.CollectMssql{Collector: db, Context: cmd.Context()}
res, err := c.Collect(nil)
if err != nil {
return err
}
return printCollectorResult(res)
},
}
cmd.Flags().StringVar(&uri, "uri", "", "SQL Server connection URI, e.g. sqlserver://user:pass@host:1433 (required)")
cmd.MarkFlagRequired("uri")
return cmd
}
+90
View File
@@ -0,0 +1,90 @@
package cli
import (
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
// MysqlCmd runs the mysql collector against a single database and prints the
// native result JSON ({"isConnected":..,"version":..,"variables":..,"error":..}).
func MysqlCmd() *cobra.Command {
var (
uri string
parameters []string
skipVerify bool
caCert string
clientCert string
clientKey string
secretName string
secretNamespace string
)
cmd := &cobra.Command{
Use: "mysql",
Short: "Run the mysql collector against a database",
Long: `Run the mysql collector: connect to a MySQL server, run "select version()",
optionally collect server variables, and print the collector result as JSON.
--uri is a go-sql-driver DSN (note: not a URL):
user:password@tcp(host:3306)/dbname
--parameters names server variables to collect via "SHOW VARIABLES"; the matching
values are returned under "variables" in the result. This flag is unique to mysql.
Example:
collect mysql --uri "root:pass@tcp(mysql.default.svc:3306)/" --parameters max_connections,version`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
db := &troubleshootv1beta2.Database{
CollectorMeta: troubleshootv1beta2.CollectorMeta{CollectorName: "mysql"},
URI: uri,
Parameters: parameters,
}
var (
client kubernetes.Interface
cfg *rest.Config
)
if skipVerify || caCert != "" || clientCert != "" || clientKey != "" || secretName != "" {
tls := &troubleshootv1beta2.TLSParams{
SkipVerify: skipVerify,
CACert: caCert,
ClientCert: clientCert,
ClientKey: clientKey,
}
if secretName != "" {
tls.Secret = &troubleshootv1beta2.TLSSecret{Name: secretName, Namespace: secretNamespace}
var err error
client, cfg, err = k8sClientForCollectors()
if err != nil {
return err
}
}
db.TLS = tls
}
c := &collect.CollectMysql{Collector: db, ClientConfig: cfg, Client: client, Context: cmd.Context()}
res, err := c.Collect(nil)
if err != nil {
return err
}
return printCollectorResult(res)
},
}
f := cmd.Flags()
f.StringVar(&uri, "uri", "", "MySQL connection DSN, e.g. user:pass@tcp(host:3306)/db (required)")
cmd.MarkFlagRequired("uri")
f.StringSliceVar(&parameters, "parameters", nil, "server variables to collect via SHOW VARIABLES (comma-separated or repeatable)")
f.BoolVar(&skipVerify, "tls-skip-verify", false, "skip TLS certificate verification")
f.StringVar(&caCert, "tls-cacert", "", "CA certificate (PEM contents or a file path)")
f.StringVar(&clientCert, "tls-client-cert", "", "client certificate (PEM contents or a file path)")
f.StringVar(&clientKey, "tls-client-key", "", "client key (PEM contents or a file path)")
f.StringVar(&secretName, "tls-secret-name", "", "name of a Secret holding TLS material (requires cluster access)")
f.StringVar(&secretNamespace, "tls-secret-namespace", "default", "namespace of the TLS Secret")
return cmd
}
+88
View File
@@ -0,0 +1,88 @@
package cli
import (
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
// PostgresCmd runs the postgres collector against a single database and prints
// the native result JSON ({"isConnected":..,"version":..,"error":..}).
func PostgresCmd() *cobra.Command {
var (
uri string
skipVerify bool
caCert string
clientCert string
clientKey string
secretName string
secretNamespace string
)
cmd := &cobra.Command{
Use: "postgres",
Short: "Run the postgres collector against a database",
Long: `Run the postgres collector: connect to a PostgreSQL server, run "select version()",
and print the collector result as JSON.
--uri is a libpq/pgx connection URI:
postgres://user:password@host:5432/dbname?sslmode=disable
TLS material may be provided inline / by file path (--tls-cacert, --tls-client-cert,
--tls-client-key) or sourced from a Secret (--tls-secret-name), which requires
cluster access.
Example:
collect postgres --uri "postgres://user:pass@pg.default.svc:5432/app?sslmode=disable"`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
db := &troubleshootv1beta2.Database{
CollectorMeta: troubleshootv1beta2.CollectorMeta{CollectorName: "postgres"},
URI: uri,
}
var (
client kubernetes.Interface
cfg *rest.Config
)
if skipVerify || caCert != "" || clientCert != "" || clientKey != "" || secretName != "" {
tls := &troubleshootv1beta2.TLSParams{
SkipVerify: skipVerify,
CACert: caCert,
ClientCert: clientCert,
ClientKey: clientKey,
}
if secretName != "" {
tls.Secret = &troubleshootv1beta2.TLSSecret{Name: secretName, Namespace: secretNamespace}
var err error
client, cfg, err = k8sClientForCollectors()
if err != nil {
return err
}
}
db.TLS = tls
}
c := &collect.CollectPostgres{Collector: db, ClientConfig: cfg, Client: client, Context: cmd.Context()}
res, err := c.Collect(nil)
if err != nil {
return err
}
return printCollectorResult(res)
},
}
f := cmd.Flags()
f.StringVar(&uri, "uri", "", "PostgreSQL connection URI (required)")
cmd.MarkFlagRequired("uri")
f.BoolVar(&skipVerify, "tls-skip-verify", false, "skip TLS certificate verification")
f.StringVar(&caCert, "tls-cacert", "", "CA certificate (PEM contents or a file path)")
f.StringVar(&clientCert, "tls-client-cert", "", "client certificate (PEM contents or a file path)")
f.StringVar(&clientKey, "tls-client-key", "", "client key (PEM contents or a file path)")
f.StringVar(&secretName, "tls-secret-name", "", "name of a Secret holding TLS material (requires cluster access)")
f.StringVar(&secretNamespace, "tls-secret-namespace", "default", "namespace of the TLS Secret")
return cmd
}
+89
View File
@@ -0,0 +1,89 @@
package cli
import (
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
// RedisCmd runs the redis collector against a single instance and prints the
// native result JSON ({"isConnected":..,"version":..,"error":..}).
func RedisCmd() *cobra.Command {
var (
uri string
skipVerify bool
caCert string
clientCert string
clientKey string
secretName string
secretNamespace string
)
cmd := &cobra.Command{
Use: "redis",
Short: "Run the redis collector against an instance",
Long: `Run the redis collector: connect to a Redis server, read "INFO server" (server
version), and print the collector result as JSON.
--uri is a go-redis connection URL:
redis://host:6379 (plaintext)
rediss://user:pass@host:6379 (TLS)
TLS material may be provided inline / by file path (--tls-cacert, --tls-client-cert,
--tls-client-key) or sourced from a Secret (--tls-secret-name), which requires
cluster access.
Example:
collect redis --uri "redis://redis.default.svc:6379"`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
db := &troubleshootv1beta2.Database{
CollectorMeta: troubleshootv1beta2.CollectorMeta{CollectorName: "redis"},
URI: uri,
}
var (
client kubernetes.Interface
cfg *rest.Config
)
if skipVerify || caCert != "" || clientCert != "" || clientKey != "" || secretName != "" {
tls := &troubleshootv1beta2.TLSParams{
SkipVerify: skipVerify,
CACert: caCert,
ClientCert: clientCert,
ClientKey: clientKey,
}
if secretName != "" {
tls.Secret = &troubleshootv1beta2.TLSSecret{Name: secretName, Namespace: secretNamespace}
var err error
client, cfg, err = k8sClientForCollectors()
if err != nil {
return err
}
}
db.TLS = tls
}
c := &collect.CollectRedis{Collector: db, ClientConfig: cfg, Client: client, Context: cmd.Context()}
res, err := c.Collect(nil)
if err != nil {
return err
}
return printCollectorResult(res)
},
}
f := cmd.Flags()
f.StringVar(&uri, "uri", "", "Redis connection URL, e.g. redis://host:6379 (required)")
cmd.MarkFlagRequired("uri")
f.BoolVar(&skipVerify, "tls-skip-verify", false, "skip TLS certificate verification")
f.StringVar(&caCert, "tls-cacert", "", "CA certificate (PEM contents or a file path)")
f.StringVar(&clientCert, "tls-client-cert", "", "client certificate (PEM contents or a file path)")
f.StringVar(&clientKey, "tls-client-key", "", "client key (PEM contents or a file path)")
f.StringVar(&secretName, "tls-secret-name", "", "name of a Secret holding TLS material (requires cluster access)")
f.StringVar(&secretNamespace, "tls-secret-namespace", "default", "namespace of the TLS Secret")
return cmd
}
+17 -4
View File
@@ -19,7 +19,9 @@ func RootCmd() *cobra.Command {
Short: "Run a collector",
Long: `Run a collector and output the results.`,
SilenceUsage: true,
PreRun: func(cmd *cobra.Command, args []string) {
// PersistentPreRun/PersistentPostRun (rather than PreRun/PostRun) so this
// setup also runs for the per-collector subcommands, not just `collect [url]`.
PersistentPreRun: func(cmd *cobra.Command, args []string) {
v := viper.GetViper()
v.BindPFlags(cmd.Flags())
@@ -38,7 +40,7 @@ func RootCmd() *cobra.Command {
return runCollect(v, args[0])
},
PostRun: func(cmd *cobra.Command, args []string) {
PersistentPostRun: func(cmd *cobra.Command, args []string) {
if err := util.StopProfiling(); err != nil {
klog.Errorf("Failed to stop profiling: %v", err)
}
@@ -49,6 +51,17 @@ func RootCmd() *cobra.Command {
cmd.AddCommand(util.VersionCmd())
// Per-collector subcommands: run a single collector and print its native
// result JSON. Each has its own flags and help. These let a collector run
// inside a Pod via the troubleshoot image (e.g. as a runPod collector), so
// the check executes from within the cluster rather than wherever the CLI runs.
cmd.AddCommand(HTTPCmd())
cmd.AddCommand(PostgresCmd())
cmd.AddCommand(MysqlCmd())
cmd.AddCommand(MssqlCmd())
cmd.AddCommand(RedisCmd())
cmd.AddCommand(ClickhouseCmd())
cmd.Flags().StringSlice("redactors", []string{}, "names of the additional redactors to use")
cmd.Flags().Bool("redact", true, "enable/disable default redactions")
cmd.Flags().String("format", "json", "output format, one of json or raw.")
@@ -56,7 +69,7 @@ func RootCmd() *cobra.Command {
cmd.Flags().String("collector-pull-policy", "", "the pull policy of the collector image")
cmd.Flags().String("selector", "", "selector (label query) to filter remote collection nodes on.")
cmd.Flags().Bool("collect-without-permissions", false, "always generate a support bundle, even if it some require additional permissions")
cmd.Flags().Bool("debug", false, "enable debug logging")
cmd.PersistentFlags().Bool("debug", false, "enable debug logging")
cmd.Flags().String("chroot", "", "Chroot to path")
// hidden in favor of the `insecure-skip-tls-verify` flag
@@ -67,7 +80,7 @@ func RootCmd() *cobra.Command {
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
k8sutil.AddFlags(cmd.Flags())
k8sutil.AddFlags(cmd.PersistentFlags())
// Initialize klog flags
logger.InitKlogFlags(cmd)
-49
View File
@@ -59,33 +59,6 @@ builds:
- -installsuffix=netgo
binary: support-bundle
- id: collect
main: ./cmd/collect/main.go
env: [CGO_ENABLED=0]
goos: [linux, darwin, windows]
goarch: [amd64, arm, arm64, riscv64]
ignore:
- goos: windows
goarch: arm
- goos: windows
goarch: riscv64
- goos: darwin
goarch: riscv64
ldflags:
- -s -w
- -X github.com/replicatedhq/troubleshoot/pkg/version.version={{ .Version }}
- -X github.com/replicatedhq/troubleshoot/pkg/version.gitSHA={{ .Commit }}
- -X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{ .Date }}
- -extldflags "-static"
flags:
- -tags=netgo
- -tags=containers_image_ostree_stub
- -tags=exclude_graphdriver_devicemapper
- -tags=exclude_graphdriver_btrfs
- -tags=containers_image_openpgp
- -installsuffix=netgo
binary: collect
archives:
- id: preflight
ids: [preflight]
@@ -160,28 +133,6 @@ archives:
dst: .
strip_parent: true
dockers:
- dockerfile: ./deploy/Dockerfile.troubleshoot
image_templates:
- "replicated/troubleshoot:latest"
- "replicated/troubleshoot:{{ .Major }}"
- "replicated/troubleshoot:{{ .Major }}.{{ .Minor }}"
- "replicated/troubleshoot:{{ .Major }}.{{ .Minor }}.{{ .Patch }}"
ids:
- support-bundle
- preflight
- collect
- dockerfile: ./deploy/Dockerfile.troubleshoot
image_templates:
- "replicated/preflight:latest"
- "replicated/preflight:{{ .Major }}"
- "replicated/preflight:{{ .Major }}.{{ .Minor }}"
- "replicated/preflight:{{ .Major }}.{{ .Minor }}.{{ .Patch }}"
ids:
- support-bundle
- preflight
- collect
universal_binaries:
- id: preflight-universal
ids: [preflight] # refers to the build id above
-13
View File
@@ -1,13 +0,0 @@
FROM debian:bookworm
WORKDIR /
RUN apt-get -qq update \
&& apt-get -qq -y install \
ca-certificates kmod
COPY support-bundle /troubleshoot/support-bundle
COPY preflight /troubleshoot/preflight
COPY collect /troubleshoot/collect
ENV PATH="/troubleshoot:${PATH}"
+38 -30
View File
@@ -1,6 +1,6 @@
module helm-template
go 1.26.1
go 1.26.5
// Always use the local version of troubleshoot so as to build using
// the latest version of the library. This will ensure the example
@@ -9,7 +9,7 @@ replace github.com/replicatedhq/troubleshoot v0.0.0 => ../../../
require (
github.com/replicatedhq/troubleshoot v0.0.0
helm.sh/helm/v3 v3.20.2
helm.sh/helm/v3 v3.21.4
sigs.k8s.io/yaml v1.6.0
)
@@ -17,24 +17,33 @@ require (
dario.cat/mergo v1.0.2 // indirect
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/semver/v3 v3.5.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/cyphar/filepath-securejoin v0.7.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.2 // indirect
github.com/go-logr/logr v1.4.4 // indirect
github.com/go-openapi/jsonpointer v1.0.0 // indirect
github.com/go-openapi/jsonreference v1.0.0 // indirect
github.com/go-openapi/swag v0.28.0 // indirect
github.com/go-openapi/swag/cmdutils v0.28.0 // indirect
github.com/go-openapi/swag/conv v0.28.0 // indirect
github.com/go-openapi/swag/fileutils v0.28.0 // indirect
github.com/go-openapi/swag/jsonutils v0.28.0 // indirect
github.com/go-openapi/swag/loading v0.28.0 // indirect
github.com/go-openapi/swag/mangling v0.28.0 // indirect
github.com/go-openapi/swag/netutils v0.28.0 // indirect
github.com/go-openapi/swag/pools v0.28.0 // indirect
github.com/go-openapi/swag/stringutils v0.28.0 // indirect
github.com/go-openapi/swag/typeutils v0.28.0 // indirect
github.com/go-openapi/swag/yamlutils v0.28.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/gnostic-models v0.7.1 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -45,29 +54,28 @@ require (
github.com/shopspring/decimal v1.4.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/net v0.52.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/crypto v0.55.0 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/term v0.41.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/term v0.45.0 // indirect
golang.org/x/text v0.41.0 // indirect
golang.org/x/time v0.15.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.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/api v0.35.3 // indirect
k8s.io/apiextensions-apiserver v0.35.3 // indirect
k8s.io/apimachinery v0.35.3 // indirect
k8s.io/client-go v0.35.3 // indirect
k8s.io/api v0.36.3 // indirect
k8s.io/apiextensions-apiserver v0.36.3 // indirect
k8s.io/apimachinery v0.36.3 // indirect
k8s.io/client-go v0.36.3 // indirect
k8s.io/klog/v2 v2.140.0 // indirect
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
sigs.k8s.io/controller-runtime v0.23.3 // indirect
k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad // indirect
k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 // indirect
sigs.k8s.io/controller-runtime v0.24.1 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.4.2 // indirect
)
+92 -68
View File
@@ -6,38 +6,66 @@ github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/cyphar/filepath-securejoin v0.7.0 h1:s0Y3ITPy6sQn5xt54DuYvTF8hu134ooYLUb58DX/HjE=
github.com/cyphar/filepath-securejoin v0.7.0/go.mod h1:ymLGms/u3BYaviIiuKFnUx8EkQEZeK6cInNoAPJA3o4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78=
github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxgL3h1H8s=
github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y=
github.com/go-openapi/jsonreference v1.0.0 h1:jlmTr6torcd1YgDQvSfNmRtKzYDO4FGBkrAdlAVWnpY=
github.com/go-openapi/jsonreference v1.0.0/go.mod h1:jtwdyGbJk0Xhe5Y+rwtglQP6Sb1WZST4rT32LWB+sv0=
github.com/go-openapi/swag v0.28.0 h1:xkgbOSKj6DZziNpyqRRAOt3GJGtgjgsd2RoyT30VWuw=
github.com/go-openapi/swag v0.28.0/go.mod h1:4qYnT3Cqr1p1VknOdPo70evN4rgQnAg6jwApHyxSGIg=
github.com/go-openapi/swag/cmdutils v0.28.0 h1:7TOeNtkYru1SG8Y34tDh9WBbLsMqGnptuxWiHREPZ4Q=
github.com/go-openapi/swag/cmdutils v0.28.0/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM=
github.com/go-openapi/swag/conv v0.28.0 h1:GtqqbyFe7vR5Y7ehxG9W6/OvrSFdf1OLeTGp40TqxH8=
github.com/go-openapi/swag/conv v0.28.0/go.mod h1:mbUE+mzctnhxi864m0Q07SpN8OowD9JhxmxuYvZZD/k=
github.com/go-openapi/swag/fileutils v0.28.0 h1:Z04XWQD7R8Eq+7GnOrjovBxPPmZzsS4gt2H2GPGIViU=
github.com/go-openapi/swag/fileutils v0.28.0/go.mod h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8=
github.com/go-openapi/swag/jsonutils v0.28.0 h1:YIch6FwO7RXzeAnbO8Tu7dWBZeUEH+4nA0HXltVTnv4=
github.com/go-openapi/swag/jsonutils v0.28.0/go.mod h1:CYM3WlTUcagR2ZoHdz54di/cbBqt82tuxuXgAjxw+mg=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0 h1:qV+VVUAx5Oro8WjVWpZeql7YReTKhT4smR4zhcOQZr0=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY=
github.com/go-openapi/swag/loading v0.28.0 h1:td8QZdZC9MIYGGSnSPKShKiK22I2tU5UQvuUhIBPRLU=
github.com/go-openapi/swag/loading v0.28.0/go.mod h1:rXB0QiQX5mMveXEA7ouM4KiiM9jVJe4K6BVbwhD1M4k=
github.com/go-openapi/swag/mangling v0.28.0 h1:pH8eyeNO9SLYsTMWJrurnNfKmDa28XrlA+HePVD53VM=
github.com/go-openapi/swag/mangling v0.28.0/go.mod h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w=
github.com/go-openapi/swag/netutils v0.28.0 h1:YXN6TALEi2pzts8/8GNm6T61HTAZsieukGZidap989k=
github.com/go-openapi/swag/netutils v0.28.0/go.mod h1:J+WYyFMLtvtCGqa6jLv+YNUmIKI3ZRQRrvfNDMoQoEQ=
github.com/go-openapi/swag/pools v0.28.0 h1:HPMZWSAfce3rdVTFcjFiCIBtDg9h4x2QlRrHipwhxeU=
github.com/go-openapi/swag/pools v0.28.0/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE=
github.com/go-openapi/swag/stringutils v0.28.0 h1:ixsc9iYgDPubHL/8nSkbnryEHpD2VRlBMLKpQyPXcDU=
github.com/go-openapi/swag/stringutils v0.28.0/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM=
github.com/go-openapi/swag/typeutils v0.28.0 h1:nRBKSBXjDgf01VDPB3fWeD9nQuhCOVeIYAkUx2tbkyY=
github.com/go-openapi/swag/typeutils v0.28.0/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ=
github.com/go-openapi/swag/yamlutils v0.28.0 h1:TV3JXH6DS46KUroDtMLAYHGkdWf5VDq3wVWFirmzROY=
github.com/go-openapi/swag/yamlutils v0.28.0/go.mod h1:x0q/yndZHEgk9Rx3DyDqzFUmHy55KTvIZldvF2dTJXs=
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0 h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0=
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo=
github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug=
github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=
github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
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=
@@ -49,16 +77,12 @@ 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/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
@@ -71,17 +95,17 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/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.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
github.com/onsi/ginkgo/v2 v2.27.4 h1:fcEcQW/A++6aZAZQNUmNjvA9PSOzefMJBerHJ4t8v8Y=
github.com/onsi/ginkgo/v2 v2.27.4/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q=
github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
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/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.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rogpeppe/go-internal v1.15.0 h1:D0RCU5rMAp+SpgkiNdrjfJ+LX4J1M32V2NeCY7EJ6hc=
github.com/rogpeppe/go-internal v1.15.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
@@ -98,32 +122,32 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
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.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
golang.org/x/mod v0.39.0 h1:UF5zwQdCRRUpHfyPwr7d4UrGiVeldIsogtzWVnczL74=
golang.org/x/mod v0.39.0/go.mod h1:bvIbwjQ0HUFFf5AKukeeYQG4ZBUG9yxQbR9aEweIwYY=
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.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.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE=
golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
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=
@@ -135,29 +159,29 @@ 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=
helm.sh/helm/v3 v3.20.2 h1:binM4rvPx5DcNsa1sIt7UZi55lRbu3pZUFmQkSoRh48=
helm.sh/helm/v3 v3.20.2/go.mod h1:Fl1kBaWCpkUrM6IYXPjQ3bdZQfFrogKArqptvueZ6Ww=
k8s.io/api v0.35.3 h1:pA2fiBc6+N9PDf7SAiluKGEBuScsTzd2uYBkA5RzNWQ=
k8s.io/api v0.35.3/go.mod h1:9Y9tkBcFwKNq2sxwZTQh1Njh9qHl81D0As56tu42GA4=
k8s.io/apiextensions-apiserver v0.35.3 h1:2fQUhEO7P17sijylbdwt0nBdXP0TvHrHj0KeqHD8FiU=
k8s.io/apiextensions-apiserver v0.35.3/go.mod h1:tK4Kz58ykRpwAEkXUb634HD1ZAegEElktz/B3jgETd8=
k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8=
k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
k8s.io/client-go v0.35.3 h1:s1lZbpN4uI6IxeTM2cpdtrwHcSOBML1ODNTCCfsP1pg=
k8s.io/client-go v0.35.3/go.mod h1:RzoXkc0mzpWIDvBrRnD+VlfXP+lRzqQjCmKtiwZ8Q9c=
helm.sh/helm/v3 v3.21.4 h1:T/GcIEXU/gNjJnkITlIZ3e9xqkZjhFTmISuStTZ6+Qg=
helm.sh/helm/v3 v3.21.4/go.mod h1:cS2FBb+xfLuaSqvEmbqIeKUVFgHdHVHtVeXb2epof3M=
k8s.io/api v0.36.3 h1:NxB+05W2UGqXWFXcLO0RB5cnqnUPP5v5sVlaOH0Iz4w=
k8s.io/api v0.36.3/go.mod h1:JzLQKqRHC5+I8RVj/lS3lCg0mg6nWI9Fo/Sk3ElxHzg=
k8s.io/apiextensions-apiserver v0.36.3 h1:dPmOAPhwTtqb1bTxbFPsy18KHPhktQeO3WUPXunZIB0=
k8s.io/apiextensions-apiserver v0.36.3/go.mod h1:KTXFqgXiuw2pRoL+Wpmttqc+up9Xt/GohadPWeLLOa4=
k8s.io/apimachinery v0.36.3 h1:PkzMRBRG8joFD8EhCuQAtNPvJlxb82FwplP26HIzvAM=
k8s.io/apimachinery v0.36.3/go.mod h1:cTSjBWgPe/6CQyBKzY/hDIRWCQQQeK0mfLbml0UYFHE=
k8s.io/client-go v0.36.3 h1:M4JdVzXxYcZk4fGpfDdYnxSwhLKWCFoQsHW6t+z8Hfg=
k8s.io/client-go v0.36.3/go.mod h1:gcPwr0c87vjjG6HB6pWEqOeuYVoXSsREjzux2j6GF30=
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80=
sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0=
k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad h1:oXImqH8mQNk7PmvzKhmN3ddJoY6OnyM225MXwGHPm0A=
k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad/go.mod h1:0/mqHCVhlumdJ3BhCfnjSZQE037nAhNodh1/hK0T8/I=
k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 h1:jVkFFVfXdXP74B/zbO3hM3hpSFD0xvhQ5U686DPurkE=
k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3/go.mod h1:M2s5JB1lIYP3jzZdorPLHXIPJzt9vv2muW5a6L9DtNM=
sigs.k8s.io/controller-runtime v0.24.1 h1:miPEwrmirImAvgME1L9qebGHrOnGJoVmVdtOU9fRfo4=
sigs.k8s.io/controller-runtime v0.24.1/go.mod h1:vFkfY5fGt5xAC/sKb8IBFKgWPNKG9OUG29dR8Y2wImw=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
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/v6 v6.3.2-0.20260122202528-d9cc6641c482 h1:2WOzJpHUBVrrkDjU4KBT8n5LDcj824eX0I5UKcgeRUs=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/structured-merge-diff/v6 v6.4.2 h1:qdOxHwrl2Kaag1aQEarlYcOA9vSyGCp3CIki3aW8c4Q=
sigs.k8s.io/structured-merge-diff/v6 v6.4.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
Generated
+61
View File
@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1783224372,
"narHash": "sha256-8i/87eeoqiGE4yOTjwSA3Eh/ziJRQEmd/unYU+K27sk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d407951447dcd00442e97087bf374aad70c04cea",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
+26
View File
@@ -0,0 +1,26 @@
{
description = "Troubleshoot development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
go_1_26
git # required for go VCS stamping during build
python3 # required by the dependency-update validation stage
];
shellHook = ''
echo "Troubleshoot dev $(go version)" # go 1.26.x required
'';
};
});
}
+148 -128
View File
@@ -1,210 +1,232 @@
module github.com/replicatedhq/troubleshoot
go 1.26.2
go 1.26.5
require (
github.com/ClickHouse/clickhouse-go/v2 v2.48.0
github.com/Masterminds/sprig/v3 v3.3.0
github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412
github.com/apparentlymart/go-cidr v1.1.1
github.com/aws/aws-sdk-go-v2 v1.41.6
github.com/aws/aws-sdk-go-v2/credentials v1.19.15
github.com/aws/aws-sdk-go-v2/service/s3 v1.100.0
github.com/aws/aws-sdk-go-v2 v1.43.6
github.com/aws/aws-sdk-go-v2/credentials v1.19.36
github.com/aws/aws-sdk-go-v2/service/s3 v1.107.2
github.com/blang/semver/v4 v4.0.0
github.com/casbin/govaluate v1.10.0
github.com/cilium/ebpf v0.21.0
github.com/cilium/ebpf v0.22.0
github.com/containerd/cgroups/v3 v3.1.3
github.com/fatih/color v1.19.0
github.com/go-logr/logr v1.4.3
github.com/go-logr/logr v1.4.4
github.com/go-redis/redis/v7 v7.4.1
github.com/go-sql-driver/mysql v1.9.3
github.com/go-sql-driver/mysql v1.10.0
github.com/gobwas/glob v0.2.3
github.com/godbus/dbus/v5 v5.2.2
github.com/google/go-containerregistry v0.21.5
github.com/google/go-containerregistry v0.21.9
github.com/google/gofuzz v1.2.0
github.com/google/uuid v1.6.0
github.com/gorilla/handlers v1.5.2
github.com/hashicorp/go-getter v1.8.6
github.com/hashicorp/go-getter v1.8.8
github.com/hashicorp/go-multierror v1.1.1
github.com/jackc/pgx/v5 v5.9.2
github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e
github.com/jackc/pgx/v5 v5.10.0
github.com/longhorn/go-common-libs v0.0.0-20260730002911-add09e6eb92c
github.com/manifoldco/promptui v0.9.0
github.com/mattn/go-isatty v0.0.22
github.com/microsoft/go-mssqldb v1.9.8
github.com/miekg/dns v1.1.72
github.com/mattn/go-isatty v0.0.24
github.com/microsoft/go-mssqldb v1.10.0
github.com/miekg/dns v1.1.73
github.com/opencontainers/image-spec v1.1.1
github.com/pkg/errors v0.9.1
github.com/replicatedhq/termui/v3 v3.1.1-0.20200811145416-f40076d26851
github.com/segmentio/ksuid v1.0.4
github.com/shirou/gopsutil/v4 v4.26.3
github.com/shirou/gopsutil/v4 v4.26.7
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
github.com/spf13/viper v1.21.0
github.com/stretchr/testify v1.11.1
github.com/stretchr/testify v1.12.1
github.com/tj/go-spin v1.1.0
github.com/vishvananda/netlink v1.3.1
github.com/vishvananda/netns v0.0.5
github.com/vmware-tanzu/velero v1.18.0
go.opentelemetry.io/otel v1.43.0
go.opentelemetry.io/otel/sdk v1.43.0
github.com/vmware-tanzu/velero v1.18.2
go.opentelemetry.io/otel v1.45.0
go.opentelemetry.io/otel/sdk v1.45.0
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f
golang.org/x/mod v0.35.0
golang.org/x/sync v0.20.0
golang.org/x/mod v0.40.0
golang.org/x/sync v0.22.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.36.0
k8s.io/apiextensions-apiserver v0.36.0
k8s.io/apimachinery v0.36.0
k8s.io/apiserver v0.36.0
k8s.io/cli-runtime v0.36.0
k8s.io/client-go v0.36.0
k8s.io/api v0.36.3
k8s.io/apiextensions-apiserver v0.36.3
k8s.io/apimachinery v0.36.3
k8s.io/apiserver v0.36.3
k8s.io/cli-runtime v0.36.3
k8s.io/client-go v0.36.3
k8s.io/klog/v2 v2.140.0
k8s.io/streaming v0.36.0
oras.land/oras-go/v2 v2.6.0
sigs.k8s.io/controller-runtime v0.23.3
k8s.io/streaming v0.36.3
oras.land/oras-go/v2 v2.6.2
sigs.k8s.io/controller-runtime v0.24.1
sigs.k8s.io/e2e-framework v0.7.0
)
require (
cel.dev/expr v0.25.1 // indirect
cloud.google.com/go/auth v0.18.2 // indirect
cel.dev/expr v0.25.2 // indirect
cloud.google.com/go/auth v0.22.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
cloud.google.com/go/monitoring v1.24.3 // indirect
cloud.google.com/go/monitoring v1.30.0 // indirect
dario.cat/mergo v1.0.2 // indirect
filippo.io/edwards25519 v1.1.1 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect
filippo.io/edwards25519 v1.2.0 // indirect
github.com/ClickHouse/ch-go v0.74.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.35.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.59.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.59.0 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/semver/v3 v3.5.0 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/ProtonMail/go-crypto v1.4.1 // indirect
github.com/andybalholm/brotli v1.2.2 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.9 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.14 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.22 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.10 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.16 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.42.0 // indirect
github.com/aws/smithy-go v1.25.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/avast/retry-go/v4 v4.7.0 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.18 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.35 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.37 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.37 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.37 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.38 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.30 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.37 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.38 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.5.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.33.6 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.38.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.45.6 // indirect
github.com/aws/smithy-go v1.27.8 // indirect
github.com/chai2010/gettext-go v1.0.3 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
github.com/cockroachdb/errors v1.14.0 // indirect
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect
github.com/cockroachdb/redact v1.1.8 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/coreos/go-systemd/v22 v22.7.0 // indirect
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/distribution/distribution/v3 v3.1.0 // indirect
github.com/docker/cli v29.4.0+incompatible // indirect
github.com/ebitengine/purego v0.10.0 // indirect
github.com/cyphar/filepath-securejoin v0.7.0 // indirect
github.com/docker/cli v29.7.1+incompatible // indirect
github.com/ebitengine/purego v0.10.2 // indirect
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.2 // indirect
github.com/getsentry/sentry-go v0.48.0 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.8.0 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/go-openapi/swag/cmdutils v0.28.0 // indirect
github.com/go-openapi/swag/conv v0.28.0 // indirect
github.com/go-openapi/swag/fileutils v0.28.0 // indirect
github.com/go-openapi/swag/jsonutils v0.28.0 // indirect
github.com/go-openapi/swag/loading v0.28.0 // indirect
github.com/go-openapi/swag/mangling v0.28.0 // indirect
github.com/go-openapi/swag/netutils v0.28.0 // indirect
github.com/go-openapi/swag/pools v0.28.0 // indirect
github.com/go-openapi/swag/stringutils v0.28.0 // indirect
github.com/go-openapi/swag/typeutils v0.28.0 // indirect
github.com/go-openapi/swag/yamlutils v0.28.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/gnostic-models v0.7.1 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.19 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72 // indirect
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.74 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jmoiron/sqlx v1.4.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/lib/pq v1.12.3 // indirect
github.com/lufia/plan9stats v0.0.0-20260801144041-2fc331e7910f // indirect
github.com/mattn/go-sqlite3 v1.14.32 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/paulmach/orb v0.13.0 // indirect
github.com/pierrec/lz4/v4 v4.1.27 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/rogpeppe/go-internal v1.15.0 // indirect
github.com/rubenv/sql-migrate v1.8.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
github.com/segmentio/asm v1.2.1 // indirect
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/spiffe/go-spiffe/v2 v2.8.1 // indirect
github.com/stretchr/objx v0.5.3 // indirect
github.com/ulikunitz/xz v0.5.16 // indirect
github.com/vladimirvivien/gexe v0.5.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.42.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/tools v0.44.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.44.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect
go.opentelemetry.io/otel/metric v1.45.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.45.0 // indirect
go.opentelemetry.io/otel/trace v1.45.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260729162451-8efbd57d26e0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gotest.tools/v3 v3.5.2 // indirect
k8s.io/component-base v0.36.0 // indirect
k8s.io/kubectl v0.36.0 // indirect
k8s.io/component-base v0.36.3 // indirect
k8s.io/kubectl v0.36.3 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.4.2 // indirect
)
require (
cloud.google.com/go v0.123.0 // indirect
cloud.google.com/go/iam v1.5.3 // indirect
cloud.google.com/go/storage v1.61.3 // indirect
cloud.google.com/go/iam v1.12.0 // indirect
cloud.google.com/go/storage v1.64.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f // indirect
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/containerd/containerd v1.7.30 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/docker-credential-helpers v0.9.5 // indirect
github.com/docker/docker-credential-helpers v0.9.8 // indirect
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/felixge/httpsnoop v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.10.1 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-openapi/jsonpointer v1.0.0 // indirect
github.com/go-openapi/jsonreference v1.0.0 // indirect
github.com/go-openapi/swag v0.28.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/googleapis/gax-go/v2 v2.17.0 // indirect
github.com/googleapis/gax-go/v2 v2.23.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-version v1.9.0
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
github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/compress v1.19.2 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-colorable v0.1.15 // indirect
github.com/mattn/go-runewidth v0.0.27 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1
github.com/moby/spdystream v0.5.1 // indirect
@@ -213,43 +235,41 @@ require (
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect
github.com/nsf/termbox-go v1.1.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/runtime-spec v1.3.0
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.20.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.11.0 // indirect
github.com/vbatts/tar-split v0.12.2 // indirect
github.com/tklauser/go-sysconf v0.4.0 // indirect
github.com/tklauser/numcpus v0.12.0 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/net v0.53.0
golang.org/x/crypto v0.55.0 // indirect
golang.org/x/net v0.58.0
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sys v0.43.0
golang.org/x/term v0.42.0 // indirect
golang.org/x/text v0.36.0
golang.org/x/sys v0.47.0
golang.org/x/term v0.45.0 // indirect
golang.org/x/text v0.41.0
golang.org/x/time v0.15.0 // indirect
google.golang.org/api v0.271.0 // indirect
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/api v0.292.0 // indirect
google.golang.org/genproto v0.0.0-20260729162451-8efbd57d26e0 // indirect
google.golang.org/grpc v1.83.0 // indirect
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
helm.sh/helm/v3 v3.20.2
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
k8s.io/kubelet v0.36.0
k8s.io/metrics v0.36.0
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2
helm.sh/helm/v3 v3.21.4
k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad // indirect
k8s.io/kubelet v0.36.3
k8s.io/metrics v0.36.3
k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3
periph.io/x/host/v3 v3.8.5
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/kustomize/api v0.21.1 // indirect
+384 -312
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -238,6 +238,8 @@ func GetAnalyzer(analyzer *troubleshootv1beta2.Analyze) Analyzer {
return &AnalyzeMssql{analyzer: analyzer.Mssql}
case analyzer.Redis != nil:
return &AnalyzeRedis{analyzer: analyzer.Redis}
case analyzer.ClickHouse != nil:
return &AnalyzeClickhouse{analyzer: analyzer.ClickHouse}
case analyzer.CephStatus != nil:
return &AnalyzeCephStatus{analyzer: analyzer.CephStatus}
case analyzer.Velero != nil:
+193
View File
@@ -0,0 +1,193 @@
package analyzer
import (
"encoding/json"
"fmt"
"path"
"strconv"
"strings"
"github.com/hashicorp/go-version"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
type AnalyzeClickhouse struct {
analyzer *troubleshootv1beta2.DatabaseAnalyze
}
func (a *AnalyzeClickhouse) Title() string {
title := a.analyzer.CheckName
if title == "" {
title = a.collectorName()
}
return title
}
func (a *AnalyzeClickhouse) IsExcluded() (bool, error) {
return isExcluded(a.analyzer.Exclude)
}
func (a *AnalyzeClickhouse) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
result, err := a.analyze(a.analyzer, getFile)
if err != nil {
return nil, err
}
result.Strict = a.analyzer.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
func (a *AnalyzeClickhouse) collectorName() string {
if a.analyzer.CollectorName != "" {
return a.analyzer.CollectorName
}
return "clickhouse"
}
func compareClickhouseConditionalToActual(conditional string, result *collect.DatabaseConnection) (bool, error) {
parts := strings.Split(strings.TrimSpace(conditional), " ")
if len(parts) != 3 {
return false, errors.New("unable to parse conditional")
}
switch parts[0] {
case "connected":
expected, err := strconv.ParseBool(parts[2])
if err != nil {
return false, errors.Wrap(err, "failed to parse bool")
}
switch parts[1] {
case "=", "==", "===":
return expected == result.IsConnected, nil
case "!=", "!==":
return expected != result.IsConnected, nil
}
return false, errors.New("unable to parse ClickHouse connected analyzer")
case "version":
expected, err := version.NewVersion(strings.ReplaceAll(parts[2], "x", "0"))
if err != nil {
return false, errors.Wrap(err, "failed to parse expected version")
}
operation := parts[1]
switch operation {
case "=", "==", "===":
operation = "="
case "!=", "!==":
operation = "!="
}
actual, err := version.NewVersion(strings.ReplaceAll(result.Version, "x", "0"))
if err != nil {
return false, errors.Wrap(err, "failed to parse ClickHouse db actual version")
}
constraints, err := version.NewConstraint(fmt.Sprintf("%s %s", operation, expected))
if err != nil {
return false, errors.Wrap(err, "failed to create constraint")
}
return constraints.Check(actual), nil
}
return false, nil
}
func (a *AnalyzeClickhouse) analyze(analyzer *troubleshootv1beta2.DatabaseAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
fullPath := path.Join("", fmt.Sprintf("clickhouse/%s.json", a.collectorName()))
collected, err := getCollectedFileContents(fullPath)
if err != nil {
return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath)
}
databaseConnection := collect.DatabaseConnection{}
if err := json.Unmarshal(collected, &databaseConnection); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal database connection result")
}
result := &AnalyzeResult{
Title: a.Title(),
IconKey: "kubernetes_clickhouse_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/clickhouse-analyze.svg",
}
for _, outcome := range analyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return result, nil
}
isMatch, err := compareClickhouseConditionalToActual(outcome.Fail.When, &databaseConnection)
if err != nil {
return result, errors.Wrap(err, "failed to compare ClickHouse database conditional")
}
if isMatch {
if databaseConnection.Error != "" {
result.Message = outcome.Fail.Message + " " + databaseConnection.Error
} else {
result.Message = outcome.Fail.Message
}
result.IsFail = true
result.URI = outcome.Fail.URI
return result, nil
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return result, nil
}
isMatch, err := compareClickhouseConditionalToActual(outcome.Warn.When, &databaseConnection)
if err != nil {
return result, errors.Wrap(err, "failed to compare ClickHouse database conditional")
}
if isMatch {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return result, nil
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return result, nil
}
isMatch, err := compareClickhouseConditionalToActual(outcome.Pass.When, &databaseConnection)
if err != nil {
return result, errors.Wrap(err, "failed to compare ClickHouse database conditional")
}
if isMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return result, nil
}
}
}
return result, nil
}
+4
View File
@@ -699,6 +699,10 @@ func (e *DefaultAnalysisEngine) convertAnalyzerToSpec(analyzer *troubleshootv1be
spec.Name = "redis"
spec.Type = "database"
spec.Config["analyzer"] = analyzer.Redis
case analyzer.ClickHouse != nil:
spec.Name = "clickhouse"
spec.Type = "database"
spec.Config["analyzer"] = analyzer.ClickHouse
// ✅ Storage analyzers
case analyzer.CephStatus != nil:
+15
View File
@@ -219,6 +219,8 @@ func compareNodeResourceConditionalToActual(conditional string, matchingNodes []
switch function {
case "count":
actualValue = len(matchingNodes)
case "countDistinct":
actualValue = countDistinctLabelValues(matchingNodes, property)
case "min":
actualValue = findMin(matchingNodes, property, resourceName)
case "max":
@@ -368,6 +370,19 @@ func getQuantity(node corev1.Node, property string, resourceName string) *resour
return nil
}
// countDistinctLabelValues returns the number of distinct values of labelKey
// across the given nodes. Nodes missing the label are ignored, so an absent
// label yields 0.
func countDistinctLabelValues(nodes []corev1.Node, labelKey string) int {
seen := map[string]struct{}{}
for _, node := range nodes {
if v, ok := node.Labels[labelKey]; ok {
seen[v] = struct{}{}
}
}
return len(seen)
}
func findSum(nodes []corev1.Node, property string, resourceName string) *resource.Quantity {
sum := resource.Quantity{}
+110
View File
@@ -1666,6 +1666,116 @@ func Test_analyzeNodeResources(t *testing.T) {
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "countDistinct spans at least 3 instance types", // countDistinct pass path across all nodes
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "instance-type spread",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "countDistinct(node.kubernetes.io/instance-type) < 3",
Message: "Fewer than 3 distinct instance types.",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "At least 3 distinct instance types.",
URI: "",
},
},
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "instance-type spread",
Message: "At least 3 distinct instance types.",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "countDistinct only counts filtered nodes", // filtering to one pool leaves a single distinct value
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "filtered instance-type spread",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "countDistinct(node.kubernetes.io/instance-type) < 3",
Message: "Fewer than 3 distinct instance types.",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "At least 3 distinct instance types.",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "node.kubernetes.io/instance-type",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"s-2vcpu-4gb"},
},
},
},
},
},
want: &AnalyzeResult{
IsPass: false,
IsFail: false,
IsWarn: true,
Title: "filtered instance-type spread",
Message: "Fewer than 3 distinct instance types.",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "countDistinct is 0 when the label is absent", // AIR-238 zone syntax; fixture nodes carry no zone label
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "zone spread",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "countDistinct(topology.kubernetes.io/zone) < 3",
Message: "Nodes span fewer than 3 availability zones.",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "Nodes span at least 3 availability zones.",
URI: "",
},
},
},
},
want: &AnalyzeResult{
IsPass: false,
IsFail: false,
IsWarn: true,
Title: "zone spread",
Message: "Nodes span fewer than 3 availability zones.",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
}
getExampleNodeContents := func(nodeName string) ([]byte, error) {
+54 -25
View File
@@ -30,6 +30,9 @@ func (a *AnalyzeSecret) Analyze(getFile getCollectedFileContents, findFiles getC
if err != nil {
return nil, err
}
if result == nil {
return nil, nil
}
result.Strict = a.analyzer.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
@@ -54,42 +57,68 @@ func (a *AnalyzeSecret) analyzeSecret(analyzer *troubleshootv1beta2.AnalyzeSecre
return nil, err
}
// The secret analyzer only supports fail (not found) and pass (found) outcomes
// per https://troubleshoot.sh/docs/analyze/secrets. If the spec contains
// neither, return an explicit error: returning (nil, nil) is swallowed by the
// Analyze wrapper into an empty result slice, so the misconfiguration would
// surface as neither a result nor an error.
// Capture fail and pass independently: a single outcome object may set both,
// so an else-if here would silently drop the second one.
var failOutcome, passOutcome *troubleshootv1beta2.SingleOutcome
for _, outcome := range analyzer.Outcomes {
if outcome.Fail != nil {
failOutcome = outcome.Fail
}
if outcome.Pass != nil {
passOutcome = outcome.Pass
}
}
if failOutcome == nil && passOutcome == nil {
return nil, fmt.Errorf("secret analyzer %s/%s must define at least one pass or fail outcome", analyzer.Namespace, analyzer.SecretName)
}
result := AnalyzeResult{
Title: a.Title(),
IconKey: "kubernetes_analyze_secret",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/secret.svg?w=13&h=16",
}
var failOutcome *troubleshootv1beta2.Outcome
for _, outcome := range analyzer.Outcomes {
if outcome.Fail != nil {
failOutcome = outcome
}
secretFound := foundSecret.SecretExists
if secretFound && analyzer.Key != "" {
secretFound = foundSecret.Key == analyzer.Key && foundSecret.KeyExists
}
if !foundSecret.SecretExists {
// Use the matched branch's configured outcome verbatim, tracking whether one
// was actually present. A configured outcome with an intentionally empty
// message (e.g. a URI-only outcome) is preserved as-is. But when the matched
// branch has NO configured outcome at all — e.g. a pass-only spec that took
// the fail path, or a fail-only spec that passed — the empty message is not
// an intentional choice, so fall back to a default diagnostic. An absent
// outcome is not the same as an intentionally empty one.
outcomeConfigured := false
if secretFound {
result.IsPass = true
if passOutcome != nil {
result.Message = passOutcome.Message
result.URI = passOutcome.URI
outcomeConfigured = true
}
} else {
result.IsFail = true
result.Message = failOutcome.Fail.Message
result.URI = failOutcome.Fail.URI
return &result, nil
}
if analyzer.Key != "" {
if foundSecret.Key != analyzer.Key || !foundSecret.KeyExists {
result.IsFail = true
result.Message = failOutcome.Fail.Message
result.URI = failOutcome.Fail.URI
return &result, nil
if failOutcome != nil {
result.Message = failOutcome.Message
result.URI = failOutcome.URI
outcomeConfigured = true
}
}
result.IsPass = true
for _, outcome := range analyzer.Outcomes {
if outcome.Pass != nil {
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
if !outcomeConfigured {
switch {
case result.IsPass:
result.Message = fmt.Sprintf("Secret %s was found in namespace %s", analyzer.SecretName, analyzer.Namespace)
case analyzer.Key != "" && foundSecret.SecretExists:
result.Message = fmt.Sprintf("Key %s was not found in secret %s/%s", analyzer.Key, analyzer.Namespace, analyzer.SecretName)
default:
result.Message = fmt.Sprintf("Secret %s was not found in namespace %s", analyzer.SecretName, analyzer.Namespace)
}
}
+234
View File
@@ -166,6 +166,118 @@ func Test_analyzeSecret(t *testing.T) {
IconURI: "https://troubleshoot.sh/images/analyzer-icons/secret.svg?w=13&h=16",
},
},
{
name: "not found with no fail outcome falls back to a default message (no configured outcome for this branch)",
analyzer: &troubleshootv1beta2.AnalyzeSecret{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "Optional Secret",
},
Namespace: "default",
SecretName: "does-not-exist",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "secret found",
},
},
},
},
mockFiles: map[string][]byte{
"secrets/default/does-not-exist.json": mustJSONMarshalIndent(t, collect.SecretOutput{
Namespace: "default",
Name: "does-not-exist",
SecretExists: false,
}),
},
want: &AnalyzeResult{
IsFail: true,
Message: "Secret does-not-exist was not found in namespace default",
Title: "Optional Secret",
IconKey: "kubernetes_analyze_secret",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/secret.svg?w=13&h=16",
},
},
{
name: "key not found with no fail outcome falls back to a default message (no configured outcome for this branch)",
analyzer: &troubleshootv1beta2.AnalyzeSecret{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "Optional Secret Key",
},
Namespace: "test-namespace",
SecretName: "test-secret",
Key: "missing-key",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "key found",
},
},
},
},
mockFiles: map[string][]byte{
"secrets/test-namespace/test-secret/missing-key.json": mustJSONMarshalIndent(t, collect.SecretOutput{
Namespace: "test-namespace",
Name: "test-secret",
Key: "missing-key",
SecretExists: true,
KeyExists: false,
}),
},
want: &AnalyzeResult{
IsFail: true,
Message: "Key missing-key was not found in secret test-namespace/test-secret",
Title: "Optional Secret Key",
IconKey: "kubernetes_analyze_secret",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/secret.svg?w=13&h=16",
},
},
{
name: "found with only fail outcome configured falls back to the default pass message, not the fail outcome's message",
analyzer: &troubleshootv1beta2.AnalyzeSecret{
Namespace: "test-namespace",
SecretName: "test-secret",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "Not found",
},
},
},
},
mockFiles: map[string][]byte{
"secrets/test-namespace/test-secret.json": mustJSONMarshalIndent(t, collect.SecretOutput{
Namespace: "test-namespace",
Name: "test-secret",
SecretExists: true,
}),
},
want: &AnalyzeResult{
IsPass: true,
Message: "Secret test-secret was found in namespace test-namespace",
Title: "Secret test-secret",
IconKey: "kubernetes_analyze_secret",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/secret.svg?w=13&h=16",
},
},
{
name: "spec with neither fail nor pass outcome returns an error so the framework surfaces the misconfiguration",
analyzer: &troubleshootv1beta2.AnalyzeSecret{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "Misconfigured",
},
Namespace: "default",
SecretName: "does-not-exist",
Outcomes: []*troubleshootv1beta2.Outcome{},
},
mockFiles: map[string][]byte{
"secrets/default/does-not-exist.json": mustJSONMarshalIndent(t, collect.SecretOutput{
Namespace: "default",
Name: "does-not-exist",
SecretExists: false,
}),
},
wantErr: true,
},
{
name: "key not found secret not found",
analyzer: &troubleshootv1beta2.AnalyzeSecret{
@@ -190,6 +302,128 @@ func Test_analyzeSecret(t *testing.T) {
},
wantErr: true, // TODO: should this be a not found error? This will not work with selectors.
},
{
name: "combined fail and pass in a single outcome, secret found uses the pass outcome",
analyzer: &troubleshootv1beta2.AnalyzeSecret{
Namespace: "test-namespace",
SecretName: "test-secret",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "Not found",
},
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "Found",
URI: "https://example.com/found",
},
},
},
},
mockFiles: map[string][]byte{
"secrets/test-namespace/test-secret.json": mustJSONMarshalIndent(t, collect.SecretOutput{
Namespace: "test-namespace",
Name: "test-secret",
SecretExists: true,
}),
},
want: &AnalyzeResult{
IsPass: true,
Message: "Found",
URI: "https://example.com/found",
Title: "Secret test-secret",
IconKey: "kubernetes_analyze_secret",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/secret.svg?w=13&h=16",
},
},
{
name: "combined fail and pass in a single outcome, secret not found uses the fail outcome",
analyzer: &troubleshootv1beta2.AnalyzeSecret{
Namespace: "test-namespace",
SecretName: "test-secret",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "Not found",
},
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "Found",
},
},
},
},
mockFiles: map[string][]byte{
"secrets/test-namespace/test-secret.json": mustJSONMarshalIndent(t, collect.SecretOutput{
Namespace: "test-namespace",
Name: "test-secret",
SecretExists: false,
}),
},
want: &AnalyzeResult{
IsFail: true,
Message: "Not found",
Title: "Secret test-secret",
IconKey: "kubernetes_analyze_secret",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/secret.svg?w=13&h=16",
},
},
{
name: "secret found with URI-only pass outcome preserves the empty message and URI",
analyzer: &troubleshootv1beta2.AnalyzeSecret{
Namespace: "test-namespace",
SecretName: "test-secret",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
URI: "https://example.com/pass",
},
},
},
},
mockFiles: map[string][]byte{
"secrets/test-namespace/test-secret.json": mustJSONMarshalIndent(t, collect.SecretOutput{
Namespace: "test-namespace",
Name: "test-secret",
SecretExists: true,
}),
},
want: &AnalyzeResult{
IsPass: true,
Message: "",
URI: "https://example.com/pass",
Title: "Secret test-secret",
IconKey: "kubernetes_analyze_secret",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/secret.svg?w=13&h=16",
},
},
{
name: "secret not found with URI-only fail outcome preserves the empty message and URI",
analyzer: &troubleshootv1beta2.AnalyzeSecret{
Namespace: "test-namespace",
SecretName: "test-secret",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
URI: "https://example.com/fail",
},
},
},
},
mockFiles: map[string][]byte{
"secrets/test-namespace/test-secret.json": mustJSONMarshalIndent(t, collect.SecretOutput{
Namespace: "test-namespace",
Name: "test-secret",
SecretExists: false,
}),
},
want: &AnalyzeResult{
IsFail: true,
Message: "",
URI: "https://example.com/fail",
Title: "Secret test-secret",
IconKey: "kubernetes_analyze_secret",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/secret.svg?w=13&h=16",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -305,6 +305,7 @@ type Analyze struct {
Mssql *DatabaseAnalyze `json:"mssql,omitempty" yaml:"mssql,omitempty"`
Mysql *DatabaseAnalyze `json:"mysql,omitempty" yaml:"mysql,omitempty"`
Redis *DatabaseAnalyze `json:"redis,omitempty" yaml:"redis,omitempty"`
ClickHouse *DatabaseAnalyze `json:"clickhouse,omitempty" yaml:"clickhouse,omitempty"`
CephStatus *CephStatusAnalyze `json:"cephStatus,omitempty" yaml:"cephStatus,omitempty"`
Velero *VeleroAnalyze `json:"velero,omitempty" yaml:"velero,omitempty"`
Longhorn *LonghornAnalyze `json:"longhorn,omitempty" yaml:"longhorn,omitempty"`
@@ -353,6 +353,7 @@ type Collect struct {
Mssql *Database `json:"mssql,omitempty" yaml:"mssql,omitempty"`
Mysql *Database `json:"mysql,omitempty" yaml:"mysql,omitempty"`
Redis *Database `json:"redis,omitempty" yaml:"redis,omitempty"`
ClickHouse *Database `json:"clickhouse,omitempty" yaml:"clickhouse,omitempty"`
Collectd *Collectd `json:"collectd,omitempty" yaml:"collectd,omitempty"`
Ceph *Ceph `json:"ceph,omitempty" yaml:"ceph,omitempty"`
Longhorn *Longhorn `json:"longhorn,omitempty" yaml:"longhorn,omitempty"`
@@ -680,6 +681,10 @@ func (c *Collect) GetName() string {
collector = "redis"
name = c.Redis.CollectorName
}
if c.ClickHouse != nil {
collector = "clickhouse"
name = c.ClickHouse.CollectorName
}
if c.Collectd != nil {
collector = "collectd"
name = c.Collectd.CollectorName
@@ -175,6 +175,11 @@ func (in *Analyze) DeepCopyInto(out *Analyze) {
*out = new(DatabaseAnalyze)
(*in).DeepCopyInto(*out)
}
if in.ClickHouse != nil {
in, out := &in.ClickHouse, &out.ClickHouse
*out = new(DatabaseAnalyze)
(*in).DeepCopyInto(*out)
}
if in.CephStatus != nil {
in, out := &in.CephStatus, &out.CephStatus
*out = new(CephStatusAnalyze)
@@ -935,6 +940,11 @@ func (in *Collect) DeepCopyInto(out *Collect) {
*out = new(Database)
(*in).DeepCopyInto(*out)
}
if in.ClickHouse != nil {
in, out := &in.ClickHouse, &out.ClickHouse
*out = new(Database)
(*in).DeepCopyInto(*out)
}
if in.Collectd != nil {
in, out := &in.Collectd, &out.Collectd
*out = new(Collectd)
@@ -68,10 +68,11 @@ type HostCollect struct {
// For phase 1, we're focusing on Database collectors with StringOrValueFrom support
type Collect struct {
// Database collectors with v1beta3 StringOrValueFrom support
Postgres *Database `json:"postgres,omitempty" yaml:"postgres,omitempty"`
Mssql *Database `json:"mssql,omitempty" yaml:"mssql,omitempty"`
Mysql *Database `json:"mysql,omitempty" yaml:"mysql,omitempty"`
Redis *Database `json:"redis,omitempty" yaml:"redis,omitempty"`
Postgres *Database `json:"postgres,omitempty" yaml:"postgres,omitempty"`
Mssql *Database `json:"mssql,omitempty" yaml:"mssql,omitempty"`
Mysql *Database `json:"mysql,omitempty" yaml:"mysql,omitempty"`
Redis *Database `json:"redis,omitempty" yaml:"redis,omitempty"`
ClickHouse *Database `json:"clickhouse,omitempty" yaml:"clickhouse,omitempty"`
// TODO: Add remaining collector types as we expand v1beta3 support
// For now, these are placeholders to make the types compile
@@ -81,6 +81,14 @@ func convertCollector(
v2collector.Redis = db
}
if v3collector.ClickHouse != nil {
db, err := convertDatabase(ctx, v3collector.ClickHouse, client, defaultNamespace)
if err != nil {
return nil, fmt.Errorf("failed to convert clickhouse collector: %w", err)
}
v2collector.ClickHouse = db
}
// TODO: Add conversion for other collector types as v1beta3 support expands
return v2collector, nil
@@ -171,7 +171,17 @@ func TestConvertToV1Beta2WithResolution_MultipleDatabases(t *testing.T) {
},
}
client := fake.NewSimpleClientset(pgSecret, mysqlSecret, redisSecret)
clickhouseSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "clickhouse-secret",
Namespace: "default",
},
Data: map[string][]byte{
"uri": []byte("http://clickhouse.example.com:9000"),
},
}
client := fake.NewSimpleClientset(pgSecret, mysqlSecret, redisSecret, clickhouseSecret)
v3spec := &SupportBundleSpec{
Collectors: []*Collect{
@@ -211,13 +221,25 @@ func TestConvertToV1Beta2WithResolution_MultipleDatabases(t *testing.T) {
},
},
},
{
ClickHouse: &Database{
URI: StringOrValueFrom{
ValueFrom: &ValueFromSource{
SecretKeyRef: &SecretKeyRef{
Name: "clickhouse-secret",
Key: "uri",
},
},
},
},
},
},
}
v2spec, err := ConvertToV1Beta2WithResolution(context.Background(), v3spec, client, "default")
require.NoError(t, err)
require.NotNil(t, v2spec)
require.Len(t, v2spec.Collectors, 3)
require.Len(t, v2spec.Collectors, 4)
require.NotNil(t, v2spec.Collectors[0].Postgres)
assert.Equal(t, "postgresql://user:pass@pg.example.com:5432/db", v2spec.Collectors[0].Postgres.URI)
@@ -227,6 +249,8 @@ func TestConvertToV1Beta2WithResolution_MultipleDatabases(t *testing.T) {
require.NotNil(t, v2spec.Collectors[2].Redis)
assert.Equal(t, "redis://redis.example.com:6379", v2spec.Collectors[2].Redis.URI)
require.NotNil(t, v2spec.Collectors[3].ClickHouse)
assert.Equal(t, "http://clickhouse.example.com:9000", v2spec.Collectors[3].ClickHouse.URI)
}
func TestConvertToV1Beta2WithResolution_SecretNotFound(t *testing.T) {
@@ -79,6 +79,11 @@ func (in *Collect) DeepCopyInto(out *Collect) {
*out = new(Database)
(*in).DeepCopyInto(*out)
}
if in.ClickHouse != nil {
in, out := &in.ClickHouse, &out.ClickHouse
*out = new(Database)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Collect.
+109
View File
@@ -0,0 +1,109 @@
package collect
import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/ClickHouse/clickhouse-go/v2"
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
var _ Collector = &CollectClickhouse{}
type CollectClickhouse struct {
Collector *troubleshootv1beta2.Database
BundlePath string
Namespace string
ClientConfig *rest.Config
Client kubernetes.Interface
Context context.Context
RBACErrors
}
func (c *CollectClickhouse) Title() string {
return getCollectorName(c)
}
func (c *CollectClickhouse) IsExcluded() (bool, error) {
return isExcluded(c.Collector.Exclude)
}
func (c *CollectClickhouse) createConnectConfig() (*clickhouse.Options, error) {
if c.Collector.URI == "" {
return nil, errors.New("clickhouse uri cannot be empty")
}
opts, err := clickhouse.ParseDSN(c.Collector.URI)
if err != nil {
return nil, errors.Wrap(err, "couldn't parse clickhouse URI")
}
if c.Collector.TLS != nil {
tlsCfg, err := createTLSConfig(c.Context, c.Client, c.Collector.TLS)
if err != nil {
return nil, err
}
opts.TLS = tlsCfg
}
return opts, nil
}
func (c *CollectClickhouse) connect() (driver.Conn, error) {
config, err := c.createConnectConfig()
if err != nil {
return nil, errors.Wrap(err, "failed to create clickhouse connection config")
}
conn, err := clickhouse.Open(config)
if err != nil {
return nil, errors.Wrap(err, "failed to open clickhouse connection")
}
return conn, nil
}
func (c *CollectClickhouse) Collect(progressChan chan<- interface{}) (CollectorResult, error) {
databaseConnection := DatabaseConnection{}
conn, err := c.connect()
if err != nil {
databaseConnection.Error = err.Error()
} else {
defer conn.Close()
if err := conn.Ping(c.Context); err != nil {
databaseConnection.Error = err.Error()
} else {
var version string
err := conn.QueryRow(c.Context, "SELECT version()").Scan(&version)
if err != nil {
databaseConnection.Error = err.Error()
} else {
databaseConnection.Version = version
databaseConnection.IsConnected = true
}
}
}
b, err := json.Marshal(databaseConnection)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal database connection")
}
collectorName := c.Collector.CollectorName
if collectorName == "" {
collectorName = "clickhouse"
}
output := NewResult()
output.SaveResult(c.BundlePath, fmt.Sprintf("clickhouse/%s.json", collectorName), bytes.NewBuffer(b))
return output, nil
}
+17 -11
View File
@@ -294,7 +294,18 @@ func (c *CollectClusterResources) Collect(progressChan chan<- interface{}) (Coll
// crs
customResources, crErrors := crs(ctx, dynamicClient, client, c.ClientConfig, namespaceNames)
for k, v := range customResources {
output.SaveResult(c.BundlePath, path.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_CUSTOM_RESOURCES, k), bytes.NewBuffer(v))
jsonPath := path.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_CUSTOM_RESOURCES, k)
output.SaveResult(c.BundlePath, jsonPath, bytes.NewBuffer(v))
// Keep a YAML symlink for backward compatibility. It points at the JSON
// file, so any analyzer that expects YAML can still read it, and the
// content is redacted through the JSON copy.
if strings.HasSuffix(k, ".json") {
yamlPath := path.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_CUSTOM_RESOURCES, strings.TrimSuffix(k, ".json")+".yaml")
if err := output.SymLinkResult(c.BundlePath, yamlPath, jsonPath); err != nil {
klog.V(2).Infof("failed to create YAML symlink for %s: %v", jsonPath, err)
}
}
}
output.SaveResult(c.BundlePath, path.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_CUSTOM_RESOURCES, fmt.Sprintf("%s-errors.json", constants.CLUSTER_RESOURCES_CUSTOM_RESOURCES)), marshalErrors(crErrors))
@@ -2294,23 +2305,18 @@ func mutatingWebhookConfigurations(ctx context.Context, client kubernetes.Interf
return b, nil
}
// storeCustomResource stores a custom resource as JSON and YAML
// We use both formats for backwards compatibility. This way we
// avoid breaking existing tools and analysers that already rely on
// the YAML format.
// storeCustomResource stores a custom resource as JSON only.
// JSON is valid YAML, so any analyzer expecting YAML can consume the JSON
// file directly. We no longer write a separate YAML file because the
// built-in redactors are authored for JSON, and the duplicate YAML copy
// would otherwise be left unredacted.
func storeCustomResource(name string, objects any, m map[string][]byte) error {
j, err := json.MarshalIndent(objects, "", " ")
if err != nil {
return err
}
y, err := yaml.Marshal(objects)
if err != nil {
return err
}
m[fmt.Sprintf("%s.json", name)] = j
m[fmt.Sprintf("%s.yaml", name)] = y
return nil
}
+1 -11
View File
@@ -28,7 +28,6 @@ import (
"k8s.io/client-go/kubernetes"
testclient "k8s.io/client-go/kubernetes/fake"
k8stesting "k8s.io/client-go/testing"
"sigs.k8s.io/yaml"
)
func init() {
@@ -512,17 +511,8 @@ func TestCollectClusterResources_CustomResource(t *testing.T) {
// Fetch the CR from cluster
res, errs := crsV1(ctx, dynamicClient, apixClient.ApiextensionsV1(), []string{"default"})
assert.Empty(t, errs)
require.Equal(t, 2, len(res))
require.Equal(t, 1, len(res))
assert.Equal(t, fromJSON(t, res["supportbundles.troubleshoot.sh/default.json"]), sbObject)
assert.Equal(t, fromYAML(t, res["supportbundles.troubleshoot.sh/default.yaml"]), sbObject)
}
func fromYAML(t *testing.T, dat []byte) troubleshootv1beta2.SupportBundle {
sb := []troubleshootv1beta2.SupportBundle{}
err := yaml.Unmarshal(dat, &sb)
require.NoError(t, err)
require.Equal(t, 1, len(sb))
return sb[0]
}
func fromJSON(t *testing.T, dat []byte) troubleshootv1beta2.SupportBundle {
+5
View File
@@ -107,6 +107,8 @@ func GetCollector(collector *troubleshootv1beta2.Collect, bundlePath string, nam
return &CollectMysql{collector.Mysql, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.Redis != nil:
return &CollectRedis{collector.Redis, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.ClickHouse != nil:
return &CollectClickhouse{collector.ClickHouse, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.Collectd != nil:
return &CollectCollectd{collector.Collectd, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.Ceph != nil:
@@ -201,6 +203,9 @@ func getCollectorName(c interface{}) string {
case *CollectRedis:
collector = "redis"
name = v.Collector.CollectorName
case *CollectClickhouse:
collector = "clickhouse"
name = v.Collector.CollectorName
case *CollectCollectd:
collector = "collectd"
name = v.Collector.CollectorName
+7 -1
View File
@@ -82,7 +82,6 @@ func loadKConfigs(kernelRelease string) (KConfigs, error) {
// https://github.com/torvalds/linux/blob/v4.3/init/Kconfig#L794
// https://github.com/torvalds/linux/blob/v4.3/init/Kconfig#L9
possiblePaths := []string{
"/proc/config.gz",
"/boot/config-" + kernelRelease,
"/usr/src/linux-" + kernelRelease + "/.config",
"/usr/src/linux/.config",
@@ -93,6 +92,13 @@ func loadKConfigs(kernelRelease string) (KConfigs, error) {
"/lib/modules/" + kernelRelease + "/build/.config",
}
// /proc/config.gz reflects the currently running kernel. Only use it when
// the requested kernel release matches the running kernel.
currentRelease, err := getKernelRelease()
if err == nil && currentRelease == kernelRelease {
possiblePaths = append([]string{"/proc/config.gz"}, possiblePaths...)
}
for _, path := range possiblePaths {
// open file for reading
f, err := os.Open(path)
+51 -3
View File
@@ -7,11 +7,12 @@ import (
"encoding/json"
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/go-sql-driver/mysql"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"
)
type CollectMysql struct {
@@ -32,11 +33,58 @@ func (c *CollectMysql) IsExcluded() (bool, error) {
return isExcluded(c.Collector.Exclude)
}
func (c *CollectMysql) Collect(progressChan chan<- interface{}) (CollectorResult, error) {
databaseConnection := DatabaseConnection{}
func (c *CollectMysql) createConnectConfig() (*mysql.Config, error) {
if c.Collector.URI == "" {
return nil, errors.New("mysql uri cannot be empty")
}
cfg, err := mysql.ParseDSN(c.Collector.URI)
if err != nil {
return nil, errors.Wrap(err, "failed to parse mysql config")
}
if c.Collector.TLS != nil {
klog.V(2).Infof("Connecting to mysql with TLS client config")
tlsConfig, err := createTLSConfig(c.Context, c.Client, c.Collector.TLS)
if err != nil {
return nil, err
}
cfg.TLS = tlsConfig
}
return cfg, nil
}
func (c *CollectMysql) connect() (*sql.DB, error) {
cfg, err := c.createConnectConfig()
if err != nil {
return nil, err
}
if c.Collector.TLS != nil {
connector, err := mysql.NewConnector(cfg)
if err != nil {
return nil, errors.Wrap(err, "failed to create mysql connector")
}
db := sql.OpenDB(connector)
return db, nil
}
db, err := sql.Open("mysql", c.Collector.URI)
if err != nil {
return nil, err
}
return db, nil
}
func (c *CollectMysql) Collect(progressChan chan<- interface{}) (CollectorResult, error) {
databaseConnection := DatabaseConnection{}
db, err := c.connect()
if err != nil {
klog.V(2).Infof("MySQL connection error: %s", err.Error())
databaseConnection.Error = err.Error()
} else {
defer db.Close()
+166
View File
@@ -0,0 +1,166 @@
package collect
import (
"context"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"testing"
"github.com/replicatedhq/troubleshoot/internal/testutils"
"github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
testclient "k8s.io/client-go/kubernetes/fake"
)
func TestCollectMysql_createConnectConfigPlainText(t *testing.T) {
tests := []struct {
name string
uri string
hasError bool
}{
{
name: "valid uri creates mysql connection config successfully",
uri: "user:password@tcp(localhost:3306)/defaultdb",
},
{
name: "empty uri fails to create mysql connection config with error",
uri: "",
hasError: true,
},
{
name: "invalid protocol fails to create mysql connection config with error",
uri: "http://somehost:3306",
hasError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &CollectMysql{
Context: context.Background(),
Collector: &v1beta2.Database{
URI: tt.uri,
},
}
cfg, err := c.createConnectConfig()
assert.Equal(t, tt.hasError, err != nil)
if err == nil {
require.NotNil(t, cfg)
assert.Equal(t, "localhost:3306", cfg.Addr)
assert.Equal(t, "defaultdb", cfg.DBName)
} else {
t.Log(err)
assert.Nil(t, cfg)
}
})
}
}
func TestCollectMysql_createConnectConfigTLS(t *testing.T) {
k8sClient := testclient.NewSimpleClientset()
c := &CollectMysql{
Client: k8sClient,
Context: context.Background(),
Collector: &v1beta2.Database{
URI: "user:password@tcp(localhost:3306)/defaultdb",
TLS: &v1beta2.TLSParams{
CACert: testutils.GetTestFixture(t, "db/ca.pem"),
ClientCert: testutils.GetTestFixture(t, "db/client.pem"),
ClientKey: testutils.GetTestFixture(t, "db/client-key.pem"),
},
},
}
cfg, err := c.createConnectConfig()
assert.NoError(t, err)
assert.NotNil(t, cfg)
assert.Equal(t, "localhost:3306", cfg.Addr)
// Check TLS config exists and is configured correctly
require.NotNil(t, cfg.TLS)
// Check client cert
require.Len(t, cfg.TLS.Certificates, 1)
require.Len(t, cfg.TLS.Certificates[0].Certificate, 1)
cert := cfg.TLS.Certificates[0]
clientCert, err := x509.ParseCertificate(cert.Certificate[0])
require.NoError(t, err)
assert.Equal(t, "CN=client,L=Didcot,ST=Oxfordshire,C=UK", clientCert.Subject.String())
// Check client key
block, _ := pem.Decode([]byte(testutils.GetTestFixture(t, "db/client-key.pem")))
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
require.NoError(t, err)
assert.True(t, key.Equal(cert.PrivateKey.(*rsa.PrivateKey)))
assert.NotNil(t, cfg.TLS.RootCAs)
assert.False(t, cfg.TLS.InsecureSkipVerify)
}
func TestCollectMysql_createConnectConfigTLSSkipVerify(t *testing.T) {
c := &CollectMysql{
Context: context.Background(),
Collector: &v1beta2.Database{
URI: "user:password@tcp(localhost:3306)/defaultdb",
TLS: &v1beta2.TLSParams{
SkipVerify: true,
},
},
}
cfg, err := c.createConnectConfig()
assert.NoError(t, err)
assert.NotNil(t, cfg)
require.NotNil(t, cfg.TLS)
assert.True(t, cfg.TLS.InsecureSkipVerify)
}
func TestCollectMysql_createConnectConfigTLSCACertOnly(t *testing.T) {
c := &CollectMysql{
Context: context.Background(),
Collector: &v1beta2.Database{
URI: "user:password@tcp(localhost:3306)/defaultdb",
TLS: &v1beta2.TLSParams{
CACert: testutils.GetTestFixture(t, "db/ca.pem"),
},
},
}
cfg, err := c.createConnectConfig()
assert.NoError(t, err)
assert.NotNil(t, cfg)
require.NotNil(t, cfg.TLS)
assert.NotNil(t, cfg.TLS.RootCAs)
assert.Empty(t, cfg.TLS.Certificates)
assert.False(t, cfg.TLS.InsecureSkipVerify)
}
func TestCollectMysql_createConnectConfigTLSSecret(t *testing.T) {
k8sClient := testclient.NewSimpleClientset()
c := &CollectMysql{
Client: k8sClient,
Context: context.Background(),
Collector: &v1beta2.Database{
URI: "user:password@tcp(localhost:3306)/defaultdb",
TLS: &v1beta2.TLSParams{
Secret: createTLSSecret(t, k8sClient, map[string]string{
"cacert": testutils.GetTestFixture(t, "db/ca.pem"),
"clientCert": testutils.GetTestFixture(t, "db/client.pem"),
"clientKey": testutils.GetTestFixture(t, "db/client-key.pem"),
}),
},
},
}
cfg, err := c.createConnectConfig()
assert.NoError(t, err)
assert.NotNil(t, cfg)
require.NotNil(t, cfg.TLS)
assert.NotNil(t, cfg.TLS.RootCAs)
assert.NotEmpty(t, cfg.TLS.Certificates)
assert.False(t, cfg.TLS.InsecureSkipVerify)
}
+38 -6
View File
@@ -7,6 +7,7 @@ import (
"io"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
@@ -16,18 +17,49 @@ import (
"k8s.io/klog/v2"
)
// Max number of concurrent redactors to run
// Ensure the number is low enough since each of the redactors
// also spawns goroutines to redact files in tar archives and
// other goroutines for each redactor spec.
const MAX_CONCURRENT_REDACTORS = 10
// Default cap on concurrent file redactors. Each redactor also spawns
// goroutines to redact files in tar archives and goroutines for each
// redactor spec, so the ceiling is intentionally low.
const DefaultMaxConcurrentRedactors = 10
// MaxConcurrentRedactorsEnvVar is the environment variable name used to
// override DefaultMaxConcurrentRedactors at runtime. Operators set this on
// the support-bundle binary (e.g. inside a Job/initContainer) when the
// default ceiling becomes a bottleneck for very large bundles.
const MaxConcurrentRedactorsEnvVar = "TROUBLESHOOT_MAX_CONCURRENT_REDACTORS"
// maxConcurrentRedactors returns the active cap on concurrent redactors.
// It reads MaxConcurrentRedactorsEnvVar; if unset, empty, non-numeric, or
// <= 0 it falls back to DefaultMaxConcurrentRedactors (with a klog warning
// for non-empty invalid input so silent misconfiguration is hard to miss).
func maxConcurrentRedactors() int {
raw, ok := os.LookupEnv(MaxConcurrentRedactorsEnvVar)
if !ok || raw == "" {
return DefaultMaxConcurrentRedactors
}
n, err := strconv.Atoi(raw)
if err != nil {
klog.Warningf("Invalid %s=%q (not an integer); falling back to default %d", MaxConcurrentRedactorsEnvVar, raw, DefaultMaxConcurrentRedactors)
return DefaultMaxConcurrentRedactors
}
if n <= 0 {
klog.Warningf("Invalid %s=%d (must be > 0); falling back to default %d", MaxConcurrentRedactorsEnvVar, n, DefaultMaxConcurrentRedactors)
return DefaultMaxConcurrentRedactors
}
if n != DefaultMaxConcurrentRedactors {
klog.Infof("Overriding concurrent redactor cap: %s=%d (default %d)", MaxConcurrentRedactorsEnvVar, n, DefaultMaxConcurrentRedactors)
}
return n
}
func RedactResult(bundlePath string, input CollectorResult, additionalRedactors []*troubleshootv1beta2.Redact) error {
wg := &sync.WaitGroup{}
// Error channel to capture errors from goroutines
errorCh := make(chan error, len(input))
limitCh := make(chan struct{}, MAX_CONCURRENT_REDACTORS)
limitCh := make(chan struct{}, maxConcurrentRedactors())
defer close(limitCh)
for k, v := range input {
+94
View File
@@ -0,0 +1,94 @@
package collect
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_maxConcurrentRedactors(t *testing.T) {
tests := []struct {
name string
setEnv bool
value string
want int
}{
{
name: "env unset returns default",
setEnv: false,
want: DefaultMaxConcurrentRedactors,
},
{
name: "empty value returns default",
setEnv: true,
value: "",
want: DefaultMaxConcurrentRedactors,
},
{
name: "valid positive int overrides default",
setEnv: true,
value: "50",
want: 50,
},
{
name: "value equal to default is honored",
setEnv: true,
value: "10",
want: DefaultMaxConcurrentRedactors,
},
{
name: "zero falls back to default",
setEnv: true,
value: "0",
want: DefaultMaxConcurrentRedactors,
},
{
name: "negative value falls back to default",
setEnv: true,
value: "-3",
want: DefaultMaxConcurrentRedactors,
},
{
name: "non-numeric value falls back to default",
setEnv: true,
value: "potato",
want: DefaultMaxConcurrentRedactors,
},
{
name: "whitespace-padded value falls back to default",
setEnv: true,
value: " 4 ",
want: DefaultMaxConcurrentRedactors,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Snapshot inherited state so each subtest is hermetic regardless
// of the order Go picks. t.Setenv would mask "env unset" cases
// with an empty value, so we manage the env directly here.
prev, hadPrev := os.LookupEnv(MaxConcurrentRedactorsEnvVar)
t.Cleanup(func() {
if hadPrev {
_ = os.Setenv(MaxConcurrentRedactorsEnvVar, prev)
} else {
_ = os.Unsetenv(MaxConcurrentRedactorsEnvVar)
}
})
if tt.setEnv {
if err := os.Setenv(MaxConcurrentRedactorsEnvVar, tt.value); err != nil {
t.Fatalf("setenv: %v", err)
}
} else {
if err := os.Unsetenv(MaxConcurrentRedactorsEnvVar); err != nil {
t.Fatalf("unsetenv: %v", err)
}
}
got := maxConcurrentRedactors()
assert.Equal(t, tt.want, got)
})
}
}
+8 -3
View File
@@ -69,7 +69,7 @@ func (c *CollectRunPod) Collect(progressChan chan<- interface{}) (result Collect
if err != nil {
return
}
result, err = savePodDetails(ctx, client, result, c.BundlePath, c.ClientConfig, pod, c.Collector)
result, err = savePodDetails(ctx, client, result, c.BundlePath, pod, c.Collector)
if err != nil {
klog.Errorf("failed to save pod details: %v", err)
}
@@ -407,7 +407,7 @@ func RunPodLogsWithOptions(ctx context.Context, client v1.CoreV1Interface, podSp
return io.ReadAll(logs)
}
func savePodDetails(ctx context.Context, client *kubernetes.Clientset, output CollectorResult, bundlePath string, clientConfig *rest.Config, pod *corev1.Pod, runPodCollector *troubleshootv1beta2.RunPod) (CollectorResult, error) {
func savePodDetails(ctx context.Context, client kubernetes.Interface, output CollectorResult, bundlePath string, pod *corev1.Pod, runPodCollector *troubleshootv1beta2.RunPod) (CollectorResult, error) {
podStatus, err := client.CoreV1().Pods(pod.Namespace).Get(ctx, pod.Name, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to get pod")
@@ -418,7 +418,12 @@ func savePodDetails(ctx context.Context, client *kubernetes.Clientset, output Co
return nil, errors.Wrap(err, "failed to get pod events")
}
podBytes, err := json.MarshalIndent(podStatus, "", " ")
// The full pod Spec can contain sensitive data such as env vars, commands, args,
// volumes, and image pull secrets. Strip it before saving the pod to the bundle.
sanitizedPod := *podStatus
sanitizedPod.Spec = corev1.PodSpec{}
podBytes, err := json.MarshalIndent(sanitizedPod, "", " ")
if err != nil {
return nil, errors.Wrap(err, "failed to marshal pod status")
}
+97
View File
@@ -2,6 +2,7 @@ package collect
import (
"context"
"encoding/json"
"testing"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
@@ -239,3 +240,99 @@ func Test_deleteImagePullSecret(t *testing.T) {
})
}
}
func TestSavePodDetails_StripsPodSpec(t *testing.T) {
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-run-pod",
Namespace: "default",
Labels: map[string]string{
"troubleshoot-role": "run-collector",
},
},
Spec: corev1.PodSpec{
NodeName: "test-node",
Containers: []corev1.Container{
{
Name: "collector",
Image: "busybox",
Command: []string{"sh", "-c", "echo secret-command"},
Args: []string{"--token", "super-secret-token"},
Env: []corev1.EnvVar{
{Name: "PASSWORD", Value: "hunter2"},
{
Name: "API_KEY",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: "my-secret"},
Key: "api-key",
},
},
},
},
},
},
ImagePullSecrets: []corev1.LocalObjectReference{{Name: "my-pull-secret"}},
Volumes: []corev1.Volume{
{
Name: "secret-vol",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{SecretName: "my-secret"},
},
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodSucceeded,
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "collector",
State: corev1.ContainerState{
Terminated: &corev1.ContainerStateTerminated{ExitCode: 0},
},
},
},
},
}
client := fake.NewSimpleClientset(pod)
collector := &troubleshootv1beta2.RunPod{
Name: "test-collector",
Namespace: "default",
}
result, err := savePodDetails(context.Background(), client, NewResult(), "", pod, collector)
require.NoError(t, err)
podKey := "test-collector/test-collector.json"
require.Contains(t, result, podKey)
saved := string(result[podKey])
// Debugging info must still be present
assert.Contains(t, saved, `"name": "test-run-pod"`)
assert.Contains(t, saved, `"phase": "Succeeded"`)
assert.Contains(t, saved, `"exitCode": 0`)
// Sensitive spec data must not be present
assert.NotContains(t, saved, "hunter2")
assert.NotContains(t, saved, "super-secret-token")
assert.NotContains(t, saved, "secret-command")
assert.NotContains(t, saved, "my-secret")
assert.NotContains(t, saved, "my-pull-secret")
assert.NotContains(t, saved, `"command":`)
assert.NotContains(t, saved, `"args":`)
assert.NotContains(t, saved, `"env":`)
// The saved JSON must still unmarshal into a Pod so downstream consumers
// (e.g. goldpinger) can read Name and Status.ContainerStatuses.
var savedPod corev1.Pod
require.NoError(t, json.Unmarshal(result[podKey], &savedPod))
assert.Equal(t, "test-run-pod", savedPod.Name)
assert.Equal(t, corev1.PodSucceeded, savedPod.Status.Phase)
assert.Len(t, savedPod.Status.ContainerStatuses, 1)
assert.Equal(t, int32(0), savedPod.Status.ContainerStatuses[0].State.Terminated.ExitCode)
assert.Empty(t, savedPod.Spec.Containers)
assert.Empty(t, savedPod.Spec.Volumes)
assert.Empty(t, savedPod.Spec.ImagePullSecrets)
}
+3
View File
@@ -386,6 +386,9 @@ func v1beta3SpecRequiresClient(spec *troubleshootv1beta3.SupportBundleSpec) bool
if c.Redis != nil && databaseRequiresClient(c.Redis) {
return true
}
if c.ClickHouse != nil && databaseRequiresClient(c.ClickHouse) {
return true
}
}
return false
+39 -42
View File
@@ -9,19 +9,23 @@ import (
"path/filepath"
"strings"
longhornns "github.com/longhorn/go-common-libs/ns"
longhornproc "github.com/longhorn/go-common-libs/proc"
lhtypes "github.com/longhorn/go-common-libs/types"
"github.com/pkg/errors"
"k8s.io/klog/v2"
iscsi_util "github.com/longhorn/go-iscsi-helper/util"
)
func newHostNamespaceExecutor() (*longhornns.Executor, error) {
return longhornns.NewNamespaceExecutor(lhtypes.ProcessNone, HostProcPath, []lhtypes.Namespace{lhtypes.NamespaceMnt})
}
func GetDiskInfo(directory string) (info *DiskInfo, err error) {
defer func() {
err = errors.Wrapf(err, "cannot get disk info of directory %v", directory)
}()
initiatorNSPath := iscsi_util.GetHostNamespacePath(HostProcPath)
mountPath := fmt.Sprintf("--mount=%s/mnt", initiatorNSPath)
output, err := Execute([]string{}, "nsenter", mountPath, "stat", "-fc", "{\"path\":\"%n\",\"fsid\":\"%i\",\"type\":\"%T\",\"freeBlock\":%f,\"totalBlock\":%b,\"blockSize\":%S}", directory)
nsDir := longhornproc.GetHostNamespaceDirectory(HostProcPath)
output, err := Execute([]string{}, "nsenter", "--mount="+nsDir+"/mnt", "stat", "-fc", "{\"path\":\"%n\",\"fsid\":\"%i\",\"type\":\"%T\",\"freeBlock\":%f,\"totalBlock\":%b,\"blockSize\":%S}", directory)
if err != nil {
return nil, err
}
@@ -51,17 +55,16 @@ func RemoveHostDirectoryContent(directory string) (err error) {
if strings.Count(dir, "/") < 2 {
return fmt.Errorf("prohibit removing the top level of directory %v", dir)
}
initiatorNSPath := iscsi_util.GetHostNamespacePath(HostProcPath)
nsExec, err := iscsi_util.NewNamespaceExecutor(initiatorNSPath)
nsExec, err := newHostNamespaceExecutor()
if err != nil {
return err
}
// check if the directory already deleted
if _, err := nsExec.Execute("ls", []string{dir}); err != nil {
if _, err := nsExec.Execute(nil, "ls", []string{dir}, lhtypes.ExecuteDefaultTimeout); err != nil {
klog.Warningf("cannot find host directory %v for removal", dir)
return nil
}
if _, err := nsExec.Execute("rm", []string{"-rf", dir}); err != nil {
if _, err := nsExec.Execute(nil, "rm", []string{"-rf", dir}, lhtypes.ExecuteDefaultTimeout); err != nil {
return err
}
return nil
@@ -84,35 +87,33 @@ func CopyHostDirectoryContent(src, dest string) (err error) {
return fmt.Errorf("prohibit copying the content for the top level of directory %v or %v", srcDir, destDir)
}
initiatorNSPath := iscsi_util.GetHostNamespacePath(HostProcPath)
nsExec, err := iscsi_util.NewNamespaceExecutor(initiatorNSPath)
nsExec, err := newHostNamespaceExecutor()
if err != nil {
return err
}
// There can be no src directory, hence returning nil is fine.
if _, err := nsExec.Execute("bash", []string{"-c", fmt.Sprintf("ls %s", filepath.Join(srcDir, "*"))}); err != nil {
if _, err := nsExec.Execute(nil, "bash", []string{"-c", fmt.Sprintf("ls %s", filepath.Join(srcDir, "*"))}, lhtypes.ExecuteDefaultTimeout); err != nil {
klog.V(2).Infof("cannot list the content of the src directory %v for the copy, will do nothing: %v", srcDir, err)
return nil
}
// Check if the dest directory exists.
if _, err := nsExec.Execute("mkdir", []string{"-p", destDir}); err != nil {
if _, err := nsExec.Execute(nil, "mkdir", []string{"-p", destDir}, lhtypes.ExecuteDefaultTimeout); err != nil {
return err
}
// The flag `-n` means not overwriting an existing file.
if _, err := nsExec.Execute("bash", []string{"-c", fmt.Sprintf("cp -an %s %s", filepath.Join(srcDir, "*"), destDir)}); err != nil {
if _, err := nsExec.Execute(nil, "bash", []string{"-c", fmt.Sprintf("cp -an %s %s", filepath.Join(srcDir, "*"), destDir)}, lhtypes.ExecuteDefaultTimeout); err != nil {
return err
}
return nil
}
func CreateDiskPathReplicaSubdirectory(path string) error {
nsPath := iscsi_util.GetHostNamespacePath(HostProcPath)
nsExec, err := iscsi_util.NewNamespaceExecutor(nsPath)
nsExec, err := newHostNamespaceExecutor()
if err != nil {
return err
}
if _, err := nsExec.Execute("mkdir", []string{"-p", filepath.Join(path, ReplicaDirectory)}); err != nil {
if _, err := nsExec.Execute(nil, "mkdir", []string{"-p", filepath.Join(path, ReplicaDirectory)}, lhtypes.ExecuteDefaultTimeout); err != nil {
return errors.Wrapf(err, "error creating data path %v on host", path)
}
@@ -120,22 +121,22 @@ func CreateDiskPathReplicaSubdirectory(path string) error {
}
func DeleteDiskPathReplicaSubdirectoryAndDiskCfgFile(
nsExec *iscsi_util.NamespaceExecutor, path string) error {
nsExec *longhornns.Executor, path string) error {
var err error
dirPath := filepath.Join(path, ReplicaDirectory)
filePath := filepath.Join(path, DiskConfigFile)
// Check if the replica directory exist, delete it
if _, err := nsExec.Execute("ls", []string{dirPath}); err == nil {
if _, err := nsExec.Execute("rmdir", []string{dirPath}); err != nil {
if _, err := nsExec.Execute(nil, "ls", []string{dirPath}, lhtypes.ExecuteDefaultTimeout); err == nil {
if _, err := nsExec.Execute(nil, "rmdir", []string{dirPath}, lhtypes.ExecuteDefaultTimeout); err != nil {
return errors.Wrapf(err, "error deleting data path %v on host", path)
}
}
// Check if the disk cfg file exist, delete it
if _, err := nsExec.Execute("ls", []string{filePath}); err == nil {
if _, err := nsExec.Execute("rm", []string{filePath}); err != nil {
if _, err := nsExec.Execute(nil, "ls", []string{filePath}, lhtypes.ExecuteDefaultTimeout); err == nil {
if _, err := nsExec.Execute(nil, "rm", []string{filePath}, lhtypes.ExecuteDefaultTimeout); err != nil {
err = errors.Wrapf(err, "error deleting disk cfg file %v on host", filePath)
}
}
@@ -145,8 +146,7 @@ func DeleteDiskPathReplicaSubdirectoryAndDiskCfgFile(
func ExpandFileSystem(volumeName string) (err error) {
devicePath := filepath.Join(DeviceDirectory, volumeName)
nsPath := iscsi_util.GetHostNamespacePath(HostProcPath)
nsExec, err := iscsi_util.NewNamespaceExecutor(nsPath)
nsExec, err := newHostNamespaceExecutor()
if err != nil {
return err
}
@@ -162,7 +162,7 @@ func ExpandFileSystem(volumeName string) (err error) {
// make sure there is a mount point for the volume before file system expansion
tmpMountNeeded := true
mountPoint := ""
mountRes, err := nsExec.Execute("bash", []string{"-c", "mount | grep \"/" + volumeName + " \" | awk '{print $3}'"})
mountRes, err := nsExec.Execute(nil, "bash", []string{"-c", "mount | grep \"/" + volumeName + " \" | awk '{print $3}'"}, lhtypes.ExecuteDefaultTimeout)
if err != nil {
klog.Warningf("failed to use command mount to get the mount info of volume %v, consider the volume as unmounted: %v", volumeName, err)
} else {
@@ -185,10 +185,10 @@ func ExpandFileSystem(volumeName string) (err error) {
if tmpMountNeeded {
mountPoint = filepath.Join(TemporaryMountPointDirectory, volumeName)
klog.V(2).Infof("The volume %v is unmounted, hence it will be temporarily mounted on %v for file system expansion", volumeName, mountPoint)
if _, err := nsExec.Execute("mkdir", []string{"-p", mountPoint}); err != nil {
if _, err := nsExec.Execute(nil, "mkdir", []string{"-p", mountPoint}, lhtypes.ExecuteDefaultTimeout); err != nil {
return errors.Wrapf(err, "failed to create a temporary mount point %v before file system expansion", mountPoint)
}
if _, err := nsExec.Execute("mount", []string{devicePath, mountPoint}); err != nil {
if _, err := nsExec.Execute(nil, "mount", []string{devicePath, mountPoint}, lhtypes.ExecuteDefaultTimeout); err != nil {
return errors.Wrapf(err, "failed to temporarily mount volume %v on %v before file system expansion", volumeName, mountPoint)
}
}
@@ -199,11 +199,11 @@ func ExpandFileSystem(volumeName string) (err error) {
case "ext3":
fallthrough
case "ext4":
if _, err = nsExec.Execute("resize2fs", []string{devicePath}); err != nil {
if _, err = nsExec.Execute(nil, "resize2fs", []string{devicePath}, lhtypes.ExecuteDefaultTimeout); err != nil {
return err
}
case "xfs":
if _, err = nsExec.Execute("xfs_growfs", []string{mountPoint}); err != nil {
if _, err = nsExec.Execute(nil, "xfs_growfs", []string{mountPoint}, lhtypes.ExecuteDefaultTimeout); err != nil {
return err
}
default:
@@ -212,10 +212,10 @@ func ExpandFileSystem(volumeName string) (err error) {
// cleanup
if tmpMountNeeded {
if _, err := nsExec.Execute("umount", []string{mountPoint}); err != nil {
if _, err := nsExec.Execute(nil, "umount", []string{mountPoint}, lhtypes.ExecuteDefaultTimeout); err != nil {
return errors.Wrapf(err, "failed to unmount volume %v on the temporary mount point %v after file system expansion", volumeName, mountPoint)
}
if _, err := nsExec.Execute("rm", []string{"-r", mountPoint}); err != nil {
if _, err := nsExec.Execute(nil, "rm", []string{"-r", mountPoint}, lhtypes.ExecuteDefaultTimeout); err != nil {
return errors.Wrapf(err, "failed to remove the temporary mount point %v after file system expansion", mountPoint)
}
}
@@ -225,8 +225,7 @@ func ExpandFileSystem(volumeName string) (err error) {
func DetectFileSystem(volumeName string) (string, error) {
devicePath := filepath.Join(DeviceDirectory, volumeName)
nsPath := iscsi_util.GetHostNamespacePath(HostProcPath)
nsExec, err := iscsi_util.NewNamespaceExecutor(nsPath)
nsExec, err := newHostNamespaceExecutor()
if err != nil {
return "", err
}
@@ -235,7 +234,7 @@ func DetectFileSystem(volumeName string) (string, error) {
// For filesystem `btrfs`, the schema is: `<device path>: UUID="<filesystem UUID>" UUID_SUB="<filesystem UUID_SUB>" TYPE="<filesystem type>"`
// For filesystem `ext4` or `xfs`, the schema is: `<device path>: UUID="<filesystem UUID>" TYPE="<filesystem type>"`
cmd := fmt.Sprintf("blkid %s | sed 's/.*TYPE=//g'", devicePath)
output, err := nsExec.Execute("bash", []string{"-c", cmd})
output, err := nsExec.Execute(nil, "bash", []string{"-c", cmd}, lhtypes.ExecuteDefaultTimeout)
if err != nil {
return "", errors.Wrapf(err, "failed to get the file system info for volume %v, maybe there is no Linux file system on the volume", volumeName)
}
@@ -247,13 +246,12 @@ func DetectFileSystem(volumeName string) (string, error) {
}
func GetDiskConfig(path string) (*DiskConfig, error) {
nsPath := iscsi_util.GetHostNamespacePath(HostProcPath)
nsExec, err := iscsi_util.NewNamespaceExecutor(nsPath)
nsExec, err := newHostNamespaceExecutor()
if err != nil {
return nil, err
}
filePath := filepath.Join(path, DiskConfigFile)
output, err := nsExec.Execute("cat", []string{filePath})
output, err := nsExec.Execute(nil, "cat", []string{filePath}, lhtypes.ExecuteDefaultTimeout)
if err != nil {
return nil, fmt.Errorf("cannot find config file %v on host: %v", filePath, err)
}
@@ -274,13 +272,12 @@ func GenerateDiskConfig(path string) (*DiskConfig, error) {
return nil, fmt.Errorf("BUG: Cannot marshal %+v: %v", cfg, err)
}
nsPath := iscsi_util.GetHostNamespacePath(HostProcPath)
nsExec, err := iscsi_util.NewNamespaceExecutor(nsPath)
nsExec, err := newHostNamespaceExecutor()
if err != nil {
return nil, err
}
filePath := filepath.Join(path, DiskConfigFile)
if _, err := nsExec.Execute("ls", []string{filePath}); err == nil {
if _, err := nsExec.Execute(nil, "ls", []string{filePath}, lhtypes.ExecuteDefaultTimeout); err == nil {
return nil, fmt.Errorf("disk cfg on %v exists, cannot override", filePath)
}
@@ -293,13 +290,13 @@ func GenerateDiskConfig(path string) (*DiskConfig, error) {
}
}()
if _, err := nsExec.ExecuteWithStdin("dd", []string{"of=" + filePath}, string(encoded)); err != nil {
if _, err := nsExec.ExecuteWithStdin(nil, "dd", []string{"of=" + filePath}, string(encoded), lhtypes.ExecuteDefaultTimeout); err != nil {
return nil, fmt.Errorf("cannot write to disk cfg on %v: %v", filePath, err)
}
if err := CreateDiskPathReplicaSubdirectory(path); err != nil {
return nil, err
}
if _, err := nsExec.Execute("sync", []string{filePath}); err != nil {
if _, err := nsExec.Execute(nil, "sync", []string{filePath}, lhtypes.ExecuteDefaultTimeout); err != nil {
return nil, fmt.Errorf("cannot sync disk cfg on %v: %v", filePath, err)
}
+15
View File
@@ -148,6 +148,21 @@ func CollectHostWithContext(
// The values of map entries will contain the collected data in bytes if the data was not stored to disk
collectResult.AllCollectedData = allCollectedData
// Local host preflight collectors are the only collection path (cluster,
// remote host, in-cluster support bundle) that skipped redaction entirely.
// A `run` collector's captured environment in particular can carry
// credentials verbatim (e.g. HTTPS_PROXY with embedded Basic Auth) into
// the bundle. See https://github.com/replicatedhq/troubleshoot/issues/2100.
_, span := otel.Tracer(constants.LIB_TRACER_NAME).Start(ctx, "Host collectors")
span.SetAttributes(attribute.String("type", "Redactors"))
if err := collect.RedactResult(opts.BundlePath, collect.CollectorResult(allCollectedData), nil); err != nil {
err = errors.Wrap(err, "failed to redact host collector results")
span.SetStatus(codes.Error, err.Error())
span.End()
return collectResult, err
}
span.End()
return collectResult, nil
}
+46
View File
@@ -1,6 +1,9 @@
package preflight
import (
"context"
"os"
"path/filepath"
"reflect"
"testing"
@@ -209,3 +212,46 @@ func TestCollectWithContext_PreservesOrderAfterClusterResources(t *testing.T) {
assert.Less(t, dataIndex, secretIndex, "data collectors should come before secret collectors, preserving relative order")
}
}
// TestCollectHostWithContext_RedactsSensitiveEnvValues verifies that a `run`
// host collector's captured environment is redacted before being written to
// the bundle. CollectHostWithContext is the only collection path (cluster,
// host, remote) that skipped redaction entirely: every env var passed to the
// command -- including credentials embedded in a proxy URL -- landed
// verbatim in <collectorName>-info.json.
func TestCollectHostWithContext_RedactsSensitiveEnvValues(t *testing.T) {
bundlePath := t.TempDir()
hostPreflight := &troubleshootv1beta2.HostPreflight{
Spec: troubleshootv1beta2.HostPreflightSpec{
Collectors: []*troubleshootv1beta2.HostCollect{
{
HostRun: &troubleshootv1beta2.HostRun{
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
CollectorName: "proxy-credential-check",
},
Command: "sh",
Args: []string{"-c", "echo ok"},
IgnoreParentEnvs: true,
Env: []string{
"HTTPS_PROXY=http://alice:sw0rdfish@proxy.example.com:3128",
},
},
},
},
},
}
_, err := CollectHostWithContext(context.Background(), CollectOpts{
ProgressChan: make(chan interface{}, 100),
BundlePath: bundlePath,
}, hostPreflight)
require.NoError(t, err)
infoPath := filepath.Join(bundlePath, "host-collectors/run-host/proxy-credential-check-info.json")
b, err := os.ReadFile(infoPath)
require.NoError(t, err)
assert.NotContains(t, string(b), "sw0rdfish", "proxy credential leaked unredacted into the host preflight bundle")
assert.Contains(t, string(b), "***HIDDEN***", "expected the built-in URL-userinfo redactor to mask the credential")
}
+31 -32
View File
@@ -35,6 +35,32 @@ func init() {
}
}
// kurlInstallerRedactors are built-in redactors scoped to the kurl installer
// custom resource. They previously applied only to the YAML copy of custom
// resources; now they target the JSON file (and the YAML symlink that points to it).
// The installer CRD can be either cluster-scoped (installers.cluster.kurl.sh.json)
// or namespaced (installers.cluster.kurl.sh/<namespace>.json), so both patterns are
// included.
var kurlInstallerRedactors = []*troubleshootv1beta2.Redact{
{
Name: "Redact kurl installer fields",
FileSelector: troubleshootv1beta2.FileSelector{
Files: []string{
fmt.Sprintf("%s/%s/%s.json", constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_CUSTOM_RESOURCES, "installers.cluster.kurl.sh"),
fmt.Sprintf("%s/%s/%s/*.json", constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_CUSTOM_RESOURCES, "installers.cluster.kurl.sh"),
},
},
Removals: troubleshootv1beta2.Removals{
Regex: []troubleshootv1beta2.Regex{
{Redactor: `(?i)("bootstrapToken"\s*:\s*")(?P<mask>[^"]*)(")`},
{Redactor: `(?i)("certKey"\s*:\s*")(?P<mask>[^"]*)(")`},
{Redactor: `(?i)("kubeadmToken"\s*:\s*")(?P<mask>[^"]*)(")`},
{Redactor: `(?i)("kubectl\.kubernetes\.io/last-applied-configuration"\s*:\s*")(?P<mask>(?:\\.|[^"\\])*)(")`},
},
},
},
}
// A regex cache to avoid recompiling the same regexes over and over
func compileRegex(pattern string) (*regexp.Regexp, error) {
regexCacheLock.Lock()
@@ -440,39 +466,12 @@ func getRedactors(path string) ([]Redactor, error) {
redactors = append(redactors, r)
}
customResources := []struct {
resource string
yamlPath string
}{
{
resource: "installers.cluster.kurl.sh",
yamlPath: "*.spec.kubernetes.bootstrapToken",
},
{
resource: "installers.cluster.kurl.sh",
yamlPath: "*.spec.kubernetes.certKey",
},
{
resource: "installers.cluster.kurl.sh",
yamlPath: "*.spec.kubernetes.kubeadmToken",
},
}
uniqueCRs := map[string]bool{}
for _, cr := range customResources {
fileglob := fmt.Sprintf("%s/%s/%s/*.yaml", constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_CUSTOM_RESOURCES, cr.resource)
redactors = append(redactors, NewYamlRedactor(cr.yamlPath, fileglob, ""))
// redact kubectl last applied annotation once for each resource since it contains copies of
// redacted fields
if !uniqueCRs[cr.resource] {
uniqueCRs[cr.resource] = true
redactors = append(redactors, &YamlRedactor{
filePath: fileglob,
maskPath: []string{"*", "metadata", "annotations", "kubectl.kubernetes.io/last-applied-configuration"},
})
}
// Add built-in redactors that are scoped to specific custom resource files.
scopedRedactors, err := buildAdditionalRedactors(path, kurlInstallerRedactors)
if err != nil {
return nil, err
}
redactors = append(redactors, scopedRedactors...)
return redactors, nil
}
+46
View File
@@ -1874,3 +1874,49 @@ func Test_redactMatchesPath(t *testing.T) {
})
}
}
func Test_RedactKurlInstallerJSON(t *testing.T) {
input := `[
{
"metadata": {
"name": "kurl",
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"cluster.kurl.sh/v1beta1\",\"kind\":\"Installer\",\"spec\":{\"kubernetes\":{\"bootstrapToken\":\"abc\",\"certKey\":\"def\",\"kubeadmToken\":\"ghi\"}}}"
}
},
"spec": {
"kubernetes": {
"bootstrapToken": "abc",
"certKey": "def",
"kubeadmToken": "ghi"
}
}
}
]`
cases := []string{
// Cluster-scoped installer (file directly under custom-resources)
"cluster-resources/custom-resources/installers.cluster.kurl.sh.json",
// Namespaced installer (file under a per-CRD directory)
"cluster-resources/custom-resources/installers.cluster.kurl.sh/default.json",
}
for _, path := range cases {
t.Run(path, func(t *testing.T) {
r, err := Redact(strings.NewReader(input), path, nil)
require.NoError(t, err)
out, err := ioutil.ReadAll(r)
require.NoError(t, err)
outStr := string(out)
require.NotContains(t, outStr, `"abc"`)
require.NotContains(t, outStr, `"def"`)
require.NotContains(t, outStr, `"ghi"`)
require.Contains(t, outStr, `"bootstrapToken": "***HIDDEN***"`)
require.Contains(t, outStr, `"certKey": "***HIDDEN***"`)
require.Contains(t, outStr, `"kubeadmToken": "***HIDDEN***"`)
require.Contains(t, outStr, `"kubectl.kubernetes.io/last-applied-configuration": "***HIDDEN***"`)
})
}
}
+26
View File
@@ -20,6 +20,9 @@ import (
"k8s.io/klog/v2"
)
// loadFromSecret is a package-level hook so tests can stub out cluster access.
var loadFromSecret = specs.LoadFromSecret
// GetSupportBundleFromURI downloads and parses a support bundle from a URI and returns a SupportBundle object
func GetSupportBundleFromURI(bundleURI string) (*troubleshootv1beta2.SupportBundle, error) {
collectorContent, err := LoadSupportBundleSpec(bundleURI)
@@ -165,6 +168,29 @@ func LoadSupportBundleSpec(arg string) ([]byte, error) {
}
func LoadRedactorSpec(arg string) ([]byte, error) {
if strings.HasPrefix(arg, "secret/") {
// format secret/namespace-name/secret-name[/data-key]
pathParts := strings.Split(arg, "/")
if len(pathParts) > 4 {
return nil, errors.Errorf("secret path %s must have at most 4 components", arg)
}
if len(pathParts) < 3 {
return nil, errors.Errorf("secret path %s must have at least 3 components", arg)
}
dataKey := "redactor-spec"
if len(pathParts) == 4 {
dataKey = pathParts[3]
}
spec, err := loadFromSecret(pathParts[1], pathParts[2], dataKey)
if err != nil {
return nil, errors.Wrap(err, "failed to get spec from secret")
}
return spec, nil
}
if strings.HasPrefix(arg, "configmap/") {
// format configmap/namespace-name/configmap-name[/data-key]
pathParts := strings.Split(arg, "/")
+60
View File
@@ -1,7 +1,9 @@
package supportbundle
import (
"fmt"
"reflect"
"strings"
"testing"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
@@ -92,3 +94,61 @@ spec:
})
}
}
func TestLoadRedactorSpec(t *testing.T) {
origLoadFromSecret := loadFromSecret
defer func() { loadFromSecret = origLoadFromSecret }()
loadFromSecret = func(namespace, secretName, key string) ([]byte, error) {
return []byte(fmt.Sprintf("namespace=%s,secret=%s,key=%s", namespace, secretName, key)), nil
}
tests := []struct {
name string
uri string
wantContent string
wantErr string
}{
{
name: "secret URI with default key",
uri: "secret/default/my-redactor",
wantContent: "namespace=default,secret=my-redactor,key=redactor-spec",
},
{
name: "secret URI with custom key",
uri: "secret/default/my-redactor/custom-key",
wantContent: "namespace=default,secret=my-redactor,key=custom-key",
},
{
name: "secret URI with too few components",
uri: "secret/default",
wantErr: "must have at least 3 components",
},
{
name: "secret URI with too many components",
uri: "secret/default/my-redactor/custom-key/extra",
wantErr: "must have at most 4 components",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := LoadRedactorSpec(tt.uri)
if tt.wantErr != "" {
if err == nil {
t.Fatalf("LoadRedactorSpec() expected error, got nil")
}
if !strings.Contains(err.Error(), tt.wantErr) {
t.Errorf("LoadRedactorSpec() error = %q, want containing %q", err.Error(), tt.wantErr)
}
return
}
if err != nil {
t.Fatalf("LoadRedactorSpec() unexpected error = %v", err)
}
if string(got) != tt.wantContent {
t.Errorf("LoadRedactorSpec() = %q, want %q", string(got), tt.wantContent)
}
})
}
}
+51
View File
@@ -0,0 +1,51 @@
contents:
repositories:
- https://apk.cve0.io
keyring:
- https://apk.cve0.io/key/cve0-signing.rsa.pub
packages:
- troubleshoot
- bash
- kmod
- ca-certificates-bundle
- securebuild-baselayout
- busybox
cmd: bash
work-dir: /
archs:
- x86_64
- aarch64
environment:
PATH: /troubleshoot:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
paths:
- path: /troubleshoot
type: directory
uid: 0
gid: 0
permissions: 0o755
- path: /troubleshoot/support-bundle
type: symlink
source: /usr/bin/support-bundle
permissions: 0o755
- path: /troubleshoot/preflight
type: symlink
source: /usr/bin/preflight
permissions: 0o755
- path: /troubleshoot/collect
type: symlink
source: /usr/bin/collect
permissions: 0o755
annotations:
org.opencontainers.image.title: Troubleshoot (SecureBuild)
org.opencontainers.image.description: Troubleshoot tools for collecting and analyzing Kubernetes cluster diagnostics
org.opencontainers.image.source: https://github.com/replicatedhq/troubleshoot
org.opencontainers.image.vendor: SecureBuild
+106
View File
@@ -0,0 +1,106 @@
package:
name: troubleshoot-head
version: "1000.0.0"
epoch: 0
description: Troubleshoot tools for collecting and analyzing Kubernetes cluster diagnostics
copyright:
- license: Apache-2.0
attestation: "SecureBuild (https://securebuild.com)"
dependencies:
runtime:
- ${{package.name}}-support-bundle=${{package.full-version}}
- ${{package.name}}-preflight=${{package.full-version}}
- ${{package.name}}-collect=${{package.full-version}}
provides:
- troubleshoot=${{package.full-version}}
var-transforms:
- from: ${{package.version}}
match: ^(\d+\.\d+)\.\d+$
replace: "$1"
to: major-minor-version
environment:
contents:
repositories:
- https://apk.cve0.io
keyring:
- https://apk.cve0.io/key/cve0-signing.rsa.pub
packages:
- busybox
- build-base
- ca-certificates-bundle
- go
- git
- bash
environment:
CGO_ENABLED: "0"
GOPROXY: https://proxy.golang.org,direct
GOSUMDB: sum.golang.org
pipeline:
- uses: git-checkout
with:
repository: https://github.com/replicatedhq/troubleshoot
tag: v${{package.version}}
depth: 1
- name: Build troubleshoot binaries
runs: |
export PATH="/usr/bin:$PATH"
export VERSION="v${{package.version}}"
make -j bin/support-bundle bin/preflight bin/collect
subpackages:
- name: ${{package.name}}-support-bundle
description: Support bundle collector for Kubernetes cluster diagnostics
dependencies:
provides:
- troubleshoot-support-bundle=${{package.full-version}}
pipeline:
- runs: |
mkdir -p ${{targets.subpkgdir}}/usr/bin
cp bin/support-bundle ${{targets.subpkgdir}}/usr/bin/support-bundle
- uses: strip
test:
pipeline:
- runs: /usr/bin/support-bundle --help
- name: ${{package.name}}-preflight
description: Preflight checker for Kubernetes cluster requirements
dependencies:
provides:
- troubleshoot-preflight=${{package.full-version}}
pipeline:
- runs: |
mkdir -p ${{targets.subpkgdir}}/usr/bin
cp bin/preflight ${{targets.subpkgdir}}/usr/bin/preflight
- uses: strip
test:
pipeline:
- runs: /usr/bin/preflight --help
- name: ${{package.name}}-collect
description: Collector for Kubernetes cluster data
dependencies:
provides:
- troubleshoot-collect=${{package.full-version}}
pipeline:
- runs: |
mkdir -p ${{targets.subpkgdir}}/usr/bin
cp bin/collect ${{targets.subpkgdir}}/usr/bin/collect
- uses: strip
test:
pipeline:
- runs: /usr/bin/collect --help
test:
environment:
contents:
packages:
- ${{package.name}}=${{package.full-version}}
pipeline:
- runs: |
/usr/bin/support-bundle --help
/usr/bin/preflight --help
/usr/bin/collect --help