Compare commits

...
82 Commits
Author SHA1 Message Date
Gerard Nguyen 6f839b389d Quick fix to prevent panic on nil collector result (#1508)
* quick fix to prevent panic on nil collector result
* do not save pod details on error
2024-03-18 16:42:52 +11:00
dependabot[bot]andlnx01 ebe98ff2f1 chore(deps): bump the go_modules group group with 2 updates (#1507)
Bumps the go_modules group group with 2 updates: google.golang.org/protobuf and [helm.sh/helm/v3](https://github.com/helm/helm).


Updates `google.golang.org/protobuf` from 1.31.0 to 1.33.0

Updates `helm.sh/helm/v3` from 3.14.2 to 3.14.3
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.14.2...v3.14.3)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: indirect
  dependency-group: go_modules-security-group
- dependency-name: helm.sh/helm/v3
  dependency-type: direct:production
  dependency-group: go_modules-security-group
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-14 00:17:47 +00:00
dependabot[bot]andlnx01 59624e88fd chore(deps): bump the go_modules group group in /examples/sdk/helm-template with 2 updates (#1506)
chore(deps): bump the go_modules group group

Bumps the go_modules group group in /examples/sdk/helm-template with 2 updates: [helm.sh/helm/v3](https://github.com/helm/helm) and google.golang.org/protobuf.


Updates `helm.sh/helm/v3` from 3.14.2 to 3.14.3
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.14.2...v3.14.3)

Updates `google.golang.org/protobuf` from 1.31.0 to 1.33.0

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  dependency-type: direct:production
  dependency-group: go_modules-security-group
- dependency-name: google.golang.org/protobuf
  dependency-type: indirect
  dependency-group: go_modules-security-group
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-14 00:17:18 +00:00
Evans Mungai 15446996f9 fix: Parse v1beta1 troubleshoot specs when using loader API (#1501) 2024-03-13 11:39:29 +00:00
dependabot[bot]andlnx01 c4237b6c40 chore(deps): bump the security group with 4 updates (#1503)
Bumps the security group with 4 updates: [github.com/containers/image/v5](https://github.com/containers/image), [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql), [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) and [golang.org/x/exp](https://github.com/golang/exp).


Updates `github.com/containers/image/v5` from 5.29.2 to 5.30.0
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.29.2...v5.30.0)

Updates `github.com/go-sql-driver/mysql` from 1.7.1 to 1.8.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.7.1...v1.8.0)

Updates `github.com/jackc/pgx/v5` from 5.5.4 to 5.5.5
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.5.4...v5.5.5)

Updates `golang.org/x/exp` from 0.0.0-20231006140011-7918f672742d to 0.0.0-20240222234643-814bf88cf225
- [Commits](https://github.com/golang/exp/commits)

---
updated-dependencies:
- dependency-name: github.com/containers/image/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/go-sql-driver/mysql
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/jackc/pgx/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: golang.org/x/exp
  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>
2024-03-12 00:17:13 +00:00
Evans Mungai d0685ea81a fix: flaky goldpinger e2e test (#1499)
Installing the chart at times takes too long. We'll increase the time
that we wait for the application to be healthy
2024-03-07 17:19:22 +00:00
Gerard Nguyen 742e92f1ee New Event Analyzer (#1474)
* add new Event analyzer
2024-03-07 08:31:45 +13:00
Gerard Nguyen 553d709043 fix panic in Velero analyzer when there's no Velero deployment found (#1497)
* fix panic in Velero analyzer when there's no Velero deployment found

* do not omit error in find files
2024-03-05 21:58:37 +11:00
dependabot[bot]andlnx01 29435b1c15 chore(deps): bump the security group with 5 updates (#1498)
Bumps the security group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) | `5.5.3` | `5.5.4` |
| [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) | `3.24.1` | `3.24.2` |
| [github.com/stretchr/testify](https://github.com/stretchr/testify) | `1.8.4` | `1.9.0` |
| [golang.org/x/mod](https://github.com/golang/mod) | `0.15.0` | `0.16.0` |
| [golang.org/x/net](https://github.com/golang/net) | `0.21.0` | `0.22.0` |


Updates `github.com/jackc/pgx/v5` from 5.5.3 to 5.5.4
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.5.3...v5.5.4)

Updates `github.com/shirou/gopsutil/v3` from 3.24.1 to 3.24.2
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.24.1...v3.24.2)

Updates `github.com/stretchr/testify` from 1.8.4 to 1.9.0
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.4...v1.9.0)

Updates `golang.org/x/mod` from 0.15.0 to 0.16.0
- [Commits](https://github.com/golang/mod/compare/v0.15.0...v0.16.0)

Updates `golang.org/x/net` from 0.21.0 to 0.22.0
- [Commits](https://github.com/golang/net/compare/v0.21.0...v0.22.0)

---
updated-dependencies:
- dependency-name: github.com/jackc/pgx/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/mod
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/net
  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>
2024-03-05 00:17:14 +00:00
dependabot[bot]andlnx01 a9b889e033 chore(deps): bump the security group with 3 updates (#1495)
Bumps the security group with 3 updates: [github.com/microsoft/go-mssqldb](https://github.com/microsoft/go-mssqldb), [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) and [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go).


Updates `github.com/microsoft/go-mssqldb` from 1.6.0 to 1.7.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.6.0...v1.7.0)

Updates `go.opentelemetry.io/otel` from 1.23.1 to 1.24.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.23.1...v1.24.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.23.1 to 1.24.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.23.1...v1.24.0)

---
updated-dependencies:
- dependency-name: github.com/microsoft/go-mssqldb
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: go.opentelemetry.io/otel
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: go.opentelemetry.io/otel/sdk
  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>
2024-02-27 18:05:45 +00:00
Xav PaiceandSalah Al Saleh 4a84e26f21 Update dependabot.yml (#1494)
* Update dependabot.yml

* update dependabot.yml

* add another label

---------

Co-authored-by: Salah Al Saleh <sg.alsaleh@gmail.com>
2024-02-27 15:44:07 +00:00
Salah Al Saleh 2eb3aa9883 Add delay between approving and merging PR in the automated PRs manager (#1493) 2024-02-28 03:06:31 +13:00
dependabot[bot]andlnx01 ffd357c1e8 chore(deps): bump the go_modules group across 2 directories with 1 update (#1492)
Bumps the go_modules group with 1 update in the / directory: [helm.sh/helm/v3](https://github.com/helm/helm).


Updates `helm.sh/helm/v3` from 3.14.1 to 3.14.2
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.14.1...v3.14.2)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  dependency-type: direct:production
  dependency-group: go_modules-security-group
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-27 06:05:54 +00:00
Salah Al Saleh 09287f624f Fix workflow name in automated PRs manager (#1491) 2024-02-26 15:16:39 -08:00
dependabot[bot]andlnx01 cd7f2dc74d chore(deps): bump helm.sh/helm/v3 from 3.14.1 to 3.14.2 in /examples/sdk/helm-template (#1490)
chore(deps): bump helm.sh/helm/v3 in /examples/sdk/helm-template

Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.14.1 to 3.14.2.
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.14.1...v3.14.2)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-26 22:41:19 +00:00
Salah Al Saleh 8132936e3e Fix automated PRs manager workflow (#1489)
* Fix automated PRs manager workflow
2024-02-26 14:30:21 -08:00
Xav PaiceandSalah Al Saleh b49d1050c9 feat: add dependabot auto merge (#1473)
* feat: add dependabot auto merge & grouping

Adds grouping for Dependabot PRs, meaning we no longer need to manually group PRs that fail tests due to incompatible groups.

Adds auto-merge of Dependabot PRs when they pass all tests.

* roll back dependabot label changes

* use secrets.GITHUB_TOKEN for automated PRs from Dependabot

* Automerge Dependabot PRS, use REPLICATED_GH_PAT for secret

* amend and simplify auto-dependabot job

* simplify PR jobs

* remove CODEOWNER ownership for go.mod

* Update .github/workflows/automated-prs-manager.yaml

Co-authored-by: Salah Al Saleh <sg.alsaleh@gmail.com>

---------

Co-authored-by: Salah Al Saleh <sg.alsaleh@gmail.com>
2024-02-23 12:28:39 +13:00
Xav Paice 27f867bd4e Update dependabot.yml (#1482) 2024-02-22 19:33:50 +13:00
Xav Paice f4d2cfb749 update Helm example for CVE (#1481) 2024-02-22 13:56:19 +13:00
dependabot[bot]andlnx01 8620e001d7 chore(deps): bump the security group with 16 updates (#1478)
Bumps the security group with 16 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/containers/image/v5](https://github.com/containers/image) | `5.29.0` | `5.29.2` |
| [github.com/google/uuid](https://github.com/google/uuid) | `1.4.0` | `1.6.0` |
| [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) | `3.23.12` | `3.24.1` |
| [github.com/vmware-tanzu/velero](https://github.com/vmware-tanzu/velero) | `1.10.3` | `1.13.0` |
| [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.23.0` | `1.23.1` |
| [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) | `1.23.0` | `1.23.1` |
| [k8s.io/api](https://github.com/kubernetes/api) | `0.29.1` | `0.29.2` |
| [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) | `0.29.0` | `0.29.2` |
| [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) | `0.29.1` | `0.29.2` |
| [k8s.io/apiserver](https://github.com/kubernetes/apiserver) | `0.29.0` | `0.29.2` |
| [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) | `0.29.1` | `0.29.2` |
| [k8s.io/client-go](https://github.com/kubernetes/client-go) | `0.29.1` | `0.29.2` |
| [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) | `0.17.0` | `0.17.2` |
| [golang.org/x/net](https://github.com/golang/net) | `0.19.0` | `0.21.0` |
| [helm.sh/helm/v3](https://github.com/helm/helm) | `3.14.0` | `3.14.1` |
| [k8s.io/metrics](https://github.com/kubernetes/metrics) | `0.29.0` | `0.29.2` |


Updates `github.com/containers/image/v5` from 5.29.0 to 5.29.2
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.29.0...v5.29.2)

Updates `github.com/google/uuid` from 1.4.0 to 1.6.0
- [Release notes](https://github.com/google/uuid/releases)
- [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/uuid/compare/v1.4.0...v1.6.0)

Updates `github.com/shirou/gopsutil/v3` from 3.23.12 to 3.24.1
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.12...v3.24.1)

Updates `github.com/vmware-tanzu/velero` from 1.10.3 to 1.13.0
- [Release notes](https://github.com/vmware-tanzu/velero/releases)
- [Changelog](https://github.com/vmware-tanzu/velero/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vmware-tanzu/velero/compare/v1.10.3...v1.13.0)

Updates `go.opentelemetry.io/otel` from 1.23.0 to 1.23.1
- [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.23.0...v1.23.1)

Updates `go.opentelemetry.io/otel/sdk` from 1.23.0 to 1.23.1
- [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.23.0...v1.23.1)

Updates `k8s.io/api` from 0.29.1 to 0.29.2
- [Commits](https://github.com/kubernetes/api/compare/v0.29.1...v0.29.2)

Updates `k8s.io/apiextensions-apiserver` from 0.29.0 to 0.29.2
- [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases)
- [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.29.0...v0.29.2)

Updates `k8s.io/apimachinery` from 0.29.1 to 0.29.2
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.29.1...v0.29.2)

Updates `k8s.io/apiserver` from 0.29.0 to 0.29.2
- [Commits](https://github.com/kubernetes/apiserver/compare/v0.29.0...v0.29.2)

Updates `k8s.io/cli-runtime` from 0.29.1 to 0.29.2
- [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.29.1...v0.29.2)

Updates `k8s.io/client-go` from 0.29.1 to 0.29.2
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.29.1...v0.29.2)

Updates `sigs.k8s.io/controller-runtime` from 0.17.0 to 0.17.2
- [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.17.0...v0.17.2)

Updates `golang.org/x/net` from 0.19.0 to 0.21.0
- [Commits](https://github.com/golang/net/compare/v0.19.0...v0.21.0)

Updates `helm.sh/helm/v3` from 3.14.0 to 3.14.1
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.14.0...v3.14.1)

Updates `k8s.io/metrics` from 0.29.0 to 0.29.2
- [Commits](https://github.com/kubernetes/metrics/compare/v0.29.0...v0.29.2)

---
updated-dependencies:
- dependency-name: github.com/containers/image/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/google/uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/vmware-tanzu/velero
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: go.opentelemetry.io/otel
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/api
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/apiextensions-apiserver
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/apimachinery
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/apiserver
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/cli-runtime
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/client-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: sigs.k8s.io/controller-runtime
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: helm.sh/helm/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/metrics
  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>
2024-02-20 16:23:56 +13:00
dependabot[bot]andlnx01 bb9a2931a1 chore(deps): bump github.com/opencontainers/image-spec from 1.1.0-rc6 to 1.1.0 (#1479)
chore(deps): bump github.com/opencontainers/image-spec

Bumps [github.com/opencontainers/image-spec](https://github.com/opencontainers/image-spec) from 1.1.0-rc6 to 1.1.0.
- [Release notes](https://github.com/opencontainers/image-spec/releases)
- [Changelog](https://github.com/opencontainers/image-spec/blob/main/RELEASES.md)
- [Commits](https://github.com/opencontainers/image-spec/compare/v1.1.0-rc6...v1.1.0)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/image-spec
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-20 16:23:10 +13:00
Xav Paice 7c6788c0c5 Update dependabot.yml (#1477) 2024-02-20 15:42:17 +13:00
Ethan Mosbaugh e24ca642aa feat: sonobuoy collector (#1469) 2024-02-16 06:56:15 -08:00
dependabot[bot]andlnx01 7dc9d6dfe4 chore(deps): bump github.com/fatih/color from 1.15.0 to 1.16.0 (#1467)
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.15.0...v1.16.0)

---
updated-dependencies:
- dependency-name: github.com/fatih/color
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-15 11:09:12 +00:00
dependabot[bot]andlnx01 118b2edc0a chore(deps): bump golang.org/x/mod from 0.14.0 to 0.15.0 (#1468)
Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.14.0 to 0.15.0.
- [Commits](https://github.com/golang/mod/compare/v0.14.0...v0.15.0)

---
updated-dependencies:
- dependency-name: golang.org/x/mod
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-15 11:07:30 +00:00
dependabot[bot]andlnx01 84d51481da chore(deps): bump oras.land/oras-go from 1.2.4 to 1.2.5 (#1464)
Bumps [oras.land/oras-go](https://github.com/oras-project/oras-go) from 1.2.4 to 1.2.5.
- [Release notes](https://github.com/oras-project/oras-go/releases)
- [Commits](https://github.com/oras-project/oras-go/compare/v1.2.4...v1.2.5)

---
updated-dependencies:
- dependency-name: oras.land/oras-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-15 10:47:46 +00:00
Evans Mungai fad7a26156 fix: improve flaky goldpinger test (#1463)
We do not need to check if goldpinger pinged or not. At times it would have successful pings that lead to test failures. Its enough to just check that we have analysis results
2024-02-09 16:33:52 +00:00
Gerard Nguyen 772d867093 update correct K8S API group for batch and policy (#1461) 2024-02-09 08:17:55 +13:00
Gerard Nguyen 5daf6d6c89 Feat: new collector run-daemonset (#1460)
* feat: new collector run-daemonset
2024-02-09 08:16:32 +13:00
Xav Paice 8b491b5702 Downgrade Velero to v1.10 (#1462)
In order to be compatible with KOTS, downgrade Velero to 1.10.

This removes some features from the Velero collector, but unblocks KOTS from being able
to import Troubleshoot.

We should be wary of updating Velero in the future to prevent this recurring.

sc-98475
2024-02-09 08:13:26 +13:00
18efea6616 chore(deps): bump go.opentelemetry.io/otel from 1.19.0 to 1.23.0 (#1459)
* chore(deps): bump go.opentelemetry.io/otel from 1.19.0 to 1.23.0

Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.19.0 to 1.23.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.19.0...v1.23.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* bump go.opentelemetry.io/otel/sdk

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Xav Paice <xav@replicated.com>
2024-02-07 11:35:41 +00:00
dependabot[bot]andlnx01 48fc8b2e84 chore(deps): bump k8s.io/klog/v2 from 2.110.1 to 2.120.1 (#1453)
Bumps [k8s.io/klog/v2](https://github.com/kubernetes/klog) from 2.110.1 to 2.120.1.
- [Release notes](https://github.com/kubernetes/klog/releases)
- [Changelog](https://github.com/kubernetes/klog/blob/main/RELEASE.md)
- [Commits](https://github.com/kubernetes/klog/compare/v2.110.1...v2.120.1)

---
updated-dependencies:
- dependency-name: k8s.io/klog/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-07 11:35:06 +00:00
Evans Mungai e2adfa3774 fix: Correct selector used to find installer specs when redacting (#1456) 2024-02-07 10:36:15 +00:00
Xav Paice 41134ca621 Revert "chore(deps): bump github.com/vmware-tanzu/velero from 1.12.0 to 1.13.0" (#1458)
Revert "chore(deps): bump github.com/vmware-tanzu/velero from 1.12.0 to 1.13.…"

This reverts commit d4c1e9cc04.
2024-02-07 13:21:01 +13:00
dependabot[bot]andlnx01 05a852d68c chore(deps): bump github.com/jackc/pgx/v5 from 5.5.2 to 5.5.3 (#1452)
Bumps [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.5.2 to 5.5.3.
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.5.2...v5.5.3)

---
updated-dependencies:
- dependency-name: github.com/jackc/pgx/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-06 20:41:43 +13:00
dependabot[bot]andlnx01 90d04b1f7d chore(deps): bump github.com/opencontainers/image-spec from 1.1.0-rc5 to 1.1.0-rc6 (#1454)
chore(deps): bump github.com/opencontainers/image-spec

Bumps [github.com/opencontainers/image-spec](https://github.com/opencontainers/image-spec) from 1.1.0-rc5 to 1.1.0-rc6.
- [Release notes](https://github.com/opencontainers/image-spec/releases)
- [Changelog](https://github.com/opencontainers/image-spec/blob/main/RELEASES.md)
- [Commits](https://github.com/opencontainers/image-spec/compare/v1.1.0-rc5...v1.1.0-rc6)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/image-spec
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-06 20:41:13 +13:00
dependabot[bot]andlnx01 de0a2c631c chore(deps): bump helm.sh/helm/v3 from 3.13.3 to 3.14.0 (#1451)
Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.13.3 to 3.14.0.
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.13.3...v3.14.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-06 20:40:48 +13:00
Evans Mungai 2def517d39 chore: additional ceph collector commands (#1450)
* Collect text representations of the ceph data which are easier to read
* Collect ceph df command output
2024-02-06 10:04:25 +13:00
Xav PaiceandEvans Mungai fe6b1c7448 Add workaround for EKS version string (#1449)
* Add workaround for EKS version string

The EKS version string returned is not semver compliant.  To work around this, we remove
the suffix for version strings that contain -eks-.

Fixes #1441

* Add parsing version test cases

* Rename function

---------

Co-authored-by: Evans Mungai <evans@replicated.com>
2024-02-06 10:04:01 +13:00
Xav Paice 710366c8e5 bump containerd to patch for CVE-2024-21626 (#1445) 2024-02-01 17:16:51 +13:00
dependabot[bot]andlnx01 96529106c7 chore(deps): bump github.com/opencontainers/runc from 1.1.10 to 1.1.12 (#1444)
Bumps [github.com/opencontainers/runc](https://github.com/opencontainers/runc) from 1.1.10 to 1.1.12.
- [Release notes](https://github.com/opencontainers/runc/releases)
- [Changelog](https://github.com/opencontainers/runc/blob/v1.1.12/CHANGELOG.md)
- [Commits](https://github.com/opencontainers/runc/compare/v1.1.10...v1.1.12)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/runc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-01 14:43:58 +13:00
Gerard Nguyen 39b371991e feat: add timeout to run host collector (#1435)
* feat: add timeout to run host collector

* return error on invalid timeout

* return -1 on context deadline exceeded
2024-01-31 12:46:01 +00:00
dependabot[bot]andlnx01 d4c1e9cc04 chore(deps): bump github.com/vmware-tanzu/velero from 1.12.0 to 1.13.0 (#1437)
Bumps [github.com/vmware-tanzu/velero](https://github.com/vmware-tanzu/velero) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/vmware-tanzu/velero/releases)
- [Changelog](https://github.com/vmware-tanzu/velero/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vmware-tanzu/velero/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/vmware-tanzu/velero
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-30 13:33:35 +00:00
dependabot[bot]andlnx01 70a0024a76 chore(deps): bump k8s.io/cli-runtime from 0.29.0 to 0.29.1 (#1439)
Bumps [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) from 0.29.0 to 0.29.1.
- [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.29.0...v0.29.1)

---
updated-dependencies:
- dependency-name: k8s.io/cli-runtime
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-30 13:33:23 +00:00
dependabot[bot]andlnx01 62984cba40 chore(deps): bump github.com/hashicorp/go-getter from 1.7.2 to 1.7.3 (#1438)
Bumps [github.com/hashicorp/go-getter](https://github.com/hashicorp/go-getter) from 1.7.2 to 1.7.3.
- [Release notes](https://github.com/hashicorp/go-getter/releases)
- [Changelog](https://github.com/hashicorp/go-getter/blob/main/.goreleaser.yml)
- [Commits](https://github.com/hashicorp/go-getter/compare/v1.7.2...v1.7.3)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/go-getter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-30 12:59:29 +00:00
Diamon WigginsandEvans Mungai 1447e18c56 feat: Allow templating of outcome messages for the JSON/YAML compare analyzers (#1432)
* feat: allow templating of the outcome message for the JSON and YAML Compare analyzers

* Update pkg/analyze/json_compare.go

Co-authored-by: Evans Mungai <evans@replicated.com>
2024-01-26 10:56:57 -05:00
Evans Mungai 37a5cce8ce chore: minor updates to docs and Makefile (#1433)
* Deprecate old Makefile targets
* Name Makefile targets to point to files they 'make' as per convention
* Update the contributing doc
* Update CLI docs left behind
2024-01-25 18:55:51 +00:00
dependabot[bot]andlnx01 80de5e3d12 chore(deps): bump github.com/spf13/cobra from 1.7.0 to 1.8.0 (#1422)
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.7.0...v1.8.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-23 19:05:54 +13:00
dependabot[bot]andlnx01 c1eddca1e5 chore(deps): bump github.com/gorilla/handlers from 1.5.1 to 1.5.2 (#1428)
Bumps [github.com/gorilla/handlers](https://github.com/gorilla/handlers) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/gorilla/handlers/releases)
- [Commits](https://github.com/gorilla/handlers/compare/v1.5.1...v1.5.2)

---
updated-dependencies:
- dependency-name: github.com/gorilla/handlers
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-23 15:31:01 +13:00
dependabot[bot]andlnx01 19a04dc24e chore(deps): bump sigs.k8s.io/controller-runtime from 0.16.3 to 0.17.0 (#1429)
Bumps [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) from 0.16.3 to 0.17.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.16.3...v0.17.0)

---
updated-dependencies:
- dependency-name: sigs.k8s.io/controller-runtime
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-23 15:30:10 +13:00
dependabot[bot]andlnx01 5d51207f4d chore(deps): bump sigs.k8s.io/yaml from 1.3.0 to 1.4.0 (#1430)
Bumps [sigs.k8s.io/yaml](https://github.com/kubernetes-sigs/yaml) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/kubernetes-sigs/yaml/releases)
- [Changelog](https://github.com/kubernetes-sigs/yaml/blob/master/RELEASE.md)
- [Commits](https://github.com/kubernetes-sigs/yaml/compare/v1.3.0...v1.4.0)

---
updated-dependencies:
- dependency-name: sigs.k8s.io/yaml
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-23 15:28:29 +13:00
dependabot[bot]andlnx01 8d727ea31f chore(deps): bump github.com/jackc/pgx/v5 from 5.4.3 to 5.5.2 (#1426)
Bumps [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.4.3 to 5.5.2.
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.4.3...v5.5.2)

---
updated-dependencies:
- dependency-name: github.com/jackc/pgx/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-18 12:51:45 +00:00
dependabot[bot]andlnx01 b46f89af7f chore(deps): bump github.com/shirou/gopsutil/v3 from 3.23.9 to 3.23.12 (#1423)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.9 to 3.23.12.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.9...v3.23.12)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-18 12:17:29 +00:00
dependabot[bot]andlnx01 5ad108fab6 chore(deps): bump github.com/spf13/viper from 1.17.0 to 1.18.2 (#1421)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.17.0 to 1.18.2.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.17.0...v1.18.2)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-18 12:08:40 +00:00
Akash Shrivastava 361e12e691 feat: [ISSUE-1401]: Added helm get values option in Helm collector (#1402)
* feat: [ISSUE-1401]: Added helm get values option in Helm collector

Signed-off-by: Akash Shrivastava <akash.shrivastava@harness.io>

* Made changes in error handling

Signed-off-by: Akash Shrivastava <akash.shrivastava@harness.io>

* feat: [ISSUE-1401]: Added test cases and fixes

Signed-off-by: Akash Shrivastava <akash.shrivastava@harness.io>

* fixed test case

Signed-off-by: Akash Shrivastava <akash.shrivastava@harness.io>

---------

Signed-off-by: Akash Shrivastava <akash.shrivastava@harness.io>
2024-01-16 19:12:43 +00:00
Xav Paice e542f4fd0a bump k8s.io packages to v0.29.0 (#1419)
* bump k8s.io packages to v0.29.0

* update Go to 1.21

* update schemas
2024-01-08 17:24:41 +00:00
Evans Mungai e4363a1e50 fix (supportbundle): Add default collectors when expected (#1418)
* fix (supportbundle): Add default collectors when expected

* Remove unnecessary change

* Add default collectors to a empty spec

* Add more tests
2024-01-08 11:32:04 +00:00
dependabot[bot]andlnx01 b5996d06ba chore(deps): bump github.com/go-logr/logr from 1.2.4 to 1.4.1 (#1412)
Bumps [github.com/go-logr/logr](https://github.com/go-logr/logr) from 1.2.4 to 1.4.1.
- [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.2.4...v1.4.1)

---
updated-dependencies:
- dependency-name: github.com/go-logr/logr
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-02 16:05:18 +00:00
dependabot[bot]andlnx01 b1b6299ed5 chore(deps): bump golang.org/x/mod from 0.13.0 to 0.14.0 (#1414)
Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.13.0 to 0.14.0.
- [Commits](https://github.com/golang/mod/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: golang.org/x/mod
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-02 16:04:29 +00:00
dependabot[bot]andlnx01 7681cc249c chore(deps): bump sigs.k8s.io/controller-runtime from 0.16.2 to 0.16.3 (#1416)
Bumps [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) from 0.16.2 to 0.16.3.
- [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.16.2...v0.16.3)

---
updated-dependencies:
- dependency-name: sigs.k8s.io/controller-runtime
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-02 16:03:33 +00:00
dependabot[bot]andlnx01 cad5e8179c chore(deps): bump helm.sh/helm/v3 from 3.12.3 to 3.13.3 (#1410)
Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.12.3 to 3.13.3.
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.12.3...v3.13.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-29 08:03:59 +13:00
Andrew Laveryandlnx01 4c77bcb7cc Update containers/image, oras-go, and docker (#1411)
* chore(deps): bump github.com/containers/image/v5 from 5.25.0 to 5.29.0

Bumps [github.com/containers/image/v5](https://github.com/containers/image) from 5.25.0 to 5.29.0.
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.25.0...v5.29.0)

---
updated-dependencies:
- dependency-name: github.com/containers/image/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* regenerate files

* bump oras-go

* changes from 'make test'

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-28 13:31:05 -05:00
Xav Paice 534931600b bump github actions/download-artifact (#1409) 2023-12-20 16:38:14 +13:00
dependabot[bot]andlnx01 acaa2edd30 chore(deps): bump github.com/containerd/containerd from 1.7.0 to 1.7.11 (#1408)
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.7.0 to 1.7.11.
- [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.0...v1.7.11)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-20 16:24:27 +13:00
Evans Mungai a86b5c4441 chore(preflight): Better error message when not results found (#1397)
code(preflight): Better error message when not results found
2023-12-20 16:10:47 +13:00
dependabot[bot]andlnx01 bd275dfbab chore(deps): bump github/codeql-action from 2 to 3 (#1403)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  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>
2023-12-20 16:09:57 +13:00
dependabot[bot]andlnx01 7093fa0865 chore(deps): bump golang.org/x/crypto from 0.14.0 to 0.17.0 in /examples/sdk/helm-template (#1406)
chore(deps): bump golang.org/x/crypto in /examples/sdk/helm-template

Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.14.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.14.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-20 16:09:20 +13:00
Evans Mungai 3012b870bd fix: flaky e2e test (#1400) 2023-12-20 16:07:30 +13:00
dependabot[bot]andlnx01 f44a3d3986 chore(deps): bump golang.org/x/crypto from 0.14.0 to 0.17.0 (#1407)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.14.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.14.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-20 16:06:33 +13:00
Evans Mungai 53113c0170 feat: goldpinger collector and analyser (#1398)
* feat: goldpinger analyser

Analyser to generate a report from goldpinger results

* Add goldpinger testdata

* Goldpinger collector

* Improvements after running tests

* More minor updates after further testing

* Better error message if a container fails to start

* A few more updates

* Add goldpinger e2e test

* Update schemas

* Clean up help installs in e2e tests

* Add resource limits to goldpinger pods

* Some minor improvements

* Some more changes noted when writing docs

* Update schemas

* A few more updates when testing with kURL

* Log goldpinger tests

* Tests before exit code
2023-12-12 11:02:41 +00:00
dependabot[bot]andlnx01 b0907d7139 chore(deps): bump actions/setup-go from 4 to 5 (#1399)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  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>
2023-12-12 10:31:47 +00:00
Evans Mungai fef12be8f1 fix(support-bundle): add Replicated user-agent header to the correct URLs (#1396)
fix(support-bundle): correct user-agent bevahiour

Only kots.io requires a user agent when downloading troubleshoot specs
2023-12-01 16:29:43 +00:00
Evans Mungai e5e26eea14 fix(support-bundle): default in-cluster collectors in host support bundle (#1394)
* fix(support-bundle): default in-cluster collectors in host support bundle

Ensure cluster-resources and cluster-info collectors are present only
when a support bundle spec contains in-cluster collectors.

* Various improvements

* Improve error messages
* Util function appending elements to a nil slice that allows adding
  specs to an empty slice of collectors/analysers/redactors

* Fix failing test
2023-11-27 18:33:02 +00:00
Evans Mungai d4623d9404 fix(collector): Let pgx library parse TLS parameters (#1390)
* fix(collector): Let pgx library parse TLS parameters

This allows the collector to respect the sslmode parameters

Fix: #1163

* Add comment

* Improve postgres collector test
2023-11-16 12:51:43 +00:00
Weiyanli Chen(York)andEvans Mungai bc4856869e fix: missing omitempty on 2 of the new fields (#1389)
* fix: missing omitempty on 2 of the new fields

* fix: Rename TS_WORKSPACE_DIR to TS_OUTPUT_DIR

---------

Co-authored-by: Evans Mungai <evans@replicated.com>
2023-11-09 13:25:11 +00:00
Weiyanli Chen(York)andEvans Mungai f6373f3e36 feat: save host run file output (#1376)
* feat: save cmd run output

* chore: schema changes

* chore: example hostCollector

* chore: add log messages to key actions

* fix: correctly inherit all parent env by default

* chore: do not save input file

the user invokes the input already got the input but those content could be sensitive to another user who received this bundle

* test: unit test for host run

* revert: "chore: do not save input file"

This reverts commit 6af77ad1ce.

that commit is wrong

* chore: fix log msg and example yaml

* Ensure child cmd runs in its own working dir

* Check filename for slashes not content

* Update logging

* Add using relative path files as commands

---------

Co-authored-by: Evans Mungai <evans@replicated.com>
2023-11-08 13:49:46 +00:00
Archit Sharma 7038da85b1 Velero analyzer (#1366)
* feat: add velero analyzer (#806)

  * updated schema
  * analyzer without collector
  * tests
  * covers deprecated Restic repository type
  * velero version from deployment image to check deprecated type
  * read for both velero pod kinds (velero*, node-agent*)

---------

Signed-off-by: Archit Sharma <archit@pm.me>
2023-11-03 18:41:17 +05:30
Jason McCampbell a7bb9ea31e Add support for Oracle OKE environment (#1387) 2023-11-02 17:16:18 +13:00
Diamon WigginsandEvans Mungai 08c3fcf3df Gracefully handle unreachable URIs in loadSupportBundleSpecsFromURIs (#1383)
* gracefully handle unreachable URIs in loadSupportBundleSpecsFromURIs

* let caller decide how to handle the error

* fix klog import

* Add a test to ensure failing to load uri does not error

---------

Co-authored-by: Evans Mungai <evans@replicated.com>
2023-10-27 14:48:09 +01:00
dependabot[bot]andlnx01 1d2c1191e2 chore(deps): bump google.golang.org/grpc from 1.58.2 to 1.58.3 (#1380)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.58.2 to 1.58.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.58.2...v1.58.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-27 11:06:39 +01:00
dependabot[bot]andlnx01 66e070a07d chore(deps): bump go.opentelemetry.io/otel/sdk from 1.18.0 to 1.19.0 (#1378)
Bumps [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) from 1.18.0 to 1.19.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.18.0...v1.19.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 10:03:45 +10:00
dependabot[bot]andlnx01 7528c57e83 chore(deps): bump golang.org/x/sync from 0.3.0 to 0.4.0 (#1373)
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.3.0 to 0.4.0.
- [Commits](https://github.com/golang/sync/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-19 16:14:39 +01:00
122 changed files with 54364 additions and 1250 deletions
+5 -1
View File
@@ -14,4 +14,8 @@
## RULES
* @replicatedhq/troubleshoot
*.md @replicatedhq/cre
*.md @replicatedhq/cre
go.mod
go.sum
/examples/sdk/helm-template/go.mod
/examples/sdk/helm-template/go.sum
+21 -3
View File
@@ -10,10 +10,28 @@ updates:
labels:
- "dependencies"
- "go"
- "type::chore"
- "type::security"
schedule:
interval: "weekly"
groups:
security:
update-types:
- "patch"
- "minor"
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/examples/sdk/helm-template" # Location of package manifests
labels:
- "dependencies"
- "go"
- "type::security"
schedule:
interval: "weekly"
groups:
security:
update-types:
- "patch"
- "minor"
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
# Workflow files stored in the
@@ -22,6 +40,6 @@ updates:
labels:
- "dependencies"
- "github-actions"
- "type::chore"
- "type::security"
schedule:
interval: "weekly"
@@ -0,0 +1,97 @@
name: Automated PRs Manager
on:
schedule:
- cron: "0 */6 * * *" # every 6 hours
workflow_dispatch: {}
jobs:
list-prs:
runs-on: ubuntu-latest
outputs:
prs: ${{ steps.list-prs.outputs.prs }}
env:
GH_TOKEN: ${{ secrets.REPLICATED_GH_PAT }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: List PRs
id: list-prs
run: |
set -euo pipefail
# list prs that are less than 24h old and exclude prs from forks
dependabot_prs=$(
gh pr list \
--author 'dependabot[bot]' \
--json url,createdAt,headRefName,headRepository,headRepositoryOwner \
-q '.[] | select((.createdAt | fromdateiso8601 > now - 24*60*60) and .headRepositoryOwner.login == "replicatedhq" and .headRepository.name == "troubleshoot")'
)
prs=$(echo "$dependabot_prs" | jq -sc '. | unique')
echo "prs=$prs" >> "$GITHUB_OUTPUT"
process-prs:
needs: list-prs
runs-on: ubuntu-latest
if: needs.list-prs.outputs.prs != '[]'
strategy:
matrix:
pr: ${{ fromJson(needs.list-prs.outputs.prs) }}
fail-fast: false
max-parallel: 1
env:
GH_TOKEN: ${{ secrets.REPLICATED_GH_PAT }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ matrix.pr.headRefName }}
- name: Process PR
run: |
set -euo pipefail
echo "Ensuring required labels..."
gh pr edit "${{ matrix.pr.url }}" --add-label "type::security"
echo "Checking status of tests..."
run_id=$(gh run list --branch "${{ matrix.pr.headRefName }}" --workflow build-test-deploy --limit 1 --json databaseId -q '.[0].databaseId')
# If there are still pending jobs, skip.
num_of_pending_jobs=$(gh run view "$run_id" --json jobs -q '.jobs[] | select(.conclusion == "") | .name' | wc -l)
if [ "$num_of_pending_jobs" -gt 0 ]; then
echo "There are still pending jobs. Skipping."
exit 0
fi
# If all checks passed, approve and merge.
if gh run view "$run_id" --json jobs -q '.jobs[] | select(.name == "validate-success") | .conclusion' | grep -q "success"; then
if gh pr checks "${{ matrix.pr.url }}"; then
echo "All tests passed. Approving and merging."
echo -e "LGTM :thumbsup: \n\nThis PR was automatically approved and merged by the [automated-prs-manager](https://github.com/replicatedhq/troubleshoot/blob/main/.github/workflows/automated-prs-manager.yaml) GitHub action" > body.txt
gh pr review --approve "${{ matrix.pr.url }}" --body-file body.txt
sleep 10
gh pr merge --auto --squash "${{ matrix.pr.url }}"
exit 0
else
echo "Some checks did not pass. Skipping."
exit 0
fi
fi
# If more than half of the jobs are successful, re-run the failed jobs.
num_of_jobs=$(gh run view "$run_id" --json jobs -q '.jobs[].name ' | wc -l)
num_of_successful_jobs=$(gh run view "$run_id" --json jobs -q '.jobs[] | select(.conclusion == "success") | .name' | wc -l)
if [ "$num_of_successful_jobs" -gt $((num_of_jobs / 2)) ]; then
echo "More than half of the jobs are successful. Re-running failed jobs."
gh run rerun "$run_id" --failed
exit 0
fi
echo "Less than half of the jobs are successful. Skipping."
+66 -27
View File
@@ -1,3 +1,5 @@
name: build-test-deploy
on:
pull_request:
types:
@@ -28,9 +30,9 @@ jobs:
test:
runs-on: ubuntu-20.04
steps:
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: "1.20"
go-version: "1.21"
- name: setup env
run: |
@@ -45,9 +47,9 @@ jobs:
test-integration:
runs-on: ubuntu-20.04
steps:
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: "1.20"
go-version: "1.21"
- name: setup env
run: |
@@ -59,16 +61,16 @@ jobs:
- uses: replicatedhq/action-k3s@main
id: k3s
with:
version: v1.23.6-k3s1
version: v1.24.1-k3s1
- run: make test-integration
ensure-schemas-are-generated:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: "1.20"
go-version: "1.21"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
@@ -82,9 +84,9 @@ jobs:
compile-preflight:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: "1.20"
go-version: "1.21"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
@@ -92,7 +94,7 @@ jobs:
shell: bash
- uses: actions/checkout@v4
- run: make generate preflight
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: preflight
path: bin/preflight
@@ -106,7 +108,7 @@ jobs:
with:
version: v1.23.6-k3s1
- name: Download preflight binary
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: preflight
path: bin/
@@ -155,7 +157,7 @@ jobs:
with:
version: v1.23.6-k3s1
- name: Download preflight binary
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: preflight
path: bin/
@@ -171,9 +173,9 @@ jobs:
compile-supportbundle:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: "1.20"
go-version: "1.21"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
@@ -181,7 +183,7 @@ jobs:
shell: bash
- uses: actions/checkout@v4
- run: make generate support-bundle
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: support-bundle
path: bin/support-bundle
@@ -196,7 +198,7 @@ jobs:
with:
version: v1.23.6-k3s1
- name: Download support-bundle binary
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: support-bundle
path: bin/
@@ -215,7 +217,7 @@ jobs:
with:
version: v1.23.6-k3s1
- name: Download support bundle binary
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: support-bundle
path: bin/
@@ -226,9 +228,9 @@ jobs:
runs-on: ubuntu-latest
needs: compile-supportbundle
steps:
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: "1.20"
go-version: "1.21"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
@@ -236,7 +238,7 @@ jobs:
shell: bash
- uses: actions/checkout@v4
- name: Download support bundle binary
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: support-bundle
path: bin/
@@ -246,9 +248,9 @@ jobs:
compile-collect:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: "1.20"
go-version: "1.21"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
@@ -256,7 +258,7 @@ jobs:
shell: bash
- uses: actions/checkout@v4
- run: make generate collect
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: collect
path: bin/collect
@@ -279,9 +281,9 @@ jobs:
with:
fetch-depth: 0
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: "1.20"
go-version: "1.21"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
@@ -310,9 +312,9 @@ jobs:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: "1.20"
go-version: "1.21"
- uses: sigstore/cosign-installer@v3
with:
@@ -348,3 +350,40 @@ jobs:
uses: rajatjindal/krew-release-bot@v0.0.46
with:
krew_template_file: deploy/krew/support-bundle.yaml
# summary jobs, these jobs will only run if all the other jobs have succeeded
validate-pr-tests:
runs-on: ubuntu-latest
needs:
- test
- test-integration
- run-examples
- compile-collect
- validate-preflight
- validate-preflight-e2e
- validate-supportbundle
- validate-supportbundle-e2e
- validate-supportbundle-e2e-go-test
- ensure-schemas-are-generated
steps:
- run: echo "All PR tests passed"
# this job will validate that the validation did not fail and that all pr-tests succeed
# it is used for the github branch protection rule
validate-success:
runs-on: ubuntu-latest
needs:
- validate-pr-tests
if: always()
steps:
# https://docs.github.com/en/actions/learn-github-actions/contexts#needs-context
# if the validate-pr-tests job was not successful, this job will fail
- name: fail if validate-pr-tests job was not successful
if: needs.validate-pr-tests.result != 'success'
run: exit 1
# if the validate-pr-tests job was successful, this job will succeed
- name: succeed if validate-pr-tests job succeeded
if: needs.validate-pr-tests.result == 'success'
run: echo "Validation succeeded"
+1 -1
View File
@@ -22,6 +22,6 @@ jobs:
severity: 'HIGH,CRITICAL'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
+4 -2
View File
@@ -16,7 +16,9 @@ jobs:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: 1.21
- name: Install Go deps
run: go mod download
@@ -30,7 +32,7 @@ jobs:
run: trivy fs --scanners license --skip-dirs ".github" . | tee license-report.txt
- name: Upload license report artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: license-report
path: license-report.txt
+1
View File
@@ -10,3 +10,4 @@ linters:
- gocyclo
- gofmt
- gosec
- govet
+4 -4
View File
@@ -23,12 +23,12 @@ To get started we recommend:
1. Go (v1.20 or later)
2. A Kubernetes cluster (we recommend <https://k3d.io/>. This requires Docker v20.10.5 or later)
3. Fork and clone the repo to $GOPATH/src/github.com/replicatedhq/
4. Run `make support-bundle preflight` to generate binaries
5. Run `make run-troubleshoot` to generate a support bundle with the `sample-troubleshoot.yaml` in the root of the repo
3. Fork and clone repo
4. Run `make clean build` to generate binaries
5. Run `make run-support-bundle` to generate a support bundle with the `sample-troubleshoot.yaml` in the root of the repo
> Note: recent versions of Go support easy cross-compilation. For example, to cross-compile a Linux binary from MacOS:
> `GOOS=linux GOARCH=amd64 make support-bundle preflight`
> `GOOS=linux GOARCH=amd64 make clean build`
6. Install [golangci-lint] linter and run `make lint` to execute additional code linters.
+67 -41
View File
@@ -39,8 +39,8 @@ BUILDPATHS = ./pkg/... ./cmd/... ./internal/...
E2EPATHS = ./test/e2e/...
TESTFLAGS ?= -v -coverprofile cover.out
.DEFAULT: all
all: test build
.DEFAULT_GOAL := all
all: clean build test
.PHONY: ffi
ffi: fmt vet
@@ -81,27 +81,35 @@ support-bundle-e2e-go-test:
fi
# Build all binaries in parallel ( -j )
build:
build: tidy
@echo "Build cli binaries"
$(MAKE) -j support-bundle preflight analyze collect
$(MAKE) -j bin/support-bundle bin/preflight bin/analyze bin/collect
mod-tidy:
.PHONY: clean
clean:
@rm -f bin/analyze
@rm -f bin/support-bundle
@rm -f bin/collect
@rm -f bin/preflight
@rm -f bin/troubleshoot.h
@rm -f bin/troubleshoot.so
@rm -f bin/schemagen
@rm -f bin/docsgen
.PHONY: tidy
tidy:
go mod tidy
.PHONY: support-bundle
support-bundle:
bin/support-bundle:
go build ${BUILDFLAGS} ${LDFLAGS} -o bin/support-bundle github.com/replicatedhq/troubleshoot/cmd/troubleshoot
.PHONY: preflight
preflight:
bin/preflight:
go build ${BUILDFLAGS} ${LDFLAGS} -o bin/preflight github.com/replicatedhq/troubleshoot/cmd/preflight
.PHONY: analyze
analyze:
bin/analyze:
go build ${BUILDFLAGS} ${LDFLAGS} -o bin/analyze github.com/replicatedhq/troubleshoot/cmd/analyze
.PHONY: collect
collect:
bin/collect:
go build ${BUILDFLAGS} ${LDFLAGS} -o bin/collect github.com/replicatedhq/troubleshoot/cmd/collect
.PHONY: fmt
@@ -138,27 +146,27 @@ check-schemas: generate schemas
fi
.PHONY: schemas
schemas: fmt vet openapischema
go build ${LDFLAGS} -o bin/schemagen github.com/replicatedhq/troubleshoot/cmd/schemagen
schemas: fmt vet openapischema bin/schemagen
./bin/schemagen --output-dir ./schemas
bin/schemagen:
go build ${LDFLAGS} -o bin/schemagen github.com/replicatedhq/troubleshoot/cmd/schemagen
.PHONY: docs
docs: fmt vet
go build ${LDFLAGS} -o bin/docsgen github.com/replicatedhq/troubleshoot/cmd/docsgen
docs: fmt vet bin/docsgen
./bin/docsgen
bin/docsgen:
go build ${LDFLAGS} -o bin/docsgen github.com/replicatedhq/troubleshoot/cmd/docsgen
controller-gen:
go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.11.2
CONTROLLER_GEN=$(shell which controller-gen)
.PHONY: client-gen
client-gen:
ifeq (, $(shell which client-gen 2>/dev/null))
go install k8s.io/code-generator/cmd/client-gen@v0.28.2
CLIENT_GEN=$(shell go env GOPATH)/bin/client-gen
else
CLIENT_GEN=$(shell which client-gen)
endif
.PHONY: release
release: export GITHUB_TOKEN = $(shell echo ${GITHUB_TOKEN_TROUBLESHOOT})
@@ -180,15 +188,15 @@ local-release:
docker push localhost:32000/preflight:alpha
.PHONY: run-preflight
run-preflight: preflight
run-preflight: bin/preflight
./bin/preflight ./examples/preflight/sample-preflight.yaml
.PHONY: run-troubleshoot
run-troubleshoot: support-bundle
.PHONY: run-support-bundle
run-support-bundle: bin/support-bundle
./bin/support-bundle ./examples/support-bundle/sample-supportbundle.yaml
.PHONY: run-analyze
run-analyze: analyze
run-analyze: bin/analyze
./bin/analyze --analyzers ./examples/support-bundle/sample-analyzers.yaml ./support-bundle.tar.gz
.PHONY: init-sbom
@@ -212,20 +220,6 @@ sbom: sbom/assets/troubleshoot-sbom.tgz
cosign sign-blob -key cosign.key sbom/assets/troubleshoot-sbom.tgz > sbom/assets/troubleshoot-sbom.tgz.sig
cosign public-key -key cosign.key -outfile sbom/assets/key.pub
longhorn:
git clone https://github.com/longhorn/longhorn-manager.git
cd longhorn-manager && git checkout v1.2.2 && cd ..
rm -rf pkg/longhorn
mv longhorn-manager/k8s/pkg pkg/longhorn
mv longhorn-manager/types pkg/longhorn/types
mv longhorn-manager/util pkg/longhorn/util
rm -rf pkg/longhorn/util/daemon
rm -rf pkg/longhorn/util/server
find pkg/longhorn -type f | xargs sed -i "s/github.com\/longhorn\/longhorn-manager\/k8s\/pkg/github.com\/replicatedhq\/troubleshoot\/pkg\/longhorn/g"
find pkg/longhorn -type f | xargs sed -i "s/github.com\/longhorn\/longhorn-manager\/types/github.com\/replicatedhq\/troubleshoot\/pkg\/longhorn\/types/g"
find pkg/longhorn -type f | xargs sed -i "s/github.com\/longhorn\/longhorn-manager\/util/github.com\/replicatedhq\/troubleshoot\/pkg\/longhorn\/util/g"
rm -rf longhorn-manager
.PHONY: scan
scan:
trivy fs \
@@ -236,11 +230,11 @@ scan:
./
.PHONY: lint
lint: fmt vet
lint:
golangci-lint run --new -c .golangci.yaml ${BUILDPATHS}
.PHONY: lint-and-fix
lint-and-fix: fmt vet
lint-and-fix:
golangci-lint run --new --fix -c .golangci.yaml ${BUILDPATHS}
.PHONY: watch
@@ -256,3 +250,35 @@ watchrsync: npm-install
npm-install:
npm --version 2>&1 >/dev/null || ( echo "npm not installed; install npm to set up watchrsync" && exit 1 )
npm list gaze-run-interrupt || npm install install --no-save gaze-run-interrupt@~2.0.0
######## Lagacy make targets ###########
# Deprecated: These can be removed
.PHONY: support-bundle
support-bundle: clean bin/support-bundle
.PHONY: preflight
preflight: clean bin/preflight
.PHONY: analyze
analyze: clean bin/analyze
.PHONY: collect
collect: clean bin/collect
.PHONY: run-troubleshoot
run-troubleshoot: run-support-bundle
longhorn:
git clone https://github.com/longhorn/longhorn-manager.git
cd longhorn-manager && git checkout v1.2.2 && cd ..
rm -rf pkg/longhorn
mv longhorn-manager/k8s/pkg pkg/longhorn
mv longhorn-manager/types pkg/longhorn/types
mv longhorn-manager/util pkg/longhorn/util
rm -rf pkg/longhorn/util/daemon
rm -rf pkg/longhorn/util/server
find pkg/longhorn -type f | xargs sed -i "s/github.com\/longhorn\/longhorn-manager\/k8s\/pkg/github.com\/replicatedhq\/troubleshoot\/pkg\/longhorn/g"
find pkg/longhorn -type f | xargs sed -i "s/github.com\/longhorn\/longhorn-manager\/types/github.com\/replicatedhq\/troubleshoot\/pkg\/longhorn\/types/g"
find pkg/longhorn -type f | xargs sed -i "s/github.com\/longhorn\/longhorn-manager\/util/github.com\/replicatedhq\/troubleshoot\/pkg\/longhorn\/util/g"
rm -rf longhorn-manager
+1 -1
View File
@@ -27,7 +27,7 @@ func runAnalyzers(v *viper.Viper, bundlePath string) error {
} else {
if !util.IsURL(specPath) {
// TODO: Better error message when we do not have a file/url etc
return fmt.Errorf("%s is not a URL and was not found (err %s)", specPath, err)
return fmt.Errorf("%s is not a URL and was not found", specPath)
}
req, err := http.NewRequest("GET", specPath, nil)
+1 -1
View File
@@ -58,7 +58,7 @@ func runCollect(v *viper.Viper, arg string) error {
collectorContent = b
} else {
if !util.IsURL(arg) {
return fmt.Errorf("%s is not a URL and was not found (err %s)", arg, err)
return fmt.Errorf("%s is not a URL and was not found", arg)
}
req, err := http.NewRequest("GET", arg, nil)
+1 -1
View File
@@ -91,7 +91,7 @@ func downloadAnalyzerSpec(specPath string) (string, error) {
specContent = string(b)
} else {
if !util.IsURL(specPath) {
return "", fmt.Errorf("%s is not a URL and was not found (err %s)", specPath, err)
return "", fmt.Errorf("%s is not a URL and was not found", specPath)
}
req, err := http.NewRequest("GET", specPath, nil)
+37 -21
View File
@@ -232,24 +232,38 @@ the %s Admin Console to begin analysis.`
return nil
}
func loadSupportBundleSpecsFromURIs(ctx context.Context, kinds *loader.TroubleshootKinds) (*loader.TroubleshootKinds, error) {
// loadSupportBundleSpecsFromURIs loads support bundle specs from URIs
func loadSupportBundleSpecsFromURIs(ctx context.Context, kinds *loader.TroubleshootKinds) error {
remoteRawSpecs := []string{}
for _, s := range kinds.SupportBundlesV1Beta2 {
if s.Spec.Uri != "" && util.IsURL(s.Spec.Uri) {
// We are using LoadSupportBundleSpec function here since it handles prompting
// users to accept insecure connections
// There is an opportunity to refactor this code in favour of the Loader APIs
// TODO: Pass ctx to LoadSupportBundleSpec
rawSpec, err := supportbundle.LoadSupportBundleSpec(s.Spec.Uri)
if err != nil {
return nil, errors.Wrapf(err, "failed to load support bundle from URI %q", s.Spec.Uri)
// In the event a spec can't be loaded, we'll just skip it and print a warning
klog.Warningf("unable to load support bundle from URI: %q: %v", s.Spec.Uri, err)
continue
}
remoteRawSpecs = append(remoteRawSpecs, string(rawSpec))
}
}
return loader.LoadSpecs(ctx, loader.LoadOptions{
if len(remoteRawSpecs) == 0 {
return nil
}
moreKinds, err := loader.LoadSpecs(ctx, loader.LoadOptions{
RawSpecs: remoteRawSpecs,
})
if err != nil {
return err
}
kinds.Add(moreKinds)
return nil
}
func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface) (*troubleshootv1beta2.SupportBundle, *troubleshootv1beta2.Redactor, error) {
@@ -262,11 +276,10 @@ func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface)
// Load additional specs from support bundle URIs
if !viper.GetBool("no-uri") {
moreKinds, err := loadSupportBundleSpecsFromURIs(ctx, kinds)
err := loadSupportBundleSpecsFromURIs(ctx, kinds)
if err != nil {
return nil, nil, err
klog.Warningf("unable to load support bundles from URIs: %v", err)
}
kinds.Add(moreKinds)
}
// Check if we have any collectors to run in the troubleshoot specs
@@ -295,25 +308,28 @@ func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface)
}
for _, c := range kinds.CollectorsV1Beta2 {
mainBundle.Spec.Collectors = append(mainBundle.Spec.Collectors, c.Spec.Collectors...)
mainBundle.Spec.Collectors = util.Append(mainBundle.Spec.Collectors, c.Spec.Collectors)
}
for _, hc := range kinds.HostCollectorsV1Beta2 {
mainBundle.Spec.HostCollectors = append(mainBundle.Spec.HostCollectors, hc.Spec.Collectors...)
mainBundle.Spec.HostCollectors = util.Append(mainBundle.Spec.HostCollectors, hc.Spec.Collectors)
}
// Ensure cluster info and cluster resources collectors are in the merged spec
// We need to add them here so when we --dry-run, these collectors are included.
// supportbundle.runCollectors duplicates this bit. We'll need to refactor it out later
// when its clearer what other code depends on this logic e.g KOTS
mainBundle.Spec.Collectors = collect.EnsureCollectorInList(
mainBundle.Spec.Collectors,
troubleshootv1beta2.Collect{ClusterInfo: &troubleshootv1beta2.ClusterInfo{}},
)
mainBundle.Spec.Collectors = collect.EnsureCollectorInList(
mainBundle.Spec.Collectors,
troubleshootv1beta2.Collect{ClusterResources: &troubleshootv1beta2.ClusterResources{}},
)
if !(len(mainBundle.Spec.HostCollectors) > 0 && len(mainBundle.Spec.Collectors) == 0) {
// Always add default collectors unless we only have host collectors
// We need to add them here so when we --dry-run, these collectors
// are included. supportbundle.runCollectors duplicates this bit.
// We'll need to refactor it out later when its clearer what other
// code depends on this logic e.g KOTS
mainBundle.Spec.Collectors = collect.EnsureCollectorInList(
mainBundle.Spec.Collectors,
troubleshootv1beta2.Collect{ClusterInfo: &troubleshootv1beta2.ClusterInfo{}},
)
mainBundle.Spec.Collectors = collect.EnsureCollectorInList(
mainBundle.Spec.Collectors,
troubleshootv1beta2.Collect{ClusterResources: &troubleshootv1beta2.ClusterResources{}},
)
}
additionalRedactors := &troubleshootv1beta2.Redactor{
TypeMeta: metav1.TypeMeta{
@@ -325,7 +341,7 @@ func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface)
},
}
for _, r := range kinds.RedactorsV1Beta2 {
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, r.Spec.Redactors...)
additionalRedactors.Spec.Redactors = util.Append(additionalRedactors.Spec.Redactors, r.Spec.Redactors)
}
return mainBundle, additionalRedactors, nil
+242 -15
View File
@@ -2,15 +2,35 @@ package cli
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/replicatedhq/troubleshoot/internal/testutils"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/httputil"
"github.com/replicatedhq/troubleshoot/pkg/loader"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
testclient "k8s.io/client-go/kubernetes/fake"
)
var orig = `
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: sb-1
spec:
uri: $MY_URI
collectors:
- configMap:
name: kube-root-ca.crt
namespace: default
`
func Test_loadSupportBundleSpecsFromURIs(t *testing.T) {
// Run a webserver to serve the spec
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -25,27 +45,234 @@ spec:
}))
defer srv.Close()
orig := `
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: sb-1
spec:
uri: ` + srv.URL + `
collectors:
- configMap:
name: kube-root-ca.crt
namespace: default
`
orig := strings.ReplaceAll(orig, "$MY_URI", srv.URL)
ctx := context.Background()
kinds, err := loader.LoadSpecs(ctx, loader.LoadOptions{RawSpec: orig})
require.NoError(t, err)
require.NotNil(t, kinds)
moreKinds, err := loadSupportBundleSpecsFromURIs(ctx, kinds)
assert.Len(t, kinds.SupportBundlesV1Beta2, 1)
assert.NotNil(t, kinds.SupportBundlesV1Beta2[0].Spec.Collectors[0].ConfigMap)
err = loadSupportBundleSpecsFromURIs(ctx, kinds)
require.NoError(t, err)
require.Len(t, moreKinds.SupportBundlesV1Beta2, 1)
assert.NotNil(t, moreKinds.SupportBundlesV1Beta2[0].Spec.Collectors[0].ClusterInfo)
require.Len(t, kinds.SupportBundlesV1Beta2, 2)
assert.NotNil(t, kinds.SupportBundlesV1Beta2[1].Spec.Collectors[0].ClusterInfo)
}
func Test_loadSupportBundleSpecsFromURIs_TimeoutError(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(2 * time.Second) // this will cause a timeout
}))
defer srv.Close()
ctx := context.Background()
kinds, err := loader.LoadSpecs(ctx, loader.LoadOptions{
RawSpec: strings.ReplaceAll(orig, "$MY_URI", srv.URL),
})
require.NoError(t, err)
// Set the timeout on the http client to 10ms
// supportbundle.LoadSupportBundleSpec does not yet use the context
before := httputil.GetHttpClient().Timeout
httputil.GetHttpClient().Timeout = 10 * time.Millisecond
defer func() {
// Reinstate the original timeout. Its a global var so we need to reset it
httputil.GetHttpClient().Timeout = before
}()
beforeJSON, err := json.Marshal(kinds)
require.NoError(t, err)
err = loadSupportBundleSpecsFromURIs(ctx, kinds)
require.NoError(t, err)
afterJSON, err := json.Marshal(kinds)
require.NoError(t, err)
assert.JSONEq(t, string(beforeJSON), string(afterJSON))
}
func Test_loadSupportBundleSpecs(t *testing.T) {
tests := []struct {
name string
args []string
expected troubleshootv1beta2.SupportBundleSpec
}{
{
name: "empty collectors array in spec, default collectors added",
args: []string{testutils.ServeFromFilePath(t, `
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
spec:
collectors: []
`)},
expected: troubleshootv1beta2.SupportBundleSpec{
Collectors: []*troubleshootv1beta2.Collect{
{
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
},
{
ClusterResources: &troubleshootv1beta2.ClusterResources{},
},
},
},
},
{
name: "no collectors defined in spec, default collectors added",
args: []string{testutils.ServeFromFilePath(t, `
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
spec:
analyzers: []
`)},
expected: troubleshootv1beta2.SupportBundleSpec{
Collectors: []*troubleshootv1beta2.Collect{
{
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
},
{
ClusterResources: &troubleshootv1beta2.ClusterResources{},
},
},
Analyzers: []*troubleshootv1beta2.Analyze{},
},
},
{
name: "collectors present but defaults missing, default collectors added",
args: []string{testutils.ServeFromFilePath(t, `
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
spec:
collectors:
- logs: {}
`)},
expected: troubleshootv1beta2.SupportBundleSpec{
Collectors: []*troubleshootv1beta2.Collect{
{
Logs: &troubleshootv1beta2.Logs{},
},
{
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
},
{
ClusterResources: &troubleshootv1beta2.ClusterResources{},
},
},
},
},
{
name: "empty support bundle spec adds default collectors",
args: []string{testutils.ServeFromFilePath(t, `
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
`)},
expected: troubleshootv1beta2.SupportBundleSpec{
Collectors: []*troubleshootv1beta2.Collect{
{
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
},
{
ClusterResources: &troubleshootv1beta2.ClusterResources{},
},
},
},
},
{
name: "sb spec with host collectors does not add default collectors",
args: []string{testutils.ServeFromFilePath(t, `
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
spec:
hostCollectors:
- cpu: {}
`)},
expected: troubleshootv1beta2.SupportBundleSpec{
HostCollectors: []*troubleshootv1beta2.HostCollect{
{
CPU: &troubleshootv1beta2.CPU{},
},
},
},
},
{
name: "host collector spec with collectors does not add default collectors",
args: []string{testutils.ServeFromFilePath(t, `
apiVersion: troubleshoot.sh/v1beta2
kind: HostCollector
spec:
collectors:
- cpu: {}
`)},
expected: troubleshootv1beta2.SupportBundleSpec{
HostCollectors: []*troubleshootv1beta2.HostCollect{
{
CPU: &troubleshootv1beta2.CPU{},
},
},
},
},
{
name: "sb spec with host and in-cluster collectors adds default collectors",
args: []string{testutils.ServeFromFilePath(t, `
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
spec:
hostCollectors:
- cpu: {}
collectors:
- logs: {}
`)},
expected: troubleshootv1beta2.SupportBundleSpec{
HostCollectors: []*troubleshootv1beta2.HostCollect{
{
CPU: &troubleshootv1beta2.CPU{},
},
},
Collectors: []*troubleshootv1beta2.Collect{
{
Logs: &troubleshootv1beta2.Logs{},
},
{
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
},
{
ClusterResources: &troubleshootv1beta2.ClusterResources{},
},
},
},
},
{
name: "sb spec with host collectors and empty in-cluster collectors does not default collectors",
args: []string{testutils.ServeFromFilePath(t, `
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
spec:
hostCollectors:
- cpu: {}
collectors: []
`)},
expected: troubleshootv1beta2.SupportBundleSpec{
HostCollectors: []*troubleshootv1beta2.HostCollect{
{
CPU: &troubleshootv1beta2.CPU{},
},
},
Collectors: []*troubleshootv1beta2.Collect{},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctx := context.Background()
client := testclient.NewSimpleClientset()
sb, _, err := loadSpecs(ctx, test.args, client)
require.NoError(t, err)
assert.Equal(t, test.expected, sb.Spec)
})
}
}
+123
View File
@@ -559,6 +559,116 @@ spec:
required:
- outcomes
type: object
event:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
kind:
type: string
namespace:
type: string
outcomes:
items:
properties:
fail:
properties:
message:
type: string
uri:
type: string
when:
type: string
type: object
pass:
properties:
message:
type: string
uri:
type: string
when:
type: string
type: object
warn:
properties:
message:
type: string
uri:
type: string
when:
type: string
type: object
type: object
type: array
reason:
type: string
regex:
type: string
strict:
type: BoolString
required:
- collectorName
- outcomes
- reason
type: object
goldpinger:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
filePath:
type: string
outcomes:
items:
properties:
fail:
properties:
message:
type: string
uri:
type: string
when:
type: string
type: object
pass:
properties:
message:
type: string
uri:
type: string
when:
type: string
type: object
warn:
properties:
message:
type: string
uri:
type: string
when:
type: string
type: object
type: object
type: array
strict:
type: BoolString
required:
- collectorName
type: object
imagePullSecret:
properties:
annotations:
@@ -1489,6 +1599,19 @@ spec:
required:
- outcomes
type: object
velero:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
exclude:
type: BoolString
strict:
type: BoolString
type: object
weaveReport:
properties:
annotations:
File diff suppressed because it is too large Load Diff
@@ -1368,8 +1368,26 @@ spec:
type: string
command:
type: string
env:
items:
type: string
type: array
exclude:
type: BoolString
ignoreParentEnvs:
type: boolean
inheritEnvs:
items:
type: string
type: array
input:
additionalProperties:
type: string
type: object
outputDir:
type: string
timeout:
type: string
required:
- args
- command
@@ -1368,8 +1368,26 @@ spec:
type: string
command:
type: string
env:
items:
type: string
type: array
exclude:
type: BoolString
ignoreParentEnvs:
type: boolean
inheritEnvs:
items:
type: string
type: array
input:
additionalProperties:
type: string
type: object
outputDir:
type: string
timeout:
type: string
required:
- args
- command
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -54,4 +54,4 @@ preflight [url] [flags]
* [preflight oci-fetch](preflight_oci-fetch.md) - Fetch a preflight from an OCI registry and print it to standard out
* [preflight version](preflight_version.md) - Print the current version and exit
###### Auto generated by spf13/cobra on 31-Aug-2023
###### Auto generated by spf13/cobra on 25-Jan-2024
+3 -2
View File
@@ -9,7 +9,8 @@ preflight oci-fetch [URI] [flags]
### Options
```
-h, --help help for oci-fetch
-h, --help help for oci-fetch
-v, --v Level number for the log level verbosity
```
### Options inherited from parent commands
@@ -33,4 +34,4 @@ preflight oci-fetch [URI] [flags]
* [preflight](preflight.md) - Run and retrieve preflight checks in a cluster
###### Auto generated by spf13/cobra on 31-Aug-2023
###### Auto generated by spf13/cobra on 25-Jan-2024
+1 -1
View File
@@ -37,4 +37,4 @@ preflight version [flags]
* [preflight](preflight.md) - Run and retrieve preflight checks in a cluster
###### Auto generated by spf13/cobra on 31-Aug-2023
###### Auto generated by spf13/cobra on 25-Jan-2024
+3 -2
View File
@@ -27,6 +27,7 @@ support-bundle [urls...] [flags]
--cpuprofile string File path to write cpu profiling data
--debug enable debug logging. This is equivalent to --v=0
--disable-compression If true, opt-out of response compression for all requests to the server
--dry-run print support bundle spec without collecting anything
-h, --help help for support-bundle
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
--interactive enable/disable interactive mode (default true)
@@ -34,7 +35,7 @@ support-bundle [urls...] [flags]
--load-cluster-specs enable/disable loading additional troubleshoot specs found within the cluster. required when no specs are provided on the command line
--memprofile string File path to write memory profiling data
-n, --namespace string If present, the namespace scope for this CLI request
--no-uri When this flag is used, Troubleshoot does not attempt to retrieve the bundle referenced by the uri: field in the spec.`
--no-uri When this flag is used, Troubleshoot does not attempt to retrieve the spec referenced by the uri: field`
-o, --output string specify the output file path for the support bundle
--redact enable/disable default redactions (default true)
--redactors strings names of the additional redactors to use
@@ -55,4 +56,4 @@ support-bundle [urls...] [flags]
* [support-bundle redact](support-bundle_redact.md) - Redact information from a generated support bundle archive
* [support-bundle version](support-bundle_version.md) - Print the current version and exit
###### Auto generated by spf13/cobra on 31-Aug-2023
###### Auto generated by spf13/cobra on 25-Jan-2024
+1 -1
View File
@@ -30,4 +30,4 @@ support-bundle analyze [url] [flags]
* [support-bundle](support-bundle.md) - Generate a support bundle
###### Auto generated by spf13/cobra on 31-Aug-2023
###### Auto generated by spf13/cobra on 25-Jan-2024
+1 -1
View File
@@ -39,4 +39,4 @@ support-bundle redact [urls...] [flags]
* [support-bundle](support-bundle.md) - Generate a support bundle
###### Auto generated by spf13/cobra on 31-Aug-2023
###### Auto generated by spf13/cobra on 25-Jan-2024
+1 -1
View File
@@ -27,4 +27,4 @@ support-bundle version [flags]
* [support-bundle](support-bundle.md) - Generate a support bundle
###### Auto generated by spf13/cobra on 31-Aug-2023
###### Auto generated by spf13/cobra on 25-Jan-2024
@@ -0,0 +1,37 @@
apiVersion: troubleshoot.sh/v1beta2
kind: HostCollector
metadata:
name: run-host-cmd-and-save-output
spec:
collectors:
- run:
collectorName: "my-custom-run"
command: "sh"
# this is for demonstration purpose only -- you probably don't want to drop your input to the bundle!
args:
- "-c"
- "cat $TS_INPUT_DIR/dummy.yaml > $TS_OUTPUT_DIR/dummy_content.yaml"
outputDir: "myCommandOutputs"
env:
- AWS_REGION=us-west-1
# if ignoreParentEnvs is true, it will not inherit envs from parent process.
# values specified in inheritEnv will not be used either
# ignoreParentEnvs: true
inheritEnvs:
- USER
input:
dummy.conf: |-
[hello]
hello = 1
[bye]
bye = 2
dummy.yaml: |-
username: postgres
password: <my-pass>
dbHost: <hostname>
map:
key: value
list:
- val1
- val2
+3
View File
@@ -72,6 +72,9 @@ spec:
- pass:
when: "== k3s"
message: K3S is a supported distribution
- pass:
when: "== oke"
message: OKE is a supported distribution
- warn:
message: Unable to determine the distribution of Kubernetes
- nodeResources:
+29 -28
View File
@@ -1,6 +1,8 @@
module helm-template
go 1.20
go 1.21
toolchain go1.22.0
// Always use the local version of troubleshoot so as to build using
// the latest version of the library. This will ensure the example
@@ -9,29 +11,28 @@ replace github.com/replicatedhq/troubleshoot v0.0.0 => ../../../
require (
github.com/replicatedhq/troubleshoot v0.0.0
helm.sh/helm/v3 v3.12.3
sigs.k8s.io/yaml v1.3.0
helm.sh/helm/v3 v3.14.3
sigs.k8s.io/yaml v1.4.0
)
require (
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/swag v0.22.10 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/josharian/intern v1.0.0 // indirect
@@ -44,30 +45,30 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.12.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/oauth2 v0.18.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.33.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.28.2 // indirect
k8s.io/apiextensions-apiserver v0.28.1 // indirect
k8s.io/apimachinery v0.28.2 // indirect
k8s.io/client-go v0.28.2 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
sigs.k8s.io/controller-runtime v0.16.2 // indirect
k8s.io/api v0.29.2 // indirect
k8s.io/apiextensions-apiserver v0.29.2 // indirect
k8s.io/apimachinery v0.29.2 // indirect
k8s.io/client-go v0.29.2 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
sigs.k8s.io/controller-runtime v0.17.2 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
)
+74 -62
View File
@@ -1,5 +1,5 @@
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
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.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
@@ -14,39 +14,45 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
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/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE=
github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI=
github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
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/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.10 h1:4y86NVn7Z2yYd6pfS4Z+Nyh3aAUL3Nul+LMbhFKy0gA=
github.com/go-openapi/swag v0.22.10/go.mod h1:Cnn8BYtRlx6BNE3DPN86f/xkapGIcLWzh3CLEb4C1jI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
@@ -61,6 +67,7 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -80,21 +87,25 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY=
github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -104,7 +115,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -120,23 +132,22 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -149,38 +160,39 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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=
@@ -194,27 +206,27 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
helm.sh/helm/v3 v3.12.3 h1:5y1+Sbty12t48T/t/CGNYUIME5BJ0WKfmW/sobYqkFg=
helm.sh/helm/v3 v3.12.3/go.mod h1:KPKQiX9IP5HX7o5YnnhViMnNuKiL/lJBVQ47GHe1R0k=
k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw=
k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg=
k8s.io/apiextensions-apiserver v0.28.1 h1:l2ThkBRjrWpw4f24uq0Da2HaEgqJZ7pcgiEUTKSmQZw=
k8s.io/apiextensions-apiserver v0.28.1/go.mod h1:sVvrI+P4vxh2YBBcm8n2ThjNyzU4BQGilCQ/JAY5kGs=
k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ=
k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU=
k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY=
k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY=
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ=
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.16.2 h1:mwXAVuEk3EQf478PQwQ48zGOXvW27UJc8NHktQVuIPU=
sigs.k8s.io/controller-runtime v0.16.2/go.mod h1:vpMu3LpI5sYWtujJOa2uPK61nB5rbwlN7BAB8aSLvGU=
helm.sh/helm/v3 v3.14.3 h1:HmvRJlwyyt9HjgmAuxHbHv3PhMz9ir/XNWHyXfmnOP4=
helm.sh/helm/v3 v3.14.3/go.mod h1:v6myVbyseSBJTzhmeE39UcPLNv6cQK6qss3dvgAySaE=
k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
k8s.io/apiextensions-apiserver v0.29.2 h1:UK3xB5lOWSnhaCk0RFZ0LUacPZz9RY4wi/yt2Iu+btg=
k8s.io/apiextensions-apiserver v0.29.2/go.mod h1:aLfYjpA5p3OwtqNXQFkhJ56TB+spV8Gc4wfMhUA3/b8=
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0=
sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
+117 -107
View File
@@ -1,60 +1,66 @@
module github.com/replicatedhq/troubleshoot
go 1.20
go 1.21
toolchain go1.21.5
require (
github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412
github.com/apparentlymart/go-cidr v1.1.0
github.com/blang/semver/v4 v4.0.0
github.com/containers/image/v5 v5.25.0
github.com/containers/image/v5 v5.30.0
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2
github.com/fatih/color v1.15.0
github.com/go-logr/logr v1.2.4
github.com/fatih/color v1.16.0
github.com/go-logr/logr v1.4.1
github.com/go-redis/redis/v7 v7.4.1
github.com/go-sql-driver/mysql v1.7.1
github.com/go-sql-driver/mysql v1.8.0
github.com/gobwas/glob v0.2.3
github.com/godbus/dbus/v5 v5.1.0
github.com/google/gofuzz v1.2.0
github.com/google/uuid v1.3.1
github.com/gorilla/handlers v1.5.1
github.com/hashicorp/go-getter v1.7.2
github.com/google/uuid v1.6.0
github.com/gorilla/handlers v1.5.2
github.com/hashicorp/go-getter v1.7.3
github.com/hashicorp/go-multierror v1.1.1
github.com/jackc/pgx/v5 v5.4.3
github.com/jackc/pgx/v5 v5.5.5
github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e
github.com/manifoldco/promptui v0.9.0
github.com/mattn/go-isatty v0.0.19
github.com/mattn/go-isatty v0.0.20
github.com/mholt/archiver/v3 v3.5.1
github.com/microsoft/go-mssqldb v1.6.0
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b
github.com/microsoft/go-mssqldb v1.7.0
github.com/opencontainers/image-spec v1.1.0
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/v3 v3.23.9
github.com/spf13/cobra v1.7.0
github.com/shirou/gopsutil/v3 v3.24.2
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.17.0
github.com/stretchr/testify v1.8.4
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.9.0
github.com/tj/go-spin v1.1.0
go.opentelemetry.io/otel v1.18.0
go.opentelemetry.io/otel/sdk v1.18.0
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
golang.org/x/sync v0.3.0
github.com/vmware-tanzu/velero v1.13.0
go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/sdk v1.24.0
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
golang.org/x/mod v0.16.0
golang.org/x/sync v0.6.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.28.2
k8s.io/apiextensions-apiserver v0.28.1
k8s.io/apimachinery v0.28.2
k8s.io/apiserver v0.28.1
k8s.io/cli-runtime v0.28.2
k8s.io/client-go v0.28.2
k8s.io/klog/v2 v2.100.1
oras.land/oras-go v1.2.3
sigs.k8s.io/controller-runtime v0.16.2
k8s.io/api v0.29.2
k8s.io/apiextensions-apiserver v0.29.2
k8s.io/apimachinery v0.29.2
k8s.io/apiserver v0.29.2
k8s.io/cli-runtime v0.29.2
k8s.io/client-go v0.29.2
k8s.io/klog/v2 v2.120.1
oras.land/oras-go v1.2.5
sigs.k8s.io/controller-runtime v0.17.2
sigs.k8s.io/e2e-framework v0.3.0
)
require (
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
dario.cat/mergo v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
@@ -62,18 +68,24 @@ require (
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/containerd/cgroups/v3 v3.0.2 // indirect
github.com/containerd/errdefs v0.1.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch/v5 v5.8.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/go-gorp/gorp/v3 v3.0.5 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-logr/stdr v1.2.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.6.8 // indirect
github.com/google/go-containerregistry v0.19.0 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
@@ -83,85 +95,86 @@ require (
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/mistifyio/go-zfs/v3 v3.0.0 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/onsi/ginkgo v1.14.0 // indirect
github.com/moby/sys/user v0.1.0 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/rubenv/sql-migrate v1.3.1 // indirect
github.com/rubenv/sql-migrate v1.5.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/sylabs/sif/v2 v2.11.1 // indirect
github.com/sylabs/sif/v2 v2.15.1 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/vladimirvivien/gexe v0.2.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.opentelemetry.io/otel/metric v1.18.0 // indirect
go.opentelemetry.io/otel/trace v1.18.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/tools v0.13.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
k8s.io/component-base v0.28.1 // indirect
k8s.io/kubectl v0.28.1 // indirect
golang.org/x/tools v0.18.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
k8s.io/component-base v0.29.2 // indirect
k8s.io/kubectl v0.29.0 // indirect
)
require (
cloud.google.com/go v0.110.7 // indirect
cloud.google.com/go/compute v1.23.0 // indirect
cloud.google.com/go/iam v1.1.1 // indirect
cloud.google.com/go/storage v1.30.1 // indirect
cloud.google.com/go v0.110.10 // indirect
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/iam v1.1.5 // indirect
cloud.google.com/go/storage v1.35.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/Microsoft/hcsshim v0.10.0-rc.7 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.12.0-rc.3 // indirect
github.com/andybalholm/brotli v1.0.1 // indirect
github.com/aws/aws-sdk-go v1.44.198 // indirect
github.com/aws/aws-sdk-go v1.44.253 // 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/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/containerd v1.7.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/containerd/containerd v1.7.13 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
github.com/containers/ocicrypt v1.1.7 // indirect
github.com/containers/storage v1.46.0 // indirect
github.com/containers/ocicrypt v1.1.9 // indirect
github.com/containers/storage v1.53.0 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v23.0.1+incompatible // indirect
github.com/docker/docker v23.0.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/cli v25.0.3+incompatible // indirect
github.com/docker/docker v25.0.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.1 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/swag v0.22.10 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-intervals v0.0.2 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@@ -173,83 +186,80 @@ require (
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8 // indirect
github.com/klauspost/compress v1.17.7 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/moby/sys/mountinfo v0.7.1 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect
github.com/nwaples/rardecode v1.1.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/runc v1.1.5 // indirect
github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/prometheus/client_golang v1.18.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/vbatts/tar-split v0.11.3 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.opencensus.io v0.24.0 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/net v0.17.0
golang.org/x/oauth2 v0.12.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0
golang.org/x/time v0.3.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/net v0.22.0
golang.org/x/oauth2 v0.18.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.143.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect
google.golang.org/grpc v1.58.2 // indirect
google.golang.org/protobuf v1.31.0 // indirect
google.golang.org/api v0.153.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
helm.sh/helm/v3 v3.12.3
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
k8s.io/metrics v0.28.2
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2
helm.sh/helm/v3 v3.14.3
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/metrics v0.29.2
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
periph.io/x/host/v3 v3.8.2
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0
)
replace (
+313 -471
View File
File diff suppressed because it is too large Load Diff
+18 -14
View File
@@ -167,25 +167,29 @@ func LoadFromCLIArgs(ctx context.Context, client kubernetes.Interface, args []st
rawSpecs = append(rawSpecs, content...)
} else {
if !util.IsURL(v) {
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, fmt.Errorf("%s is not a URL and was not found (err %s)", v, err))
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, fmt.Errorf("%s is not a URL and was not found", v))
}
// Download preflight specs
rawSpec, err := downloadFromHttpURL(ctx, v, map[string]string{"User-Agent": "Replicated_Preflight/v1beta2"})
parsedURL, err := url.ParseRequestURI(v)
if err != nil {
return nil, err
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
}
rawSpecs = append(rawSpecs, rawSpec)
// Download support bundle specs
rawSpec, err = downloadFromHttpURL(ctx, v, map[string]string{
"User-Agent": "Replicated_Troubleshoot/v1beta1",
"Bundle-Upload-Host": fmt.Sprintf("%s://%s", u.Scheme, u.Host),
})
if err != nil {
return nil, err
if parsedURL.Host == "kots.io" {
// To download specs from kots.io, we need to set the User-Agent header
rawSpec, err := downloadFromHttpURL(ctx, v, map[string]string{
"User-Agent": "Replicated_Troubleshoot/v1beta1",
})
if err != nil {
return nil, err
}
rawSpecs = append(rawSpecs, rawSpec)
} else {
rawSpec, err := downloadFromHttpURL(ctx, v, nil)
if err != nil {
return nil, err
}
rawSpecs = append(rawSpecs, rawSpec)
}
rawSpecs = append(rawSpecs, rawSpec)
}
}
}
+23
View File
@@ -3,6 +3,8 @@ package specs
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"testing"
@@ -10,6 +12,7 @@ import (
"github.com/replicatedhq/troubleshoot/internal/testutils"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/loader"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
@@ -201,3 +204,23 @@ spec:
},
}
}
func TestLoadFromURI(t *testing.T) {
m := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`apiVersion: troubleshoot.sh/v1beta2
apiVersion: troubleshoot.sh/v1beta2
kind: HostCollector
metadata:
name: cpu
spec:
collectors:
- cpu: {}
`))
}))
defer m.Close()
client := testclient.NewSimpleClientset()
specs, err := LoadFromCLIArgs(context.Background(), client, []string{m.URL}, viper.New())
require.NoError(t, err)
require.Len(t, specs.HostCollectorsV1Beta2, 1)
}
+28 -7
View File
@@ -15,16 +15,25 @@ import (
func GetTestFixture(t *testing.T, path string) string {
t.Helper()
p := path
if !filepath.IsAbs(path) {
p = filepath.Join("../../testdata", path)
}
b, err := os.ReadFile(p)
b, err := os.ReadFile(TestFixtureFilePath(t, path))
require.NoError(t, err)
return string(b)
}
// FileDir returns the directory of the current source file.
func TestFixtureFilePath(t *testing.T, path string) string {
t.Helper()
if !filepath.IsAbs(path) {
p, err := filepath.Abs(filepath.Join(FileDir(), "../../testdata", path))
require.NoError(t, err)
return p
} else {
return path
}
}
// FileDir returns the directory of this source file
func FileDir() string {
_, filename, _, _ := runtime.Caller(0)
return filepath.Dir(filename)
@@ -32,9 +41,13 @@ func FileDir() string {
// Generates a temporary filename
func TempFilename(prefix string) string {
return filepath.Join(os.TempDir(), generateTempName(prefix))
}
func generateTempName(prefix string) string {
randBytes := make([]byte, 16)
rand.Read(randBytes)
return filepath.Join(os.TempDir(), fmt.Sprintf("%s_%s", prefix, hex.EncodeToString(randBytes)))
return fmt.Sprintf("%s_%s", prefix, hex.EncodeToString(randBytes))
}
func CreateTestFile(t *testing.T, path string) {
@@ -69,3 +82,11 @@ func AsJSON(t *testing.T, v interface{}) string {
return string(b)
}
}
func ServeFromFilePath(t *testing.T, data string) string {
t.Helper()
path := filepath.Join(t.TempDir(), generateTempName("testfile"))
CreateTestFileWithData(t, path, data)
return path
}
+53
View File
@@ -1,9 +1,11 @@
package util
import (
"bytes"
"net/url"
"os"
"strings"
"text/template"
"golang.org/x/text/cases"
"golang.org/x/text/language"
@@ -52,3 +54,54 @@ func EstimateNumberOfLines(text string) int {
}
return n
}
// Append appends elements in src to target.
// We have this function because of how the
// builtin append() function works. It treats
// target nil slices the same as empty slices.
func Append[T any](target []T, src []T) []T {
// Do nothing only if src is nil
if src == nil {
return target
}
// In case target is nil, we need to initialize it
// since append() will not do it for us when len(src) == 0
if target == nil {
target = []T{}
}
return append(target, src...)
}
// IsInCluster returns true if the code is running within a process
// inside a kubernetes pod
func IsInCluster() bool {
// This is a best effort check, it's not guaranteed to be accurate
host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
if len(host) == 0 || len(port) == 0 {
return false
}
return true
}
// RenderTemplate renders a template and returns the result as a string
func RenderTemplate(tpl string, data interface{}) (string, error) {
// Create a new template and parse the letter into it
t, err := template.New("data").Parse(tpl)
if err != nil {
return "", err
}
// Create a new buffer
buf := new(bytes.Buffer)
// Execute the template and write the bytes to the buffer
err = t.Execute(buf, data)
if err != nil {
return "", err
}
// Return the string representation of the buffer
return buf.String(), nil
}
+108
View File
@@ -194,3 +194,111 @@ func Test_EstimateNumberOfLines(t *testing.T) {
})
}
}
func TestAppend(t *testing.T) {
tests := []struct {
name string
target []string
src []string
want []string
}{
{
name: "empty target",
target: []string{},
src: []string{"a", "b", "c"},
want: []string{"a", "b", "c"},
},
{
name: "empty src",
target: []string{"a", "b", "c"},
src: []string{},
want: []string{"a", "b", "c"},
},
{
name: "non-empty target and src",
target: []string{"a", "b", "c"},
src: []string{"d", "e", "f"},
want: []string{"a", "b", "c", "d", "e", "f"},
},
{
name: "nil target",
target: nil,
src: []string{"a", "b", "c"},
want: []string{"a", "b", "c"},
},
{
name: "nil src",
target: []string{"a", "b", "c"},
src: nil,
want: []string{"a", "b", "c"},
},
{
name: "nil target and empty src",
target: nil,
src: []string{},
want: []string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Append(tt.target, tt.src)
assert.Equal(t, tt.want, got, "Append() = %v, want %v", got, tt.want)
})
}
}
func TestRenderTemplate(t *testing.T) {
tests := []struct {
name string
tpl string
data interface{}
want string
wantErr bool
}{
{
name: "empty template and data",
tpl: "",
data: nil,
want: "",
wantErr: false,
},
{
name: "empty template with data",
tpl: "",
data: map[string]string{"Name": "World"},
want: "",
wantErr: false,
},
{
name: "empty data with template with no keys",
tpl: "Hello, World!",
data: nil,
want: "Hello, World!",
wantErr: false,
},
{
name: "simple template",
tpl: "Hello, {{ .Name }}!",
data: map[string]string{"Name": "World"},
want: "Hello, World!",
wantErr: false,
},
{
name: "template with missing key",
tpl: "Hello, {{ .Name }}!",
data: map[string]string{"Name2": "World"},
want: "Hello, <no value>!",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := RenderTemplate(tt.tpl, tt.data)
if (err != nil) != tt.wantErr {
t.Errorf("RenderTemplate() error = %v, wantErr %v", err, tt.wantErr)
return
}
assert.Equal(t, tt.want, got, "RenderTemplate() = %v, want %v", got, tt.want)
})
}
}
+25
View File
@@ -1,16 +1,20 @@
package analyzer
import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/multitype"
"github.com/replicatedhq/troubleshoot/pkg/redact"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
@@ -226,6 +230,8 @@ func getAnalyzer(analyzer *troubleshootv1beta2.Analyze) Analyzer {
return &AnalyzeRedis{analyzer: analyzer.Redis}
case analyzer.CephStatus != nil:
return &AnalyzeCephStatus{analyzer: analyzer.CephStatus}
case analyzer.Velero != nil:
return &AnalyzeVelero{analyzer: analyzer.Velero}
case analyzer.Longhorn != nil:
return &AnalyzeLonghorn{analyzer: analyzer.Longhorn}
case analyzer.RegistryImages != nil:
@@ -238,6 +244,10 @@ func getAnalyzer(analyzer *troubleshootv1beta2.Analyze) Analyzer {
return &AnalyzeClusterResource{analyzer: analyzer.ClusterResource}
case analyzer.Certificates != nil:
return &AnalyzeCertificates{analyzer: analyzer.Certificates}
case analyzer.Goldpinger != nil:
return &AnalyzeGoldpinger{analyzer: analyzer.Goldpinger}
case analyzer.Event != nil:
return &AnalyzeEvent{analyzer: analyzer.Event}
default:
return nil
}
@@ -265,3 +275,18 @@ func DedupAnalyzers(allAnalyzers []*troubleshootv1beta2.Analyze) []*troubleshoot
}
return finalAnalyzers
}
func stripRedactedLines(yaml []byte) []byte {
buf := bytes.NewBuffer(yaml)
scanner := bufio.NewScanner(buf)
out := []byte{}
for scanner.Scan() {
line := strings.ReplaceAll(scanner.Text(), redact.MASK_TEXT, "HIDDEN")
out = append(out, []byte(line)...)
out = append(out, '\n')
}
return out
}
+11 -1
View File
@@ -42,7 +42,7 @@ func analyzeClusterVersion(analyzer *troubleshootv1beta2.ClusterVersion, getColl
return nil, errors.Wrap(err, "failed to parse cluster_version.json")
}
k8sVersion, err := semver.Make(strings.TrimLeft(collectorClusterVersion.String, "v"))
k8sVersion, err := parseK8sVersionString(collectorClusterVersion.String)
if err != nil {
return nil, errors.Wrap(err, "failed to parse semver from cluster_version.json")
}
@@ -58,6 +58,16 @@ func title(checkName string) string {
return checkName
}
func parseK8sVersionString(version string) (semver.Version, error) {
// Workaround for https://github.com/aws/containers-roadmap/issues/1404
// for EKS, replace pre-release gitVersion string with the release version
if strings.Contains(version, "-eks-") {
version = strings.Split(version, "-")[0]
}
return semver.Make(strings.TrimLeft(version, "v"))
}
func analyzeClusterVersionResult(k8sVersion semver.Version, outcomes []*troubleshootv1beta2.Outcome, checkName string) (*AnalyzeResult, error) {
for _, outcome := range outcomes {
when := ""
+39
View File
@@ -6,6 +6,7 @@ import (
"github.com/blang/semver/v4"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
)
func Test_analyzeClusterVersionResult(t *testing.T) {
@@ -103,3 +104,41 @@ func Test_analyzeClusterVersionResult(t *testing.T) {
})
}
}
func Test_parseVersionString(t *testing.T) {
tests := []struct {
name string
rawVersion string
want semver.Version
wantErr bool
}{
{
name: "valid version",
rawVersion: "1.17.0",
want: semver.MustParse("1.17.0"),
},
{
name: "valid version with v prefix",
rawVersion: "v1.17.0",
want: semver.MustParse("1.17.0"),
},
{
name: "invalid version",
rawVersion: "v1.17",
want: semver.Version{},
wantErr: true,
},
{
name: "EKS version",
rawVersion: "v1.25.16-eks-8cb36c9",
want: semver.MustParse("1.25.16"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseK8sVersionString(tt.rawVersion)
assert.Equal(t, tt.wantErr, err != nil, "parseVersionString() error = %v, wantErr %v", err, tt.wantErr)
assert.Equal(t, tt.want, got, "parseVersionString() = %v, want %v", got, tt.want)
})
}
}
+11
View File
@@ -26,6 +26,7 @@ type providers struct {
minikube bool
rke2 bool
k3s bool
oke bool
}
type Provider int
@@ -45,6 +46,7 @@ const (
minikube Provider = iota
rke2 Provider = iota
k3s Provider = iota
oke Provider = iota
)
type AnalyzeDistribution struct {
@@ -125,6 +127,11 @@ func ParseNodesForProviders(nodes []corev1.Node) (providers, string) {
foundProviders.k3s = true
stringProvider = "k3s"
}
if k == "oci.oraclecloud.com/fault-domain" {
// Based on: https://docs.oracle.com/en-us/iaas/Content/ContEng/Reference/contengsupportedlabelsusecases.htm
foundProviders.oke = true
stringProvider = "oke"
}
}
for k := range node.ObjectMeta.Annotations {
@@ -326,6 +333,8 @@ func compareDistributionConditionalToActual(conditional string, actual providers
isMatch = actual.rke2
case k3s:
isMatch = actual.k3s
case oke:
isMatch = actual.oke
}
switch parts[0] {
@@ -366,6 +375,8 @@ func mustNormalizeDistributionName(raw string) Provider {
return rke2
case "k3s":
return k3s
case "oke":
return oke
}
return unknown
+218
View File
@@ -0,0 +1,218 @@
package analyzer
import (
"bytes"
"encoding/json"
"fmt"
"path"
"regexp"
"strconv"
"strings"
"text/template"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
corev1 "k8s.io/api/core/v1"
"k8s.io/klog/v2"
)
type AnalyzeEvent struct {
analyzer *troubleshootv1beta2.EventAnalyze
}
type eventFilter struct {
kind string
reason string
msgRegex string
}
func (a *AnalyzeEvent) Title() string {
if a.analyzer.CheckName != "" {
return a.analyzer.CheckName
}
if a.analyzer.CollectorName != "" {
return a.analyzer.CollectorName
}
return "Event"
}
func (a *AnalyzeEvent) IsExcluded() (bool, error) {
return isExcluded(a.analyzer.Exclude)
}
func (a *AnalyzeEvent) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
// required check
if a.analyzer.Reason == "" {
return nil, errors.New("reason is required")
}
// read collected events based on namespace
namespace := getNamespace(a.analyzer.Namespace)
fullPath := path.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_EVENTS, namespace)
fullPath = fmt.Sprintf("%s.json", fullPath)
fileContent, err := getFile(fullPath)
if err != nil {
return nil, errors.Wrapf(err, "failed to read collected events for namespace: %s", namespace)
}
events, err := convertToEventList(fileContent)
if err != nil {
return nil, errors.Wrap(err, "failed to read collected events")
}
// filter if there's single event matched with the given criteria
// match: Reason && Kind (optional) && MessageRegex (optional)
// e.g. Reason: Unhealthy. Kind: Pod. Message: Readiness probe failed:...
event := getEvent(events, eventFilter{
kind: a.analyzer.Kind,
reason: a.analyzer.Reason,
msgRegex: a.analyzer.RegexPattern,
})
return analyzeEventResult(event, a.analyzer.Outcomes, a.Title())
}
func getNamespace(namespace string) string {
if namespace == "" {
return corev1.NamespaceDefault
}
return namespace
}
func convertToEventList(data []byte) (*corev1.EventList, error) {
var eventList corev1.EventList
err := json.Unmarshal(data, &eventList)
if err != nil {
return nil, fmt.Errorf("failed to convert []byte to corev1.EventList: %w", err)
}
return &eventList, nil
}
func getEvent(events *corev1.EventList, filter eventFilter) *corev1.Event {
var (
re *regexp.Regexp
errParseRegex error
)
if filter.msgRegex != "" {
re, errParseRegex = regexp.Compile(filter.msgRegex)
if errParseRegex != nil {
klog.V(2).Infof("failed to read message regex: %v", errParseRegex)
return nil
}
}
for _, event := range events.Items {
if !matchReason(event.Reason, filter.reason) {
continue
}
if !matchKind(event.InvolvedObject.Kind, filter.kind) {
continue
}
if re == nil || re.MatchString(event.Message) {
klog.V(2).Infof("event matched: %v for reason: %s kind: %s messageRegex: %s ", event, filter.reason, filter.kind, filter.msgRegex)
return &event
}
}
return nil
}
func matchReason(actual, expected string) bool {
// not possible to have empty reason
if expected == "" {
return false
}
return strings.EqualFold(actual, expected)
}
func matchKind(actual, expected string) bool {
// kind is optional
if expected == "" {
return true
}
return strings.EqualFold(actual, expected)
}
func analyzeEventResult(event *corev1.Event, outcomes []*troubleshootv1beta2.Outcome, checkName string) ([]*AnalyzeResult, error) {
results := []*AnalyzeResult{}
// for now, only support single outcome
// we will return when there's a matched event
willReturn := event != nil
result := &AnalyzeResult{
Title: checkName,
IconKey: "kubernetes_event",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
}
for _, o := range outcomes {
if o.Fail != nil {
toReturn, err := strconv.ParseBool(o.Fail.When)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse when condition: %s", o.Fail.When)
}
if toReturn == willReturn {
result.IsFail = true
result.Message = decorateMessage(o.Fail.Message, event)
result.URI = o.Fail.URI
break
}
}
if o.Warn != nil {
toReturn, err := strconv.ParseBool(o.Warn.When)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse when condition: %s", o.Warn.When)
}
if toReturn == willReturn {
result.IsWarn = true
result.Message = decorateMessage(o.Warn.Message, event)
result.URI = o.Warn.URI
break
}
}
if o.Pass != nil {
toReturn, err := strconv.ParseBool(o.Pass.When)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse when condition: %s", o.Pass.When)
}
if toReturn == willReturn {
result.IsPass = true
result.Message = decorateMessage(o.Pass.Message, event)
result.URI = o.Pass.URI
break
}
}
}
results = append(results, result)
return results, nil
}
func decorateMessage(message string, event *corev1.Event) string {
if event == nil {
return message
}
out := fmt.Sprintf("Event matched. Reason: %s Name: %s Message: %s", event.Reason, event.InvolvedObject.Name, event.Message)
tmpl := template.New("event")
msgTmpl, err := tmpl.Parse(message)
if err != nil {
klog.V(2).Infof("failed to parse message template: %v", err)
return out
}
var m bytes.Buffer
err = msgTmpl.Execute(&m, event)
if err != nil {
klog.V(2).Infof("failed to render message template: %v", err)
return out
}
return strings.TrimSpace(m.String())
}
+310
View File
@@ -0,0 +1,310 @@
package analyzer
import (
"testing"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
)
func TestAnalyzeEvent(t *testing.T) {
tests := []struct {
name string
analyzer troubleshootv1beta2.EventAnalyze
expectResult []AnalyzeResult
files map[string][]byte
err error
}{
{
name: "reason is required",
analyzer: troubleshootv1beta2.EventAnalyze{
CollectorName: "event-collector-0",
Kind: "Pod",
Namespace: "default",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "true",
Message: "test",
},
},
},
},
err: errors.New("reason is required"),
files: map[string][]byte{
"cluster-resources/events/default.json": []byte(`
{
"kind": "EventList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "722"
},
"items": [
{
"kind": "Event",
"apiVersion": "v1",
"metadata": {
"name": "nginx-rc",
"namespace": "default",
"creationTimestamp": "2022-01-01T00:00:00Z"
},
"involvedObject": {
"kind": "Pod",
"name": "nginx-rc-12345",
"namespace": "default"
},
"reason": "OOMKilled",
"message": "The container was killed due to an out-of-memory condition.",
"type": "Warning"
}
]
}
`),
},
},
{
name: "fail when OOMKilled event is present",
analyzer: troubleshootv1beta2.EventAnalyze{
CollectorName: "event-collector-1",
Kind: "Pod",
Namespace: "default",
Reason: "OOMKilled",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "true",
Message: "Detect OOMKilled event with {{ .InvolvedObject.Kind }}-{{ .InvolvedObject.Name }} with message {{ .Message }}",
},
Pass: &troubleshootv1beta2.SingleOutcome{
When: "false",
Message: "No OOMKilled event detected",
},
},
},
},
expectResult: []AnalyzeResult{
{
Title: "event-collector-1",
IconKey: "kubernetes_event",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
IsFail: true,
IsWarn: false,
IsPass: false,
Message: "Detect OOMKilled event with Pod-nginx-rc-12345 with message The container was killed due to an out-of-memory condition.",
},
},
files: map[string][]byte{
"cluster-resources/events/default.json": []byte(`
{
"kind": "EventList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "722"
},
"items": [
{
"kind": "Event",
"apiVersion": "v1",
"metadata": {
"name": "nginx-rc",
"namespace": "default",
"creationTimestamp": "2022-01-01T00:00:00Z"
},
"involvedObject": {
"kind": "Pod",
"name": "nginx-rc-12345",
"namespace": "default"
},
"reason": "OOMKilled",
"message": "The container was killed due to an out-of-memory condition.",
"type": "Warning"
}
]
}
`),
},
},
{
name: "pass when no FailedMount event is present",
analyzer: troubleshootv1beta2.EventAnalyze{
CollectorName: "event-collector-2",
Kind: "Pod",
Namespace: "default",
Reason: "FailedMount",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "false",
Message: "No FailedMount event detected",
},
},
},
},
expectResult: []AnalyzeResult{
{
Title: "event-collector-2",
IconKey: "kubernetes_event",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
IsFail: false,
IsWarn: false,
IsPass: true,
Message: "No FailedMount event detected",
},
},
files: map[string][]byte{
"cluster-resources/events/default.json": []byte(`
{
"kind": "EventList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "722"
},
"items": [
{
"kind": "Event",
"apiVersion": "v1",
"metadata": {
"name": "nginx-rc-1",
"namespace": "default",
"creationTimestamp": "2022-01-01T00:00:00Z"
},
"involvedObject": {
"kind": "Pod",
"name": "nginx-rc-12345",
"namespace": "default"
},
"reason": "Created",
"message": "Created container",
"type": "Normal"
},
{
"kind": "Event",
"apiVersion": "v1",
"metadata": {
"name": "nginx-rc-2",
"namespace": "default",
"creationTimestamp": "2022-01-01T00:00:00Z"
},
"involvedObject": {
"kind": "Pod",
"name": "nginx-rc-67890",
"namespace": "default"
},
"reason": "Started",
"message": "Started container",
"type": "Normal"
}
]
}
`),
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
getFile := func(n string) ([]byte, error) {
if b, ok := test.files[n]; ok {
return b, nil
}
return nil, errors.New("file not found")
}
findFiles := func(n string, _ []string) (map[string][]byte, error) {
return nil, errors.New("method not implemented")
}
a := &AnalyzeEvent{
analyzer: &test.analyzer,
}
actual, err := a.Analyze(getFile, findFiles)
if test.err != nil {
req.EqualError(err, test.err.Error())
return
}
req.NoError(err)
unPointered := []AnalyzeResult{}
for _, v := range actual {
unPointered = append(unPointered, *v)
}
req.ElementsMatch(test.expectResult, unPointered)
})
}
}
func TestAnalyzeEventResult(t *testing.T) {
event := &corev1.Event{
InvolvedObject: corev1.ObjectReference{
Kind: "Pod",
Name: "foo-pod",
},
Reason: "Unhealthy",
Message: "foo-message",
Type: "Warning",
}
outcomes := []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "true",
Message: "No unhealthy pods allowed",
},
Pass: &troubleshootv1beta2.SingleOutcome{
When: "false",
Message: "No unhealthy pod detected",
},
},
}
checkName := "Test Event"
expectedResults := []*AnalyzeResult{
{
Title: "Test Event",
IconKey: "kubernetes_event",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
IsFail: true,
IsWarn: false,
IsPass: false,
Message: "No unhealthy pods allowed",
},
}
results, err := analyzeEventResult(event, outcomes, checkName)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(results) != len(expectedResults) {
t.Fatalf("unexpected number of results, got %d, want %d", len(results), len(expectedResults))
}
for i, result := range results {
expectedResult := expectedResults[i]
if result.Title != expectedResult.Title {
t.Errorf("unexpected title, got %s, want %s", result.Title, expectedResult.Title)
}
if result.IsFail != expectedResult.IsFail {
t.Errorf("unexpected IsFail value, got %v, want %v", result.IsFail, expectedResult.IsFail)
}
if result.IsWarn != expectedResult.IsWarn {
t.Errorf("unexpected IsWarn value, got %v, want %v", result.IsWarn, expectedResult.IsWarn)
}
if result.IsPass != expectedResult.IsPass {
t.Errorf("unexpected IsPass value, got %v, want %v", result.IsPass, expectedResult.IsPass)
}
if result.Message != expectedResult.Message {
t.Errorf("unexpected message, got %s, want %s", result.Message, expectedResult.Message)
}
}
}
+135
View File
@@ -0,0 +1,135 @@
package analyzer
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
)
type AnalyzeGoldpinger struct {
analyzer *troubleshootv1beta2.GoldpingerAnalyze
}
type checkAllOutput struct {
Hosts []struct {
HostIP string `json:"hostIP"`
PodIP string `json:"podIP"`
PodName string `json:"podName"`
} `json:"hosts"`
Responses map[string]struct {
HostIP string `json:"hostIP"`
PodIP string `json:"podIP"`
OK bool `json:"OK"`
Response struct {
PodResults map[string]struct {
HostIP string `json:"hostIP"`
OK bool `json:"OK"`
PingTime string `json:"pingTime"`
PodIP string `json:"podIP"`
Response struct {
BootTime string `json:"boot_time"`
} `json:"response"`
Error string `json:"error"`
ResponseTimeMS int `json:"response-time-ms"`
StatusCode int `json:"status-code"`
} `json:"podResults"`
} `json:"response"`
} `json:"responses"`
}
func (a *AnalyzeGoldpinger) Title() string {
title := a.analyzer.CheckName
if title == "" {
title = a.collectorName()
}
return title
}
func (a *AnalyzeGoldpinger) IsExcluded() (bool, error) {
return isExcluded(a.analyzer.Exclude)
}
func (a *AnalyzeGoldpinger) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
caoFilePath := constants.GP_CHECK_ALL_RESULTS_PATH
// To allow analysing older support bundles, we can provide a custom file path
if a.analyzer.FilePath != "" {
caoFilePath = a.analyzer.FilePath
}
collected, err := getFile(caoFilePath)
if err != nil {
return nil, errors.Wrapf(err, "failed to read collected file path: %q", caoFilePath)
}
var cao checkAllOutput
err = json.Unmarshal(collected, &cao)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal collected goldpinger output")
}
return a.podPingsAnalysis(&cao), nil
}
func (a *AnalyzeGoldpinger) collectorName() string {
if a.analyzer.CollectorName != "" {
return a.analyzer.CollectorName
}
return "goldpinger"
}
func (a *AnalyzeGoldpinger) podPingsAnalysis(cao *checkAllOutput) []*AnalyzeResult {
results := []*AnalyzeResult{}
for _, host := range cao.Hosts {
// Check if the pod from a host has any ping errors from other pods
targetPod := host.PodName
pingsSucceeded := true
for srcPod, resp := range cao.Responses {
res := &AnalyzeResult{
IconKey: "kubernetes",
Strict: a.analyzer.Strict.BoolOrDefaultFalse(),
}
// Get ping result for the pod
podResult, ok := resp.Response.PodResults[targetPod]
if !ok {
// Pod not found in ping results from the source pod
res.IsWarn = true
res.Title = fmt.Sprintf("Missing ping results for %q pod", targetPod)
res.Message = fmt.Sprintf("Ping result for %q pod from %q pod is missing", targetPod, srcPod)
pingsSucceeded = false
results = append(results, res)
continue
}
if !podResult.OK {
// Ping was not successful
res.IsFail = true
res.Title = fmt.Sprintf("Ping from %q pod to %q pod failed", srcPod, targetPod)
res.Message = fmt.Sprintf("Ping error: %s", podResult.Error)
pingsSucceeded = false
results = append(results, res)
continue
}
}
// If all pings succeeded, add a pass result
if pingsSucceeded {
results = append(results, &AnalyzeResult{
IconKey: "kubernetes",
Strict: a.analyzer.Strict.BoolOrDefaultFalse(),
IsPass: true,
Title: fmt.Sprintf("Pings to %q pod succeeded", targetPod),
Message: fmt.Sprintf("Pings to %q pod from all other pods in the cluster succeeded", targetPod),
})
}
}
return results
}
+125
View File
@@ -0,0 +1,125 @@
package analyzer
import (
"encoding/json"
"testing"
"github.com/replicatedhq/troubleshoot/internal/testutils"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestUnmarshallingCheckAllResults(t *testing.T) {
s := testutils.GetTestFixture(t, "goldpinger/checkall-with-error.json")
var res checkAllOutput
err := json.Unmarshal([]byte(s), &res)
require.NoError(t, err)
assert.Len(t, res.Hosts, 3)
assert.Equal(t, "goldpinger-tbdsb", res.Hosts[1].PodName)
assert.Equal(t, "10.32.2.2", res.Responses["goldpinger-4hctt"].Response.PodResults["goldpinger-jj9mw"].PodIP)
assert.Equal(t,
`Get "http://10.32.0.9:80/ping": context deadline exceeded`,
res.Responses["goldpinger-tbdsb"].Response.PodResults["goldpinger-4hctt"].Error,
)
}
func TestAnalyzeGoldpinger_podPingsAnalysis(t *testing.T) {
tests := []struct {
name string
cao *checkAllOutput
want []*AnalyzeResult
}{
{
name: "no ping errors",
cao: caoFixture(t, "goldpinger/checkall-success.json"),
want: []*AnalyzeResult{
{
Title: "Pings to \"goldpinger-kpz4g\" pod succeeded",
Message: "Pings to \"goldpinger-kpz4g\" pod from all other pods in the cluster succeeded",
IconKey: "kubernetes",
IsPass: true,
},
{
Title: "Pings to \"goldpinger-k6d2j\" pod succeeded",
Message: "Pings to \"goldpinger-k6d2j\" pod from all other pods in the cluster succeeded",
IconKey: "kubernetes",
IsPass: true,
},
{
Title: "Pings to \"goldpinger-5ck4d\" pod succeeded",
Message: "Pings to \"goldpinger-5ck4d\" pod from all other pods in the cluster succeeded",
IconKey: "kubernetes",
IsPass: true,
},
},
},
{
name: "with some ping errors",
cao: caoFixture(t, "goldpinger/checkall-with-error.json"),
want: []*AnalyzeResult{
{
Title: "Ping from \"goldpinger-jj9mw\" pod to \"goldpinger-4hctt\" pod failed",
Message: "Ping error: Get \"http://10.32.0.9:80/ping\": context deadline exceeded",
IconKey: "kubernetes",
IsFail: true,
},
{
Title: "Ping from \"goldpinger-jj9mw\" pod to \"goldpinger-tbdsb\" pod failed",
Message: "Ping error: Get \"http://10.32.1.2:80/ping\": context deadline exceeded",
IconKey: "kubernetes",
IsFail: true,
},
{
Title: "Ping from \"goldpinger-4hctt\" pod to \"goldpinger-jj9mw\" pod failed",
Message: "Ping error: Get \"http://10.32.2.2:80/ping\": context deadline exceeded",
IconKey: "kubernetes",
IsFail: true,
},
{
Title: "Ping from \"goldpinger-4hctt\" pod to \"goldpinger-tbdsb\" pod failed",
Message: "Ping error: Get \"http://10.32.1.2:80/ping\": context deadline exceeded",
IconKey: "kubernetes",
IsFail: true,
},
{
Title: "Ping from \"goldpinger-tbdsb\" pod to \"goldpinger-4hctt\" pod failed",
Message: "Ping error: Get \"http://10.32.0.9:80/ping\": context deadline exceeded",
IconKey: "kubernetes",
IsFail: true,
},
{
Title: "Ping from \"goldpinger-tbdsb\" pod to \"goldpinger-jj9mw\" pod failed",
Message: "Ping error: Get \"http://10.32.2.2:80/ping\": context deadline exceeded",
IconKey: "kubernetes",
IsFail: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := &AnalyzeGoldpinger{
analyzer: &troubleshootv1beta2.GoldpingerAnalyze{},
}
got := a.podPingsAnalysis(tt.cao)
// Check existence of each want. Maps are not ordered, so we can't just compare
for _, want := range tt.want {
assert.Contains(t, got, want)
}
assert.Len(t, got, len(tt.want))
})
}
}
func caoFixture(t *testing.T, path string) *checkAllOutput {
t.Helper()
s := testutils.GetTestFixture(t, path)
var res checkAllOutput
err := json.Unmarshal([]byte(s), &res)
require.NoError(t, err)
return &res
}
+18
View File
@@ -10,6 +10,7 @@ import (
"strconv"
"github.com/pkg/errors"
util "github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
iutils "github.com/replicatedhq/troubleshoot/pkg/interfaceutils"
"k8s.io/client-go/util/jsonpath"
@@ -54,6 +55,8 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js
return nil, errors.Wrap(err, "failed to parse collected data as json")
}
originalActual := actual
if analyzer.Path != "" {
actual, err = iutils.GetAtPath(actual, analyzer.Path)
if err != nil {
@@ -112,6 +115,11 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js
}
}
outcome.Fail.Message, err = util.RenderTemplate(outcome.Fail.Message, originalActual)
if err != nil {
return nil, errors.Wrap(err, "failed to render template on outcome message")
}
if when == equal {
result.IsFail = true
result.Message = outcome.Fail.Message
@@ -128,6 +136,11 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js
}
}
outcome.Warn.Message, err = util.RenderTemplate(outcome.Warn.Message, originalActual)
if err != nil {
return nil, errors.Wrap(err, "failed to render template on outcome message")
}
if when == equal {
result.IsWarn = true
result.Message = outcome.Warn.Message
@@ -144,6 +157,11 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js
}
}
outcome.Pass.Message, err = util.RenderTemplate(outcome.Pass.Message, originalActual)
if err != nil {
return nil, errors.Wrap(err, "failed to render template on outcome message")
}
if when == equal {
result.IsPass = true
result.Message = outcome.Pass.Message
+42
View File
@@ -756,6 +756,48 @@ func Test_jsonCompare(t *testing.T) {
]
}`),
},
{
name: "basic comparison with outcome message templated",
analyzer: troubleshootv1beta2.JsonCompare{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "Status: {{ .morestuff.status }}, Info: {{ .morestuff.info }}",
When: "true",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "Status: {{ .morestuff.status }}, Info: {{ .morestuff.info }}",
When: "false",
},
},
},
CollectorName: "json-compare",
FileName: "json-compare.json",
Path: "morestuff.status",
Value: `"ready"`,
},
expectResult: AnalyzeResult{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "json-compare",
Message: "Status: notready, Info: morestuff is not ready",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
fileContents: []byte(`{
"stuff": {
"status": "ready",
"info": "this stuff is ready"
},
"morestuff": {
"status": "notready",
"info": "morestuff is not ready"
}
}`),
},
}
for _, test := range tests {
-19
View File
@@ -1,19 +1,15 @@
package analyzer
import (
"bufio"
"bytes"
"fmt"
"path/filepath"
"reflect"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
longhornv1beta1 "github.com/replicatedhq/troubleshoot/pkg/longhorn/apis/longhorn/v1beta1"
longhorntypes "github.com/replicatedhq/troubleshoot/pkg/longhorn/types"
"github.com/replicatedhq/troubleshoot/pkg/redact"
"gopkg.in/yaml.v2"
)
@@ -241,21 +237,6 @@ func analyzeLonghornEngine(engine *longhornv1beta1.Engine) *AnalyzeResult {
return result
}
func stripRedactedLines(yaml []byte) []byte {
buf := bytes.NewBuffer(yaml)
scanner := bufio.NewScanner(buf)
out := []byte{}
for scanner.Scan() {
line := strings.ReplaceAll(scanner.Text(), redact.MASK_TEXT, "HIDDEN")
out = append(out, []byte(line)...)
out = append(out, '\n')
}
return out
}
func analyzeLonghornReplicaChecksums(volumeName string, checksums []map[string]string) *AnalyzeResult {
result := &AnalyzeResult{
Title: fmt.Sprintf("Longhorn Volume Replica Corruption: %s", volumeName),
+62
View File
@@ -0,0 +1,62 @@
package analyzer
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ResticRepositorySpec is the specification for a ResticRepository.
type ResticRepositorySpec struct {
// VolumeNamespace is the namespace this restic repository contains
// pod volume backups for.
VolumeNamespace string `json:"volumeNamespace"`
// BackupStorageLocation is the name of the BackupStorageLocation
// that should contain this repository.
BackupStorageLocation string `json:"backupStorageLocation"`
// ResticIdentifier is the full restic-compatible string for identifying
// this repository.
ResticIdentifier string `json:"resticIdentifier"`
// MaintenanceFrequency is how often maintenance should be run.
MaintenanceFrequency metav1.Duration `json:"maintenanceFrequency"`
}
// ResticRepositoryPhase represents the lifecycle phase of a ResticRepository.
// +kubebuilder:validation:Enum=New;Ready;NotReady
type ResticRepositoryPhase string
const (
ResticRepositoryPhaseNew ResticRepositoryPhase = "New"
ResticRepositoryPhaseReady ResticRepositoryPhase = "Ready"
ResticRepositoryPhaseNotReady ResticRepositoryPhase = "NotReady"
)
// ResticRepositoryStatus is the current status of a ResticRepository.
type ResticRepositoryStatus struct {
// Phase is the current state of the ResticRepository.
// +optional
Phase ResticRepositoryPhase `json:"phase,omitempty"`
// Message is a message about the current status of the ResticRepository.
// +optional
Message string `json:"message,omitempty"`
// LastMaintenanceTime is the last time maintenance was run.
// +optional
// +nullable
LastMaintenanceTime *metav1.Time `json:"lastMaintenanceTime,omitempty"`
}
type ResticRepository struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// +optional
Spec ResticRepositorySpec `json:"spec,omitempty"`
// +optional
Status ResticRepositoryStatus `json:"status,omitempty"`
}
+738
View File
@@ -0,0 +1,738 @@
package analyzer
import (
"encoding/json"
"fmt"
"path/filepath"
"strings"
appsV1 "k8s.io/api/apps/v1"
restic_types "github.com/replicatedhq/troubleshoot/pkg/analyze/types"
"golang.org/x/mod/semver"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
)
type AnalyzeVelero struct {
analyzer *troubleshootv1beta2.VeleroAnalyze
}
func (a *AnalyzeVelero) Title() string {
title := a.analyzer.CheckName
if title == "" {
title = "Velero"
}
return title
}
func (a *AnalyzeVelero) IsExcluded() (bool, error) {
return isExcluded(a.analyzer.Exclude)
}
func (a *AnalyzeVelero) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
results, err := a.veleroStatus(a.analyzer, getFile, findFiles)
if err != nil {
return nil, err
}
for i := range results {
results[i].Strict = a.analyzer.Strict.BoolOrDefaultFalse()
}
return results, nil
}
func (a *AnalyzeVelero) veleroStatus(analyzer *troubleshootv1beta2.VeleroAnalyze, getFileContents getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
excludeFiles := []string{}
results := []*AnalyzeResult{}
oldVeleroRepoType := false
veleroVersion, err := getVeleroVersion(excludeFiles, findFiles)
if err != nil {
return nil, errors.Wrap(err, "Unable to find velero deployment")
}
// Check if the version string is valid erer
if !semver.IsValid(veleroVersion) {
return nil, errors.Errorf("Invalid velero semver: %s", veleroVersion)
}
// check if veleroVersion is less than 1.10.x
compareResult := semver.Compare(veleroVersion, "1.10.0")
if compareResult < 0 {
oldVeleroRepoType = true
}
if oldVeleroRepoType == true {
// old velero (v1.9.x) has a BackupRepositoryTypeRestic
// get resticrepositories.velero.io
resticRepositoriesDir := GetVeleroResticRepositoriesDirectory()
resticRepositoriesGlob := filepath.Join(resticRepositoriesDir, "*.json")
resticRepositoriesJson, err := findFiles(resticRepositoriesGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero restic repositories files under %s", resticRepositoriesDir)
}
resticRepositories := []*restic_types.ResticRepository{}
for key, resticRepositoryJson := range resticRepositoriesJson {
var resticRepositoryArray []*restic_types.ResticRepository
err := json.Unmarshal(resticRepositoryJson, &resticRepositories)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal restic repository json from %s", key)
}
resticRepositories = append(resticRepositories, resticRepositoryArray...)
}
results = append(results, analyzeResticRepositories(resticRepositories)...)
} else {
// velerov1.Version
// get backuprepositories.velero.io
backupRepositoriesDir := GetVeleroBackupRepositoriesDirectory()
backupRepositoriesGlob := filepath.Join(backupRepositoriesDir, "*.json")
backupRepositoriesJson, err := findFiles(backupRepositoriesGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero backup repositories files under %s", backupRepositoriesDir)
}
backupRepositories := []*velerov1.BackupRepository{}
for key, backupRepositoryJson := range backupRepositoriesJson {
var backupRepositoryArray []*velerov1.BackupRepository
err := json.Unmarshal(backupRepositoryJson, &backupRepositoryArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal backup repository json from %s", key)
}
backupRepositories = append(backupRepositories, backupRepositoryArray...)
}
results = append(results, analyzeBackupRepositories(backupRepositories)...)
}
// get backups.velero.io
backupsDir := GetVeleroBackupsDirectory()
backupsGlob := filepath.Join(backupsDir, "*.json")
veleroJSONs, err := findFiles(backupsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero backup files")
}
backups := []*velerov1.Backup{}
for _, veleroJSON := range veleroJSONs {
if err != nil {
return nil, errors.Wrapf(err, "failed to read velero backup file %s", veleroJSON)
}
var veleroBackups []*velerov1.Backup
err = json.Unmarshal(veleroJSON, &veleroBackups)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal velero backup file %s", veleroJSON)
}
backups = append(backups, veleroBackups...)
}
// get backupstoragelocations.velero.io
backupStorageLocationsDir := GetVeleroBackupStorageLocationsDirectory()
backupStorageLocationsGlob := filepath.Join(backupStorageLocationsDir, "*.json")
backupStorageLocationsJson, err := findFiles(backupStorageLocationsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero backup storage locations files under %s", backupStorageLocationsDir)
}
backupStorageLocations := []*velerov1.BackupStorageLocation{}
for key, backupStorageLocationJson := range backupStorageLocationsJson {
var backupStorageLocationArray []*velerov1.BackupStorageLocation
err := json.Unmarshal(backupStorageLocationJson, &backupStorageLocationArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal backup storage location json from %s", key)
}
backupStorageLocations = append(backupStorageLocations, backupStorageLocationArray...)
}
// get deletebackuprequests.velero.io
deleteBackupRequestsDir := GetVeleroDeleteBackupRequestsDirectory()
deleteBackupRequestsGlob := filepath.Join(deleteBackupRequestsDir, "*.json")
deleteBackupRequestsJson, err := findFiles(deleteBackupRequestsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero delete backup requests files under %s", deleteBackupRequestsDir)
}
deleteBackupRequests := []*velerov1.DeleteBackupRequest{}
for key, deleteBackupRequestJson := range deleteBackupRequestsJson {
var deleteBackupRequestArray []*velerov1.DeleteBackupRequest
err := json.Unmarshal(deleteBackupRequestJson, &deleteBackupRequestArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal delete backup request json from %s", key)
}
deleteBackupRequests = append(deleteBackupRequests, deleteBackupRequestArray...)
}
// get podvolumebackups.velero.io
podVolumeBackupsDir := GetVeleroPodVolumeBackupsDirectory()
podVolumeBackupsGlob := filepath.Join(podVolumeBackupsDir, "*.json")
podVolumeBackupsJson, err := findFiles(podVolumeBackupsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero pod volume backups files under %s", podVolumeBackupsDir)
}
podVolumeBackups := []*velerov1.PodVolumeBackup{}
for key, podVolumeBackupJson := range podVolumeBackupsJson {
var podVolumeBackupArray []*velerov1.PodVolumeBackup
err := json.Unmarshal(podVolumeBackupJson, &podVolumeBackupArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal pod volume backup json from %s", key)
}
podVolumeBackups = append(podVolumeBackups, podVolumeBackupArray...)
}
// get podvolumerestores.velero.io
podVolumeRestoresDir := GetVeleroPodVolumeRestoresDirectory()
podVolumeRestoresGlob := filepath.Join(podVolumeRestoresDir, "*.json")
podVolumeRestoresJson, err := findFiles(podVolumeRestoresGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero pod volume restores files under %s", podVolumeRestoresDir)
}
podVolumeRestores := []*velerov1.PodVolumeRestore{}
for key, podVolumeRestoreJson := range podVolumeRestoresJson {
var podVolumeRestoreArray []*velerov1.PodVolumeRestore
err := json.Unmarshal(podVolumeRestoreJson, &podVolumeRestoreArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal pod volume restore json from %s", key)
}
podVolumeRestores = append(podVolumeRestores, podVolumeRestoreArray...)
}
// get restores.velero.io
restoresDir := GetVeleroRestoresDirectory()
restoresGlob := filepath.Join(restoresDir, "*.json")
restoresJson, err := findFiles(restoresGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero restores files under %s", restoresDir)
}
restores := []*velerov1.Restore{}
for key, restoreJson := range restoresJson {
var restoreArray []*velerov1.Restore
err := json.Unmarshal(restoreJson, &restoreArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal restore json from %s", key)
}
restores = append(restores, restoreArray...)
}
// get schedules.velero.io
schedulesDir := GetVeleroSchedulesDirectory()
schedulesGlob := filepath.Join(schedulesDir, "*.json")
schedulesJson, err := findFiles(schedulesGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero schedules files under %s", schedulesDir)
}
schedules := []*velerov1.Schedule{}
for key, scheduleJson := range schedulesJson {
var scheduleArray []*velerov1.Schedule
err := json.Unmarshal(scheduleJson, &scheduleArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal schedule json from %s", key)
}
schedules = append(schedules, scheduleArray...)
}
// get serverstatusrequests.velero.io
serverStatusRequestsDir := GetVeleroServerStatusRequestsDirectory()
serverStatusRequestsGlob := filepath.Join(serverStatusRequestsDir, "*.json")
serverStatusRequestsJson, err := findFiles(serverStatusRequestsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero server status requests files under %s", serverStatusRequestsDir)
}
serverStatusRequests := []*velerov1.ServerStatusRequest{}
for key, serverStatusRequestJson := range serverStatusRequestsJson {
var serverStatusRequestArray []*velerov1.ServerStatusRequest
err := json.Unmarshal(serverStatusRequestJson, &serverStatusRequestArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal server status request json from %s", key)
}
serverStatusRequests = append(serverStatusRequests, serverStatusRequestArray...)
}
// get volumesnapshotlocations.velero.io
volumeSnapshotLocationsDir := GetVeleroVolumeSnapshotLocationsDirectory()
volumeSnapshotLocationsGlob := filepath.Join(volumeSnapshotLocationsDir, "*.json")
volumeSnapshotLocationsJson, err := findFiles(volumeSnapshotLocationsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero volume snapshot locations files under %s", volumeSnapshotLocationsDir)
}
volumeSnapshotLocations := []*velerov1.VolumeSnapshotLocation{}
for key, volumeSnapshotLocationJson := range volumeSnapshotLocationsJson {
var volumeSnapshotLocationArray []*velerov1.VolumeSnapshotLocation
err := json.Unmarshal(volumeSnapshotLocationJson, &volumeSnapshotLocationArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal volume snapshot location json from %s", key)
}
volumeSnapshotLocations = append(volumeSnapshotLocations, volumeSnapshotLocationArray...)
}
logsDir := GetVeleroLogsDirectory()
logsGlob := filepath.Join(logsDir, "node-agent*", "*.log")
nodeAgentlogs, err := findFiles(logsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero logs files under %s", logsDir)
}
veleroLogsGlob := filepath.Join(logsDir, "velero*", "*.log")
veleroLogs, err := findFiles(veleroLogsGlob, excludeFiles)
results = append(results, analyzeLogs(nodeAgentlogs, "node-agent*")...)
results = append(results, analyzeLogs(veleroLogs, "velero*")...)
results = append(results, analyzeBackups(backups)...)
results = append(results, analyzeBackupStorageLocations(backupStorageLocations)...)
results = append(results, analyzeDeleteBackupRequests(deleteBackupRequests)...)
results = append(results, analyzePodVolumeBackups(podVolumeBackups)...)
results = append(results, analyzePodVolumeRestores(podVolumeRestores)...)
results = append(results, analyzeRestores(restores)...)
results = append(results, analyzeSchedules(schedules)...)
results = append(results, analyzeVolumeSnapshotLocations(volumeSnapshotLocations)...)
return aggregateResults(results), nil
}
func analyzeBackupRepositories(backupRepositories []*velerov1.BackupRepository) []*AnalyzeResult {
results := []*AnalyzeResult{}
readyCount := 0
backupRepositoriesResult := &AnalyzeResult{
Title: "At least 1 Backup Repository configured",
}
if len(backupRepositories) == 0 {
backupRepositoriesResult.IsFail = true
backupRepositoriesResult.Message = "No backup repositories configured"
} else {
for _, backupRepository := range backupRepositories {
if backupRepository.Status.Phase != velerov1.BackupRepositoryPhaseReady {
result := &AnalyzeResult{
Title: fmt.Sprintf("Backup Repository %s", backupRepository.Name),
}
result.Message = fmt.Sprintf("Backup Repository [%s] is in phase %s", backupRepository.Name, backupRepository.Status.Phase)
result.IsWarn = true
results = append(results, result)
} else {
readyCount++
}
}
if readyCount > 0 {
backupRepositoriesResult.IsPass = true
backupRepositoriesResult.Message = fmt.Sprintf("Found %d backup repositories configured and %d Ready", len(backupRepositories), readyCount)
} else {
backupRepositoriesResult.IsWarn = true
backupRepositoriesResult.Message = fmt.Sprintf("Found %d configured backup repositories, but none are ready", len(backupRepositories))
}
}
results = append(results, backupRepositoriesResult)
return results
}
func analyzeResticRepositories(resticRepositories []*restic_types.ResticRepository) []*AnalyzeResult {
results := []*AnalyzeResult{}
readyCount := 0
resticRepositoriesResult := &AnalyzeResult{
Title: "At least 1 Restic Repository configured",
}
if len(resticRepositories) == 0 {
resticRepositoriesResult.IsFail = true
resticRepositoriesResult.Message = "No restic repositories configured"
} else {
for _, resticRepository := range resticRepositories {
if resticRepository.Status.Phase != restic_types.ResticRepositoryPhaseReady {
result := &AnalyzeResult{
Title: fmt.Sprintf("Restic Repository %s", resticRepository.GetName()),
}
result.Message = fmt.Sprintf("Restic Repository [%s] is in phase %s", resticRepository.Name, resticRepository.Status.Phase)
result.IsWarn = true
results = append(results, result)
} else {
readyCount++
}
}
if readyCount > 0 {
resticRepositoriesResult.IsPass = true
resticRepositoriesResult.Message = fmt.Sprintf("Found %d restic repositories configured and %d Ready", len(resticRepositories), readyCount)
} else {
resticRepositoriesResult.IsWarn = true
resticRepositoriesResult.Message = fmt.Sprintf("Found %d configured restic repositories, but none are ready", len(resticRepositories))
}
}
results = append(results, resticRepositoriesResult)
return results
}
func analyzeBackups(backups []*velerov1.Backup) []*AnalyzeResult {
results := []*AnalyzeResult{}
failedPhases := map[velerov1.BackupPhase]bool{
velerov1.BackupPhaseFailed: true,
velerov1.BackupPhasePartiallyFailed: true,
velerov1.BackupPhaseFailedValidation: true,
}
for _, backup := range backups {
if failedPhases[backup.Status.Phase] {
result := &AnalyzeResult{
Title: fmt.Sprintf("Backup %s", backup.Name),
}
result.IsFail = true
result.Message = fmt.Sprintf("Backup %s phase is %s", backup.Name, backup.Status.Phase)
results = append(results, result)
}
}
if len(backups) > 0 {
results = append(results, &AnalyzeResult{
Title: "Velero Backups",
IsPass: true,
Message: fmt.Sprintf("Found %d backups", len(backups)),
})
}
return results
}
func analyzeBackupStorageLocations(backupStorageLocations []*velerov1.BackupStorageLocation) []*AnalyzeResult {
results := []*AnalyzeResult{}
availableCount := 0
bslResult := &AnalyzeResult{
Title: "At least 1 Backup Storage Location configured",
}
if len(backupStorageLocations) == 0 {
bslResult.IsFail = true
bslResult.Message = "No backup storage locations configured"
} else {
for _, backupStorageLocation := range backupStorageLocations {
if backupStorageLocation.Status.Phase != velerov1.BackupStorageLocationPhaseAvailable {
result := &AnalyzeResult{
Title: fmt.Sprintf("Backup Storage Location %s", backupStorageLocation.Name),
}
result.Message = fmt.Sprintf("Backup Storage Location [%s] is in phase %s", backupStorageLocation.Name, backupStorageLocation.Status.Phase)
result.IsWarn = true
results = append(results, result)
} else {
availableCount++
}
}
if availableCount > 0 {
bslResult.IsPass = true
bslResult.Message = fmt.Sprintf("Found %d backup storage locations configured and %d Available", len(backupStorageLocations), availableCount)
} else {
bslResult.IsWarn = true
bslResult.Message = fmt.Sprintf("Found %d configured backup storage locations, but none are available", len(backupStorageLocations))
}
}
results = append(results, bslResult)
return results
}
func analyzeDeleteBackupRequests(deleteBackupRequests []*velerov1.DeleteBackupRequest) []*AnalyzeResult {
results := []*AnalyzeResult{}
inProgressCount := 0
if len(deleteBackupRequests) > 0 {
for _, deleteBackupRequest := range deleteBackupRequests {
if deleteBackupRequest.Status.Phase == velerov1.DeleteBackupRequestPhaseInProgress {
inProgressCount++
}
}
if inProgressCount > 0 {
deleteBackupRequestsResult := &AnalyzeResult{
Title: "Delete Backup Requests summary",
}
deleteBackupRequestsResult.IsWarn = true
deleteBackupRequestsResult.Message = fmt.Sprintf("Found %d delete backup requests in progress", inProgressCount)
results = append(results, deleteBackupRequestsResult)
}
}
return results
}
func analyzePodVolumeBackups(podVolumeBackups []*velerov1.PodVolumeBackup) []*AnalyzeResult {
results := []*AnalyzeResult{}
failures := 0
if len(podVolumeBackups) > 0 {
for _, podVolumeBackup := range podVolumeBackups {
if podVolumeBackup.Status.Phase == velerov1.PodVolumeBackupPhaseFailed {
result := &AnalyzeResult{
Title: fmt.Sprintf("Pod Volume Backup %s", podVolumeBackup.Name),
}
result.IsFail = true
result.Message = fmt.Sprintf("Pod Volume Backup %s phase is %s", podVolumeBackup.Name, podVolumeBackup.Status.Phase)
results = append(results, result)
failures++
}
}
if failures == 0 {
results = append(results, &AnalyzeResult{
Title: "Pod Volume Backups",
IsPass: true,
Message: fmt.Sprintf("Found %d pod volume backups", len(podVolumeBackups)),
})
}
}
return results
}
func analyzePodVolumeRestores(podVolumeRestores []*velerov1.PodVolumeRestore) []*AnalyzeResult {
results := []*AnalyzeResult{}
failures := 0
if len(podVolumeRestores) > 0 {
for _, podVolumeRestore := range podVolumeRestores {
if podVolumeRestore.Status.Phase == velerov1.PodVolumeRestorePhaseFailed {
result := &AnalyzeResult{
Title: fmt.Sprintf("Pod Volume Restore %s", podVolumeRestore.Name),
}
result.IsFail = true
result.Message = fmt.Sprintf("Pod Volume Restore %s phase is %s", podVolumeRestore.Name, podVolumeRestore.Status.Phase)
results = append(results, result)
failures++
}
}
if failures == 0 {
results = append(results, &AnalyzeResult{
Title: "Pod Volume Restores",
IsPass: true,
Message: fmt.Sprintf("Found %d pod volume restores", len(podVolumeRestores)),
})
}
}
return results
}
func analyzeRestores(restores []*velerov1.Restore) []*AnalyzeResult {
results := []*AnalyzeResult{}
failures := 0
if len(restores) > 0 {
failedPhases := map[velerov1.RestorePhase]bool{
velerov1.RestorePhaseFailed: true,
velerov1.RestorePhasePartiallyFailed: true,
velerov1.RestorePhaseFailedValidation: true,
}
for _, restore := range restores {
if failedPhases[restore.Status.Phase] {
result := &AnalyzeResult{
Title: fmt.Sprintf("Restore %s", restore.Name),
}
result.IsFail = true
result.Message = fmt.Sprintf("Restore %s phase is %s", restore.Name, restore.Status.Phase)
results = append(results, result)
failures++
}
}
if failures == 0 {
results = append(results, &AnalyzeResult{
Title: "Velero Restores",
IsPass: true,
Message: fmt.Sprintf("Found %d restores", len(restores)),
})
}
}
return results
}
func analyzeSchedules(schedules []*velerov1.Schedule) []*AnalyzeResult {
results := []*AnalyzeResult{}
failures := 0
if len(schedules) > 0 {
for _, schedule := range schedules {
if schedule.Status.Phase == velerov1.SchedulePhaseFailedValidation {
result := &AnalyzeResult{
Title: fmt.Sprintf("Schedule %s", schedule.Name),
}
result.IsFail = true
result.Message = fmt.Sprintf("Schedule %s phase is %s", schedule.Name, schedule.Status.Phase)
results = append(results, result)
failures++
}
}
if failures == 0 {
results = append(results, &AnalyzeResult{
Title: "Velero Schedules",
IsPass: true,
Message: fmt.Sprintf("Found %d schedules", len(schedules)),
})
}
}
return results
}
func analyzeVolumeSnapshotLocations(volumeSnapshotLocations []*velerov1.VolumeSnapshotLocation) []*AnalyzeResult {
results := []*AnalyzeResult{}
failures := 0
if len(volumeSnapshotLocations) > 0 {
for _, volumeSnapshotLocation := range volumeSnapshotLocations {
if volumeSnapshotLocation.Status.Phase == velerov1.VolumeSnapshotLocationPhaseUnavailable {
result := &AnalyzeResult{
Title: fmt.Sprintf("Volume Snapshot Location %s", volumeSnapshotLocation.Name),
}
result.IsFail = true
result.Message = fmt.Sprintf("Volume Snapshot Location %s phase is %s", volumeSnapshotLocation.Name, volumeSnapshotLocation.Status.Phase)
results = append(results, result)
failures++
}
}
if failures == 0 {
results = append(results, &AnalyzeResult{
Title: "Velero Volume Snapshot Locations",
IsPass: true,
Message: fmt.Sprintf("Found %d volume snapshot locations", len(volumeSnapshotLocations)),
})
}
}
return results
}
func analyzeLogs(logs map[string][]byte, kind string) []*AnalyzeResult {
results := []*AnalyzeResult{}
if len(logs) > 0 {
for key, logBytes := range logs {
logContent := string(logBytes)
result := &AnalyzeResult{
Title: fmt.Sprintf("Velero logs for pod [%s]", key),
}
if strings.Contains(logContent, "permission denied") {
result.IsWarn = true
result.Message = fmt.Sprintf("Found 'permission denied' in %s pod log file(s)", kind)
results = append(results, result)
continue
}
if strings.Contains(logContent, "error") || strings.Contains(logContent, "panic") || strings.Contains(logContent, "fatal") {
result.IsWarn = true
result.Message = fmt.Sprintf("Found error|panic|fatal in %s pod log file(s)", kind)
results = append(results, result)
}
}
results = append(results, &AnalyzeResult{
Title: fmt.Sprintf("Velero Logs analysis for kind [%s]", kind),
IsPass: true,
Message: fmt.Sprintf("Found %d log files", len(logs)),
})
}
return results
}
func aggregateResults(results []*AnalyzeResult) []*AnalyzeResult {
out := []*AnalyzeResult{}
resultFailed := false
for _, result := range results {
if result.IsFail {
resultFailed = true
}
out = append(out, result)
}
if len(results) > 0 {
if resultFailed == false {
out = append(out, &AnalyzeResult{
Title: "Velero Status",
IsPass: true,
Message: "Velero setup is healthy",
})
}
if resultFailed == true {
out = append(out, &AnalyzeResult{
Title: "Velero Status",
IsWarn: true,
Message: "Velero setup is not entirely healthy",
})
}
}
return out
}
func getVeleroVersion(excludedFiles []string, findFiles getChildCollectedFileContents) (string, error) {
veleroDeploymentDir := "cluster-resources/deployments"
veleroVersion := ""
veleroDeploymentGlob := filepath.Join(veleroDeploymentDir, "velero.json")
veleroDeploymentJson, err := findFiles(veleroDeploymentGlob, excludedFiles)
if err != nil {
return "", errors.Wrapf(err, "failed to find Velero deployment")
}
if len(veleroDeploymentJson) == 0 {
return "", errors.Errorf("could not find Velero deployment in %s", veleroDeploymentDir)
}
var deploymentList *appsV1.DeploymentList
// should run only once
for key, veleroDeploymentJsonBytes := range veleroDeploymentJson {
err := json.Unmarshal(veleroDeploymentJsonBytes, &deploymentList)
if err != nil {
return "", errors.Wrapf(err, "failed to unmarshal Velero deployment json from %s", key)
}
break
}
if deploymentList == nil {
return "", errors.Errorf("could not find Velero deployment")
}
for _, deployment := range deploymentList.Items {
for _, container := range deployment.Spec.Template.Spec.Containers {
if container.Name == "velero" {
container_image := container.Image
veleroVersion = strings.Split(container_image, ":")[1]
return veleroVersion, nil
}
}
}
return "", errors.Errorf("could not find Velero container in deployment")
}
func GetVeleroBackupsDirectory() string {
return "cluster-resources/custom-resources/backups.velero.io"
}
func GetVeleroBackupRepositoriesDirectory() string {
return "cluster-resources/custom-resources/backuprepositories.velero.io"
}
func GetVeleroBackupStorageLocationsDirectory() string {
return "cluster-resources/custom-resources/backupstoragelocations.velero.io"
}
func GetVeleroDeleteBackupRequestsDirectory() string {
return "cluster-resources/custom-resources/deletebackuprequests.velero.io"
}
func GetVeleroDownloadRequestsDirectory() string {
return "cluster-resources/custom-resources/downloadrequests.velero.io"
}
func GetVeleroLogsDirectory() string {
return "velero/logs"
}
func GetVeleroPodVolumeBackupsDirectory() string {
return "cluster-resources/custom-resources/podvolumebackups.velero.io"
}
func GetVeleroPodVolumeRestoresDirectory() string {
return "cluster-resources/custom-resources/podvolumerestores.velero.io"
}
func GetVeleroRestoresDirectory() string {
return "cluster-resources/custom-resources/restores.velero.io"
}
func GetVeleroSchedulesDirectory() string {
return "cluster-resources/custom-resources/schedules.velero.io"
}
func GetVeleroServerStatusRequestsDirectory() string {
return "cluster-resources/custom-resources/serverstatusrequests.velero.io"
}
func GetVeleroVolumeSnapshotLocationsDirectory() string {
return "cluster-resources/custom-resources/volumesnapshotlocations.velero.io"
}
func GetVeleroResticRepositoriesDirectory() string {
return "cluster-resources/custom-resources/resticrepositories.velero.io"
}
+994
View File
@@ -0,0 +1,994 @@
package analyzer
import (
"encoding/json"
"reflect"
"testing"
"time"
restic_types "github.com/replicatedhq/troubleshoot/pkg/analyze/types"
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestAnalyzeVelero_BackupRepositories(t *testing.T) {
type args struct {
backupRepositories []*velerov1.BackupRepository
}
tests := []struct {
name string
args args
want []*AnalyzeResult
}{
{
name: "no backup repositories",
args: args{
backupRepositories: []*velerov1.BackupRepository{},
},
want: []*AnalyzeResult{
{
Title: "At least 1 Backup Repository configured",
Message: "No backup repositories configured",
IsFail: true,
},
},
},
{
name: "1 backup repository and 1 Ready",
args: args{
backupRepositories: []*velerov1.BackupRepository{
{
Spec: velerov1.BackupRepositorySpec{
BackupStorageLocation: "default",
VolumeNamespace: "velero",
},
Status: velerov1.BackupRepositoryStatus{
Phase: velerov1.BackupRepositoryPhaseReady,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "At least 1 Backup Repository configured",
Message: "Found 1 backup repositories configured and 1 Ready",
IsPass: true,
},
},
},
{
name: "2 backup repositories and 1 Ready",
args: args{
backupRepositories: []*velerov1.BackupRepository{
{
ObjectMeta: metav1.ObjectMeta{
Name: "default-default-restic-245sd",
Namespace: "velero",
},
Spec: velerov1.BackupRepositorySpec{
BackupStorageLocation: "default",
VolumeNamespace: "velero",
},
Status: velerov1.BackupRepositoryStatus{
Phase: velerov1.BackupRepositoryPhaseReady,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "builders-default-restic-jdtd8",
Namespace: "velero",
},
Spec: velerov1.BackupRepositorySpec{
BackupStorageLocation: "builders-default-restic-jdtd8",
VolumeNamespace: "velero",
},
Status: velerov1.BackupRepositoryStatus{
Phase: velerov1.BackupRepositoryPhaseNotReady,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Backup Repository builders-default-restic-jdtd8",
Message: "Backup Repository [builders-default-restic-jdtd8] is in phase NotReady",
IsWarn: true,
},
{
Title: "At least 1 Backup Repository configured",
Message: "Found 2 backup repositories configured and 1 Ready",
IsPass: true,
},
},
},
{
name: "1 backup repository and none Ready",
args: args{
backupRepositories: []*velerov1.BackupRepository{
{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "velero",
},
Spec: velerov1.BackupRepositorySpec{
BackupStorageLocation: "default",
VolumeNamespace: "velero",
},
Status: velerov1.BackupRepositoryStatus{
Phase: velerov1.BackupRepositoryPhaseNotReady,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Backup Repository default",
Message: "Backup Repository [default] is in phase NotReady",
IsWarn: true,
},
{
Title: "At least 1 Backup Repository configured",
Message: "Found 1 configured backup repositories, but none are ready",
IsWarn: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := analyzeBackupRepositories(tt.args.backupRepositories); !reflect.DeepEqual(got, tt.want) {
t.Errorf("analyzeBackupRepositories() = %v, want %v", got, tt.want)
}
})
}
}
func TestAnalyzeVelero_ResticRepositories(t *testing.T) {
type args struct {
resticRepositories []*restic_types.ResticRepository
}
tests := []struct {
name string
args args
want []*AnalyzeResult
}{
{
name: "no restic repositories",
args: args{
resticRepositories: []*restic_types.ResticRepository{},
},
want: []*AnalyzeResult{
{
Title: "At least 1 Restic Repository configured",
Message: "No restic repositories configured",
IsFail: true,
},
},
},
{
name: "1 restic repository and 1 Ready",
args: args{
resticRepositories: []*restic_types.ResticRepository{
{
ObjectMeta: metav1.ObjectMeta{
Name: "default-default-restic-245sd",
Namespace: "velero",
},
Spec: restic_types.ResticRepositorySpec{
BackupStorageLocation: "default",
VolumeNamespace: "velero",
},
Status: restic_types.ResticRepositoryStatus{
Phase: restic_types.ResticRepositoryPhaseReady,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "At least 1 Restic Repository configured",
Message: "Found 1 restic repositories configured and 1 Ready",
IsPass: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := analyzeResticRepositories(tt.args.resticRepositories); !reflect.DeepEqual(got, tt.want) {
t.Errorf("analyzeResticRepositories() = %v, want %v", got, tt.want)
}
})
}
}
func TestAnalyzeVelero_Backups(t *testing.T) {
type args struct {
backups []*velerov1.Backup
}
tests := []struct {
name string
args args
want []*AnalyzeResult
}{
{
name: "no backups",
args: args{
backups: []*velerov1.Backup{},
},
want: []*AnalyzeResult{},
},
{
name: "1 backup and 1 Completed",
args: args{
backups: []*velerov1.Backup{
{
ObjectMeta: metav1.ObjectMeta{
Name: "observability-backup",
Namespace: "velero",
},
Spec: velerov1.BackupSpec{
IncludedNamespaces: []string{"monitoring"},
},
Status: velerov1.BackupStatus{
Phase: velerov1.BackupPhaseCompleted,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Velero Backups",
Message: "Found 1 backups",
IsPass: true,
},
},
},
{
name: "1 backup and 1 Failed",
args: args{
backups: []*velerov1.Backup{
{
ObjectMeta: metav1.ObjectMeta{
Name: "application-backup",
Namespace: "velero",
},
Spec: velerov1.BackupSpec{
IncludedNamespaces: []string{"shazam"},
},
Status: velerov1.BackupStatus{
Phase: velerov1.BackupPhaseFailed,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Backup application-backup",
Message: "Backup application-backup phase is Failed",
IsFail: true,
},
{
Title: "Velero Backups",
Message: "Found 1 backups",
IsPass: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := analyzeBackups(tt.args.backups); !reflect.DeepEqual(got, tt.want) {
t.Errorf("analyzeBackups() = %v, want %v", got, tt.want)
}
})
}
}
func TestAnalyzeVelero_BackupStorageLocations(t *testing.T) {
type args struct {
backupStorageLocations []*velerov1.BackupStorageLocation
}
tests := []struct {
name string
args args
want []*AnalyzeResult
}{
{
name: "no backup storage locations",
args: args{
backupStorageLocations: []*velerov1.BackupStorageLocation{},
},
want: []*AnalyzeResult{
{
Title: "At least 1 Backup Storage Location configured",
Message: "No backup storage locations configured",
IsFail: true,
},
},
},
{
name: "1 backup storage location and 1 Available",
args: args{
backupStorageLocations: []*velerov1.BackupStorageLocation{
{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "velero",
},
Spec: velerov1.BackupStorageLocationSpec{
Provider: "aws",
},
Status: velerov1.BackupStorageLocationStatus{
Phase: velerov1.BackupStorageLocationPhaseAvailable,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "At least 1 Backup Storage Location configured",
Message: "Found 1 backup storage locations configured and 1 Available",
IsPass: true,
},
},
},
{
name: "1 backup storage location and 1 Unavailable",
args: args{
backupStorageLocations: []*velerov1.BackupStorageLocation{
{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "velero",
},
Spec: velerov1.BackupStorageLocationSpec{
Provider: "aws",
},
Status: velerov1.BackupStorageLocationStatus{
Phase: velerov1.BackupStorageLocationPhaseUnavailable,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Backup Storage Location default",
Message: "Backup Storage Location [default] is in phase Unavailable",
IsWarn: true,
},
{
Title: "At least 1 Backup Storage Location configured",
Message: "Found 1 configured backup storage locations, but none are available",
IsWarn: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := analyzeBackupStorageLocations(tt.args.backupStorageLocations); !reflect.DeepEqual(got, tt.want) {
t.Errorf("analyzeBackupStorageLocations() = %v, want %v", got, tt.want)
}
})
}
}
func TestAnalyzeVelero_DeleteBackupRequests(t *testing.T) {
type args struct {
deleteBackupRequests []*velerov1.DeleteBackupRequest
}
tests := []struct {
name string
args args
want []*AnalyzeResult
}{
{
name: "no backup delete requests",
args: args{
deleteBackupRequests: []*velerov1.DeleteBackupRequest{},
},
want: []*AnalyzeResult{},
},
{
name: "backup delete requests completed",
args: args{
deleteBackupRequests: []*velerov1.DeleteBackupRequest{
{
ObjectMeta: metav1.ObjectMeta{
Name: "observability-backup-20210308150016",
Namespace: "velero",
},
Spec: velerov1.DeleteBackupRequestSpec{
BackupName: "observability-backup",
},
Status: velerov1.DeleteBackupRequestStatus{
Phase: velerov1.DeleteBackupRequestPhaseProcessed,
},
},
},
},
want: []*AnalyzeResult{},
},
{
name: "backup delete requests summarize in progress",
args: args{
deleteBackupRequests: []*velerov1.DeleteBackupRequest{
{
ObjectMeta: metav1.ObjectMeta{
Name: "observability-backup-20210308150016",
Namespace: "velero",
},
Spec: velerov1.DeleteBackupRequestSpec{
BackupName: "observability-backup",
},
Status: velerov1.DeleteBackupRequestStatus{
Phase: velerov1.DeleteBackupRequestPhaseInProgress,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Delete Backup Requests summary",
Message: "Found 1 delete backup requests in progress",
IsWarn: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := analyzeDeleteBackupRequests(tt.args.deleteBackupRequests); !reflect.DeepEqual(got, tt.want) {
t.Errorf("analyzeDeleteBackupRequests() = %v, want %v", got, tt.want)
}
})
}
}
func TestAnalyzeVelero_PodVolumeBackups(t *testing.T) {
type args struct {
podVolumeBackups []*velerov1.PodVolumeBackup
}
tests := []struct {
name string
args args
want []*AnalyzeResult
}{
{
name: "no pod volume backups",
args: args{
podVolumeBackups: []*velerov1.PodVolumeBackup{},
},
want: []*AnalyzeResult{},
},
{
name: "pod volume backups",
args: args{
podVolumeBackups: []*velerov1.PodVolumeBackup{
{
Spec: velerov1.PodVolumeBackupSpec{
Node: "test-node-1",
Pod: corev1.ObjectReference{
Kind: "Pod",
Name: "kotsadm-76ddbc96c4-fsr88",
Namespace: "default",
},
Volume: "backup",
BackupStorageLocation: "default",
},
Status: velerov1.PodVolumeBackupStatus{
Phase: velerov1.PodVolumeBackupPhaseCompleted,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Pod Volume Backups",
Message: "Found 1 pod volume backups",
IsPass: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := analyzePodVolumeBackups(tt.args.podVolumeBackups); !reflect.DeepEqual(got, tt.want) {
t.Errorf("analyzePodVolumeBackups() = %v, want %v", got, tt.want)
}
})
}
}
func TestAnalyzeVelero_PodVolumeRestores(t *testing.T) {
type args struct {
podVolumeRestores []*velerov1.PodVolumeRestore
}
tests := []struct {
name string
args args
want []*AnalyzeResult
}{
{
name: "no pod volume restores",
args: args{
podVolumeRestores: []*velerov1.PodVolumeRestore{},
},
want: []*AnalyzeResult{},
},
{
name: "pod volume restores - no failures",
args: args{
podVolumeRestores: []*velerov1.PodVolumeRestore{
{
Spec: velerov1.PodVolumeRestoreSpec{
Pod: corev1.ObjectReference{
Kind: "Pod",
Name: "kotsadm-76ddbc96c4-fsr88",
Namespace: "default",
},
Volume: "backup",
BackupStorageLocation: "default",
},
Status: velerov1.PodVolumeRestoreStatus{
Phase: velerov1.PodVolumeRestorePhaseCompleted,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Pod Volume Restores",
Message: "Found 1 pod volume restores",
IsPass: true,
},
},
},
{
name: "pod volume restores - failures",
args: args{
podVolumeRestores: []*velerov1.PodVolumeRestore{
{
ObjectMeta: metav1.ObjectMeta{
Name: "observability-backup-20210308150016",
Namespace: "velero",
},
Spec: velerov1.PodVolumeRestoreSpec{
Pod: corev1.ObjectReference{
Kind: "Pod",
Name: "kotsadm-76ddbc96c4-fsr88",
Namespace: "default",
},
Volume: "backup",
BackupStorageLocation: "default",
},
Status: velerov1.PodVolumeRestoreStatus{
Phase: velerov1.PodVolumeRestorePhaseFailed,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Pod Volume Restore observability-backup-20210308150016",
Message: "Pod Volume Restore observability-backup-20210308150016 phase is Failed",
IsFail: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := analyzePodVolumeRestores(tt.args.podVolumeRestores); !reflect.DeepEqual(got, tt.want) {
t.Errorf("analyzePodVolumeRestores() = %v, want %v", got, tt.want)
}
})
}
}
func TestAnalyzeVelero_Restores(t *testing.T) {
type args struct {
restores []*velerov1.Restore
}
tests := []struct {
name string
args args
want []*AnalyzeResult
}{
{
name: "no restores",
args: args{
restores: []*velerov1.Restore{},
},
want: []*AnalyzeResult{},
},
{
name: "restores completed",
args: args{
restores: []*velerov1.Restore{
{
Spec: velerov1.RestoreSpec{
BackupName: "observability-backup",
},
Status: velerov1.RestoreStatus{
Phase: velerov1.RestorePhaseCompleted,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Velero Restores",
Message: "Found 1 restores",
IsPass: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := analyzeRestores(tt.args.restores); !reflect.DeepEqual(got, tt.want) {
t.Errorf("analyzeRestores() = %v, want %v", got, tt.want)
}
})
}
}
func TestAnalyzeVelero_Schedules(t *testing.T) {
type args struct {
schedules []*velerov1.Schedule
}
tests := []struct {
name string
args args
want []*AnalyzeResult
}{
{
name: "no schedules",
args: args{
schedules: []*velerov1.Schedule{},
},
want: []*AnalyzeResult{},
},
{
name: "schedules configured",
args: args{
schedules: []*velerov1.Schedule{
{
ObjectMeta: metav1.ObjectMeta{
Name: "daily-backup",
Namespace: "velero",
},
Spec: velerov1.ScheduleSpec{
Schedule: "0 0 * * *",
Template: velerov1.BackupSpec{
StorageLocation: "default",
IncludedNamespaces: []string{
"default",
},
IncludedResources: []string{
"*",
},
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "velero",
},
},
},
},
Status: velerov1.ScheduleStatus{
Phase: velerov1.SchedulePhaseEnabled,
LastBackup: &metav1.Time{
Time: time.Date(2023, 3, 8, 15, 0, 16, 0, time.UTC),
},
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Velero Schedules",
Message: "Found 1 schedules",
IsPass: true,
},
},
},
{
name: "schedules - failures",
args: args{
schedules: []*velerov1.Schedule{
{
ObjectMeta: metav1.ObjectMeta{
Name: "daily-backup",
Namespace: "velero",
},
Spec: velerov1.ScheduleSpec{
Schedule: "0 0 * * *",
Template: velerov1.BackupSpec{
StorageLocation: "default",
IncludedNamespaces: []string{
"default",
},
IncludedResources: []string{
"*",
},
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "velero",
},
},
},
},
Status: velerov1.ScheduleStatus{
Phase: velerov1.SchedulePhaseFailedValidation,
LastBackup: &metav1.Time{
Time: time.Date(2023, 3, 8, 15, 0, 16, 0, time.UTC),
},
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Schedule daily-backup",
Message: "Schedule daily-backup phase is FailedValidation",
IsFail: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := analyzeSchedules(tt.args.schedules); !reflect.DeepEqual(got, tt.want) {
t.Errorf("analyzeSchedules() = %v, want %v", got, tt.want)
}
})
}
}
func TestAnalyzeVelero_VolumeSnapshotLocations(t *testing.T) {
type args struct {
volumeSnapshotLocations []*velerov1.VolumeSnapshotLocation
}
tests := []struct {
name string
args args
want []*AnalyzeResult
}{
{
name: "no volume snapshot locations",
args: args{
volumeSnapshotLocations: []*velerov1.VolumeSnapshotLocation{},
},
want: []*AnalyzeResult{},
},
{
name: "volume snapshot locations configured",
args: args{
volumeSnapshotLocations: []*velerov1.VolumeSnapshotLocation{
{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "velero",
},
Spec: velerov1.VolumeSnapshotLocationSpec{
Provider: "aws",
Config: map[string]string{
"region": "us-east-1",
},
},
Status: velerov1.VolumeSnapshotLocationStatus{
Phase: velerov1.VolumeSnapshotLocationPhaseAvailable,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Velero Volume Snapshot Locations",
Message: "Found 1 volume snapshot locations",
IsPass: true,
},
},
},
{
name: "volume snapshot locations - failures",
args: args{
volumeSnapshotLocations: []*velerov1.VolumeSnapshotLocation{
{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "velero",
},
Spec: velerov1.VolumeSnapshotLocationSpec{
Provider: "aws",
Config: map[string]string{
"region": "us-east-1",
},
},
Status: velerov1.VolumeSnapshotLocationStatus{
Phase: velerov1.VolumeSnapshotLocationPhaseUnavailable,
},
},
},
},
want: []*AnalyzeResult{
{
Title: "Volume Snapshot Location default",
Message: "Volume Snapshot Location default phase is Unavailable",
IsFail: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := analyzeVolumeSnapshotLocations(tt.args.volumeSnapshotLocations); !reflect.DeepEqual(got, tt.want) {
t.Errorf("analyzeVolumeSnapshotLocations() = %v, want %v", got, tt.want)
}
})
}
}
func TestAnalyzeVelero_Logs(t *testing.T) {
type args struct {
logs map[string][]byte
kind string
}
tests := []struct {
name string
args args
want []*AnalyzeResult
}{
{
name: "no logs",
args: args{
logs: map[string][]byte{},
kind: "node-agent*",
},
want: []*AnalyzeResult{},
},
{
name: "logs - no errors in node-agent* pods",
args: args{
logs: map[string][]byte{
"node-agent-m6n9j": []byte("level=info msg=... backup=velero/sample-app controller=podvolumebacku"),
},
kind: "node-agent*",
},
want: []*AnalyzeResult{
{
Title: "Velero Logs analysis for kind [node-agent*]",
Message: "Found 1 log files",
IsPass: true,
},
},
},
{
name: "logs - no errors in velero* pods",
args: args{
logs: map[string][]byte{
"velero-788ff7c9dd-mslfl": []byte("level=info msg=BackupStorageLocations... controller=backup-storage-location"),
},
kind: "velero*",
},
want: []*AnalyzeResult{
{
Title: "Velero Logs analysis for kind [velero*]",
Message: "Found 1 log files",
IsPass: true,
},
},
},
{
name: "logs - errors in node-agent* pods",
args: args{
logs: map[string][]byte{
"node-agent-m6n9j": []byte("level=error msg=... backup=velero/sample-app controller=podvolumebacku"),
},
kind: "node-agent*",
},
want: []*AnalyzeResult{
{
Title: "Velero logs for pod [node-agent-m6n9j]",
Message: "Found error|panic|fatal in node-agent* pod log file(s)",
IsWarn: true,
},
{
Title: "Velero Logs analysis for kind [node-agent*]",
Message: "Found 1 log files",
IsPass: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := analyzeLogs(tt.args.logs, tt.args.kind); !reflect.DeepEqual(got, tt.want) {
t.Errorf("analyzeLogs() = %v, want %v", got, tt.want)
gotJSON, _ := json.MarshalIndent(got, "", " ")
wantJSON, _ := json.MarshalIndent(tt.want, "", " ")
t.Logf("\nGot: %s\nWant: %s", gotJSON, wantJSON)
}
})
}
}
func TestAnalyzeVelero_Results(t *testing.T) {
type args struct {
results []*AnalyzeResult
}
tests := []struct {
name string
args args
want []*AnalyzeResult
}{
{
name: "no results",
args: args{
results: []*AnalyzeResult{},
},
want: []*AnalyzeResult{},
},
{
name: "results - pass",
args: args{
results: []*AnalyzeResult{
{
Title: "random Velero CRD check",
IsPass: true,
Message: "CRD status is healthy",
},
},
},
want: []*AnalyzeResult{
{
Title: "random Velero CRD check",
IsPass: true,
Message: "CRD status is healthy",
},
{
Title: "Velero Status",
IsPass: true,
Message: "Velero setup is healthy",
},
},
},
{
name: "results - fail",
args: args{
results: []*AnalyzeResult{
{
Title: "random Velero CRD check failure",
IsFail: true,
Message: "CRD status - Failed",
},
},
},
want: []*AnalyzeResult{
{
Title: "random Velero CRD check failure",
IsFail: true,
Message: "CRD status - Failed",
},
{
Title: "Velero Status",
IsWarn: true,
Message: "Velero setup is not entirely healthy",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := aggregateResults(tt.args.results); !reflect.DeepEqual(got, tt.want) {
t.Errorf("aggregateResults() = %v, want %v", got, tt.want)
gotJSON, _ := json.MarshalIndent(got, "", " ")
wantJSON, _ := json.MarshalIndent(tt.want, "", " ")
t.Logf("\nGot: %s\nWant: %s", gotJSON, wantJSON)
}
})
}
}
+18
View File
@@ -6,6 +6,7 @@ import (
"strconv"
"github.com/pkg/errors"
util "github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
iutils "github.com/replicatedhq/troubleshoot/pkg/interfaceutils"
"gopkg.in/yaml.v2"
@@ -50,6 +51,8 @@ func (a *AnalyzeYamlCompare) analyzeYamlCompare(analyzer *troubleshootv1beta2.Ya
return nil, errors.Wrap(err, "failed to parse collected data as yaml doc")
}
originalActual := actual
if analyzer.Path != "" {
actual, err = iutils.GetAtPath(actual, analyzer.Path)
if err != nil {
@@ -81,6 +84,11 @@ func (a *AnalyzeYamlCompare) analyzeYamlCompare(analyzer *troubleshootv1beta2.Ya
}
}
outcome.Fail.Message, err = util.RenderTemplate(outcome.Fail.Message, originalActual)
if err != nil {
return nil, errors.Wrap(err, "failed to render template on outcome message")
}
if when == equal {
result.IsFail = true
result.Message = outcome.Fail.Message
@@ -96,6 +104,11 @@ func (a *AnalyzeYamlCompare) analyzeYamlCompare(analyzer *troubleshootv1beta2.Ya
}
}
outcome.Warn.Message, err = util.RenderTemplate(outcome.Warn.Message, originalActual)
if err != nil {
return nil, errors.Wrap(err, "failed to render template on outcome message")
}
if when == equal {
result.IsWarn = true
result.Message = outcome.Warn.Message
@@ -111,6 +124,11 @@ func (a *AnalyzeYamlCompare) analyzeYamlCompare(analyzer *troubleshootv1beta2.Ya
}
}
outcome.Pass.Message, err = util.RenderTemplate(outcome.Pass.Message, originalActual)
if err != nil {
return nil, errors.Wrap(err, "failed to render template on outcome message")
}
if when == equal {
result.IsPass = true
result.Message = outcome.Pass.Message
+39
View File
@@ -430,6 +430,45 @@ otherstuff:
},
fileContents: []byte(``),
},
{
name: "basic comparison with outcome message templated",
analyzer: troubleshootv1beta2.YamlCompare{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "Status: {{ .stuff.status }}, Info: {{ .stuff.info }}",
When: "true",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "Status: {{ .stuff.status }}, Info: {{ .stuff.info }}",
When: "false",
},
},
},
CollectorName: "yaml-compare",
FileName: "yaml-compare.yaml",
Value: `ready`,
Path: "stuff.status",
},
expectResult: AnalyzeResult{
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "yaml-compare",
Message: "Status: ready, Info: stuff is ready",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
fileContents: []byte(`
stuff:
status: ready
info: stuff is ready
morestuff:
status: notready
info: morestuff is not ready`),
},
}
for _, test := range tests {
@@ -186,6 +186,10 @@ type CephStatusAnalyze struct {
Namespace string `json:"namespace" yaml:"namespace"`
}
type VeleroAnalyze struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
}
type LonghornAnalyze struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
@@ -221,6 +225,23 @@ type CertificatesAnalyze struct {
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
}
type GoldpingerAnalyze struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes,omitempty" yaml:"outcomes,omitempty"`
CollectorName string `json:"collectorName" yaml:"collectorName"`
FilePath string `json:"filePath,omitempty" yaml:"filePath,omitempty"`
}
type EventAnalyze struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
CollectorName string `json:"collectorName" yaml:"collectorName"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
Reason string `json:"reason" yaml:"reason"`
RegexPattern string `json:"regex,omitempty" yaml:"regex,omitempty"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
}
type Analyze struct {
ClusterVersion *ClusterVersion `json:"clusterVersion,omitempty" yaml:"clusterVersion,omitempty"`
StorageClass *StorageClass `json:"storageClass,omitempty" yaml:"storageClass,omitempty"`
@@ -245,10 +266,13 @@ type Analyze struct {
Mysql *DatabaseAnalyze `json:"mysql,omitempty" yaml:"mysql,omitempty"`
Redis *DatabaseAnalyze `json:"redis,omitempty" yaml:"redis,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"`
RegistryImages *RegistryImagesAnalyze `json:"registryImages,omitempty" yaml:"registryImages,omitempty"`
WeaveReport *WeaveReportAnalyze `json:"weaveReport,omitempty" yaml:"weaveReport,omitempty"`
Sysctl *SysctlAnalyze `json:"sysctl,omitempty" yaml:"sysctl,omitempty"`
ClusterResource *ClusterResource `json:"clusterResource,omitempty" yaml:"clusterResource,omitempty"`
Certificates *CertificatesAnalyze `json:"certificates,omitempty" yaml:"certificates,omitempty"`
Goldpinger *GoldpingerAnalyze `json:"goldpinger,omitempty" yaml:"goldpinger,omitempty"`
Event *EventAnalyze `json:"event,omitempty" yaml:"event,omitempty"`
}
@@ -107,6 +107,15 @@ type RunPod struct {
PodSpec corev1.PodSpec `json:"podSpec,omitempty" yaml:"podSpec,omitempty"`
}
type RunDaemonSet struct {
CollectorMeta `json:",inline" yaml:",inline"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Namespace string `json:"namespace" yaml:"namespace"`
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
ImagePullSecret *ImagePullSecrets `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
PodSpec corev1.PodSpec `json:"podSpec,omitempty" yaml:"podSpec,omitempty"`
}
type ImagePullSecrets struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Data map[string]string `json:"data,omitempty" yaml:"data,omitempty"`
@@ -257,6 +266,25 @@ type Helm struct {
CollectorMeta `json:",inline" yaml:",inline"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
ReleaseName string `json:"releaseName,omitempty" yaml:"releaseName,omitempty"`
CollectValues bool `json:"collectValues,omitempty" yaml:"collectValues,omitempty"`
}
type Goldpinger struct {
CollectorMeta `json:",inline" yaml:",inline"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
PodLaunchOptions *PodLaunchOptions `json:"podLaunchOptions,omitempty" yaml:"podLaunchOptions,omitempty"`
}
type PodLaunchOptions struct {
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
Image string `json:"image,omitempty" yaml:"image,omitempty"`
ImagePullSecret *ImagePullSecrets `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
ServiceAccountName string `json:"serviceAccountName,omitempty" yaml:"serviceAccountName,omitempty"`
}
type Sonobuoy struct {
CollectorMeta `json:",inline" yaml:",inline"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
}
type Collect struct {
@@ -268,6 +296,7 @@ type Collect struct {
Logs *Logs `json:"logs,omitempty" yaml:"logs,omitempty"`
Run *Run `json:"run,omitempty" yaml:"run,omitempty"`
RunPod *RunPod `json:"runPod,omitempty" yaml:"runPod,omitempty"`
RunDaemonSet *RunDaemonSet `json:"runDaemonSet,omitempty" yaml:"runDaemonSet,omitempty"`
Exec *Exec `json:"exec,omitempty" yaml:"exec,omitempty"`
Data *Data `json:"data,omitempty" yaml:"data,omitempty"`
Copy *Copy `json:"copy,omitempty" yaml:"copy,omitempty"`
@@ -284,6 +313,8 @@ type Collect struct {
Sysctl *Sysctl `json:"sysctl,omitempty" yaml:"sysctl,omitempty"`
Certificates *Certificates `json:"certificates,omitempty" yaml:"certificates,omitempty"`
Helm *Helm `json:"helm,omitempty" yaml:"helm,omitempty"`
Goldpinger *Goldpinger `json:"goldpinger,omitempty" yaml:"goldpinger,omitempty"`
Sonobuoy *Sonobuoy `json:"sonobuoy,omitempty" yaml:"sonobuoy,omitempty"`
}
func (c *Collect) AccessReviewSpecs(overrideNS string) []authorizationv1.SelfSubjectAccessReviewSpec {
@@ -417,6 +448,19 @@ func (c *Collect) AccessReviewSpecs(overrideNS string) []authorizationv1.SelfSub
},
NonResourceAttributes: nil,
})
} else if c.RunDaemonSet != nil {
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: pickNamespaceOrDefault(c.RunDaemonSet.Namespace, overrideNS),
Verb: "create",
Group: "",
Version: "",
Resource: "pods",
Subresource: "",
Name: "",
},
NonResourceAttributes: nil,
})
} else if c.Exec != nil {
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
@@ -527,6 +571,10 @@ func (c *Collect) GetName() string {
collector = "run-pod"
name = c.RunPod.CollectorName
}
if c.RunDaemonSet != nil {
collector = "run-daemonset"
name = c.RunDaemonSet.CollectorName
}
if c.Exec != nil {
collector = "exec"
name = c.Exec.CollectorName
@@ -180,8 +180,14 @@ type HostServices struct {
type HostRun struct {
HostCollectorMeta `json:",inline" yaml:",inline"`
Command string `json:"command"`
Args []string `json:"args"`
Command string `json:"command"`
Args []string `json:"args"`
OutputDir string `json:"outputDir,omitempty" yaml:"outputDir,omitempty"`
Input map[string]string `json:"input,omitempty" yaml:"input,omitempty"`
Env []string `json:"env,omitempty" yaml:"env,omitempty"`
InheritEnvs []string `json:"inheritEnvs,omitempty" yaml:"inheritEnvs,omitempty"`
IgnoreParentEnvs bool `json:"ignoreParentEnvs,omitempty" yaml:"ignoreParentEnvs,omitempty"`
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
}
type HostCollect struct {
@@ -169,6 +169,11 @@ func (in *Analyze) DeepCopyInto(out *Analyze) {
*out = new(CephStatusAnalyze)
(*in).DeepCopyInto(*out)
}
if in.Velero != nil {
in, out := &in.Velero, &out.Velero
*out = new(VeleroAnalyze)
(*in).DeepCopyInto(*out)
}
if in.Longhorn != nil {
in, out := &in.Longhorn, &out.Longhorn
*out = new(LonghornAnalyze)
@@ -199,6 +204,16 @@ func (in *Analyze) DeepCopyInto(out *Analyze) {
*out = new(CertificatesAnalyze)
(*in).DeepCopyInto(*out)
}
if in.Goldpinger != nil {
in, out := &in.Goldpinger, &out.Goldpinger
*out = new(GoldpingerAnalyze)
(*in).DeepCopyInto(*out)
}
if in.Event != nil {
in, out := &in.Event, &out.Event
*out = new(EventAnalyze)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Analyze.
@@ -807,6 +822,11 @@ func (in *Collect) DeepCopyInto(out *Collect) {
*out = new(RunPod)
(*in).DeepCopyInto(*out)
}
if in.RunDaemonSet != nil {
in, out := &in.RunDaemonSet, &out.RunDaemonSet
*out = new(RunDaemonSet)
(*in).DeepCopyInto(*out)
}
if in.Exec != nil {
in, out := &in.Exec, &out.Exec
*out = new(Exec)
@@ -887,6 +907,16 @@ func (in *Collect) DeepCopyInto(out *Collect) {
*out = new(Helm)
(*in).DeepCopyInto(*out)
}
if in.Goldpinger != nil {
in, out := &in.Goldpinger, &out.Goldpinger
*out = new(Goldpinger)
(*in).DeepCopyInto(*out)
}
if in.Sonobuoy != nil {
in, out := &in.Sonobuoy, &out.Sonobuoy
*out = new(Sonobuoy)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Collect.
@@ -1387,6 +1417,33 @@ func (in *Distribution) DeepCopy() *Distribution {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EventAnalyze) DeepCopyInto(out *EventAnalyze) {
*out = *in
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(Outcome)
(*in).DeepCopyInto(*out)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventAnalyze.
func (in *EventAnalyze) DeepCopy() *EventAnalyze {
if in == nil {
return nil
}
out := new(EventAnalyze)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Exec) DeepCopyInto(out *Exec) {
*out = *in
@@ -1503,6 +1560,54 @@ func (in *Get) DeepCopy() *Get {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Goldpinger) DeepCopyInto(out *Goldpinger) {
*out = *in
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
if in.PodLaunchOptions != nil {
in, out := &in.PodLaunchOptions, &out.PodLaunchOptions
*out = new(PodLaunchOptions)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Goldpinger.
func (in *Goldpinger) DeepCopy() *Goldpinger {
if in == nil {
return nil
}
out := new(Goldpinger)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GoldpingerAnalyze) DeepCopyInto(out *GoldpingerAnalyze) {
*out = *in
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
if in.Outcomes != nil {
in, out := &in.Outcomes, &out.Outcomes
*out = make([]*Outcome, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(Outcome)
(*in).DeepCopyInto(*out)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GoldpingerAnalyze.
func (in *GoldpingerAnalyze) DeepCopy() *GoldpingerAnalyze {
if in == nil {
return nil
}
out := new(GoldpingerAnalyze)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTP) DeepCopyInto(out *HTTP) {
*out = *in
@@ -2302,6 +2407,23 @@ func (in *HostRun) DeepCopyInto(out *HostRun) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Input != nil {
in, out := &in.Input, &out.Input
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Env != nil {
in, out := &in.Env, &out.Env
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.InheritEnvs != nil {
in, out := &in.InheritEnvs, &out.InheritEnvs
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostRun.
@@ -2967,6 +3089,26 @@ func (in *Outcome) DeepCopy() *Outcome {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodLaunchOptions) DeepCopyInto(out *PodLaunchOptions) {
*out = *in
if in.ImagePullSecret != nil {
in, out := &in.ImagePullSecret, &out.ImagePullSecret
*out = new(ImagePullSecrets)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodLaunchOptions.
func (in *PodLaunchOptions) DeepCopy() *PodLaunchOptions {
if in == nil {
return nil
}
out := new(PodLaunchOptions)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Post) DeepCopyInto(out *Post) {
*out = *in
@@ -3978,6 +4120,28 @@ func (in *Run) DeepCopy() *Run {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RunDaemonSet) DeepCopyInto(out *RunDaemonSet) {
*out = *in
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
if in.ImagePullSecret != nil {
in, out := &in.ImagePullSecret, &out.ImagePullSecret
*out = new(ImagePullSecrets)
(*in).DeepCopyInto(*out)
}
in.PodSpec.DeepCopyInto(&out.PodSpec)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunDaemonSet.
func (in *RunDaemonSet) DeepCopy() *RunDaemonSet {
if in == nil {
return nil
}
out := new(RunDaemonSet)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RunPod) DeepCopyInto(out *RunPod) {
*out = *in
@@ -4036,6 +4200,22 @@ func (in *SingleOutcome) DeepCopy() *SingleOutcome {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Sonobuoy) DeepCopyInto(out *Sonobuoy) {
*out = *in
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sonobuoy.
func (in *Sonobuoy) DeepCopy() *Sonobuoy {
if in == nil {
return nil
}
out := new(Sonobuoy)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *StatefulsetStatus) DeepCopyInto(out *StatefulsetStatus) {
*out = *in
@@ -4654,6 +4834,22 @@ func (in *UDPPortStatusAnalyze) DeepCopy() *UDPPortStatusAnalyze {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VeleroAnalyze) DeepCopyInto(out *VeleroAnalyze) {
*out = *in
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VeleroAnalyze.
func (in *VeleroAnalyze) DeepCopy() *VeleroAnalyze {
if in == nil {
return nil
}
out := new(VeleroAnalyze)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WeaveReportAnalyze) DeepCopyInto(out *WeaveReportAnalyze) {
*out = *in
+33 -11
View File
@@ -19,6 +19,7 @@ package troubleshootclientset
import (
"fmt"
"net/http"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2"
@@ -33,8 +34,7 @@ type Interface interface {
TroubleshootV1beta2() troubleshootv1beta2.TroubleshootV1beta2Interface
}
// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
// Clientset contains the clients for groups.
type Clientset struct {
*discovery.DiscoveryClient
troubleshootV1beta1 *troubleshootv1beta1.TroubleshootV1beta1Client
@@ -62,26 +62,49 @@ func (c *Clientset) Discovery() discovery.DiscoveryInterface {
// NewForConfig creates a new Clientset for the given config.
// If config's RateLimiter is not set and QPS and Burst are acceptable,
// NewForConfig will generate a rate-limiter in configShallowCopy.
// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
// where httpClient was generated with rest.HTTPClientFor(c).
func NewForConfig(c *rest.Config) (*Clientset, error) {
configShallowCopy := *c
if configShallowCopy.UserAgent == "" {
configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent()
}
// share the transport between all clients
httpClient, err := rest.HTTPClientFor(&configShallowCopy)
if err != nil {
return nil, err
}
return NewForConfigAndClient(&configShallowCopy, httpClient)
}
// NewForConfigAndClient creates a new Clientset for the given config and http client.
// Note the http client provided takes precedence over the configured transport values.
// If config's RateLimiter is not set and QPS and Burst are acceptable,
// NewForConfigAndClient will generate a rate-limiter in configShallowCopy.
func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) {
configShallowCopy := *c
if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
if configShallowCopy.Burst <= 0 {
return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
}
configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
}
var cs Clientset
var err error
cs.troubleshootV1beta1, err = troubleshootv1beta1.NewForConfig(&configShallowCopy)
cs.troubleshootV1beta1, err = troubleshootv1beta1.NewForConfigAndClient(&configShallowCopy, httpClient)
if err != nil {
return nil, err
}
cs.troubleshootV1beta2, err = troubleshootv1beta2.NewForConfig(&configShallowCopy)
cs.troubleshootV1beta2, err = troubleshootv1beta2.NewForConfigAndClient(&configShallowCopy, httpClient)
if err != nil {
return nil, err
}
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient)
if err != nil {
return nil, err
}
@@ -91,12 +114,11 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
// NewForConfigOrDie creates a new Clientset for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *Clientset {
var cs Clientset
cs.troubleshootV1beta1 = troubleshootv1beta1.NewForConfigOrDie(c)
cs.troubleshootV1beta2 = troubleshootv1beta2.NewForConfigOrDie(c)
cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
return &cs
cs, err := NewForConfig(c)
if err != nil {
panic(err)
}
return cs
}
// New creates a new Clientset for the given RESTClient.
@@ -23,7 +23,6 @@ import (
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakeAnalyzers struct {
ns string
}
var analyzersResource = schema.GroupVersionResource{Group: "troubleshoot.replicated.com", Version: "v1beta1", Resource: "analyzers"}
var analyzersResource = v1beta1.SchemeGroupVersion.WithResource("analyzers")
var analyzersKind = schema.GroupVersionKind{Group: "troubleshoot.replicated.com", Version: "v1beta1", Kind: "Analyzer"}
var analyzersKind = v1beta1.SchemeGroupVersion.WithKind("Analyzer")
// Get takes name of the analyzer, and returns the corresponding analyzer object, and an error if there is any.
func (c *FakeAnalyzers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Analyzer, err error) {
@@ -116,7 +115,7 @@ func (c *FakeAnalyzers) UpdateStatus(ctx context.Context, analyzer *v1beta1.Anal
// Delete takes name of the analyzer and deletes it. Returns an error if one occurs.
func (c *FakeAnalyzers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(analyzersResource, c.ns, name), &v1beta1.Analyzer{})
Invokes(testing.NewDeleteActionWithOptions(analyzersResource, c.ns, name, opts), &v1beta1.Analyzer{})
return err
}
@@ -23,7 +23,6 @@ import (
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakeCollectors struct {
ns string
}
var collectorsResource = schema.GroupVersionResource{Group: "troubleshoot.replicated.com", Version: "v1beta1", Resource: "collectors"}
var collectorsResource = v1beta1.SchemeGroupVersion.WithResource("collectors")
var collectorsKind = schema.GroupVersionKind{Group: "troubleshoot.replicated.com", Version: "v1beta1", Kind: "Collector"}
var collectorsKind = v1beta1.SchemeGroupVersion.WithKind("Collector")
// Get takes name of the collector, and returns the corresponding collector object, and an error if there is any.
func (c *FakeCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Collector, err error) {
@@ -116,7 +115,7 @@ func (c *FakeCollectors) UpdateStatus(ctx context.Context, collector *v1beta1.Co
// Delete takes name of the collector and deletes it. Returns an error if one occurs.
func (c *FakeCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(collectorsResource, c.ns, name), &v1beta1.Collector{})
Invokes(testing.NewDeleteActionWithOptions(collectorsResource, c.ns, name, opts), &v1beta1.Collector{})
return err
}
@@ -23,7 +23,6 @@ import (
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakePreflights struct {
ns string
}
var preflightsResource = schema.GroupVersionResource{Group: "troubleshoot.replicated.com", Version: "v1beta1", Resource: "preflights"}
var preflightsResource = v1beta1.SchemeGroupVersion.WithResource("preflights")
var preflightsKind = schema.GroupVersionKind{Group: "troubleshoot.replicated.com", Version: "v1beta1", Kind: "Preflight"}
var preflightsKind = v1beta1.SchemeGroupVersion.WithKind("Preflight")
// Get takes name of the preflight, and returns the corresponding preflight object, and an error if there is any.
func (c *FakePreflights) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Preflight, err error) {
@@ -116,7 +115,7 @@ func (c *FakePreflights) UpdateStatus(ctx context.Context, preflight *v1beta1.Pr
// Delete takes name of the preflight and deletes it. Returns an error if one occurs.
func (c *FakePreflights) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(preflightsResource, c.ns, name), &v1beta1.Preflight{})
Invokes(testing.NewDeleteActionWithOptions(preflightsResource, c.ns, name, opts), &v1beta1.Preflight{})
return err
}
@@ -23,7 +23,6 @@ import (
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakeRedactors struct {
ns string
}
var redactorsResource = schema.GroupVersionResource{Group: "troubleshoot.replicated.com", Version: "v1beta1", Resource: "redactors"}
var redactorsResource = v1beta1.SchemeGroupVersion.WithResource("redactors")
var redactorsKind = schema.GroupVersionKind{Group: "troubleshoot.replicated.com", Version: "v1beta1", Kind: "Redactor"}
var redactorsKind = v1beta1.SchemeGroupVersion.WithKind("Redactor")
// Get takes name of the redactor, and returns the corresponding redactor object, and an error if there is any.
func (c *FakeRedactors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Redactor, err error) {
@@ -116,7 +115,7 @@ func (c *FakeRedactors) UpdateStatus(ctx context.Context, redactor *v1beta1.Reda
// Delete takes name of the redactor and deletes it. Returns an error if one occurs.
func (c *FakeRedactors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(redactorsResource, c.ns, name), &v1beta1.Redactor{})
Invokes(testing.NewDeleteActionWithOptions(redactorsResource, c.ns, name, opts), &v1beta1.Redactor{})
return err
}
@@ -23,7 +23,6 @@ import (
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakeSupportBundles struct {
ns string
}
var supportbundlesResource = schema.GroupVersionResource{Group: "troubleshoot.replicated.com", Version: "v1beta1", Resource: "supportbundles"}
var supportbundlesResource = v1beta1.SchemeGroupVersion.WithResource("supportbundles")
var supportbundlesKind = schema.GroupVersionKind{Group: "troubleshoot.replicated.com", Version: "v1beta1", Kind: "SupportBundle"}
var supportbundlesKind = v1beta1.SchemeGroupVersion.WithKind("SupportBundle")
// Get takes name of the supportBundle, and returns the corresponding supportBundle object, and an error if there is any.
func (c *FakeSupportBundles) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.SupportBundle, err error) {
@@ -116,7 +115,7 @@ func (c *FakeSupportBundles) UpdateStatus(ctx context.Context, supportBundle *v1
// Delete takes name of the supportBundle and deletes it. Returns an error if one occurs.
func (c *FakeSupportBundles) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(supportbundlesResource, c.ns, name), &v1beta1.SupportBundle{})
Invokes(testing.NewDeleteActionWithOptions(supportbundlesResource, c.ns, name, opts), &v1beta1.SupportBundle{})
return err
}
@@ -18,6 +18,8 @@ limitations under the License.
package v1beta1
import (
"net/http"
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
"github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme"
rest "k8s.io/client-go/rest"
@@ -58,12 +60,28 @@ func (c *TroubleshootV1beta1Client) SupportBundles(namespace string) SupportBund
}
// NewForConfig creates a new TroubleshootV1beta1Client for the given config.
// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
// where httpClient was generated with rest.HTTPClientFor(c).
func NewForConfig(c *rest.Config) (*TroubleshootV1beta1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientFor(&config)
httpClient, err := rest.HTTPClientFor(&config)
if err != nil {
return nil, err
}
return NewForConfigAndClient(&config, httpClient)
}
// NewForConfigAndClient creates a new TroubleshootV1beta1Client for the given config and http client.
// Note the http client provided takes precedence over the configured transport values.
func NewForConfigAndClient(c *rest.Config, h *http.Client) (*TroubleshootV1beta1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientForConfigAndClient(&config, h)
if err != nil {
return nil, err
}
@@ -23,7 +23,6 @@ import (
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakeAnalyzers struct {
ns string
}
var analyzersResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "analyzers"}
var analyzersResource = v1beta2.SchemeGroupVersion.WithResource("analyzers")
var analyzersKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "Analyzer"}
var analyzersKind = v1beta2.SchemeGroupVersion.WithKind("Analyzer")
// Get takes name of the analyzer, and returns the corresponding analyzer object, and an error if there is any.
func (c *FakeAnalyzers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.Analyzer, err error) {
@@ -116,7 +115,7 @@ func (c *FakeAnalyzers) UpdateStatus(ctx context.Context, analyzer *v1beta2.Anal
// Delete takes name of the analyzer and deletes it. Returns an error if one occurs.
func (c *FakeAnalyzers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(analyzersResource, c.ns, name), &v1beta2.Analyzer{})
Invokes(testing.NewDeleteActionWithOptions(analyzersResource, c.ns, name, opts), &v1beta2.Analyzer{})
return err
}
@@ -23,7 +23,6 @@ import (
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakeCollectors struct {
ns string
}
var collectorsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "collectors"}
var collectorsResource = v1beta2.SchemeGroupVersion.WithResource("collectors")
var collectorsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "Collector"}
var collectorsKind = v1beta2.SchemeGroupVersion.WithKind("Collector")
// Get takes name of the collector, and returns the corresponding collector object, and an error if there is any.
func (c *FakeCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.Collector, err error) {
@@ -116,7 +115,7 @@ func (c *FakeCollectors) UpdateStatus(ctx context.Context, collector *v1beta2.Co
// Delete takes name of the collector and deletes it. Returns an error if one occurs.
func (c *FakeCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(collectorsResource, c.ns, name), &v1beta2.Collector{})
Invokes(testing.NewDeleteActionWithOptions(collectorsResource, c.ns, name, opts), &v1beta2.Collector{})
return err
}
@@ -23,7 +23,6 @@ import (
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakeHostCollectors struct {
ns string
}
var hostcollectorsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "hostcollectors"}
var hostcollectorsResource = v1beta2.SchemeGroupVersion.WithResource("hostcollectors")
var hostcollectorsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "HostCollector"}
var hostcollectorsKind = v1beta2.SchemeGroupVersion.WithKind("HostCollector")
// Get takes name of the hostCollector, and returns the corresponding hostCollector object, and an error if there is any.
func (c *FakeHostCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.HostCollector, err error) {
@@ -116,7 +115,7 @@ func (c *FakeHostCollectors) UpdateStatus(ctx context.Context, hostCollector *v1
// Delete takes name of the hostCollector and deletes it. Returns an error if one occurs.
func (c *FakeHostCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(hostcollectorsResource, c.ns, name), &v1beta2.HostCollector{})
Invokes(testing.NewDeleteActionWithOptions(hostcollectorsResource, c.ns, name, opts), &v1beta2.HostCollector{})
return err
}
@@ -23,7 +23,6 @@ import (
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakeHostPreflights struct {
ns string
}
var hostpreflightsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "hostpreflights"}
var hostpreflightsResource = v1beta2.SchemeGroupVersion.WithResource("hostpreflights")
var hostpreflightsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "HostPreflight"}
var hostpreflightsKind = v1beta2.SchemeGroupVersion.WithKind("HostPreflight")
// Get takes name of the hostPreflight, and returns the corresponding hostPreflight object, and an error if there is any.
func (c *FakeHostPreflights) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.HostPreflight, err error) {
@@ -116,7 +115,7 @@ func (c *FakeHostPreflights) UpdateStatus(ctx context.Context, hostPreflight *v1
// Delete takes name of the hostPreflight and deletes it. Returns an error if one occurs.
func (c *FakeHostPreflights) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(hostpreflightsResource, c.ns, name), &v1beta2.HostPreflight{})
Invokes(testing.NewDeleteActionWithOptions(hostpreflightsResource, c.ns, name, opts), &v1beta2.HostPreflight{})
return err
}
@@ -23,7 +23,6 @@ import (
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakePreflights struct {
ns string
}
var preflightsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "preflights"}
var preflightsResource = v1beta2.SchemeGroupVersion.WithResource("preflights")
var preflightsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "Preflight"}
var preflightsKind = v1beta2.SchemeGroupVersion.WithKind("Preflight")
// Get takes name of the preflight, and returns the corresponding preflight object, and an error if there is any.
func (c *FakePreflights) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.Preflight, err error) {
@@ -116,7 +115,7 @@ func (c *FakePreflights) UpdateStatus(ctx context.Context, preflight *v1beta2.Pr
// Delete takes name of the preflight and deletes it. Returns an error if one occurs.
func (c *FakePreflights) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(preflightsResource, c.ns, name), &v1beta2.Preflight{})
Invokes(testing.NewDeleteActionWithOptions(preflightsResource, c.ns, name, opts), &v1beta2.Preflight{})
return err
}
@@ -23,7 +23,6 @@ import (
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakeRedactors struct {
ns string
}
var redactorsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "redactors"}
var redactorsResource = v1beta2.SchemeGroupVersion.WithResource("redactors")
var redactorsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "Redactor"}
var redactorsKind = v1beta2.SchemeGroupVersion.WithKind("Redactor")
// Get takes name of the redactor, and returns the corresponding redactor object, and an error if there is any.
func (c *FakeRedactors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.Redactor, err error) {
@@ -116,7 +115,7 @@ func (c *FakeRedactors) UpdateStatus(ctx context.Context, redactor *v1beta2.Reda
// Delete takes name of the redactor and deletes it. Returns an error if one occurs.
func (c *FakeRedactors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(redactorsResource, c.ns, name), &v1beta2.Redactor{})
Invokes(testing.NewDeleteActionWithOptions(redactorsResource, c.ns, name, opts), &v1beta2.Redactor{})
return err
}
@@ -23,7 +23,6 @@ import (
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakeRemoteCollectors struct {
ns string
}
var remotecollectorsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "remotecollectors"}
var remotecollectorsResource = v1beta2.SchemeGroupVersion.WithResource("remotecollectors")
var remotecollectorsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "RemoteCollector"}
var remotecollectorsKind = v1beta2.SchemeGroupVersion.WithKind("RemoteCollector")
// Get takes name of the remoteCollector, and returns the corresponding remoteCollector object, and an error if there is any.
func (c *FakeRemoteCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.RemoteCollector, err error) {
@@ -116,7 +115,7 @@ func (c *FakeRemoteCollectors) UpdateStatus(ctx context.Context, remoteCollector
// Delete takes name of the remoteCollector and deletes it. Returns an error if one occurs.
func (c *FakeRemoteCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(remotecollectorsResource, c.ns, name), &v1beta2.RemoteCollector{})
Invokes(testing.NewDeleteActionWithOptions(remotecollectorsResource, c.ns, name, opts), &v1beta2.RemoteCollector{})
return err
}
@@ -23,7 +23,6 @@ import (
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
@@ -35,9 +34,9 @@ type FakeSupportBundles struct {
ns string
}
var supportbundlesResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "supportbundles"}
var supportbundlesResource = v1beta2.SchemeGroupVersion.WithResource("supportbundles")
var supportbundlesKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "SupportBundle"}
var supportbundlesKind = v1beta2.SchemeGroupVersion.WithKind("SupportBundle")
// Get takes name of the supportBundle, and returns the corresponding supportBundle object, and an error if there is any.
func (c *FakeSupportBundles) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.SupportBundle, err error) {
@@ -116,7 +115,7 @@ func (c *FakeSupportBundles) UpdateStatus(ctx context.Context, supportBundle *v1
// Delete takes name of the supportBundle and deletes it. Returns an error if one occurs.
func (c *FakeSupportBundles) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(supportbundlesResource, c.ns, name), &v1beta2.SupportBundle{})
Invokes(testing.NewDeleteActionWithOptions(supportbundlesResource, c.ns, name, opts), &v1beta2.SupportBundle{})
return err
}
@@ -18,6 +18,8 @@ limitations under the License.
package v1beta2
import (
"net/http"
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme"
rest "k8s.io/client-go/rest"
@@ -73,12 +75,28 @@ func (c *TroubleshootV1beta2Client) SupportBundles(namespace string) SupportBund
}
// NewForConfig creates a new TroubleshootV1beta2Client for the given config.
// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
// where httpClient was generated with rest.HTTPClientFor(c).
func NewForConfig(c *rest.Config) (*TroubleshootV1beta2Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientFor(&config)
httpClient, err := rest.HTTPClientFor(&config)
if err != nil {
return nil, err
}
return NewForConfigAndClient(&config, httpClient)
}
// NewForConfigAndClient creates a new TroubleshootV1beta2Client for the given config and http client.
// Note the http client provided takes precedence over the configured transport values.
func NewForConfigAndClient(c *rest.Config, h *http.Client) (*TroubleshootV1beta2Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientForConfigAndClient(&config, h)
if err != nil {
return nil, err
}
+33 -7
View File
@@ -35,6 +35,12 @@ var CephCommands = []CephCommand{
Format: "json",
DefaultTimeout: "30s",
},
{
ID: "status-txt",
Command: []string{"ceph", "status"},
Format: "txt",
DefaultTimeout: "30s",
},
{
ID: "fs",
Command: []string{"ceph", "fs", "status"},
@@ -42,6 +48,12 @@ var CephCommands = []CephCommand{
Format: "json",
DefaultTimeout: "30s",
},
{
ID: "fs-txt",
Command: []string{"ceph", "fs", "status"},
Format: "txt",
DefaultTimeout: "30s",
},
{
ID: "fs-ls",
Command: []string{"ceph", "fs", "ls"},
@@ -53,7 +65,7 @@ var CephCommands = []CephCommand{
ID: "osd-status",
Command: []string{"ceph", "osd", "status"},
Args: []string{"-f", "json-pretty"},
Format: "txt",
Format: "json",
DefaultTimeout: "30s",
},
{
@@ -88,15 +100,17 @@ var CephCommands = []CephCommand{
ID: "rgw-stats", // the disk usage (and other stats) of each object store bucket
Command: []string{"radosgw-admin", "bucket", "stats"},
Args: []string{"--rgw-cache-enabled=false"},
Format: "txt",
Format: "json",
DefaultTimeout: "30s", // include a default timeout because this command will hang if the RGW daemon isn't running/is unhealthy
},
{
ID: "rbd-du", // the disk usage of each PVC
Command: []string{"rbd", "du"},
Args: []string{"--pool=replicapool"},
Format: "txt",
DefaultTimeout: "30s",
ID: "rbd-du-txt", // the disk usage of each PVC
Command: []string{"rbd", "du"},
Args: []string{"--pool=replicapool"},
Format: "txt",
// On ceph clusters with a lot of data, this command can take a long time to run
// especially if fast-diff rbd_default_features is disabled
DefaultTimeout: "60s",
},
{
ID: "df", // the disk usage of each pool
@@ -105,6 +119,12 @@ var CephCommands = []CephCommand{
Format: "json",
DefaultTimeout: "30s",
},
{
ID: "df-txt", // the disk usage of each pool
Command: []string{"ceph", "df"},
Format: "txt",
DefaultTimeout: "30s",
},
{
ID: "osd-df",
Command: []string{"ceph", "osd", "df"},
@@ -112,6 +132,12 @@ var CephCommands = []CephCommand{
Format: "json",
DefaultTimeout: "30s",
},
{
ID: "osd-df-txt",
Command: []string{"ceph", "osd", "df"},
Format: "txt",
DefaultTimeout: "30s",
},
}
type CollectCeph struct {
+25 -27
View File
@@ -91,25 +91,23 @@ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
+OkuE6N36B9K
-----END CERTIFICATE-----`,
"validCert": `-----BEGIN CERTIFICATE-----
MIIDejCCAmKgAwIBAgIEAZaq0DANBgkqhkiG9w0BAQsFADAuMRgwFgYDVQQDEw9Q
cm9qZWN0IENvbnRvdXIxEjAQBgNVBAUTCTYxNTkyOTg5MTAeFw0yMzAyMjQwNDI3
MThaFw0yNDAyMjUwNDI3MTZaMBAxDjAMBgNVBAMTBWVudm95MIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqsNmmxb1ICso6Ay25lapcRyvLxAX/5u422uV
eiNn5jseCVXfg1jJr2Symrgou2dgMtIpZoVKT7w0el8sNpD+az5oMOWUNfTGEpYI
5zNAhtiedxCWcX15gfezOZ/DECL7HP8U37JFVdazm0CjvlQWI+8rFGvFQJJFDLYJ
h0fHGfbX6L/ST6cANtkXZyIU6CYFSgniuuDjHmQnQr6CC8lkisJxY5QVS7MZ02RR
nU/dK14ABY+mo/0ZeBKR5si04hr4i18nJJnk4DnHN+jQ/WWWSO1yLqr9kAOj37dA
nRAeKuzkx/VbU8DC/3sh0otcazoWO470D+irOy1is0whDArLNwIDAQABo4G9MIG6
MA4GA1UdDwEB/wQEAwIE8DAdBgNVHQ4EFgQUbAavOY+vIXgc44k8GvLHH6mdzzYw
HwYDVR0jBBgwFoAUb+S3Mu7cbwZiqZaEHTEMyUhLyH4waAYDVR0RBGEwX4IFZW52
b3mCFGVudm95LnByb2plY3Rjb250b3VyghhlbnZveS5wcm9qZWN0Y29udG91ci5z
dmOCJmVudm95LnByb2plY3Rjb250b3VyLnN2Yy5jbHVzdGVyLmxvY2FsMA0GCSqG
SIb3DQEBCwUAA4IBAQBIKpBD1T9tugzJF7lajbdulXTb9qGibwQALqauskX9Sq57
po/R2TjyxywLn4DgM7BAzzu9qfHWf+S4eQjRUHQshPbUEX9CEsSd5tCu8ZHVbBds
6qFagl2+YQ9ng0Xwta9ezvctM3T6Dy9Kkf5OOe9ysMEsBX7s8NFxe68Qku+cExr3
78oERlIoNOlT0cNbFLAlH2svNv1uB4qOThRDha52L+mlUdZfTMYZAwNDJWm52t/M
NCIm5NJ5jAJpcJmoEb+JMP3j0x6wydHDXFtGm3WRggZRcrjasyodSKK6szbf96+9
6syzAwvg9xxNtFxwbhRqqplMEz2sDWaggTrxCQzd
MIIDEjCCAfqgAwIBAgIUWG+wobb7huLDKQ4bUn80QKsbVSQwDQYJKoZIhvcNAQEL
BQAwGDEWMBQGA1UEAwwNRXhhbXBsZVNlcnZlcjAeFw0yNDAyMjYyMjA5MjdaFw0y
OTAyMjQyMjA5MjdaMBgxFjAUBgNVBAMMDUV4YW1wbGVTZXJ2ZXIwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsb9MSNERE7l4b/vxzkRnL6qvkjodbY3yQ
DDCbG29jJ71wSwYoz0v4n/Q/tDaJDBUcSjSPIa+L6Gn1BGLNUD2LFec5H5XlwcDq
A0D8qzvPLMYaIw4p04M/2Wvkqme79yQta8jXaCnPxTkgysno/FuQR+nDMqXoyW01
wMmj8JAYFs9MVPbmP97RqbVHnn7cydzvQvi4+dfBuII9L+TvTQY2o42bbCqCcmov
16m0uXp98j4CFfv/8KhCmQcqS0uyvp+J/iJZzEow+1NP72pozPEPv9JhXsYDb+vH
KxX5k44fYXVYVQHxYQllLjDDRcbz2DJh2DQXMZTiTLE63tC0m2RjAgMBAAGjVDBS
MDEGA1UdEQQqMCiCCGV4YW1wbGUxgghleGFtcGxlMoIIZXhhbXBsZTOCCGV4YW1w
bGU0MB0GA1UdDgQWBBTEkowqyNS1ez5XJ0shro/18UQlqTANBgkqhkiG9w0BAQsF
AAOCAQEANhnxV81NZmt8FcgrcRt73WdKDCE0hQbci65ZkAbcTVOCAs6Rw+aEC04L
0gKJT4Zmx3kR3fUwcDQRhjoXIrhmvP59k/8SN0N+Ua50oEYikaetsxEDr8oikwP2
eHDKU451jZZfcyII2Jx8XjgtiExcNZL3N9ZtSstp3I9n9BSmMdQHDIvPxNeGdJri
l4YGTm+sJLs+c5efSNaEPw1Dr6bhAPamNQwvIspuASJlCTcMCLw2+F3dEolAFUuM
E5wI9498c+P3pvl85UfiQISTu95jOl7YmZOYl8xBpu9yhQ7kjWhVRdVAnhl9Ts9F
3TD68rCMXegJYyg5VbiwVdSPkcw3eQ==
-----END CERTIFICATE-----`,
"nonCert": `-----BEGIN CERTIFICATE-----
Oy1is0whDArLNwIDAQABo4G9MIG6
@@ -248,16 +246,16 @@ func TestCertParser(t *testing.T) {
CertificateChain: []ParsedCertificate{
{
CertName: "tls.crt",
Subject: "CN=envoy",
Subject: "CN=ExampleServer",
SubjectAlternativeNames: []string{
"envoy",
"envoy.projectcontour",
"envoy.projectcontour.svc",
"envoy.projectcontour.svc.cluster.local",
"example1",
"example2",
"example3",
"example4",
},
Issuer: "SERIALNUMBER=615929891,CN=Project Contour",
NotAfter: time.Date(2024, time.February, 25, 4, 27, 16, 0, time.UTC),
NotBefore: time.Date(2023, time.February, 24, 4, 27, 18, 0, time.UTC),
Issuer: "CN=ExampleServer",
NotAfter: time.Date(2029, time.February, 24, 22, 9, 27, 0, time.UTC),
NotBefore: time.Date(2024, time.February, 26, 22, 9, 27, 0, time.UTC),
IsValid: true,
IsCA: false,
},
+8 -6
View File
@@ -120,19 +120,16 @@ func (c *CollectClusterResources) Collect(progressChan chan<- interface{}) (Coll
var namespaceNames []string
if len(c.Collector.Namespaces) > 0 {
namespaces, namespaceErrors := getNamespaces(ctx, client, c.Collector.Namespaces)
klog.V(4).Infof("checking for namespaces access: %s", string(namespaces))
namespaceNames = c.Collector.Namespaces
output.SaveResult(c.BundlePath, path.Join(constants.CLUSTER_RESOURCES_DIR, fmt.Sprintf("%s.json", constants.CLUSTER_RESOURCES_NAMESPACES)), bytes.NewBuffer(namespaces))
output.SaveResult(c.BundlePath, path.Join(constants.CLUSTER_RESOURCES_DIR, fmt.Sprintf("%s-errors.json", constants.CLUSTER_RESOURCES_NAMESPACES)), marshalErrors(namespaceErrors))
} else if c.Namespace != "" {
namespace, namespaceErrors := getNamespace(ctx, client, c.Namespace)
klog.V(4).Infof("checking for namespace access: %s", string(namespace))
output.SaveResult(c.BundlePath, path.Join(constants.CLUSTER_RESOURCES_DIR, fmt.Sprintf("%s.json", constants.CLUSTER_RESOURCES_NAMESPACES)), bytes.NewBuffer(namespace))
output.SaveResult(c.BundlePath, path.Join(constants.CLUSTER_RESOURCES_DIR, fmt.Sprintf("%s-errors.json", constants.CLUSTER_RESOURCES_NAMESPACES)), marshalErrors(namespaceErrors))
namespaceNames = append(namespaceNames, c.Namespace)
} else {
namespaces, namespaceList, namespaceErrors := getAllNamespaces(ctx, client)
klog.V(4).Infof("checking for all namespaces access: %s", string(namespaces))
output.SaveResult(c.BundlePath, path.Join(constants.CLUSTER_RESOURCES_DIR, fmt.Sprintf("%s.json", constants.CLUSTER_RESOURCES_NAMESPACES)), bytes.NewBuffer(namespaces))
output.SaveResult(c.BundlePath, path.Join(constants.CLUSTER_RESOURCES_DIR, fmt.Sprintf("%s-errors.json", constants.CLUSTER_RESOURCES_NAMESPACES)), marshalErrors(namespaceErrors))
if namespaceList != nil {
@@ -146,6 +143,7 @@ func (c *CollectClusterResources) Collect(progressChan chan<- interface{}) (Coll
reviewStatuses, reviewStatusErrors := getSelfSubjectRulesReviews(ctx, client, namespaceNames)
// auth cani
klog.V(2).Infof("checking [%s] namespaces for permissions to collect resources", strings.Join(namespaceNames, ", "))
authCanI := authCanI(reviewStatuses, namespaceNames)
for k, v := range authCanI {
output.SaveResult(c.BundlePath, path.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_AUTH_CANI, k), bytes.NewBuffer(v))
@@ -160,8 +158,12 @@ func (c *CollectClusterResources) Collect(progressChan chan<- interface{}) (Coll
filteredNamespaces = append(filteredNamespaces, ns)
}
}
if len(filteredNamespaces) != len(namespaceNames) {
klog.V(2).Infof("filtered namespaces down to [%s] after evaluating permissions", strings.Join(filteredNamespaces, ", "))
} else {
klog.V(2).Infof("no namespaces filtered out after evaluating permissions")
}
namespaceNames = filteredNamespaces
klog.V(4).Infof("filtered to namespaceNames %s", namespaceNames)
}
// pods
@@ -504,7 +506,7 @@ func pods(ctx context.Context, client *kubernetes.Clientset, namespaces []string
}
func getPodDisruptionBudgets(ctx context.Context, client *kubernetes.Clientset, namespaces []string) (map[string][]byte, map[string]string) {
ok, err := discovery.HasResource(client, "policy.k8s.io/v1", "PodDisruptionBudgets")
ok, err := discovery.HasResource(client, "policy/v1", "PodDisruptionBudgets")
if err != nil {
return nil, map[string]string{"": err.Error()}
}
@@ -799,7 +801,7 @@ func jobs(ctx context.Context, client *kubernetes.Clientset, namespaces []string
}
func cronJobs(ctx context.Context, client *kubernetes.Clientset, namespaces []string) (map[string][]byte, map[string]string) {
ok, err := discovery.HasResource(client, "batch.k8s.io/v1", "CronJobs")
ok, err := discovery.HasResource(client, "batch/v1", "CronJobs")
if err != nil {
return nil, map[string]string{"": err.Error()}
}
+13
View File
@@ -75,6 +75,8 @@ func GetCollector(collector *troubleshootv1beta2.Collect, bundlePath string, nam
return &CollectRun{collector.Run, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.RunPod != nil:
return &CollectRunPod{collector.RunPod, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.RunDaemonSet != nil:
return &CollectRunDaemonSet{collector.RunDaemonSet, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.Exec != nil:
return &CollectExec{collector.Exec, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.Data != nil:
@@ -116,6 +118,10 @@ func GetCollector(collector *troubleshootv1beta2.Collect, bundlePath string, nam
return &CollectCertificates{collector.Certificates, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.Helm != nil:
return &CollectHelm{collector.Helm, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.Goldpinger != nil:
return &CollectGoldpinger{collector.Goldpinger, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.Sonobuoy != nil:
return &CollectSonobuoyResults{collector.Sonobuoy, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
default:
return nil, false
}
@@ -150,6 +156,9 @@ func getCollectorName(c interface{}) string {
case *CollectRunPod:
collector = "run-pod"
name = v.Collector.CollectorName
case *CollectRunDaemonSet:
collector = "run-daemonset"
name = v.Collector.CollectorName
case *CollectExec:
collector = "exec"
name = v.Collector.CollectorName
@@ -198,6 +207,10 @@ func getCollectorName(c interface{}) string {
collector = "certificates"
case *CollectHelm:
collector = "helm"
case *CollectGoldpinger:
collector = "goldpinger"
case *CollectSonobuoyResults:
collector = "sonobuoy"
default:
collector = "<none>"
}
+8 -5
View File
@@ -47,6 +47,7 @@ func (c *CollectExec) Collect(progressChan chan<- interface{}) (CollectorResult,
errCh := make(chan error, 1)
resultCh := make(chan CollectorResult, 1)
// TODO: Use a context with timeout instead of a goroutine
go func() {
b, err := execWithoutTimeout(c.ClientConfig, c.BundlePath, c.Collector)
if err != nil {
@@ -78,12 +79,12 @@ func execWithoutTimeout(clientConfig *rest.Config, bundlePath string, execCollec
pods, podsErrors := listPodsInSelectors(ctx, client, execCollector.Namespace, execCollector.Selector)
if len(podsErrors) > 0 {
output.SaveResult(bundlePath, getExecErrosFileName(execCollector), marshalErrors(podsErrors))
output.SaveResult(bundlePath, getExecErrorsFileName(execCollector), marshalErrors(podsErrors))
}
if len(pods) > 0 {
for _, pod := range pods {
stdout, stderr, execErrors := getExecOutputs(clientConfig, client, pod, execCollector)
stdout, stderr, execErrors := getExecOutputs(ctx, clientConfig, client, pod, execCollector)
path := filepath.Join(execCollector.Name, pod.Namespace, pod.Name)
if len(stdout) > 0 {
@@ -103,7 +104,9 @@ func execWithoutTimeout(clientConfig *rest.Config, bundlePath string, execCollec
return output, nil
}
func getExecOutputs(clientConfig *rest.Config, client *kubernetes.Clientset, pod corev1.Pod, execCollector *troubleshootv1beta2.Exec) ([]byte, []byte, []string) {
func getExecOutputs(
ctx context.Context, clientConfig *rest.Config, client *kubernetes.Clientset, pod corev1.Pod, execCollector *troubleshootv1beta2.Exec,
) ([]byte, []byte, []string) {
container := pod.Spec.Containers[0].Name
if execCollector.ContainerName != "" {
container = execCollector.ContainerName
@@ -133,7 +136,7 @@ func getExecOutputs(clientConfig *rest.Config, client *kubernetes.Clientset, pod
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
err = exec.Stream(remotecommand.StreamOptions{
err = exec.StreamWithContext(ctx, remotecommand.StreamOptions{
Stdin: nil,
Stdout: stdout,
Stderr: stderr,
@@ -147,7 +150,7 @@ func getExecOutputs(clientConfig *rest.Config, client *kubernetes.Clientset, pod
return stdout.Bytes(), stderr.Bytes(), nil
}
func getExecErrosFileName(execCollector *troubleshootv1beta2.Exec) string {
func getExecErrorsFileName(execCollector *troubleshootv1beta2.Exec) string {
if len(execCollector.Name) > 0 {
return fmt.Sprintf("%s-errors.json", execCollector.Name)
}
+214
View File
@@ -0,0 +1,214 @@
package collect
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"time"
"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"
)
// Collect goldpinger results from goldpinger service running in a cluster
// The results are stored in goldpinger/check_all.json since we use
// the /check_all endpoint
type CollectGoldpinger struct {
Collector *troubleshootv1beta2.Goldpinger
BundlePath string
Namespace string
ClientConfig *rest.Config
Client kubernetes.Interface
Context context.Context
RBACErrors
}
func (c *CollectGoldpinger) Title() string {
return getCollectorName(c)
}
func (c *CollectGoldpinger) IsExcluded() (bool, error) {
return isExcluded(c.Collector.Exclude)
}
func (c *CollectGoldpinger) Collect(progressChan chan<- interface{}) (CollectorResult, error) {
output := NewResult()
var results []byte
var err error
if util.IsInCluster() {
klog.V(2).Infof("Collector running in cluster, querying goldpinger endpoint straight away")
results, err = c.fetchCheckAllOutput()
if err != nil {
errMsg := fmt.Sprintf("Failed to query goldpinger endpoint in cluster: %v", err)
klog.V(2).Infof(errMsg)
err = output.SaveResult(c.BundlePath, "goldpinger/error.txt", bytes.NewBuffer([]byte(errMsg)))
return output, err
}
} else {
klog.V(2).Infof("Launch pod to query goldpinger endpoint then collect results from pod logs")
results, err = c.runPodAndCollectGPResults(progressChan)
if err != nil {
errMsg := fmt.Sprintf("Failed to run pod to collect goldpinger results: %v", err)
klog.V(2).Infof(errMsg)
err = output.SaveResult(c.BundlePath, "goldpinger/error.txt", bytes.NewBuffer([]byte(errMsg)))
return output, err
}
}
err = output.SaveResult(c.BundlePath, constants.GP_CHECK_ALL_RESULTS_PATH, bytes.NewBuffer(results))
return output, err
}
func (c *CollectGoldpinger) fetchCheckAllOutput() ([]byte, error) {
client := &http.Client{
Timeout: time.Minute, // Long enough timeout
}
req, err := http.NewRequestWithContext(c.Context, "GET", c.endpoint(), nil)
if err != nil {
return nil, err
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, string(body))
}
return body, nil
}
func (c *CollectGoldpinger) runPodAndCollectGPResults(progressChan chan<- interface{}) ([]byte, error) {
rest.InClusterConfig()
namespace := "default"
serviceAccountName := ""
image := constants.GP_DEFAULT_IMAGE
var imagePullSecret *troubleshootv1beta2.ImagePullSecrets
if c.Collector.PodLaunchOptions != nil {
if c.Collector.PodLaunchOptions.Namespace != "" {
namespace = c.Collector.PodLaunchOptions.Namespace
}
if c.Collector.PodLaunchOptions.ServiceAccountName != "" {
serviceAccountName = c.Collector.PodLaunchOptions.ServiceAccountName
if err := checkForExistingServiceAccount(c.Context, c.Client, namespace, serviceAccountName); err != nil {
return nil, err
}
}
if c.Collector.PodLaunchOptions.Image != "" {
image = c.Collector.PodLaunchOptions.Image
}
imagePullSecret = c.Collector.PodLaunchOptions.ImagePullSecret
}
runPodCollectorName := "ts-goldpinger-collector"
collectorContainerName := "collector"
runPodSpec := &troubleshootv1beta2.RunPod{
CollectorMeta: troubleshootv1beta2.CollectorMeta{
CollectorName: runPodCollectorName,
},
Name: runPodCollectorName,
Namespace: namespace,
Timeout: time.Minute.String(),
ImagePullSecret: imagePullSecret,
PodSpec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyNever,
ServiceAccountName: serviceAccountName,
Containers: []corev1.Container{
{
Image: image,
ImagePullPolicy: corev1.PullIfNotPresent,
Name: collectorContainerName,
Command: []string{"wget"},
Args: []string{"-q", "-O-", c.endpoint()},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("50m"),
corev1.ResourceMemory: resource.MustParse("64Mi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("256Mi"),
},
},
},
},
},
}
rbacErrors := c.GetRBACErrors()
// Pass an empty bundle path since we don't need to save the results
runPodCollector := &CollectRunPod{runPodSpec, "", c.Namespace, c.ClientConfig, c.Client, c.Context, rbacErrors}
output, err := runPodCollector.Collect(progressChan)
if err != nil {
return nil, err
}
// Check if the collector container exited with an error
var pod corev1.Pod
err = json.Unmarshal(output[fmt.Sprintf("%s/%s.json", runPodCollectorName, runPodCollectorName)], &pod)
if err != nil {
return nil, err
}
var terminationError *corev1.ContainerStateTerminated
for _, status := range pod.Status.ContainerStatuses {
if status.Name == collectorContainerName && status.State.Terminated != nil {
if status.State.Terminated.ExitCode != 0 {
terminationError = status.State.Terminated
}
}
}
podLogs := output[fmt.Sprintf("%s/%s.log", runPodCollectorName, runPodCollectorName)]
if terminationError != nil {
m := map[string]string{
"podName": pod.Name,
"exitCode": strconv.Itoa(int(terminationError.ExitCode)),
"reason": terminationError.Reason,
"message": terminationError.Message,
"logs": string(podLogs),
}
b, err := json.MarshalIndent(m, "", " ")
if err != nil {
return nil, err
}
return nil, errors.New(string(b))
}
return podLogs, nil
}
func (c *CollectGoldpinger) endpoint() string {
namespace := c.Collector.Namespace
if namespace == "" {
namespace = constants.GP_DEFAULT_NAMESPACE
}
return fmt.Sprintf("http://goldpinger.%s.svc.cluster.local:80/check_all", namespace)
}
+68 -23
View File
@@ -5,12 +5,11 @@ import (
"context"
"encoding/json"
"fmt"
"log"
"strconv"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"helm.sh/helm/v3/pkg/action"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
@@ -39,10 +38,11 @@ type ReleaseInfo struct {
// Helm release version information struct
type VersionInfo struct {
Revision string `json:"revision"`
Date string `json:"date"`
Status string `json:"status"`
IsPending bool `json:"isPending,omitempty"`
Revision string `json:"revision"`
Date string `json:"date"`
Status string `json:"status"`
IsPending bool `json:"isPending,omitempty"`
Values map[string]interface{} `json:"values,omitempty"`
}
func (c *CollectHelm) Title() string {
@@ -57,9 +57,15 @@ func (c *CollectHelm) Collect(progressChan chan<- interface{}) (CollectorResult,
output := NewResult()
releaseInfos, err := helmReleaseHistoryCollector(c.Collector.ReleaseName, c.Collector.Namespace)
releaseInfos, err := helmReleaseHistoryCollector(c.Collector.ReleaseName, c.Collector.Namespace, c.Collector.CollectValues)
if err != nil {
return nil, errors.Wrap(err, "failed to get Helm release history")
errsToMarhsal := []string{}
for _, e := range err {
errsToMarhsal = append(errsToMarhsal, e.Error())
}
output.SaveResult(c.BundlePath, "helm/errors.json", marshalErrors(errsToMarhsal))
klog.Errorf("error collecting helm release info: %v", err)
return output, nil
}
releaseInfoByNamespace := helmReleaseInfoByNamespaces(releaseInfos)
@@ -72,22 +78,23 @@ func (c *CollectHelm) Collect(progressChan chan<- interface{}) (CollectorResult,
}
filePath := fmt.Sprintf("helm/%s.json", namespace)
err := output.SaveResult(c.BundlePath, filePath, bytes.NewBuffer(helmHistoryJson))
if err != nil {
return nil, err
if c.Collector.ReleaseName != "" {
filePath = fmt.Sprintf("helm/%s/%s.json", namespace, c.Collector.ReleaseName)
}
output.SaveResult(c.BundlePath, filePath, bytes.NewBuffer(helmHistoryJson))
}
return output, nil
}
func helmReleaseHistoryCollector(releaseName string, namespace string) ([]ReleaseInfo, error) {
func helmReleaseHistoryCollector(releaseName string, namespace string, collectValues bool) ([]ReleaseInfo, []error) {
var results []ReleaseInfo
error_list := []error{}
actionConfig := new(action.Configuration)
if err := actionConfig.Init(nil, namespace, "", klog.V(2).Infof); err != nil {
return nil, errors.Wrap(err, "failed to initialize Helm action config")
return nil, []error{err}
}
// If releaseName is specified, get the history of that release
@@ -95,17 +102,18 @@ func helmReleaseHistoryCollector(releaseName string, namespace string) ([]Releas
getAction := action.NewGet(actionConfig)
r, err := getAction.Run(releaseName)
if err != nil {
return nil, errors.Wrapf(err, "failed to get Helm release %s", releaseName)
return nil, []error{err}
}
versionInfo, err := getVersionInfo(actionConfig, r.Name, r.Namespace, collectValues)
results = append(results, ReleaseInfo{
ReleaseName: r.Name,
Chart: r.Chart.Metadata.Name,
ChartVersion: r.Chart.Metadata.Version,
AppVersion: r.Chart.Metadata.AppVersion,
Namespace: r.Namespace,
VersionInfo: getVersionInfo(actionConfig, releaseName),
VersionInfo: versionInfo,
})
return results, nil
return results, []error{err}
}
// If releaseName is not specified, get the history of all releases
@@ -114,39 +122,64 @@ func helmReleaseHistoryCollector(releaseName string, namespace string) ([]Releas
listAction := action.NewList(actionConfig)
releases, err := listAction.Run()
if err != nil {
log.Fatalf("Failed to list Helm releases: %v", err)
return nil, []error{err}
}
for _, r := range releases {
versionInfo, err := getVersionInfo(actionConfig, r.Name, r.Namespace, collectValues)
if err != nil {
error_list = append(error_list, err)
}
results = append(results, ReleaseInfo{
ReleaseName: r.Name,
Chart: r.Chart.Metadata.Name,
ChartVersion: r.Chart.Metadata.Version,
AppVersion: r.Chart.Metadata.AppVersion,
Namespace: r.Namespace,
VersionInfo: getVersionInfo(actionConfig, r.Name),
VersionInfo: versionInfo,
})
}
if len(error_list) > 0 {
return nil, error_list
}
return results, nil
}
func getVersionInfo(actionConfig *action.Configuration, releaseName string) []VersionInfo {
func getVersionInfo(actionConfig *action.Configuration, releaseName, namespace string, collectValues bool) ([]VersionInfo, error) {
versionCollect := []VersionInfo{}
error_list := []error{}
history, _ := action.NewHistory(actionConfig).Run(releaseName)
history, err := action.NewHistory(actionConfig).Run(releaseName)
if err != nil {
return nil, err
}
for _, release := range history {
values := map[string]interface{}{}
if collectValues {
values, err = getHelmValues(releaseName, namespace, release.Version)
if err != nil {
error_list = append(error_list, err)
}
}
versionCollect = append(versionCollect, VersionInfo{
Revision: strconv.Itoa(release.Version),
Date: release.Info.LastDeployed.String(),
Status: release.Info.Status.String(),
IsPending: release.Info.Status.IsPending(),
Values: values,
})
}
return versionCollect
if len(error_list) > 0 {
errs := []string{}
for _, e := range error_list {
errs = append(errs, e.Error())
}
return nil, errors.New(strings.Join(errs, "\n"))
}
return versionCollect, nil
}
func helmReleaseInfoByNamespaces(releaseInfo []ReleaseInfo) map[string][]ReleaseInfo {
@@ -158,3 +191,15 @@ func helmReleaseInfoByNamespaces(releaseInfo []ReleaseInfo) map[string][]Release
return releaseInfoByNamespace
}
func getHelmValues(releaseName, namespace string, revision int) (map[string]interface{}, error) {
actionConfig := new(action.Configuration)
if err := actionConfig.Init(nil, namespace, "", klog.V(2).Infof); err != nil {
return nil, err
}
getAction := action.NewGetValues(actionConfig)
getAction.AllValues = true
getAction.Version = revision
helmValues, err := getAction.Run(releaseName)
return helmValues, err
}
@@ -24,16 +24,16 @@ func Test_HostCertParser(t *testing.T) {
CertificatePath: path,
CertificateChain: []ParsedCertificate{
{
Subject: "CN=envoy",
Subject: "CN=ExampleServer",
SubjectAlternativeNames: []string{
"envoy",
"envoy.projectcontour",
"envoy.projectcontour.svc",
"envoy.projectcontour.svc.cluster.local",
"example1",
"example2",
"example3",
"example4",
},
Issuer: "SERIALNUMBER=615929891,CN=Project Contour",
NotAfter: time.Date(2024, time.February, 25, 4, 27, 16, 0, time.UTC),
NotBefore: time.Date(2023, time.February, 24, 4, 27, 18, 0, time.UTC),
Issuer: "CN=ExampleServer",
NotAfter: time.Date(2029, time.February, 24, 22, 9, 27, 0, time.UTC),
NotBefore: time.Date(2024, time.February, 26, 22, 9, 27, 0, time.UTC),
IsValid: true,
IsCA: false,
},
+189 -21
View File
@@ -2,19 +2,27 @@ package collect
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"k8s.io/klog/v2"
)
type HostRunInfo struct {
Command string `json:"command"`
ExitCode string `json:"exitCode"`
Error string `json:"error"`
Command string `json:"command"`
ExitCode string `json:"exitCode"`
Error string `json:"error"`
OutputDir string `json:"outputDir"`
Input string `json:"input"`
Env []string `json:"env"`
}
type CollectHostRun struct {
@@ -31,33 +39,118 @@ func (c *CollectHostRun) IsExcluded() (bool, error) {
}
func (c *CollectHostRun) Collect(progressChan chan<- interface{}) (map[string][]byte, error) {
runHostCollector := c.hostCollector
var (
cmdOutputTempDir string
cmdInputTempDir string
bundleOutputRelativePath string
)
cmd := exec.Command(runHostCollector.Command, runHostCollector.Args...)
runHostCollector := c.hostCollector
collectorName := runHostCollector.CollectorName
if collectorName == "" {
collectorName = "run-host"
}
var (
timeout time.Duration
cmd *exec.Cmd
errInvalidDuration error
)
ctx := context.Background()
cmdPath := c.attemptToConvertCmdToAbsPath()
if runHostCollector.Timeout != "" {
timeout, errInvalidDuration = time.ParseDuration(runHostCollector.Timeout)
if errInvalidDuration != nil {
return nil, errors.Wrapf(errInvalidDuration, "failed to parse timeout %q", runHostCollector.Timeout)
}
}
if timeout <= time.Duration(0) {
cmd = exec.Command(cmdPath, runHostCollector.Args...)
} else {
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
cmd = exec.CommandContext(timeoutCtx, cmdPath, runHostCollector.Args...)
}
klog.V(2).Infof("Run host collector command: %q", cmd.String())
runInfo := &HostRunInfo{
Command: cmd.String(),
ExitCode: "0",
}
err := c.processEnvVars(cmd)
if err != nil {
return nil, errors.Wrap(err, "failed to parse env variable")
}
// Create a working directory for the command
wkdir, err := os.MkdirTemp("", collectorName)
defer os.RemoveAll(wkdir)
if err != nil {
return nil, errors.Wrap(err, "failed to create temp dir for host run")
}
// Change the working directory for the command to ensure the command
// does not polute the parent/caller working directory
cmd.Dir = wkdir
// if we choose to save result for the command run
if runHostCollector.OutputDir != "" {
cmdOutputTempDir = filepath.Join(wkdir, runHostCollector.OutputDir)
err = os.MkdirAll(cmdOutputTempDir, 0755)
if err != nil {
return nil, errors.New(fmt.Sprintf("failed to create dir for: %s", runHostCollector.OutputDir))
}
cmd.Env = append(cmd.Env,
fmt.Sprintf("TS_OUTPUT_DIR=%s", cmdOutputTempDir),
)
}
if runHostCollector.Input != nil {
cmdInputTempDir = filepath.Join(wkdir, "input")
err = os.MkdirAll(cmdInputTempDir, 0755)
if err != nil {
return nil, errors.New("failed to create temp dir for host run input")
}
for inFilename, inFileContent := range runHostCollector.Input {
if strings.Contains(inFilename, "/") {
return nil, errors.New("Input filename contains '/'")
}
cmdInputFilePath := filepath.Join(cmdInputTempDir, inFilename)
err = os.WriteFile(cmdInputFilePath, []byte(inFileContent), 0644)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("failed to write input file: %s to temp directory", inFilename))
}
}
cmd.Env = append(cmd.Env,
fmt.Sprintf("TS_INPUT_DIR=%s", cmdInputTempDir),
)
}
collectorRelativePath := filepath.Join("host-collectors/run-host", collectorName)
runInfo.Env = cmd.Env
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
runInfo := HostRunInfo{
Command: cmd.String(),
ExitCode: "0",
}
err := cmd.Run()
err = cmd.Run()
if err != nil {
if werr, ok := err.(*exec.ExitError); ok {
runInfo.ExitCode = strings.TrimPrefix(werr.Error(), "exit status ")
runInfo.Error = stderr.String()
} else if err == context.DeadlineExceeded {
runInfo.ExitCode = "-1"
runInfo.Error = fmt.Sprintf("command timed out after %s", timeout.String())
} else {
return nil, errors.Wrap(err, "failed to run")
}
}
collectorName := c.hostCollector.CollectorName
if collectorName == "" {
collectorName = "run-host"
}
output := NewResult()
resultInfo := filepath.Join("host-collectors/run-host", collectorName+"-info.json")
result := filepath.Join("host-collectors/run-host", collectorName+".txt")
@@ -66,14 +159,89 @@ func (c *CollectHostRun) Collect(progressChan chan<- interface{}) (map[string][]
return nil, errors.Wrap(err, "failed to marshal run host result")
}
output := NewResult()
output.SaveResult(c.BundlePath, resultInfo, bytes.NewBuffer(b))
output.SaveResult(c.BundlePath, result, bytes.NewBuffer(stdout.Bytes()))
runHostOutput := map[string][]byte{
resultInfo: b,
result: stdout.Bytes(),
// walkthrough the output directory and save result for each file
if runHostCollector.OutputDir != "" {
runInfo.OutputDir = runHostCollector.OutputDir
bundleOutputRelativePath = filepath.Join(collectorRelativePath, runHostCollector.OutputDir)
klog.V(2).Infof("Saving command output to %q in bundle", bundleOutputRelativePath)
output.SaveResults(c.BundlePath, bundleOutputRelativePath, cmdOutputTempDir)
}
return runHostOutput, nil
return output, nil
}
func (c *CollectHostRun) processEnvVars(cmd *exec.Cmd) error {
runHostCollector := c.hostCollector
if runHostCollector.IgnoreParentEnvs {
klog.V(2).Info("Not inheriting the environment variables!")
if runHostCollector.InheritEnvs != nil {
klog.V(2).Infof("The following environment variables will not be loaded to the command: [%s]",
strings.Join(runHostCollector.InheritEnvs, ","))
}
// clears the parent env vars
cmd.Env = []string{}
populateGuaranteedEnvVars(cmd)
} else if runHostCollector.InheritEnvs != nil {
for _, key := range runHostCollector.InheritEnvs {
envVal, found := os.LookupEnv(key)
if !found {
return errors.New(fmt.Sprintf("inherit env variable is not found: %s", key))
}
cmd.Env = append(cmd.Env,
fmt.Sprintf("%s=%s", key, envVal))
}
populateGuaranteedEnvVars(cmd)
} else {
cmd.Env = os.Environ()
}
if runHostCollector.Env != nil {
for i := range runHostCollector.Env {
parts := strings.Split(runHostCollector.Env[i], "=")
if len(parts) == 2 && parts[0] != "" && parts[1] != "" {
cmd.Env = append(cmd.Env,
fmt.Sprintf("%s", runHostCollector.Env[i]))
} else {
return errors.New(fmt.Sprintf("env variable entry is missing '=' : %s", runHostCollector.Env[i]))
}
}
}
return nil
}
func populateGuaranteedEnvVars(cmd *exec.Cmd) {
guaranteedEnvs := []string{"PATH", "KUBECONFIG", "PWD"}
for _, key := range guaranteedEnvs {
guaranteedEnvVal, found := os.LookupEnv(key)
if found {
cmd.Env = append(cmd.Env,
fmt.Sprintf("%s=%s", key, guaranteedEnvVal))
}
}
}
// attemptToConvertCmdToAbsPath checks if the command is a file path or command name
// If it is a file path, it will return the absolute path else
// it will return the command name as is and leave the resolution to cmd.Run()
// This enables passing commands using relative paths e.g. "./my-command"
// which is not possible with cmd.Run() since the child process runs
// in a different working directory
func (c *CollectHostRun) attemptToConvertCmdToAbsPath() string {
// Attempt to check if the command is file path or command name
cmdAbsPath, err := filepath.Abs(c.hostCollector.Command)
if err != nil {
return c.hostCollector.Command
}
// Check if the file exists
_, err = os.Stat(cmdAbsPath)
if err != nil {
return c.hostCollector.Command
}
return cmdAbsPath
}
+234
View File
@@ -0,0 +1,234 @@
package collect
import (
"fmt"
"os"
"os/exec"
"path"
"testing"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestProcessEnv(t *testing.T) {
pathEnv := fmt.Sprintf("%s=%s", "PATH", os.Getenv("PATH"))
pwdEnv := fmt.Sprintf("%s=%s", "PWD", os.Getenv("PWD"))
homeEnv := fmt.Sprintf("%s=%s", "HOME", os.Getenv("HOME"))
os.Setenv("KUBECONFIG", "/some/kubeconfig")
kubeconfigEnv := fmt.Sprintf("%s=%s", "KUBECONFIG", os.Getenv("KUBECONFIG"))
tests := []struct {
name string
collector *CollectHostRun
parentEnv []string
want []string
}{
{
name: "setting Env field",
collector: &CollectHostRun{
hostCollector: &troubleshootv1beta2.HostRun{
Env: []string{
"USER=dummy",
"AWS_REGION=us-east-1",
},
},
BundlePath: "",
},
want: append([]string{
"USER=dummy",
"AWS_REGION=us-east-1",
}, os.Environ()...),
},
{
name: "guaranteed env",
collector: &CollectHostRun{
hostCollector: &troubleshootv1beta2.HostRun{
IgnoreParentEnvs: true,
},
BundlePath: "",
},
want: []string{pathEnv, pwdEnv, kubeconfigEnv},
},
{
name: "ignoring parent env",
collector: &CollectHostRun{
hostCollector: &troubleshootv1beta2.HostRun{
Env: []string{
"USER=dummy",
"AWS_REGION=us-east-1",
},
IgnoreParentEnvs: true,
// inheritEnv will be ignored if IgnoreParentEnvs is true
InheritEnvs: []string{
"HOME",
},
},
BundlePath: "",
},
want: append([]string{
"USER=dummy",
"AWS_REGION=us-east-1",
}, pathEnv, pwdEnv, kubeconfigEnv),
},
{
name: "inheriting a subset of parent env",
collector: &CollectHostRun{
hostCollector: &troubleshootv1beta2.HostRun{
Env: []string{
"AWS_REGION=us-east-1",
},
InheritEnvs: []string{
"HOME",
},
},
BundlePath: "",
},
want: append([]string{
"AWS_REGION=us-east-1",
}, pathEnv, pwdEnv, homeEnv, kubeconfigEnv),
},
}
for _, test := range tests {
cmd := exec.Command(test.collector.hostCollector.Command, test.collector.hostCollector.Args...)
err := test.collector.processEnvVars(cmd)
require.NoError(t, err)
require.ElementsMatch(t, test.want, cmd.Env)
}
}
func TestCollectHostRun_Collect(t *testing.T) {
testDir, mkdirErr := os.MkdirTemp("", "host-run-test-*")
defer os.RemoveAll(testDir)
require.NoError(t, mkdirErr)
tests := []struct {
name string
collector *CollectHostRun
parentEnv []string
want map[string][]byte
wantError bool
}{
{
name: "saving cmd run output",
collector: &CollectHostRun{
hostCollector: &troubleshootv1beta2.HostRun{
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
CollectorName: "my-cmd-with-output",
},
Command: "sh",
Args: []string{"-c", "echo ${TS_INPUT_DIR}/dummy.conf > $TS_OUTPUT_DIR/input-file.txt"},
Input: map[string]string{
"dummy.conf": "[hello]\nhello = 1",
},
OutputDir: "magic-output",
},
BundlePath: path.Join(testDir, "bundle-1"),
},
want: CollectorResult{
"host-collectors/run-host/my-cmd-with-output-info.json": nil,
"host-collectors/run-host/my-cmd-with-output.txt": nil,
"host-collectors/run-host/my-cmd-with-output/magic-output/input-file.txt": nil,
},
},
{
name: "invalid input filename",
collector: &CollectHostRun{
hostCollector: &troubleshootv1beta2.HostRun{
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
CollectorName: "test",
},
Input: map[string]string{
"/home/ec2-user/invalid-path.conf": "[hello]\nhello = 1",
"valid-path.conf": "[env]\ndummy = dummy",
},
},
},
wantError: true,
},
}
for _, test := range tests {
got, err := test.collector.Collect(nil)
if test.wantError {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, test.want, got)
}
}
}
func TestCollectHostRunCollectWithTimeout(t *testing.T) {
tests := []struct {
name string
collector *CollectHostRun
wantError bool
}{
{
name: "no timeout",
collector: &CollectHostRun{
hostCollector: &troubleshootv1beta2.HostRun{
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
CollectorName: "no timeout",
},
Command: "echo",
Args: []string{"1"},
},
},
wantError: false,
},
{
name: "invalid timeout",
collector: &CollectHostRun{
hostCollector: &troubleshootv1beta2.HostRun{
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
CollectorName: "negative timeout",
},
Command: "echo",
Args: []string{"1"},
Timeout: "thisistheway",
},
},
wantError: true,
},
{
name: "negative timeout",
collector: &CollectHostRun{
hostCollector: &troubleshootv1beta2.HostRun{
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
CollectorName: "negative timeout",
},
Command: "echo",
Args: []string{"1"},
Timeout: "-10s",
},
},
wantError: false,
},
{
name: "endless command with timeout",
collector: &CollectHostRun{
hostCollector: &troubleshootv1beta2.HostRun{
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
CollectorName: "endless command",
},
Command: "ping",
Args: []string{"google.com"},
Timeout: "200ms",
},
},
wantError: false,
},
}
for _, test := range tests {
got, err := test.collector.Collect(nil)
if test.wantError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.NotNil(t, got)
}
}
}
+1 -1
View File
@@ -49,7 +49,7 @@ func (c *CollectMssql) Collect(progressChan chan<- interface{}) (CollectorResult
db, err := sql.Open("mssql", c.Collector.URI)
if err != nil {
klog.V(2).Infof("Failed to connect to %q MSSQL Database: %w", connUrl.Host, err)
klog.V(2).Infof("Failed to connect to %q MSSQL Database: %v", connUrl.Host, err)
databaseConnection.Error = err.Error()
} else {
defer db.Close()
+67 -8
View File
@@ -5,6 +5,8 @@ import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"regexp"
"github.com/jackc/pgx/v5"
@@ -12,6 +14,7 @@ import (
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"
)
type CollectPostgres struct {
@@ -37,21 +40,75 @@ func (c *CollectPostgres) createConnectConfig() (*pgx.ConnConfig, error) {
return nil, errors.New("postgres uri cannot be empty")
}
cfg, err := pgx.ParseConfig(c.Collector.URI)
if err != nil {
return nil, errors.Wrap(err, "failed to parse postgres config")
}
if c.Collector.TLS != nil {
tlsCfg, err := createTLSConfig(c.Context, c.Client, c.Collector.TLS)
klog.V(2).Infof("Connecting to postgres with TLS client config")
// Set the libpq TLS environment variables since pgx parses them to
// create the TLS configuration (tls.Config instance) to connect with
// https://www.postgresql.org/docs/current/libpq-envars.html
caCert, clientCert, clientKey, err := getTLSParamTriplet(c.Context, c.Client, c.Collector.TLS)
if err != nil {
return nil, err
}
tlsCfg.ServerName = cfg.Host
cfg.TLSConfig = tlsCfg
// Drop the TLS params to files and set the paths to their
// respective environment variables
// The environment variables are unset after the connection config
// is created. Their respective files are deleted as well.
tmpdir, err := os.MkdirTemp("", "ts-postgres-collector")
if err != nil {
return nil, errors.Wrap(err, "failed to create temp dir to store postgres collector TLS files")
}
defer os.RemoveAll(tmpdir)
if caCert != "" {
caCertPath := filepath.Join(tmpdir, "ca.crt")
err = os.WriteFile(caCertPath, []byte(caCert), 0644)
if err != nil {
return nil, errors.Wrap(err, "failed to write ca cert to file")
}
err = os.Setenv("PGSSLROOTCERT", caCertPath)
if err != nil {
return nil, errors.Wrap(err, "failed to set PGSSLROOTCERT environment variable")
}
klog.V(2).Infof("'PGSSLROOTCERT' environment variable set to %q", caCertPath)
defer os.Unsetenv("PGSSLROOTCERT")
}
if clientCert != "" {
clientCertPath := filepath.Join(tmpdir, "client.crt")
err = os.WriteFile(clientCertPath, []byte(clientCert), 0644)
if err != nil {
return nil, errors.Wrap(err, "failed to write client cert to file")
}
err = os.Setenv("PGSSLCERT", clientCertPath)
if err != nil {
return nil, errors.Wrap(err, "failed to set PGSSLCERT environment variable")
}
klog.V(2).Infof("'PGSSLCERT' environment variable set to %q", clientCertPath)
defer os.Unsetenv("PGSSLCERT")
}
if clientKey != "" {
clientKeyPath := filepath.Join(tmpdir, "client.key")
err = os.WriteFile(clientKeyPath, []byte(clientKey), 0600)
if err != nil {
return nil, errors.Wrap(err, "failed to write client key to file")
}
err = os.Setenv("PGSSLKEY", clientKeyPath)
if err != nil {
return nil, errors.Wrap(err, "failed to set PGSSLKEY environment variable")
}
klog.V(2).Infof("'PGSSLKEY' environment variable set to %q", clientKeyPath)
defer os.Unsetenv("PGSSLKEY")
}
}
cfg, err := pgx.ParseConfig(c.Collector.URI)
if err != nil {
return nil, errors.Wrap(err, "failed to parse postgres config")
}
klog.V(2).Infof("Successfully parsed postgres config")
return cfg, nil
}
@@ -74,8 +131,10 @@ func (c *CollectPostgres) Collect(progressChan chan<- interface{}) (CollectorRes
conn, err := c.connect()
if err != nil {
klog.V(2).Infof("Postgres connection error: %s", err.Error())
databaseConnection.Error = err.Error()
} else {
klog.V(2).Info("Successfully connected to postgres")
defer conn.Close(c.Context)
query := `select version()`
+19 -2
View File
@@ -2,6 +2,9 @@ package collect
import (
"context"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"testing"
"github.com/replicatedhq/troubleshoot/internal/testutils"
@@ -100,7 +103,7 @@ func TestCollectPostgres_createConnectConfigTLS(t *testing.T) {
Client: k8sClient,
Context: context.Background(),
Collector: &v1beta2.Database{
URI: "postgresql://user:password@my-pghost:5432/defaultdb?sslmode=require",
URI: "postgresql://user:password@my-pghost:5432/defaultdb?sslmode=verify-full",
TLS: &v1beta2.TLSParams{
CACert: testutils.GetTestFixture(t, "db/ca.pem"),
ClientCert: testutils.GetTestFixture(t, "db/client.pem"),
@@ -113,7 +116,21 @@ func TestCollectPostgres_createConnectConfigTLS(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, connCfg)
assert.Equal(t, connCfg.Host, "my-pghost")
assert.NotNil(t, connCfg.TLSConfig.Certificates)
// Check client cert
require.Len(t, connCfg.TLSConfig.Certificates, 1)
require.Len(t, connCfg.TLSConfig.Certificates[0].Certificate, 1)
cert := connCfg.TLSConfig.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, connCfg.TLSConfig.RootCAs)
assert.False(t, connCfg.TLSConfig.InsecureSkipVerify)
}
+41
View File
@@ -4,9 +4,12 @@ import (
"archive/tar"
"bytes"
"compress/gzip"
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"github.com/pkg/errors"
"k8s.io/klog/v2"
@@ -136,6 +139,44 @@ func (r CollectorResult) SaveResult(bundlePath string, relativePath string, read
return nil
}
// SaveResults walk a target directory and call SaveResult on all files retrieved from the walk.
func (r CollectorResult) SaveResults(bundlePath, relativePath, targetDir string) error {
dirPath := path.Join(bundlePath, relativePath)
if err := os.MkdirAll(dirPath, 0777); err != nil {
return errors.Wrap(err, "failed to create output file directory")
}
err := filepath.WalkDir(targetDir, func(path string, d os.DirEntry, err error) error {
if err != nil {
return errors.Wrap(err, "error from WalkDirFunc")
}
if !d.IsDir() {
file, err := os.Open(path)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to open file: %s", path))
}
fileBytes, err := io.ReadAll(file)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to read file: %s", path))
}
bundleRelativePath := filepath.Join(relativePath, strings.TrimPrefix(path, targetDir+"/"))
err = r.SaveResult(bundlePath, bundleRelativePath, bytes.NewBuffer(fileBytes))
if err != nil {
return errors.Wrap(err, "error from SaveResult call")
}
}
return nil
})
if err != nil {
return errors.Wrap(err, "error from WalkDir call")
}
return nil
}
func (r CollectorResult) ReplaceResult(bundlePath string, relativePath string, reader io.Reader) error {
if bundlePath == "" {
data, err := io.ReadAll(reader)
+1 -1
View File
@@ -43,7 +43,7 @@ func (c *CollectRun) Collect(progressChan chan<- interface{}) (CollectorResult,
serviceAccountName = c.Collector.ServiceAccountName
}
if err := checkForExistingServiceAccount(c.Client, namespace, serviceAccountName); err != nil {
if err := checkForExistingServiceAccount(c.Context, c.Client, namespace, serviceAccountName); err != nil {
return nil, err
}
+294
View File
@@ -0,0 +1,294 @@
package collect
import (
"bytes"
"context"
"fmt"
"io"
"path/filepath"
"strings"
"sync"
"time"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"
)
const (
defaultTimeout = time.Duration(60 * time.Second)
)
type CollectRunDaemonSet struct {
Collector *troubleshootv1beta2.RunDaemonSet
BundlePath string
Namespace string
ClientConfig *rest.Config
Client kubernetes.Interface
Context context.Context
RBACErrors
}
func (c *CollectRunDaemonSet) Title() string {
return getCollectorName(c)
}
func (c *CollectRunDaemonSet) IsExcluded() (bool, error) {
return isExcluded(c.Collector.Exclude)
}
func (c *CollectRunDaemonSet) Collect(progressChan chan<- interface{}) (CollectorResult, error) {
ctx := context.Background()
client, err := kubernetes.NewForConfig(c.ClientConfig)
if err != nil {
return nil, errors.Wrap(err, "failed to create client from config")
}
// create DaemonSet Spec
dsSpec, err := createDaemonSetSpec(c.Collector)
if err != nil {
return nil, errors.Wrap(err, "failed to create DaemonSet spec")
}
// create ImagePullSecret
var secretName string
if c.Collector.ImagePullSecret != nil && c.Collector.ImagePullSecret.Data != nil {
secretName, err = createSecret(ctx, client, dsSpec.ObjectMeta.Namespace, c.Collector.ImagePullSecret)
if err != nil {
return nil, errors.Wrap(err, "failed to create ImagePullSecret")
}
dsSpec.Spec.Template.Spec.ImagePullSecrets = append(dsSpec.Spec.Template.Spec.ImagePullSecrets, corev1.LocalObjectReference{Name: secretName})
}
// run DaemonSet
ds, err := client.AppsV1().DaemonSets(dsSpec.ObjectMeta.Namespace).Create(ctx, dsSpec, metav1.CreateOptions{})
klog.V(2).Infof("DaemonSet %s has been created", ds.Name)
if err != nil {
return nil, errors.Wrap(err, "failed to create DaemonSet")
}
defer func() {
// delete DaemonSet
err := client.AppsV1().DaemonSets(ds.ObjectMeta.Namespace).Delete(ctx, ds.ObjectMeta.Name, metav1.DeleteOptions{})
if err != nil {
klog.Errorf("Failed to delete DaemonSet %s: %v", ds.Name, err)
}
// delete ImagePullSecret
if secretName != "" {
err := client.CoreV1().Secrets(ds.ObjectMeta.Namespace).Delete(ctx, secretName, metav1.DeleteOptions{})
if err != nil {
klog.Errorf("Failed to delete Secret %s: %v", secretName, err)
}
}
}()
// set custom timeout if any
var (
timeout time.Duration
errInvalidDuration error
)
if c.Collector.Timeout != "" {
timeout, errInvalidDuration = time.ParseDuration(c.Collector.Timeout)
if errInvalidDuration != nil {
return nil, errors.Wrapf(errInvalidDuration, "failed to parse timeout %q", c.Collector.Timeout)
}
}
if timeout <= time.Duration(0) {
timeout = defaultTimeout
}
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
// block till DaemonSet has right number of scheduled Pods
err = waitForDaemonSetPods(timeoutCtx, client, ds)
if err != nil {
return nil, errors.Wrap(err, "failed to wait for DaemonSet pods")
}
klog.V(2).Infof("DaemonSet %s has desired number of pods", ds.Name)
// get all Pods in DaemonSet
pods, err := client.CoreV1().Pods(ds.ObjectMeta.Namespace).List(ctx, metav1.ListOptions{
LabelSelector: getLabelSelector(ds),
})
if err != nil {
return nil, errors.Wrap(err, "failed to list pods")
}
results := NewResult()
// collect logs from all Pods
// or save error message if failed to get logs
wg := &sync.WaitGroup{}
mtx := &sync.Mutex{}
for _, pod := range pods.Items {
wg.Add(1)
go func(pod corev1.Pod) {
defer wg.Done()
select {
case <-timeoutCtx.Done():
klog.Errorf("Timeout reached while waiting for pod %s", pod.Name)
return
default:
}
var logs []byte
nodeName, err := getPodNodeAtCompletion(timeoutCtx, client.CoreV1(), pod)
if err != nil {
nodeName = fmt.Sprintf("unknown-node-%s", pod.Name)
errString := fmt.Sprintf("Failed to get node name/wait for pod %s to complete: %v", pod.Name, err)
klog.Error(errString)
logs = []byte(errString)
} else {
logs, err = getPodLog(timeoutCtx, client.CoreV1(), pod)
if err != nil {
errString := fmt.Sprintf("Failed to get log from pod %s: %v", pod.Name, err)
klog.Error(errString)
logs = []byte(errString)
}
}
mtx.Lock()
defer mtx.Unlock()
results[nodeName] = logs
klog.V(2).Infof("Collected logs for pod %s", pod.Name)
}(pod)
}
wg.Wait()
output := NewResult()
for k, v := range results {
filename := k + ".log"
err := output.SaveResult(c.BundlePath, filepath.Join(c.Collector.Name, filename), bytes.NewBuffer(v))
if err != nil {
return nil, err
}
}
return output, nil
}
func createDaemonSetSpec(c *troubleshootv1beta2.RunDaemonSet) (*appsv1.DaemonSet, error) {
ds := &appsv1.DaemonSet{}
labels := make(map[string]string)
labels["troubleshoot-role"] = "run-daemonset-collector"
namespace := "default"
if c.Namespace != "" {
namespace = c.Namespace
}
ds.ObjectMeta = metav1.ObjectMeta{
GenerateName: fmt.Sprintf("run-daemonset-%s-", c.Name),
Namespace: namespace,
Labels: labels,
}
ds.Spec = appsv1.DaemonSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Labels: labels,
},
Spec: c.PodSpec,
},
}
return ds, nil
}
func getLabelSelector(ds *appsv1.DaemonSet) string {
labelSelector := ""
for k, v := range ds.Spec.Template.ObjectMeta.Labels {
labelSelector += k + "=" + v + ","
}
return strings.TrimSuffix(labelSelector, ",")
}
func getPodLog(ctx context.Context, client v1.CoreV1Interface, pod corev1.Pod) ([]byte, error) {
podLogOpts := corev1.PodLogOptions{
Container: pod.Spec.Containers[0].Name,
}
req := client.Pods(pod.Namespace).GetLogs(pod.Name, &podLogOpts)
logs, err := req.Stream(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to get log stream")
}
defer logs.Close()
return io.ReadAll(logs)
}
// getPodNodeAtCompletion waits for the Pod to complete and returns the node name
func getPodNodeAtCompletion(ctx context.Context, client v1.CoreV1Interface, pod corev1.Pod) (string, error) {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return "", ctx.Err()
case <-ticker.C:
pod, err := client.Pods(pod.Namespace).Get(ctx, pod.Name, metav1.GetOptions{})
if err != nil {
return "", errors.Wrap(err, "failed to get pod")
}
if pod.Status.Phase == corev1.PodFailed || pod.Status.Phase == corev1.PodSucceeded {
return pod.Spec.NodeName, nil
}
// we assume a container restart means the Pod has completed before
if len(pod.Status.ContainerStatuses) > 0 && pod.Status.ContainerStatuses[0].RestartCount > 0 {
return pod.Spec.NodeName, nil
}
if pod.Status.Phase == corev1.PodPending {
for _, v := range pod.Status.ContainerStatuses {
if v.State.Waiting != nil && v.State.Waiting.Reason == "ImagePullBackOff" {
return "", errors.New("wait for pod aborted after getting pod status 'ImagePullBackOff'")
}
}
}
}
}
}
// waitForDaemonSetPods waits for the DaemonSet to have the desired number of pods scheduled
func waitForDaemonSetPods(ctx context.Context, client kubernetes.Interface, ds *appsv1.DaemonSet) error {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
ds, err := client.AppsV1().DaemonSets(ds.ObjectMeta.Namespace).Get(ctx, ds.ObjectMeta.Name, metav1.GetOptions{})
if err != nil {
return errors.Wrap(err, "failed to get DaemonSet")
}
// we return as soon as the desired number of pods are scheduled
if ds.Status.DesiredNumberScheduled == ds.Status.CurrentNumberScheduled {
return nil
}
}
}
}
+70
View File
@@ -0,0 +1,70 @@
package collect
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
)
func TestWaitForDaemonSetPods(t *testing.T) {
ctx := context.TODO()
client := fake.NewSimpleClientset()
ds := &appsv1.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "connectivity",
},
Status: appsv1.DaemonSetStatus{
DesiredNumberScheduled: 2,
CurrentNumberScheduled: 2,
},
}
_, err := client.AppsV1().DaemonSets("default").Create(ctx, ds, metav1.CreateOptions{})
assert.NoError(t, err)
err = waitForDaemonSetPods(ctx, client, ds)
assert.NoError(t, err)
}
func TestGetPodNodeAtCompletion(t *testing.T) {
ctx := context.TODO()
client := fake.NewSimpleClientset()
pod := corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "test-pod",
},
Spec: corev1.PodSpec{
NodeName: "foo-node",
Containers: []corev1.Container{
{
Name: "connectivity",
Image: "curlimages/curl",
Args: []string{"-IsL", "https://docs.replicated.com"},
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodSucceeded,
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "connectivity",
RestartCount: 1,
},
},
},
}
_, err := client.CoreV1().Pods("default").Create(ctx, &pod, metav1.CreateOptions{})
assert.NoError(t, err)
nodeName, err := getPodNodeAtCompletion(ctx, client.CoreV1(), pod)
assert.NoError(t, err)
assert.Equal(t, "foo-node", nodeName)
}
+14 -7
View File
@@ -6,7 +6,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"path/filepath"
"sync"
"time"
@@ -44,8 +44,9 @@ func (c *CollectRunPod) IsExcluded() (bool, error) {
return isExcluded(c.Collector.Exclude)
}
func (c *CollectRunPod) Collect(progressChan chan<- interface{}) (CollectorResult, error) {
func (c *CollectRunPod) Collect(progressChan chan<- interface{}) (result CollectorResult, err error) {
ctx := context.Background()
result = NewResult()
client, err := kubernetes.NewForConfig(c.ClientConfig)
if err != nil {
@@ -70,9 +71,10 @@ func (c *CollectRunPod) Collect(progressChan chan<- interface{}) (CollectorResul
}()
}
result := NewResult()
defer func() {
if err != nil {
return
}
result, err = savePodDetails(ctx, client, result, c.BundlePath, c.ClientConfig, pod, c.Collector)
if err != nil {
klog.Errorf("failed to save pod details: %v", err)
@@ -94,6 +96,7 @@ func (c *CollectRunPod) Collect(progressChan chan<- interface{}) (CollectorResul
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
// TODO: Use context with timeout instead of go routine
go func() {
b, err := runWithoutTimeout(timeoutCtx, c.BundlePath, c.ClientConfig, pod, c.Collector)
if err != nil {
@@ -406,7 +409,7 @@ func RunPodLogs(ctx context.Context, client v1.CoreV1Interface, podSpec *corev1.
}
defer logs.Close()
return ioutil.ReadAll(logs)
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) {
@@ -467,13 +470,17 @@ func deletePod(ctx context.Context, client *kubernetes.Clientset, pod *corev1.Po
})
if err != nil {
zeroGracePeriod := int64(0)
klog.V(2).Infof("Pod %s forcefully deleted after reaching the maximum wait time of %d seconds", pod.Name, constants.MAX_TIME_TO_WAIT_FOR_POD_DELETION/time.Second)
klog.V(2).Infof("Forcefully deleting pod %s after reaching the maximum wait time of %d seconds due to err=%v",
pod.Name, constants.MAX_TIME_TO_WAIT_FOR_POD_DELETION/time.Second, err,
)
if err := client.CoreV1().Pods(pod.Namespace).Delete(context.Background(), pod.Name, metav1.DeleteOptions{
GracePeriodSeconds: &zeroGracePeriod,
}); err != nil {
klog.Errorf("Failed to wait for pod %s deletion: %v", pod.Name, err)
klog.Errorf("Forced deletion of pod %s failed: %v", pod.Name, err)
return
}
klog.V(2).Infof("Pod %s in %s namespace has been deleted", pod.Name, pod.Namespace)
} else {
klog.V(2).Infof("Pod %s in %s namespace has been deleted", pod.Name, pod.Namespace)
}
}
+7 -7
View File
@@ -36,7 +36,7 @@ type podRunner struct {
}
func (r *podRunner) run(ctx context.Context, collector *troubleshootv1beta2.HostCollect, namespace string, name string, nodeName string, results chan<- map[string][]byte) error {
cm, pod, err := CreateCollector(r.client, r.scheme, nil, name, namespace, nodeName, runnerServiceAccountName, runnerJobType, collector, r.image, r.pullPolicy)
cm, pod, err := CreateCollector(ctx, r.client, r.scheme, nil, name, namespace, nodeName, runnerServiceAccountName, runnerJobType, collector, r.image, r.pullPolicy)
if err != nil {
return errors.Wrap(err, "failed to create collector")
}
@@ -62,13 +62,13 @@ func (r *podRunner) run(ctx context.Context, collector *troubleshootv1beta2.Host
return nil
}
func CreateCollector(client *kubernetes.Clientset, scheme *runtime.Scheme, ownerRef metav1.Object, name string, namespace string, nodeName string, serviceAccountName string, jobType string, collect *troubleshootv1beta2.HostCollect, image string, pullPolicy string) (*corev1.ConfigMap, *corev1.Pod, error) {
func CreateCollector(ctx context.Context, client *kubernetes.Clientset, scheme *runtime.Scheme, ownerRef metav1.Object, name string, namespace string, nodeName string, serviceAccountName string, jobType string, collect *troubleshootv1beta2.HostCollect, image string, pullPolicy string) (*corev1.ConfigMap, *corev1.Pod, error) {
configMap, err := createCollectorConfigMap(client, scheme, ownerRef, name, namespace, collect)
if err != nil {
return nil, nil, err
}
pod, err := createCollectorPod(client, scheme, ownerRef, name, namespace, nodeName, serviceAccountName, jobType, collect, configMap, image, pullPolicy)
pod, err := createCollectorPod(ctx, client, scheme, ownerRef, name, namespace, nodeName, serviceAccountName, jobType, collect, configMap, image, pullPolicy)
if err != nil {
return nil, nil, err
}
@@ -143,19 +143,19 @@ func createCollectorConfigMap(client *kubernetes.Clientset, scheme *runtime.Sche
return created, nil
}
func createCollectorPod(client kubernetes.Interface, scheme *runtime.Scheme, ownerRef metav1.Object, name string, namespace string, nodeName string, serviceAccountName string, jobType string, collect *troubleshootv1beta2.HostCollect, configMap *corev1.ConfigMap, image string, pullPolicy string) (*corev1.Pod, error) {
func createCollectorPod(ctx context.Context, client kubernetes.Interface, scheme *runtime.Scheme, ownerRef metav1.Object, name string, namespace string, nodeName string, serviceAccountName string, jobType string, collect *troubleshootv1beta2.HostCollect, configMap *corev1.ConfigMap, image string, pullPolicy string) (*corev1.Pod, error) {
if serviceAccountName == "" {
serviceAccountName = "default"
}
_, err := client.CoreV1().Pods(namespace).Get(context.Background(), name, metav1.GetOptions{})
_, err := client.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{})
if err == nil {
return nil, fmt.Errorf("pod %q already exists", name)
} else if !kuberneteserrors.IsNotFound(err) {
return nil, err
}
if err := checkForExistingServiceAccount(client, namespace, serviceAccountName); err != nil {
if err := checkForExistingServiceAccount(ctx, client, namespace, serviceAccountName); err != nil {
return nil, err
}
@@ -261,7 +261,7 @@ func createCollectorPod(client kubernetes.Interface, scheme *runtime.Scheme, own
var created *corev1.Pod
createFn := func() error {
created, err = client.CoreV1().Pods(namespace).Create(context.Background(), &pod, metav1.CreateOptions{})
created, err = client.CoreV1().Pods(namespace).Create(ctx, &pod, metav1.CreateOptions{})
if err != nil && !kerrors.IsAlreadyExists(err) {
return err
}
+176
View File
@@ -0,0 +1,176 @@
package collect
import (
"archive/tar"
"compress/gzip"
"context"
"fmt"
"io"
"path/filepath"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/remotecommand"
"k8s.io/klog/v2"
)
const (
DefaultSonobuoyNamespace = "sonobuoy"
DefaultSonobuoyAggregatorPodName = "sonobuoy"
DefaultSonobuoyAggregatorContainerName = "kube-sonobuoy"
DefaultSonobuoyAggregatorResultsPath = "/tmp/sonobuoy"
)
type CollectSonobuoyResults struct {
Collector *troubleshootv1beta2.Sonobuoy
BundlePath string
Namespace string // this is not used
ClientConfig *rest.Config
Client kubernetes.Interface
Context context.Context
RBACErrors
}
func (c *CollectSonobuoyResults) Title() string {
return getCollectorName(c)
}
func (c *CollectSonobuoyResults) IsExcluded() (bool, error) {
return isExcluded(c.Collector.Exclude)
}
func (c *CollectSonobuoyResults) Collect(progressChan chan<- interface{}) (CollectorResult, error) {
namespace := DefaultSonobuoyNamespace
if c.Collector.Namespace != "" {
namespace = c.Collector.Namespace
}
podName := DefaultSonobuoyAggregatorPodName
resultsPath := DefaultSonobuoyAggregatorResultsPath
containerName := DefaultSonobuoyAggregatorContainerName
_, err := c.Client.CoreV1().Pods(namespace).Get(c.Context, podName, metav1.GetOptions{})
if kerrors.IsNotFound(err) {
return nil, fmt.Errorf("sonobuoy pod %s in namespace %s not found", podName, namespace)
} else if err != nil {
return nil, fmt.Errorf("failed to get sonobuoy pod %s in namespace %s: %v", podName, namespace, err)
}
reader, ec, err := sonobuoyRetrieveResults(c.Context, c.Client, c.ClientConfig, namespace, podName, containerName, resultsPath)
if err != nil {
return nil, fmt.Errorf("failed to retrieve sonobuoy results: %v", err)
}
output := NewResult()
ec2 := make(chan error, 1)
go func() {
defer close(ec2)
gz, err := gzip.NewReader(reader)
if err != nil {
ec2 <- errors.Wrap(err, "failed to create gzip reader")
return
}
defer gz.Close()
tr := tar.NewReader(gz)
for {
header, err := tr.Next()
if err == io.EOF {
return
} else if err != nil {
ec2 <- errors.Wrap(err, "failed to read tar header")
return
}
if header.Typeflag != tar.TypeReg {
continue
}
filename := filepath.Clean(header.Name) // sanitize the filename
klog.V(2).Infof("Sonobuoy collector found file: %s", filename)
err = output.SaveResult(c.BundlePath, filepath.Join("sonobuoy", filename), tr)
if err != nil {
ec2 <- errors.Wrapf(err, "failed to save result for %s", filename)
return
}
}
}()
err = <-ec2
if err != nil {
_, _ = io.Copy(io.Discard, reader) // ensure the stream is closed
return nil, fmt.Errorf("failed to write sonobuoy results: %v", err)
}
err = <-ec
if err != nil {
return nil, fmt.Errorf("failed to retrieve sonobuoy results: %v", err)
}
return output, nil
}
// sonobuoyRetrieveResults copies results from a sonobuoy run into a Reader in tar format.
// It also returns a channel of errors, where any errors encountered when writing results
// will be sent, and an error in the case where the config validation fails.
func sonobuoyRetrieveResults(
ctx context.Context, client kubernetes.Interface, restConfig *rest.Config, namespace, podName, containerName, path string,
) (io.Reader, <-chan error, error) {
ec := make(chan error, 1)
cmd := sonobuoyTarCmd(path)
klog.V(2).Infof(
"Sonobuoy collector runing command: kubectl exec -n %s %s -c %s -- %s",
namespace, podName, containerName, strings.Join(cmd, " "),
)
restClient := client.CoreV1().RESTClient()
req := restClient.Post().
Resource("pods").
Name(podName).
Namespace(namespace).
SubResource("exec").
Param("container", containerName)
req.VersionedParams(&corev1.PodExecOptions{
Container: containerName,
Command: cmd,
Stdin: false,
Stdout: true,
Stderr: false,
}, scheme.ParameterCodec)
executor, err := remotecommand.NewSPDYExecutor(restConfig, "POST", req.URL())
if err != nil {
return nil, ec, err
}
reader, writer := io.Pipe()
go func(writer *io.PipeWriter, ec chan error) {
defer writer.Close()
defer close(ec)
err = executor.StreamWithContext(ctx, remotecommand.StreamOptions{
Stdout: writer,
Tty: false,
})
if err != nil {
ec <- err
}
}(writer, ec)
return reader, ec, nil
}
func sonobuoyTarCmd(path string) []string {
return []string{
"/sonobuoy",
"splat",
path,
}
}
+25 -13
View File
@@ -139,12 +139,30 @@ func listNodesInSelector(ctx context.Context, client *kubernetes.Clientset, sele
nodes, err := client.CoreV1().Nodes().List(ctx, listOptions)
if err != nil {
return nil, fmt.Errorf("Can't get the list of nodes, got: %w", err)
return nil, fmt.Errorf("can't get the list of nodes, got: %w", err)
}
return nodes.Items, nil
}
func getTLSParamTriplet(
ctx context.Context, client kubernetes.Interface, params *troubleshootv1beta2.TLSParams,
) (string, string, string, error) {
var caCert, clientCert, clientKey string
if params.Secret != nil {
var err error
caCert, clientCert, clientKey, err = getTLSParamsFromSecret(ctx, client, params.Secret)
if err != nil {
return caCert, clientCert, clientKey, err
}
} else {
caCert = params.CACert
clientCert = params.ClientCert
clientKey = params.ClientKey
}
return caCert, clientCert, clientKey, nil
}
func createTLSConfig(ctx context.Context, client kubernetes.Interface, params *troubleshootv1beta2.TLSParams) (*tls.Config, error) {
rootCA, err := x509.SystemCertPool()
if err != nil {
@@ -158,21 +176,15 @@ func createTLSConfig(ctx context.Context, client kubernetes.Interface, params *t
return tlsCfg, nil
}
var caCert, clientCert, clientKey string
if params.Secret != nil {
caCert, clientCert, clientKey, err = getTLSParamsFromSecret(ctx, client, params.Secret)
if err != nil {
return nil, err
}
} else {
caCert = params.CACert
clientCert = params.ClientCert
clientKey = params.ClientKey
caCert, clientCert, clientKey, err := getTLSParamTriplet(ctx, client, params)
if err != nil {
return nil, err
}
if ok := rootCA.AppendCertsFromPEM([]byte(caCert)); !ok {
return nil, fmt.Errorf("failed to append CA cert to root CA bundle")
}
tlsCfg.RootCAs = rootCA
if clientCert == "" && clientKey == "" {
@@ -229,8 +241,8 @@ func getTLSParamsFromSecret(ctx context.Context, client kubernetes.Interface, se
return caCert, clientCert, clientKey, nil
}
func checkForExistingServiceAccount(client kubernetes.Interface, namespace string, serviceAccountName string) error {
_, err := client.CoreV1().ServiceAccounts(namespace).Get(context.Background(), serviceAccountName, metav1.GetOptions{})
func checkForExistingServiceAccount(ctx context.Context, client kubernetes.Interface, namespace string, serviceAccountName string) error {
_, err := client.CoreV1().ServiceAccounts(namespace).Get(ctx, serviceAccountName, metav1.GetOptions{})
if err != nil {
return errors.Wrapf(err, "Failed to get service account %s", serviceAccountName)
+2 -2
View File
@@ -323,11 +323,11 @@ func Test_checkForExistingServiceAccount(t *testing.T) {
_, err := client.CoreV1().ServiceAccounts(tt.namespace).Create(ctx, tt.mockServiceAccount, metav1.CreateOptions{})
require.NoError(t, err)
err = checkForExistingServiceAccount(client, tt.namespace, tt.serviceAccountName)
err = checkForExistingServiceAccount(ctx, client, tt.namespace, tt.serviceAccountName)
assert.Equal(t, tt.wantErr, err != nil)
}
err := checkForExistingServiceAccount(client, tt.namespace, tt.serviceAccountName)
err := checkForExistingServiceAccount(ctx, client, tt.namespace, tt.serviceAccountName)
assert.Equal(t, tt.wantErr, err != nil)
})
}
+12
View File
@@ -79,6 +79,7 @@ const (
// Troubleshoot spec constants
Troubleshootv1beta2Kind = "troubleshoot.sh/v1beta2"
Troubleshootv1beta1Kind = "troubleshoot.replicated.com/v1beta1"
// TermUI Display Constants
MESSAGE_TEXT_PADDING = 4
@@ -91,4 +92,15 @@ const (
// This is the muximum size the buffer can grow to
// Its not what the buffer will be allocated to initially
SCANNER_MAX_SIZE = 10 * 1024 * 1024 // 10MB
// Goldpinger constants
GP_CHECK_ALL_RESULTS_PATH = "goldpinger/check_all.json"
// GP_DEFAULT_IMAGE is the default image used for goldpinger
// "replicated/kurl-util" would be better
// since its always in airgap envs, but its tagged
// with the kurl versions which would not work since they
// are not always the same
GP_DEFAULT_IMAGE = "alpine:3"
GP_DEFAULT_NAMESPACE = "default"
)
+5 -1
View File
@@ -153,6 +153,10 @@ func (l *specLoader) loadFromStrings(rawSpecs ...string) (*TroubleshootKinds, er
// For secrets and configmaps, extract support bundle, redactor or preflight specs
// For troubleshoot kinds, pass them through
for _, rawDoc := range multiRawDocs {
if rawDoc == "" {
continue
}
var parsed parsedDoc
err := yaml.Unmarshal([]byte(rawDoc), &parsed)
@@ -192,7 +196,7 @@ func (l *specLoader) loadFromStrings(rawSpecs ...string) (*TroubleshootKinds, er
default:
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Errorf("%T type is not a Secret or ConfigMap", v))
}
} else if parsed.APIVersion == constants.Troubleshootv1beta2Kind {
} else if parsed.APIVersion == constants.Troubleshootv1beta2Kind || parsed.APIVersion == constants.Troubleshootv1beta1Kind {
// If it's not a configmap or secret, just append it to the splitdocs
splitdocs = append(splitdocs, rawDoc)
} else {
+32
View File
@@ -371,6 +371,38 @@ func TestLoadingMultidocsWithTroubleshootSpecs(t *testing.T) {
}, kinds.SupportBundlesV1Beta2)
}
func TestLoadingV1Beta1CollectorSpec(t *testing.T) {
kinds, err := LoadSpecs(context.Background(), LoadOptions{RawSpec: `kind: Collector
apiVersion: troubleshoot.replicated.com/v1beta1
metadata:
name: collector-sample
spec:
collectors:
- clusterInfo: {}
`})
require.NoError(t, err)
require.NotNil(t, kinds)
assert.Equal(t, []troubleshootv1beta2.Collector{
{
TypeMeta: metav1.TypeMeta{
Kind: "Collector",
APIVersion: "troubleshoot.sh/v1beta2",
},
ObjectMeta: metav1.ObjectMeta{
Name: "collector-sample",
},
Spec: troubleshootv1beta2.CollectorSpec{
Collectors: []*troubleshootv1beta2.Collect{
{
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
},
},
},
},
}, kinds.CollectorsV1Beta2)
}
func TestLoadingConfigMapWithMultipleSpecs_PreflightSupportBundleAndRedactorDataKeys(t *testing.T) {
s := testutils.GetTestFixture(t, "yamldocs/multidoc-spec-2.yaml")
l := specLoader{}
+3 -3
View File
@@ -114,8 +114,8 @@ func RunPreflights(interactive bool, output string, format string, args []string
preflightSpecName = spec.Name
}
if collectResults == nil && uploadCollectResults == nil {
return types.NewExitCodeError(constants.EXIT_CODE_CATCH_ALL, errors.New("no results"))
if len(collectResults) == 0 && len(uploadCollectResults) == 0 {
return types.NewExitCodeError(constants.EXIT_CODE_CATCH_ALL, errors.New("no data was collected"))
}
analyzeResults := []*analyzer.AnalyzeResult{}
@@ -142,7 +142,7 @@ func RunPreflights(interactive bool, output string, format string, args []string
progressCollection.Wait()
if len(analyzeResults) == 0 {
return types.NewExitCodeError(constants.EXIT_CODE_CATCH_ALL, errors.New("no data has been collected"))
return types.NewExitCodeError(constants.EXIT_CODE_CATCH_ALL, errors.New("completed with no analysis results"))
}
if interactive {

Some files were not shown because too many files have changed in this diff Show More