Compare commits

...
122 Commits
Author SHA1 Message Date
Xav Paice 86b7e54466 Revert "feat: save YAML spec used to generate support bundle/preflight" (#1715)
Revert "feat: save YAML spec used to generate support bundle/preflight (#1713)"

This reverts commit f6f51acbd5.
2025-01-06 09:42:58 +11:00
Gerard Nguyen f6f51acbd5 feat: save YAML spec used to generate support bundle/preflight (#1713)
* save YAML spec of support bundle

* save YAML spec of preflight

* add unit test

* redact TLS private key by default in output spec

* update YAML path for HTTP TLS redactor
2025-01-04 11:35:43 +11:00
Dexter YanandEvans Mungai 64ee9e5596 feat(nodeResources): add GPU support (#1708)
* feat(nodeResources): add GPU support

* add resourceCapacity and sum test

* update with make schemas

* Correct tests names

Signed-off-by: Evans Mungai <evans@replicated.com>

---------

Signed-off-by: Evans Mungai <evans@replicated.com>
Co-authored-by: Evans Mungai <evans@replicated.com>
2025-01-03 15:11:10 +13:00
Dexter Yan 277272249b fix: upgrade go version to 1.23.0 for helm-template (#1712) 2024-12-31 14:35:39 +13:00
dependabot[bot]andlnx01 607124381c chore(deps): bump github.com/distribution/distribution/v3 from 3.0.0-rc.1 to 3.0.0-rc.2 (#1711)
chore(deps): bump github.com/distribution/distribution/v3

Bumps [github.com/distribution/distribution/v3](https://github.com/distribution/distribution) from 3.0.0-rc.1 to 3.0.0-rc.2.
- [Release notes](https://github.com/distribution/distribution/releases)
- [Commits](https://github.com/distribution/distribution/compare/v3.0.0-rc.1...v3.0.0-rc.2)

---
updated-dependencies:
- dependency-name: github.com/distribution/distribution/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-12-31 00:23:32 +00:00
dependabot[bot]andlnx01 b9b20753e9 chore(deps): bump github.com/vmware-tanzu/velero from 1.15.0 to 1.15.1 in the security group (#1710)
chore(deps): bump github.com/vmware-tanzu/velero in the security group

Bumps the security group with 1 update: [github.com/vmware-tanzu/velero](https://github.com/vmware-tanzu/velero).


Updates `github.com/vmware-tanzu/velero` from 1.15.0 to 1.15.1
- [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.15.0...v1.15.1)

---
updated-dependencies:
- dependency-name: github.com/vmware-tanzu/velero
  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-12-31 00:23:04 +00:00
3f1314c00d chore(deps): bump the security group across 1 directory with 17 updates (#1709)
* chore(deps): bump the security group across 1 directory with 17 updates

Bumps the security group with 13 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [github.com/cilium/ebpf](https://github.com/cilium/ebpf) | `0.16.0` | `0.17.1` |
| [github.com/containerd/cgroups/v3](https://github.com/containerd/cgroups) | `3.0.4` | `3.0.5` |
| [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) | `5.7.1` | `5.7.2` |
| [github.com/microsoft/go-mssqldb](https://github.com/microsoft/go-mssqldb) | `1.7.2` | `1.8.0` |
| [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.32.0` | `1.33.0` |
| [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) | `1.32.0` | `1.33.0` |
| [k8s.io/api](https://github.com/kubernetes/api) | `0.31.3` | `0.32.0` |
| [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) | `0.31.3` | `0.32.0` |
| [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) | `0.31.3` | `0.32.0` |
| [golang.org/x/net](https://github.com/golang/net) | `0.31.0` | `0.33.0` |
| [helm.sh/helm/v3](https://github.com/helm/helm) | `3.16.3` | `3.16.4` |
| [k8s.io/kubelet](https://github.com/kubernetes/kubelet) | `0.31.3` | `0.32.0` |
| [k8s.io/metrics](https://github.com/kubernetes/metrics) | `0.31.3` | `0.32.0` |



Updates `github.com/cilium/ebpf` from 0.16.0 to 0.17.1
- [Release notes](https://github.com/cilium/ebpf/releases)
- [Commits](https://github.com/cilium/ebpf/compare/v0.16.0...v0.17.1)

Updates `github.com/containerd/cgroups/v3` from 3.0.4 to 3.0.5
- [Release notes](https://github.com/containerd/cgroups/releases)
- [Commits](https://github.com/containerd/cgroups/compare/v3.0.4...v3.0.5)

Updates `github.com/jackc/pgx/v5` from 5.7.1 to 5.7.2
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.7.1...v5.7.2)

Updates `github.com/microsoft/go-mssqldb` from 1.7.2 to 1.8.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.7.2...v1.8.0)

Updates `go.opentelemetry.io/otel` from 1.32.0 to 1.33.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.32.0...v1.33.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.32.0 to 1.33.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.32.0...v1.33.0)

Updates `k8s.io/api` from 0.31.3 to 0.32.0
- [Commits](https://github.com/kubernetes/api/compare/v0.31.3...v0.32.0)

Updates `k8s.io/apiextensions-apiserver` from 0.31.3 to 0.32.0
- [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases)
- [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.31.3...v0.32.0)

Updates `k8s.io/apimachinery` from 0.31.3 to 0.32.0
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.31.3...v0.32.0)

Updates `k8s.io/apiserver` from 0.31.3 to 0.32.0
- [Commits](https://github.com/kubernetes/apiserver/compare/v0.31.3...v0.32.0)

Updates `k8s.io/cli-runtime` from 0.31.3 to 0.32.0
- [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.31.3...v0.32.0)

Updates `k8s.io/client-go` from 0.31.3 to 0.32.0
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.31.3...v0.32.0)

Updates `golang.org/x/net` from 0.31.0 to 0.33.0
- [Commits](https://github.com/golang/net/compare/v0.31.0...v0.33.0)

Updates `helm.sh/helm/v3` from 3.16.3 to 3.16.4
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.16.3...v3.16.4)

Updates `k8s.io/kubelet` from 0.31.3 to 0.32.0
- [Commits](https://github.com/kubernetes/kubelet/compare/v0.31.3...v0.32.0)

Updates `k8s.io/metrics` from 0.31.3 to 0.32.0
- [Commits](https://github.com/kubernetes/metrics/compare/v0.31.3...v0.32.0)

Updates `k8s.io/utils` from 0.0.0-20240711033017-18e509b52bc8 to 0.0.0-20241104100929-3ea5e8cea738
- [Commits](https://github.com/kubernetes/utils/commits)

---
updated-dependencies:
- dependency-name: github.com/cilium/ebpf
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/containerd/cgroups/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  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: 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
- dependency-name: k8s.io/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/apiextensions-apiserver
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/apimachinery
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/apiserver
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/cli-runtime
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/client-go
  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
- dependency-name: helm.sh/helm/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/kubelet
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/metrics
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/utils
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
...

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

* upgrade go version 1.23.0

* make schemas

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Dexter Yan <yanshaocong@gmail.com>
2024-12-30 17:25:11 +13:00
dependabot[bot]andlnx01 fcfdc63634 chore(deps): bump golang.org/x/crypto from 0.29.0 to 0.31.0 in the go_modules group (#1705)
chore(deps): bump golang.org/x/crypto in the go_modules group

Bumps the go_modules group with 1 update: [golang.org/x/crypto](https://github.com/golang/crypto).


Updates `golang.org/x/crypto` from 0.29.0 to 0.31.0
- [Commits](https://github.com/golang/crypto/compare/v0.29.0...v0.31.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-17 06:08:34 +00:00
dependabot[bot]andlnx01 d67cdb7b90 chore(deps): bump helm.sh/helm/v3 from 3.16.3 to 3.16.4 in /examples/sdk/helm-template in the security group (#1704)
chore(deps): bump helm.sh/helm/v3

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


Updates `helm.sh/helm/v3` from 3.16.3 to 3.16.4
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.16.3...v3.16.4)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  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-12-17 00:25:25 +00:00
Gerard Nguyen a6fbf144b8 feat: container statuses analyzer (#1698)
* new schema for analyzer ClusterContainerStatues
2024-12-04 10:36:23 +11:00
dependabot[bot]andlnx01 b38a6a0389 chore(deps): bump sigs.k8s.io/controller-runtime from 0.19.2 to 0.19.3 in the security group (#1702)
chore(deps): bump sigs.k8s.io/controller-runtime in the security group

Bumps the security group with 1 update: [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime).


Updates `sigs.k8s.io/controller-runtime` from 0.19.2 to 0.19.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.19.2...v0.19.3)

---
updated-dependencies:
- dependency-name: sigs.k8s.io/controller-runtime
  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-12-03 00:25:53 +00:00
Dexter Yan de15d02f39 fix(make): improve generate cmd for go mod (#1699) 2024-12-02 18:27:33 +13:00
Miguel Varela Ramos 8e2647077d feat: add support for matchExpressions when filtering for nodes (#1697)
* feat: add support for matchExpressions when filtering for nodes

* fix: make generate
2024-11-30 23:15:26 +11:00
dependabot[bot]andlnx01 6e5db6d895 chore(deps): bump the security group with 12 updates (#1696)
Bumps the security group with 12 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/containerd/cgroups/v3](https://github.com/containerd/cgroups) | `3.0.3` | `3.0.4` |
| [github.com/stretchr/testify](https://github.com/stretchr/testify) | `1.9.0` | `1.10.0` |
| [golang.org/x/exp](https://github.com/golang/exp) | `0.0.0-20241009180824-f66d83c29e7c` | `0.0.0-20241108190413-2d47ceb2692f` |
| [k8s.io/api](https://github.com/kubernetes/api) | `0.31.2` | `0.31.3` |
| [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) | `0.31.2` | `0.31.3` |
| [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) | `0.31.2` | `0.31.3` |
| [k8s.io/apiserver](https://github.com/kubernetes/apiserver) | `0.31.2` | `0.31.3` |
| [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) | `0.31.2` | `0.31.3` |
| [k8s.io/client-go](https://github.com/kubernetes/client-go) | `0.31.2` | `0.31.3` |
| [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) | `0.19.1` | `0.19.2` |
| [k8s.io/kubelet](https://github.com/kubernetes/kubelet) | `0.31.2` | `0.31.3` |
| [k8s.io/metrics](https://github.com/kubernetes/metrics) | `0.31.2` | `0.31.3` |


Updates `github.com/containerd/cgroups/v3` from 3.0.3 to 3.0.4
- [Release notes](https://github.com/containerd/cgroups/releases)
- [Commits](https://github.com/containerd/cgroups/compare/v3.0.3...v3.0.4)

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

Updates `golang.org/x/exp` from 0.0.0-20241009180824-f66d83c29e7c to 0.0.0-20241108190413-2d47ceb2692f
- [Commits](https://github.com/golang/exp/commits)

Updates `k8s.io/api` from 0.31.2 to 0.31.3
- [Commits](https://github.com/kubernetes/api/compare/v0.31.2...v0.31.3)

Updates `k8s.io/apiextensions-apiserver` from 0.31.2 to 0.31.3
- [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases)
- [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.31.2...v0.31.3)

Updates `k8s.io/apimachinery` from 0.31.2 to 0.31.3
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.31.2...v0.31.3)

Updates `k8s.io/apiserver` from 0.31.2 to 0.31.3
- [Commits](https://github.com/kubernetes/apiserver/compare/v0.31.2...v0.31.3)

Updates `k8s.io/cli-runtime` from 0.31.2 to 0.31.3
- [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.31.2...v0.31.3)

Updates `k8s.io/client-go` from 0.31.2 to 0.31.3
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.31.2...v0.31.3)

Updates `sigs.k8s.io/controller-runtime` from 0.19.1 to 0.19.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.19.1...v0.19.2)

Updates `k8s.io/kubelet` from 0.31.2 to 0.31.3
- [Commits](https://github.com/kubernetes/kubelet/compare/v0.31.2...v0.31.3)

Updates `k8s.io/metrics` from 0.31.2 to 0.31.3
- [Commits](https://github.com/kubernetes/metrics/compare/v0.31.2...v0.31.3)

---
updated-dependencies:
- dependency-name: github.com/containerd/cgroups/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/exp
  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: k8s.io/kubelet
  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-11-26 00:24:27 +00:00
Ash ecc92b1e3e [bug] Quick fix for handling non 200 status codes when loading specs from URI (#1695)
* Quick fix for handling non 200 status codes when loading specs from URI

Go http client already handles 3xx responses for us

* note
2024-11-25 15:04:38 +00:00
Ricardo Maraschini 9f5f0633cf feat: rename templating variables (#1693)
when templating the output of the namespace connectivity check we were
referring to the 'fromCIDR' as 'fromNamespace'. it makes way more sense
to refer to it as 'fromCIDR' as this is how it is provided in the input
for the collector.

as this is a brand new feature it is very unlikely that anyone is using
this feature (except for the embedded cluster that still needs to be
patched accodringly).

this is how the analyser were defined before:

```yaml
apiVersion: troubleshoot.sh/v1beta2
kind: HostPreflight
metadata:
    name: ec-cluster-preflight
spec:
    analyzers:
        - networkNamespaceConnectivity:
            collectorName: check-network-connectivity
            outcomes:
            - pass:
                message: "Communication between {{ .FromNamespace }} and {{ .ToNamespace }} is working"
            - fail:
                message: "{{ .ErrorMessage }}"
```

and this is how it is now:

```yaml
apiVersion: troubleshoot.sh/v1beta2
kind: HostPreflight
metadata:
    name: ec-cluster-preflight
spec:
    analyzers:
        - networkNamespaceConnectivity:
            collectorName: check-network-connectivity
            outcomes:
            - pass:
                message: "Communication between {{ .FromCIDR }} and {{ .ToCIDR }} is working"
            - fail:
                message: "{{ .ErrorMessage }}"

```
2024-11-21 16:03:50 +01:00
Dexter Yan 6167fd8a5e fix(collector): fix dns collector limited to 63 chars (#1690) 2024-11-19 17:47:24 +13:00
91bf0242d4 chore(deps): bump the security group with 12 updates (#1683)
* chore(deps): bump the security group with 12 updates

Bumps the security group with 12 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/containers/image/v5](https://github.com/containers/image) | `5.32.2` | `5.33.0` |
| [github.com/vishvananda/netlink](https://github.com/vishvananda/netlink) | `1.2.1-beta.2` | `1.3.0` |
| [github.com/vishvananda/netns](https://github.com/vishvananda/netns) | `0.0.4` | `0.0.5` |
| [github.com/vmware-tanzu/velero](https://github.com/vmware-tanzu/velero) | `1.14.1` | `1.15.0` |
| [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.31.0` | `1.32.0` |
| [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) | `1.31.0` | `1.32.0` |
| [golang.org/x/exp](https://github.com/golang/exp) | `0.0.0-20240613232115-7f521ea00fb8` | `0.0.0-20241009180824-f66d83c29e7c` |
| [golang.org/x/mod](https://github.com/golang/mod) | `0.21.0` | `0.22.0` |
| [golang.org/x/sync](https://github.com/golang/sync) | `0.8.0` | `0.9.0` |
| [golang.org/x/net](https://github.com/golang/net) | `0.30.0` | `0.31.0` |
| [golang.org/x/sys](https://github.com/golang/sys) | `0.26.0` | `0.27.0` |
| [golang.org/x/text](https://github.com/golang/text) | `0.19.0` | `0.20.0` |


Updates `github.com/containers/image/v5` from 5.32.2 to 5.33.0
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.32.2...v5.33.0)

Updates `github.com/vishvananda/netlink` from 1.2.1-beta.2 to 1.3.0
- [Release notes](https://github.com/vishvananda/netlink/releases)
- [Commits](https://github.com/vishvananda/netlink/compare/v1.2.1-beta.2...v1.3.0)

Updates `github.com/vishvananda/netns` from 0.0.4 to 0.0.5
- [Release notes](https://github.com/vishvananda/netns/releases)
- [Commits](https://github.com/vishvananda/netns/compare/v0.0.4...v0.0.5)

Updates `github.com/vmware-tanzu/velero` from 1.14.1 to 1.15.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.14.1...v1.15.0)

Updates `go.opentelemetry.io/otel` from 1.31.0 to 1.32.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.31.0...v1.32.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.31.0 to 1.32.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.31.0...v1.32.0)

Updates `golang.org/x/exp` from 0.0.0-20240613232115-7f521ea00fb8 to 0.0.0-20241009180824-f66d83c29e7c
- [Commits](https://github.com/golang/exp/commits)

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

Updates `golang.org/x/sync` from 0.8.0 to 0.9.0
- [Commits](https://github.com/golang/sync/compare/v0.8.0...v0.9.0)

Updates `golang.org/x/net` from 0.30.0 to 0.31.0
- [Commits](https://github.com/golang/net/compare/v0.30.0...v0.31.0)

Updates `golang.org/x/sys` from 0.26.0 to 0.27.0
- [Commits](https://github.com/golang/sys/compare/v0.26.0...v0.27.0)

Updates `golang.org/x/text` from 0.19.0 to 0.20.0
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.19.0...v0.20.0)

---
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/vishvananda/netlink
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/vishvananda/netns
  dependency-type: direct:production
  update-type: version-update:semver-patch
  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-minor
  dependency-group: security
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/exp
  dependency-type: direct:production
  update-type: version-update:semver-patch
  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/sync
  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
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/text
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
...

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

* upgrade go version

* fix go sum merge

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Dexter Yan <yanshaocong@gmail.com>
2024-11-19 16:06:21 +13:00
dependabot[bot]andlnx01 5a820c02ff chore(deps): bump helm.sh/helm/v3 from 3.16.2 to 3.16.3 in /examples/sdk/helm-template in the security group (#1692)
chore(deps): bump helm.sh/helm/v3

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


Updates `helm.sh/helm/v3` from 3.16.2 to 3.16.3
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.16.2...v3.16.3)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  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-11-19 00:24:47 +00:00
dependabot[bot]andlnx01 cf10bd0e82 chore(deps): bump the security group with 13 updates (#1691)
Bumps the security group with 13 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/containers/image/v5](https://github.com/containers/image) | `5.32.2` | `5.33.0` |
| [github.com/vishvananda/netlink](https://github.com/vishvananda/netlink) | `1.2.1-beta.2` | `1.3.0` |
| [github.com/vishvananda/netns](https://github.com/vishvananda/netns) | `0.0.4` | `0.0.5` |
| [github.com/vmware-tanzu/velero](https://github.com/vmware-tanzu/velero) | `1.14.1` | `1.15.0` |
| [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.31.0` | `1.32.0` |
| [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) | `1.31.0` | `1.32.0` |
| [golang.org/x/exp](https://github.com/golang/exp) | `0.0.0-20240613232115-7f521ea00fb8` | `0.0.0-20241009180824-f66d83c29e7c` |
| [golang.org/x/mod](https://github.com/golang/mod) | `0.21.0` | `0.22.0` |
| [golang.org/x/sync](https://github.com/golang/sync) | `0.8.0` | `0.9.0` |
| [golang.org/x/net](https://github.com/golang/net) | `0.30.0` | `0.31.0` |
| [golang.org/x/sys](https://github.com/golang/sys) | `0.26.0` | `0.27.0` |
| [golang.org/x/text](https://github.com/golang/text) | `0.19.0` | `0.20.0` |
| [helm.sh/helm/v3](https://github.com/helm/helm) | `3.16.2` | `3.16.3` |


Updates `github.com/containers/image/v5` from 5.32.2 to 5.33.0
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.32.2...v5.33.0)

Updates `github.com/vishvananda/netlink` from 1.2.1-beta.2 to 1.3.0
- [Release notes](https://github.com/vishvananda/netlink/releases)
- [Commits](https://github.com/vishvananda/netlink/compare/v1.2.1-beta.2...v1.3.0)

Updates `github.com/vishvananda/netns` from 0.0.4 to 0.0.5
- [Release notes](https://github.com/vishvananda/netns/releases)
- [Commits](https://github.com/vishvananda/netns/compare/v0.0.4...v0.0.5)

Updates `github.com/vmware-tanzu/velero` from 1.14.1 to 1.15.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.14.1...v1.15.0)

Updates `go.opentelemetry.io/otel` from 1.31.0 to 1.32.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.31.0...v1.32.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.31.0 to 1.32.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.31.0...v1.32.0)

Updates `golang.org/x/exp` from 0.0.0-20240613232115-7f521ea00fb8 to 0.0.0-20241009180824-f66d83c29e7c
- [Commits](https://github.com/golang/exp/commits)

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

Updates `golang.org/x/sync` from 0.8.0 to 0.9.0
- [Commits](https://github.com/golang/sync/compare/v0.8.0...v0.9.0)

Updates `golang.org/x/net` from 0.30.0 to 0.31.0
- [Commits](https://github.com/golang/net/compare/v0.30.0...v0.31.0)

Updates `golang.org/x/sys` from 0.26.0 to 0.27.0
- [Commits](https://github.com/golang/sys/compare/v0.26.0...v0.27.0)

Updates `golang.org/x/text` from 0.19.0 to 0.20.0
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.19.0...v0.20.0)

Updates `helm.sh/helm/v3` from 3.16.2 to 3.16.3
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.16.2...v3.16.3)

---
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/vishvananda/netlink
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/vishvananda/netns
  dependency-type: direct:production
  update-type: version-update:semver-patch
  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-minor
  dependency-group: security
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/exp
  dependency-type: direct:production
  update-type: version-update:semver-patch
  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/sync
  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
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/text
  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
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 00:24:21 +00:00
Gerard Nguyen 7bb88e6b83 feat: ensure Copy collector run last (#1688)
* ensure Copy collector run last

* * add unit test
* reorder in Preflight as well
2024-11-15 10:59:38 +11:00
dependabot[bot]andlnx01 be2c2128c6 chore(deps): bump rajatjindal/krew-release-bot from 0.0.46 to 0.0.47 (#1685)
Bumps [rajatjindal/krew-release-bot](https://github.com/rajatjindal/krew-release-bot) from 0.0.46 to 0.0.47.
- [Release notes](https://github.com/rajatjindal/krew-release-bot/releases)
- [Changelog](https://github.com/rajatjindal/krew-release-bot/blob/main/.goreleaser.yml)
- [Commits](https://github.com/rajatjindal/krew-release-bot/compare/v0.0.46...v0.0.47)

---
updated-dependencies:
- dependency-name: rajatjindal/krew-release-bot
  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-11-13 16:41:15 +13:00
Dexter Yan 9ef117f080 upgrade go version to 1.22.8 (#1689) 2024-11-13 16:40:37 +13:00
Dexter Yan 1a828fa90b fix(analyzer): add missing warning in outcome (#1687) 2024-11-13 16:32:54 +13:00
Gerard Nguyen 9970fb1371 feat: [sc-114709] [troubleshoot] gracefully handle inability to load specs if multiple specs provided (#1686)
gracefully handle unable to load URL spec
2024-11-13 10:01:32 +13:00
dependabot[bot]andlnx01 0213fbb124 chore(deps): bump github.com/distribution/distribution/v3 from 3.0.0-beta.1 to 3.0.0-rc.1 (#1684)
chore(deps): bump github.com/distribution/distribution/v3

Bumps [github.com/distribution/distribution/v3](https://github.com/distribution/distribution) from 3.0.0-beta.1 to 3.0.0-rc.1.
- [Release notes](https://github.com/distribution/distribution/releases)
- [Commits](https://github.com/distribution/distribution/compare/v3.0.0-beta.1...v3.0.0-rc.1)

---
updated-dependencies:
- dependency-name: github.com/distribution/distribution/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-11-12 00:22:43 +00:00
deeeea7cec exec remote host collectors in a daemonset (#1671)
Co-authored-by: Gerard Nguyen <gerard@replicated.com>
Co-authored-by: Dexter Yan <yanshaocong@gmail.com>
2024-11-12 08:47:24 +13:00
João Antunes 197f6de425 feat(host_analyzer): add host sysctl analyzer (#1681)
* feat(host_analyzer): add host sysctl analyzer

* chore: add e2e tests to support bundle collection

* chore: missing spec e2e test update

* chore: cleanup remote collector and use parse operator

* chore: update schemas
2024-11-08 18:55:24 +00:00
Xav PaiceandDexter Yan 2f62240ed3 remove redundant tests (#1677)
* remove redundant tests

* fix workflow syntax error

* fix workflow syntax error

* fix k3s version for tests

* fix k3s version for tests

* f

* add vet to test-integration

---------

Co-authored-by: Dexter Yan <yanshaocong@gmail.com>
2024-11-08 10:54:26 +00:00
Dexter Yan 40eef3cdc8 feat(e2e): add local host support bundle test (#1680) 2024-11-08 17:14:06 +13:00
Evans MungaiandDexter Yan d25aa7d0ea fix: Do not fail analysis if node list does not exist (#1678)
* fix: Do not error if node list does not exist

Signed-off-by: Evans Mungai <evans@replicated.com>

* fix test fail

---------

Signed-off-by: Evans Mungai <evans@replicated.com>
Co-authored-by: Dexter Yan <yanshaocong@gmail.com>
2024-11-08 09:53:03 +13:00
João AntunesandEvans Mungai 77c9968ff6 feat(host_sysctl): add host sysctl collector (#1676)
* feat(host_sysctl): add host sysctl collector

* chore: add examples

* Update pkg/collect/host_sysctl.go

Co-authored-by: Evans Mungai <evans@replicated.com>

* chore: use sysctl package vs exec calls

* chore: make linter happy

* chore: make schemas

* chore: go back to sysctl exec

* chore: make linter happy

---------

Co-authored-by: Evans Mungai <evans@replicated.com>
2024-11-07 18:18:11 +00:00
Diamon Wiggins 06506ed95d Fix remote host collection RBAC checks (#1672)
* fix remote host collection rbac checks

* move saveNodeList into collectRemoteHost function

* fix resource attribute list and retrieve namespace from kubeconfig

* revert change to set a default namespace from kubeconfig

* remove duplicate code
2024-11-07 10:07:27 -05:00
Ricardo Maraschini e272683bce feat: implement collector and analyser for network namespace connectivity (#1670)
* feat: implement collector and analyser for network namespace connectivity

checks if two network namespaces can talk to each other on udp and tcp.
its usage is as follows:

```yaml
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
  name: test
spec:
  hostCollectors:
  - networkNamespaceConnectivity:
      collectorName: check-network-connectivity
      fromCIDR: 10.0.0.0/24
      toCIDR: 10.0.1.0/24
  hostAnalyzers:
  - networkNamespaceConnectivity:
      collectorName: check-network-connectivity
      outcomes:
      - pass:
          message: "Communication between 10.0.0.0/24 and 10.0.1.0/24 is working"
      - fail:
          message: "Communication between 10.0.0.0/24 and 10.0.1.0/24 isn't working"
```

if this fails then you may need to enable `forwarding` with:

```bash
sysctl -w net.ipv4.ip_forward=1
```

if it still fails then you may need to configure firewalld to allow the
traffic or simply disable it for sake of testing.

* chore: rebuild schemas

* chore: remove unused property

* chore: disable namespaces for other platforms

* chore: make sure we timeout temporary servers

* feat: analyzer now supports multi-node collection

* feat: check both udp and tcp even on failure

check both protocols even if one fails. this pr commit also introduces a
timeout that can be set by the user.

* feat: add templating to the failure outcome

allow users to dump the errors found during the analysis.

* chore: addressing pr comments

* feat: delete interface pair before namespace

even though the interface pair is deleted everyttime we delete the
namespace on my tests we better delete it before we delete the
namespace.

this comes out of a review comment where some people seem to still be
able to see the interface pair even after the namespace is deleted.

i.e. better safe than sorry.

* chore: fix typo on comment
2024-11-06 11:30:13 +01:00
AshandGerard Nguyen ea900a1881 chore: Refactor host cpu analyzer for remote collection (#1664)
* Refactor host cpu analyzer for remote collection

---------

Co-authored-by: Gerard Nguyen <gerard@replicated.com>
2024-11-06 14:43:27 +11:00
Gerard NguyenandDiamon Wiggins f0b8de68ae feat: multiple nodes analyzers (#1667)
* implement refactor for multiple node analyzers

---------

Co-authored-by: Diamon Wiggins <38189728+diamonwiggins@users.noreply.github.com>
2024-11-04 14:17:39 +11:00
AshandEvans Mungai 544a700062 [sc-114813] copy HostCollector fails to copy binary files when run in cluster (#1669)
* Don't convert output bytes to string

This prevents binary files getting mangled when the collector ourput is being passed around between functions

* Update pkg/collect/runner.go

Co-authored-by: Evans Mungai <evans@replicated.com>

* organise imports

---------

Co-authored-by: Evans Mungai <evans@replicated.com>
2024-10-31 10:44:35 +00:00
Dexter Yan 059b5d14d2 fix(collector): limit run pod collector to delete only one related secret (#1668)
* fix(collector): limit run pod collector to delete only related secret

* change to ctx
2024-10-30 14:19:30 +00:00
dependabot[bot]andlnx01 b33d5538e9 chore(deps): bump the security group with 11 updates (#1665)
Bumps the security group with 11 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/fatih/color](https://github.com/fatih/color) | `1.17.0` | `1.18.0` |
| [k8s.io/api](https://github.com/kubernetes/api) | `0.31.1` | `0.31.2` |
| [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) | `0.31.1` | `0.31.2` |
| [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) | `0.31.1` | `0.31.2` |
| [k8s.io/apiserver](https://github.com/kubernetes/apiserver) | `0.31.1` | `0.31.2` |
| [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) | `0.31.1` | `0.31.2` |
| [k8s.io/client-go](https://github.com/kubernetes/client-go) | `0.31.1` | `0.31.2` |
| [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) | `0.19.0` | `0.19.1` |
| [sigs.k8s.io/e2e-framework](https://github.com/kubernetes-sigs/e2e-framework) | `0.4.0` | `0.5.0` |
| [k8s.io/kubelet](https://github.com/kubernetes/kubelet) | `0.31.1` | `0.31.2` |
| [k8s.io/metrics](https://github.com/kubernetes/metrics) | `0.31.1` | `0.31.2` |


Updates `github.com/fatih/color` from 1.17.0 to 1.18.0
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.17.0...v1.18.0)

Updates `k8s.io/api` from 0.31.1 to 0.31.2
- [Commits](https://github.com/kubernetes/api/compare/v0.31.1...v0.31.2)

Updates `k8s.io/apiextensions-apiserver` from 0.31.1 to 0.31.2
- [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases)
- [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.31.1...v0.31.2)

Updates `k8s.io/apimachinery` from 0.31.1 to 0.31.2
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.31.1...v0.31.2)

Updates `k8s.io/apiserver` from 0.31.1 to 0.31.2
- [Commits](https://github.com/kubernetes/apiserver/compare/v0.31.1...v0.31.2)

Updates `k8s.io/cli-runtime` from 0.31.1 to 0.31.2
- [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.31.1...v0.31.2)

Updates `k8s.io/client-go` from 0.31.1 to 0.31.2
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.31.1...v0.31.2)

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

Updates `sigs.k8s.io/e2e-framework` from 0.4.0 to 0.5.0
- [Release notes](https://github.com/kubernetes-sigs/e2e-framework/releases)
- [Changelog](https://github.com/kubernetes-sigs/e2e-framework/blob/main/RELEASE.md)
- [Commits](https://github.com/kubernetes-sigs/e2e-framework/compare/v0.4.0...v0.5.0)

Updates `k8s.io/kubelet` from 0.31.1 to 0.31.2
- [Commits](https://github.com/kubernetes/kubelet/compare/v0.31.1...v0.31.2)

Updates `k8s.io/metrics` from 0.31.1 to 0.31.2
- [Commits](https://github.com/kubernetes/metrics/compare/v0.31.1...v0.31.2)

---
updated-dependencies:
- dependency-name: github.com/fatih/color
  dependency-type: direct:production
  update-type: version-update:semver-minor
  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: sigs.k8s.io/e2e-framework
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/kubelet
  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-10-29 06:08:07 +00:00
Evans Mungai deda4ce98c feat: Do not prompt users to save support bundle analysis results (#1662)
In interactive mode, do not prompt users to save support
bundle analysis results. Users end up providing this file
instead of the support bundle archive. The analysis results
are contained in the support bundle archive already

Signed-off-by: Evans Mungai <evans@replicated.com>
2024-10-25 13:03:16 +01:00
Dexter Yan 350418c6e9 feat(host-collector): add progress for host collector (#1659) 2024-10-25 15:34:09 +13:00
ada mancini eacff7112f support adding a CA cert to http collector (#1624)
* add a TLS parameter for cacert

* pass a ca cert into http request

* test preflight

* make schemas

* log extra information from http request

* pass a proxy into the collector spec

* hitting a segfault; breakpoint

* accept a dir, file, or a string-literal as CA

* move tls params into get, put, post methods

* test for cert untrusted response

* make generate

* make schemas

* more test cases

* make schemas

* dont include system certs

* make generate && make schemas

* resolve gosec G402 warning

* remove old check for system certs

* ignore errcheck "return value not checked" linter errors
2024-10-23 18:15:08 -04:00
Ricardo MaraschiniandEvans Mungai 7ed2f4bff2 feat: Handle failure to load cluster specs gracefully in support-bundle command (#1660)
* feat: Handle failure to load cluster specs gracefully in support-bundle command

In some scenarios, we don't want to fail when unable to load specs from
the cluster. This is particularly useful when:

- A host support bundle is available on disk.
- There are specs defined in the cluster.
- The cluster is malfunctioning or inaccessible.
- We still need to generate a support bundle using only the host specs.
- This change allows users to generate a support bundle even if the
  embedded cluster is not functioning properly, making the process more
  resilient.

The primary motivation is to introduce a new command:

```
./embedded-cluster support-bundle
```

When executed, this command attempts to collect both host and cluster
specs. However, if the embedded cluster is broken or unavailable, the
command will skip loading the cluster specs and focus on the host,
ensuring that users can still gather critical information without
interruption.

* chore: use fmt printf instead of klog

* Update internal/specs/specs.go

Co-authored-by: Evans Mungai <evans@replicated.com>

---------

Co-authored-by: Evans Mungai <evans@replicated.com>
2024-10-23 11:43:56 +01:00
Emmanuel Ferdman af8f682244 fix: update feature epic proposal template (#1654)
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
2024-10-23 16:52:47 +13:00
Ash c968fca125 Allow collect to chroot itself (#1658)
* Enable chroot

* typo

* platform specific chroot functions

* Add friendly chroot warning if running without elevated permissions
2024-10-22 17:06:07 +01:00
Ash 0fb0a07e55 [collect] accept stdin (#1657) 2024-10-22 15:06:17 +01:00
Dexter Yan 0d21eed5f8 fix(support): add missing host collectors for ParseSupportBundle (#1656)
* fix(support): add missing host collectors for ParseSupportBundle

* update

* add host ananlyers
2024-10-22 13:07:44 +13:00
b88bc8ddf7 Refactor Multi Node Analyzers (#1646)
* initial refactor of host os analyzer

* refactor remote collect analysis

---------

Signed-off-by: Evans Mungai <evans@replicated.com>
Co-authored-by: Gerard Nguyen <gerard@replicated.com>
Co-authored-by: Evans Mungai <evans@replicated.com>
2024-10-22 10:45:50 +13:00
Evans Mungai 9c24ab6067 chore: Remove preempted deprecation warnings (#1655)
Signed-off-by: Evans Mungai <evans@replicated.com>
2024-10-22 08:35:36 +11:00
Gerard Nguyen 289102f16d bug: fix nil check in host collector filter (#1653)
* add nil check in filter host collector
2024-10-18 15:58:32 +11:00
Dexter Yan 77fd7da315 fix(make): make sure github action can check schemas change (#1651) 2024-10-16 13:27:14 +13:00
dependabot[bot]andlnx01 78ee833f45 chore(deps): bump helm.sh/helm/v3 from 3.16.1 to 3.16.2 in /examples/sdk/helm-template in the security group (#1650)
chore(deps): bump helm.sh/helm/v3

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


Updates `helm.sh/helm/v3` from 3.16.1 to 3.16.2
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.16.1...v3.16.2)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  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-10-15 00:23:43 +00:00
dependabot[bot]andlnx01 781853251d chore(deps): bump the security group with 3 updates (#1649)
Bumps the security group with 3 updates: [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go), [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) and [helm.sh/helm/v3](https://github.com/helm/helm).


Updates `go.opentelemetry.io/otel` from 1.30.0 to 1.31.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.30.0...v1.31.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.30.0 to 1.31.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.30.0...v1.31.0)

Updates `helm.sh/helm/v3` from 3.16.1 to 3.16.2
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.16.1...v3.16.2)

---
updated-dependencies:
- 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
- dependency-name: helm.sh/helm/v3
  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-10-15 00:23:02 +00:00
Gerard Nguyen ffa1c040e2 fix: [sc-111255] CRD analyzer outcomes has no Warn field (#1647)
add warn field to CRD analyzer
2024-10-14 14:36:42 +11:00
Evans Mungai 0113624352 chore(support-bundle): respect using load-cluster-specs=false (#1634)
* fix: Allow using load-cluster-specs=false

Signed-off-by: Evans Mungai <evans@replicated.com>

* Some more simplification

Signed-off-by: Evans Mungai <evans@replicated.com>

* Ensure error in loading specs is printed in CLI

Signed-off-by: Evans Mungai <evans@replicated.com>

* Run linter

Signed-off-by: Evans Mungai <evans@replicated.com>

* Fix failing tests

Signed-off-by: Evans Mungai <evans@replicated.com>

* Remove unnecessary test case rename

Signed-off-by: Evans Mungai <evans@replicated.com>

* Fix error wrapping

Signed-off-by: Evans Mungai <evans@replicated.com>

* Check if load-cluster-specs was provided in cli

Signed-off-by: Evans Mungai <evans@replicated.com>

* Better wording in comments

Signed-off-by: Evans Mungai <evans@replicated.com>

---------

Signed-off-by: Evans Mungai <evans@replicated.com>
2024-10-11 13:48:32 -04:00
Shubhag Saxena 52efd167ad feat: allow users to check cpu arch (#1644) 2024-10-10 18:59:22 +05:30
Evans Mungai 402d111745 chore: Explicitly run make lint (#1645)
* chore: Explicitly run make lint

- Install golangci-lint make target to use in action
- Do not run go fmt when generating schemas
- Increase golangci timeout

Signed-off-by: Evans Mungai <evans@replicated.com>

* Add setup env step

Signed-off-by: Evans Mungai <evans@replicated.com>

* Add build-tags to golangci-lint command

Signed-off-by: Evans Mungai <evans@replicated.com>

---------

Signed-off-by: Evans Mungai <evans@replicated.com>
2024-10-09 14:03:23 -05:00
Diamon WigginsandDexter Yan 8105fa00e9 Refactor Remote Host Collection (#1633)
* refactor remote collectors

* add remotecollect params struct

* remove commented checkrbac function

* removed unused function

* add temp comments

* refactor to not require RemoteCollect method per collector

* removed unneeded param

* removed unneeded param

* more refactor

* more refactor

* remove unneeded function

* remove debug print

* fix analyzer results

* move rbac to separate file

* be more specific with rbac function name

* fix imports

* fix node list file

* make k8s rest client config consistent with in cluster collection

* add ctx and otel tracing

* add test for allCollectedData

* move runHostCollectorsInPod to spec instead of metadata

* make generate

* fix broken references to supportbundle metadata

* add e2e tests

* update loader tests

* fix tests

* fix hostos remote collector spec

* update remoteHostCollectrs.yaml

---------

Co-authored-by: Dexter Yan <yanshaocong@gmail.com>
2024-10-09 18:38:49 +13:00
dependabot[bot]andlnx01 e7c07a7d79 chore(deps): bump the security group with 3 updates (#1639)
Bumps the security group with 3 updates: [golang.org/x/net](https://github.com/golang/net), [golang.org/x/sys](https://github.com/golang/sys) and [golang.org/x/text](https://github.com/golang/text).


Updates `golang.org/x/net` from 0.29.0 to 0.30.0
- [Commits](https://github.com/golang/net/compare/v0.29.0...v0.30.0)

Updates `golang.org/x/sys` from 0.25.0 to 0.26.0
- [Commits](https://github.com/golang/sys/compare/v0.25.0...v0.26.0)

Updates `golang.org/x/text` from 0.18.0 to 0.19.0
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.18.0...v0.19.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/text
  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-10-08 00:22:10 +00:00
Evans Mungai 0240a632c9 chore: Collect endpointslices resources (#1636)
Signed-off-by: Evans Mungai <evans@replicated.com>
2024-10-03 14:55:53 +01:00
AshandEvans Mungai f58f02560f Allow goldpinger / goldpinger util images to be set in collector spec (#1635)
* Add image parameter to the goldpinger collector

* Pass image directly as a function arg

Also allow util image to be set in spec

* Remove pointless util image override

* Update pkg/collect/goldpinger.go

Co-authored-by: Evans Mungai <evans@replicated.com>

* Simplify image override

---------

Co-authored-by: Evans Mungai <evans@replicated.com>
2024-10-03 13:18:36 +01:00
Ricardo Maraschini 2efbc20b7c feat: allow users to check cpu flags (#1631)
allow users to check if specific cpu flags are supported by the host.

```yaml
apiVersion: troubleshoot.sh/v1beta2
kind: HostPreflight
metadata:
  name: ec-cluster-preflight
spec:
  collectors:
  - cpu: {}
  analyzers:
  - cpu:
      checkName: CPU
      outcomes:
        - pass:
            when: hasFlags cmov,cx8,fpu,fxsr,mmx
            message: CPU supports all required flags
        - fail:
            message: CPU not supported
```
2024-10-01 10:48:25 +02:00
Gerard Nguyen c1c4b612a4 feat: [sc-113128] Create node list file before running remote host collector (#1632)
* create node list
2024-10-01 14:43:24 +10:00
Gerard Nguyen d60f9a6b76 change resolvedFromSearch content (#1629) 2024-09-30 11:22:10 +10:00
Ricardo Maraschini 668b7ed0b2 feat: add CPU micro architecture support (#1628)
allows troubleshoot to collect and analyze CPU micro architecture. this
is an usage example:

```yaml
apiVersion: troubleshoot.sh/v1beta2
kind: HostPreflight
metadata:
  name: ec-cluster-preflight
spec:
  collectors:
  - cpu: {}
  analyzers:
  - cpu:
      checkName: CPU
      outcomes:
        - pass:
            when: 'supports x86-64-v2'
            message: CPU supports x86-64-v2
        - fail:
            message: CPU does not support x86-64-v2
```
2024-09-27 17:16:49 +02:00
Evans Mungai 2bb611cda1 bug: Remove duplicate results in preflights (#1626)
Change to stop re-analysing preflight results when uploadResultsTo is present leading to duplicate results

Signed-off-by: Evans Mungai <evans@replicated.com>
2024-09-26 15:25:39 +01:00
Dexter Yan 142015cce3 feat(analyzer): enable host os info analyzer to support multiple nodes (#1618) 2024-09-26 10:25:08 +12:00
Evans Mungai 83f02f4705 feat: Install goldpinger daemonset if one does not exist when running goldpinger collector (#1619)
* feat: Install goldpinger if one does not exist when running goldpinger collector

- Deploy golpinger daemonset if one is not detected in the cluster
- Clean up all deployed resources
- Add delay to allow users to wait for goldpinger to perform checks

Signed-off-by: Evans Mungai <evans@replicated.com>

* Add missing test data file

Signed-off-by: Evans Mungai <evans@replicated.com>

* Better naming of create resource functions

Signed-off-by: Evans Mungai <evans@replicated.com>

---------

Signed-off-by: Evans Mungai <evans@replicated.com>
2024-09-24 17:17:14 +01:00
dependabot[bot]andlnx01 2f78fee284 chore(deps): bump github.com/miekg/dns from 1.1.57 to 1.1.62 in the security group (#1621)
chore(deps): bump github.com/miekg/dns in the security group

Bumps the security group with 1 update: [github.com/miekg/dns](https://github.com/miekg/dns).


Updates `github.com/miekg/dns` from 1.1.57 to 1.1.62
- [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release)
- [Commits](https://github.com/miekg/dns/compare/v1.1.57...v1.1.62)

---
updated-dependencies:
- dependency-name: github.com/miekg/dns
  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-09-24 00:22:37 +00:00
Dexter Yan e97b9613a5 feat(support-bundle): add runHostCollectorsInPod in spec (#1608) 2024-09-20 11:57:58 -05:00
Gerard Nguyen 8823f7d99e feat: host collector for DNS (#1617)
* add struct for host dns collector

* add miekg/dns

* add more logs

* nit

* new field names

* use Hostnames instead of Names

* misc update

* make schemas

* no error when there is no resolv.conf

* query all searches

* add summary.json file

* merge summary into result file

* query AAAA and CNAME as well

* update schema for hostnames to be required
2024-09-20 08:13:57 +10:00
dependabot[bot]andlnx01 d73082ac93 chore(deps): bump the security group with 12 updates (#1615)
Bumps the security group with 12 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) | `5.7.0` | `5.7.1` |
| [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.29.0` | `1.30.0` |
| [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) | `1.29.0` | `1.30.0` |
| [k8s.io/api](https://github.com/kubernetes/api) | `0.31.0` | `0.31.1` |
| [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) | `0.31.0` | `0.31.1` |
| [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) | `0.31.0` | `0.31.1` |
| [k8s.io/apiserver](https://github.com/kubernetes/apiserver) | `0.31.0` | `0.31.1` |
| [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) | `0.31.0` | `0.31.1` |
| [k8s.io/client-go](https://github.com/kubernetes/client-go) | `0.31.0` | `0.31.1` |
| [helm.sh/helm/v3](https://github.com/helm/helm) | `3.15.4` | `3.16.1` |
| [k8s.io/kubelet](https://github.com/kubernetes/kubelet) | `0.31.0` | `0.31.1` |
| [k8s.io/metrics](https://github.com/kubernetes/metrics) | `0.31.0` | `0.31.1` |


Updates `github.com/jackc/pgx/v5` from 5.7.0 to 5.7.1
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.7.0...v5.7.1)

Updates `go.opentelemetry.io/otel` from 1.29.0 to 1.30.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.29.0...v1.30.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.29.0 to 1.30.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.29.0...v1.30.0)

Updates `k8s.io/api` from 0.31.0 to 0.31.1
- [Commits](https://github.com/kubernetes/api/compare/v0.31.0...v0.31.1)

Updates `k8s.io/apiextensions-apiserver` from 0.31.0 to 0.31.1
- [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases)
- [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.31.0...v0.31.1)

Updates `k8s.io/apimachinery` from 0.31.0 to 0.31.1
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.31.0...v0.31.1)

Updates `k8s.io/apiserver` from 0.31.0 to 0.31.1
- [Commits](https://github.com/kubernetes/apiserver/compare/v0.31.0...v0.31.1)

Updates `k8s.io/cli-runtime` from 0.31.0 to 0.31.1
- [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.31.0...v0.31.1)

Updates `k8s.io/client-go` from 0.31.0 to 0.31.1
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.31.0...v0.31.1)

Updates `helm.sh/helm/v3` from 3.15.4 to 3.16.1
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.15.4...v3.16.1)

Updates `k8s.io/kubelet` from 0.31.0 to 0.31.1
- [Commits](https://github.com/kubernetes/kubelet/compare/v0.31.0...v0.31.1)

Updates `k8s.io/metrics` from 0.31.0 to 0.31.1
- [Commits](https://github.com/kubernetes/metrics/compare/v0.31.0...v0.31.1)

---
updated-dependencies:
- dependency-name: github.com/jackc/pgx/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  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
- 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: helm.sh/helm/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/kubelet
  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-09-17 00:19:10 +00:00
dependabot[bot]andlnx01 cf8c08b913 chore(deps): bump helm.sh/helm/v3 from 3.15.4 to 3.16.1 in /examples/sdk/helm-template in the security group (#1614)
chore(deps): bump helm.sh/helm/v3

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


Updates `helm.sh/helm/v3` from 3.15.4 to 3.16.1
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.15.4...v3.16.1)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  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-09-17 00:18:38 +00:00
Evans Mungai aea4f7c87c feat: Optionally save preflight bundles to disk (#1612)
* feat: Optionally save preflight bundles to disk

Signed-off-by: Evans Mungai <evans@replicated.com>

* Add e2e test of saving preflight bundle

Signed-off-by: Evans Mungai <evans@replicated.com>

* Update cli docs

Signed-off-by: Evans Mungai <evans@replicated.com>

* Expose GetVersionFile function publicly

Signed-off-by: Evans Mungai <evans@replicated.com>

* Store analysis.json file in preflight bundle

Signed-off-by: Evans Mungai <evans@replicated.com>

* Run go fmt when running lint fixers

Signed-off-by: Evans Mungai <evans@replicated.com>

* Always generate a preflight bundle in CLI

Signed-off-by: Evans Mungai <evans@replicated.com>

* Print saving bundle message to stderr

Signed-off-by: Evans Mungai <evans@replicated.com>

* Revert changes in docs directory

Signed-off-by: Evans Mungai <evans@replicated.com>

* Use NewResult constructor

Signed-off-by: Evans Mungai <evans@replicated.com>

* Log always when preflight bundle is saved to disk

Signed-off-by: Evans Mungai <evans@replicated.com>

---------

Signed-off-by: Evans Mungai <evans@replicated.com>
2024-09-16 23:36:52 +01:00
Gerard Nguyen 05dcae2388 fix: [sc-112114] registry collector failed to talk to Replicated private registry (#1613)
decode auth for registry secret
2024-09-13 15:03:52 +01:00
Diamon Wiggins 0c63880528 fix: --redactors flag is dropped if no spec provided (#1611)
don't drop redactors when 0 positional arguments are provided
2024-09-12 09:01:44 +12:00
Gerard Nguyen 7484b10914 feat: [sc-110727] troubleshoot: collector/analyzer for wildcard dns (#1606)
* store DNS collector in JSON output for analyze later

* fix incorrect path

* configurable dns image

* make non resolvable domain configurable

* nit update address field

* * update dns util image
* add unit test
2024-09-11 14:35:30 +10:00
dependabot[bot]andlnx01 f66216154a chore(deps): bump the security group with 5 updates (#1609)
Bumps the security group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) | `5.6.0` | `5.7.0` |
| [golang.org/x/mod](https://github.com/golang/mod) | `0.20.0` | `0.21.0` |
| [golang.org/x/net](https://github.com/golang/net) | `0.28.0` | `0.29.0` |
| [golang.org/x/sys](https://github.com/golang/sys) | `0.24.0` | `0.25.0` |
| [golang.org/x/text](https://github.com/golang/text) | `0.17.0` | `0.18.0` |


Updates `github.com/jackc/pgx/v5` from 5.6.0 to 5.7.0
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.6.0...v5.7.0)

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

Updates `golang.org/x/net` from 0.28.0 to 0.29.0
- [Commits](https://github.com/golang/net/compare/v0.28.0...v0.29.0)

Updates `golang.org/x/sys` from 0.24.0 to 0.25.0
- [Commits](https://github.com/golang/sys/compare/v0.24.0...v0.25.0)

Updates `golang.org/x/text` from 0.17.0 to 0.18.0
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.17.0...v0.18.0)

---
updated-dependencies:
- dependency-name: github.com/jackc/pgx/v5
  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
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/text
  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-09-10 00:21:53 +00:00
bd6c5b976d chore(deps): bump the security group across 1 directory with 16 updates (#1604)
* chore(deps): bump the security group across 1 directory with 16 updates

Bumps the security group with 12 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [github.com/containers/image/v5](https://github.com/containers/image) | `5.32.1` | `5.32.2` |
| [github.com/hashicorp/go-getter](https://github.com/hashicorp/go-getter) | `1.7.5` | `1.7.6` |
| [github.com/vmware-tanzu/velero](https://github.com/vmware-tanzu/velero) | `1.14.0` | `1.14.1` |
| [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.28.0` | `1.29.0` |
| [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) | `1.28.0` | `1.29.0` |
| [k8s.io/api](https://github.com/kubernetes/api) | `0.30.3` | `0.31.0` |
| [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) | `0.30.3` | `0.31.0` |
| [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) | `0.30.3` | `0.31.0` |
| [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) | `0.18.5` | `0.19.0` |
| [helm.sh/helm/v3](https://github.com/helm/helm) | `3.15.3` | `3.15.4` |
| [k8s.io/kubelet](https://github.com/kubernetes/kubelet) | `0.30.3` | `0.31.0` |
| [k8s.io/metrics](https://github.com/kubernetes/metrics) | `0.30.3` | `0.31.0` |



Updates `github.com/containers/image/v5` from 5.32.1 to 5.32.2
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.32.1...v5.32.2)

Updates `github.com/hashicorp/go-getter` from 1.7.5 to 1.7.6
- [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.5...v1.7.6)

Updates `github.com/vmware-tanzu/velero` from 1.14.0 to 1.14.1
- [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.14.0...v1.14.1)

Updates `go.opentelemetry.io/otel` from 1.28.0 to 1.29.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.28.0...v1.29.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.28.0 to 1.29.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.28.0...v1.29.0)

Updates `k8s.io/api` from 0.30.3 to 0.31.0
- [Commits](https://github.com/kubernetes/api/compare/v0.30.3...v0.31.0)

Updates `k8s.io/apiextensions-apiserver` from 0.30.3 to 0.31.0
- [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases)
- [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.30.3...v0.31.0)

Updates `k8s.io/apimachinery` from 0.30.3 to 0.31.0
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.30.3...v0.31.0)

Updates `k8s.io/apiserver` from 0.30.3 to 0.31.0
- [Commits](https://github.com/kubernetes/apiserver/compare/v0.30.3...v0.31.0)

Updates `k8s.io/cli-runtime` from 0.30.3 to 0.31.0
- [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.30.3...v0.31.0)

Updates `k8s.io/client-go` from 0.30.3 to 0.31.0
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.30.3...v0.31.0)

Updates `sigs.k8s.io/controller-runtime` from 0.18.5 to 0.19.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.18.5...v0.19.0)

Updates `helm.sh/helm/v3` from 3.15.3 to 3.15.4
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.15.3...v3.15.4)

Updates `k8s.io/kubelet` from 0.30.3 to 0.31.0
- [Commits](https://github.com/kubernetes/kubelet/compare/v0.30.3...v0.31.0)

Updates `k8s.io/metrics` from 0.30.3 to 0.31.0
- [Commits](https://github.com/kubernetes/metrics/compare/v0.30.3...v0.31.0)

Updates `k8s.io/utils` from 0.0.0-20230726121419-3b25d923346b to 0.0.0-20240711033017-18e509b52bc8
- [Commits](https://github.com/kubernetes/utils/commits)

---
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/hashicorp/go-getter
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/vmware-tanzu/velero
  dependency-type: direct:production
  update-type: version-update:semver-patch
  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
- dependency-name: k8s.io/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/apiextensions-apiserver
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/apimachinery
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/apiserver
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/cli-runtime
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/client-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: sigs.k8s.io/controller-runtime
  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/kubelet
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/metrics
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/utils
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
...

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

* Run go mod tidy

Signed-off-by: Evans Mungai <evans@replicated.com>

* Update schemas and thier generator tools

* controller-gen to v0.16.2
* client-gen to v0.28.13

Signed-off-by: Evans Mungai <evans@replicated.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Evans Mungai <evans@replicated.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Evans Mungai <evans@replicated.com>
2024-09-04 15:54:07 +12:00
Dexter Yan 0a2c9c74ab feat(analyzer): allow templating for Node Resources Analyzer (#1605)
* feat(analyzer): allow templating for Node Resources Analyzer
2024-09-02 09:42:40 +12:00
Diamon Wiggins e685cb9cf3 feat: make load-cluster-specs the default (#1602)
* make load-cluster-specs the default
2024-08-26 09:54:15 -04:00
Ethan Mosbaugh 3aaffb424b fix(e2e): race condition causes tests to fail (#1603) 2024-08-23 07:07:02 -07:00
Ethan Mosbaugh 1b1efa133e feat(fio): add option to disable runtime (#1601) 2024-08-22 16:47:08 -07:00
Evans Mungai ff31f5af0b Log when analysers fail to match any outcome conditions (#1597)
Signed-off-by: Evans Mungai <evans@replicated.com>
2024-08-20 10:52:28 +01:00
dependabot[bot]andlnx01 8ab2b5e247 chore(deps): bump helm.sh/helm/v3 from 3.15.3 to 3.15.4 in /examples/sdk/helm-template in the security group (#1599)
chore(deps): bump helm.sh/helm/v3

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


Updates `helm.sh/helm/v3` from 3.15.3 to 3.15.4
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.15.3...v3.15.4)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  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-08-20 00:20:37 +00:00
Dexter Yan 55608c12ed feat(support-bundle): add text that explains where support bundle to share (#1595) 2024-08-16 10:07:26 +12:00
Diamon Wiggins fa14616009 Log non-existentent analyzers instead of adding to analyzer results (#1593)
log to debug non-existent analyzes instead of adding to analyzers results
2024-08-14 15:34:36 -04:00
dependabot[bot]andlnx01 d83d8ebfc6 chore(deps): bump the security group with 5 updates (#1592)
Bumps the security group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/containers/image/v5](https://github.com/containers/image) | `5.32.0` | `5.32.1` |
| [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) | `0.18.4` | `0.18.5` |
| [golang.org/x/net](https://github.com/golang/net) | `0.27.0` | `0.28.0` |
| [golang.org/x/sys](https://github.com/golang/sys) | `0.23.0` | `0.24.0` |
| [golang.org/x/text](https://github.com/golang/text) | `0.16.0` | `0.17.0` |


Updates `github.com/containers/image/v5` from 5.32.0 to 5.32.1
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.32.0...v5.32.1)

Updates `sigs.k8s.io/controller-runtime` from 0.18.4 to 0.18.5
- [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.18.4...v0.18.5)

Updates `golang.org/x/net` from 0.27.0 to 0.28.0
- [Commits](https://github.com/golang/net/compare/v0.27.0...v0.28.0)

Updates `golang.org/x/sys` from 0.23.0 to 0.24.0
- [Commits](https://github.com/golang/sys/compare/v0.23.0...v0.24.0)

Updates `golang.org/x/text` from 0.16.0 to 0.17.0
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: github.com/containers/image/v5
  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: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/text
  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-08-13 00:21:04 +00:00
Gerard Nguyen 47656a8e6f feat: etcd collector (#1589)
* new schema for etcd collector

* add placeholder

* wip

* get supported distribution

* add exec implementation

* wait for etcd pod to be ready

* misc

* update k0s etcd certs path

* fix unit tests

* address code reviews

* update from code review

* add etcdctl version
2024-08-13 08:42:26 +10:00
Gerard Nguyen 60263caf78 feat: [sc-108732] Can't add annotations in pods executed with the runPod collector (#1590)
add new field annotations for run pod collector
2024-08-08 10:13:36 +10:00
dependabot[bot]andlnx01 87cedca468 chore(deps): bump the security group with 3 updates (#1591)
Bumps the security group with 3 updates: [golang.org/x/mod](https://github.com/golang/mod), [golang.org/x/sync](https://github.com/golang/sync) and [golang.org/x/sys](https://github.com/golang/sys).


Updates `golang.org/x/mod` from 0.19.0 to 0.20.0
- [Commits](https://github.com/golang/mod/compare/v0.19.0...v0.20.0)

Updates `golang.org/x/sync` from 0.7.0 to 0.8.0
- [Commits](https://github.com/golang/sync/compare/v0.7.0...v0.8.0)

Updates `golang.org/x/sys` from 0.22.0 to 0.23.0
- [Commits](https://github.com/golang/sys/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/mod
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/sys
  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-08-06 00:20:09 +00:00
dependabot[bot]andlnx01 16237e4459 chore(deps): bump the security group with 4 updates (#1588)
Bumps the security group with 4 updates: [github.com/cilium/ebpf](https://github.com/cilium/ebpf), [github.com/containers/image/v5](https://github.com/containers/image), [golang.org/x/exp](https://github.com/golang/exp) and [oras.land/oras-go](https://github.com/oras-project/oras-go).


Updates `github.com/cilium/ebpf` from 0.11.0 to 0.16.0
- [Release notes](https://github.com/cilium/ebpf/releases)
- [Commits](https://github.com/cilium/ebpf/compare/v0.11.0...v0.16.0)

Updates `github.com/containers/image/v5` from 5.31.1 to 5.32.0
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.31.1...v5.32.0)

Updates `golang.org/x/exp` from 0.0.0-20240506185415-9bf2ced13842 to 0.0.0-20240613232115-7f521ea00fb8
- [Commits](https://github.com/golang/exp/commits)

Updates `oras.land/oras-go` from 1.2.5 to 1.2.6
- [Release notes](https://github.com/oras-project/oras-go/releases)
- [Commits](https://github.com/oras-project/oras-go/compare/v1.2.5...v1.2.6)

---
updated-dependencies:
- dependency-name: github.com/cilium/ebpf
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: github.com/containers/image/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: golang.org/x/exp
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: oras.land/oras-go
  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-07-30 00:20:17 +00:00
Gerard Nguyen a57171a918 feat: [sc-108689] troubleshoot: journald collector (#1586)
* add schema for Journald Host Collector

* implement journald host collector

* update host collector

* add --no-pager
2024-07-29 09:44:43 +10:00
Evans Mungai 01d5804977 feat: cgroups host collector (#1581)
Linux control groups host collector that detects whether the specified mountPoint is a cgroup filesystem and what version it is. The collector also collects information of the configured cgroup controllers.

Signed-off-by: Evans Mungai <evans@replicated.com>
2024-07-24 16:46:04 +01:00
22cc02cd29 chore(deps): bump github.com/distribution/distribution/v3 from 3.0.0-alpha.1 to 3.0.0-beta.1 (#1576)
* chore(deps): bump github.com/distribution/distribution/v3

Bumps [github.com/distribution/distribution/v3](https://github.com/distribution/distribution) from 3.0.0-alpha.1 to 3.0.0-beta.1.
- [Release notes](https://github.com/distribution/distribution/releases)
- [Commits](https://github.com/distribution/distribution/compare/v3.0.0-alpha.1...v3.0.0-beta.1)

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

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

* Run go mod tidy

Signed-off-by: Evans Mungai <evans@replicated.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Evans Mungai <evans@replicated.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Evans Mungai <evans@replicated.com>
2024-07-24 16:10:24 +01:00
Evans Mungai 1444c01725 feat: json compare host analyser (#1582)
* feat: json compore host analyser

Signed-off-by: Evans Mungai <evans@replicated.com>

* Add missing json compare host analyser file

Signed-off-by: Evans Mungai <evans@replicated.com>

* Generate schemas

Signed-off-by: Evans Mungai <evans@replicated.com>

* Fix failing tests

Signed-off-by: Evans Mungai <evans@replicated.com>

* Ensure json compare analyser always has a title

Signed-off-by: Evans Mungai <evans@replicated.com>

---------

Signed-off-by: Evans Mungai <evans@replicated.com>
2024-07-24 14:27:20 +01:00
Evans Mungai 0020c1129e feat: Allow checking kernel versions only in host os analyzer (#1585)
* Allow checking kernel versions only in host os analyzer

Signed-off-by: Evans Mungai <evans@replicated.com>

* Minor fix in logic

Signed-off-by: Evans Mungai <evans@replicated.com>

* Fix formatting

Signed-off-by: Evans Mungai <evans@replicated.com>

---------

Signed-off-by: Evans Mungai <evans@replicated.com>
2024-07-24 07:04:59 +01:00
dependabot[bot]andlnx01 1788376472 chore(deps): bump the security group with 9 updates (#1583)
Bumps the security group with 9 updates:

| Package | From | To |
| --- | --- | --- |
| [k8s.io/api](https://github.com/kubernetes/api) | `0.30.2` | `0.30.3` |
| [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) | `0.30.2` | `0.30.3` |
| [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) | `0.30.2` | `0.30.3` |
| [k8s.io/apiserver](https://github.com/kubernetes/apiserver) | `0.30.2` | `0.30.3` |
| [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) | `0.30.2` | `0.30.3` |
| [k8s.io/client-go](https://github.com/kubernetes/client-go) | `0.30.2` | `0.30.3` |
| [helm.sh/helm/v3](https://github.com/helm/helm) | `3.15.2` | `3.15.3` |
| [k8s.io/kubelet](https://github.com/kubernetes/kubelet) | `0.30.2` | `0.30.3` |
| [k8s.io/metrics](https://github.com/kubernetes/metrics) | `0.30.2` | `0.30.3` |


Updates `k8s.io/api` from 0.30.2 to 0.30.3
- [Commits](https://github.com/kubernetes/api/compare/v0.30.2...v0.30.3)

Updates `k8s.io/apiextensions-apiserver` from 0.30.2 to 0.30.3
- [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases)
- [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.30.2...v0.30.3)

Updates `k8s.io/apimachinery` from 0.30.2 to 0.30.3
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.30.2...v0.30.3)

Updates `k8s.io/apiserver` from 0.30.2 to 0.30.3
- [Commits](https://github.com/kubernetes/apiserver/compare/v0.30.2...v0.30.3)

Updates `k8s.io/cli-runtime` from 0.30.2 to 0.30.3
- [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.30.2...v0.30.3)

Updates `k8s.io/client-go` from 0.30.2 to 0.30.3
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.30.2...v0.30.3)

Updates `helm.sh/helm/v3` from 3.15.2 to 3.15.3
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.15.2...v3.15.3)

Updates `k8s.io/kubelet` from 0.30.2 to 0.30.3
- [Commits](https://github.com/kubernetes/kubelet/compare/v0.30.2...v0.30.3)

Updates `k8s.io/metrics` from 0.30.2 to 0.30.3
- [Commits](https://github.com/kubernetes/metrics/compare/v0.30.2...v0.30.3)

---
updated-dependencies:
- 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: helm.sh/helm/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/kubelet
  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-07-23 00:20:09 +00:00
Gerard Nguyen 04e656a0a5 fix: [sc-106256] Add missing uri field to troubleshoot.sh types (#1578)
* new no-uri flag for preflight
* implement load additional spec from URIs
2024-07-19 08:23:55 +10:00
dependabot[bot]andlnx01 790c8d4bde chore(deps): bump helm.sh/helm/v3 from 3.15.2 to 3.15.3 in /examples/sdk/helm-template in the security group (#1577)
chore(deps): bump helm.sh/helm/v3

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


Updates `helm.sh/helm/v3` from 3.15.2 to 3.15.3
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.15.2...v3.15.3)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  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-07-16 00:20:26 +00:00
Gerard Nguyen 4e999d6bfb fix: [sc-106256] Add missing uri field to troubleshoot.sh types (#1574)
add uri field for top level type
2024-07-15 13:07:04 +10:00
dependabot[bot]andlnx01 403b1ceccf chore(deps): bump the security group with 4 updates (#1573)
Bumps the security group with 4 updates: [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go), [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go), [golang.org/x/mod](https://github.com/golang/mod) and [golang.org/x/net](https://github.com/golang/net).


Updates `go.opentelemetry.io/otel` from 1.27.0 to 1.28.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.27.0...v1.28.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.27.0 to 1.28.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.27.0...v1.28.0)

Updates `golang.org/x/mod` from 0.18.0 to 0.19.0
- [Commits](https://github.com/golang/mod/compare/v0.18.0...v0.19.0)

Updates `golang.org/x/net` from 0.26.0 to 0.27.0
- [Commits](https://github.com/golang/net/compare/v0.26.0...v0.27.0)

---
updated-dependencies:
- 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
- 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-07-09 00:19:26 +00:00
Gerard Nguyen 8173759e52 feat: [sc-106927] Allow kernelConfig analyser to check kernel capability is either built in or loaded for EC host preflights (#1572)
* allow multiple value in kernel config check

* update unit test
2024-07-09 09:45:42 +10:00
Gerard Nguyen f5f02f5a80 fix: [sc-107456] exec collector is running in all pods matched the selector (#1571)
only exec in 1 pod
2024-07-08 09:31:29 +10:00
Gerard Nguyen e882f44ae9 Gerard/sc 106216/b registry image collector (#1570)
* update registry auth with username and password

* add unit test
2024-07-03 08:06:37 +10:00
dependabot[bot]andlnx01 d79ff950ca chore(deps): bump the security group with 3 updates (#1569)
Bumps the security group with 3 updates: [github.com/containers/image/v5](https://github.com/containers/image), [github.com/hashicorp/go-getter](https://github.com/hashicorp/go-getter) and [k8s.io/klog/v2](https://github.com/kubernetes/klog).


Updates `github.com/containers/image/v5` from 5.31.0 to 5.31.1
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.31.0...v5.31.1)

Updates `github.com/hashicorp/go-getter` from 1.7.4 to 1.7.5
- [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.4...v1.7.5)

Updates `k8s.io/klog/v2` from 2.130.0 to 2.130.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.130.0...v2.130.1)

---
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/hashicorp/go-getter
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: k8s.io/klog/v2
  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-06-25 00:19:09 +00:00
Gerard Nguyen 191ebdb598 feat: [sc-106759] Troubleshoot: uri field only download when we're not downloading (#1567)
* remove uri: when url is provided
2024-06-24 10:12:41 +10:00
Gerard Nguyen edfa01c5c4 feat: [sc-106625] http analyzer for in-cluster (#1566)
* http analzyer for in-cluster
* make check-schemas
2024-06-19 12:14:37 +10:00
dependabot[bot]andlnx01 18ec6adc12 chore(deps): bump helm.sh/helm/v3 from 3.15.1 to 3.15.2 in /examples/sdk/helm-template in the security group (#1564)
chore(deps): bump helm.sh/helm/v3

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


Updates `helm.sh/helm/v3` from 3.15.1 to 3.15.2
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.15.1...v3.15.2)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  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-06-18 06:06:30 +00:00
dependabot[bot]andlnx01 15b908f9e0 chore(deps): bump the security group with 12 updates (#1565)
Bumps the security group with 12 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/spf13/cobra](https://github.com/spf13/cobra) | `1.8.0` | `1.8.1` |
| [github.com/vmware-tanzu/velero](https://github.com/vmware-tanzu/velero) | `1.13.2` | `1.14.0` |
| [k8s.io/api](https://github.com/kubernetes/api) | `0.30.1` | `0.30.2` |
| [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) | `0.30.1` | `0.30.2` |
| [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) | `0.30.1` | `0.30.2` |
| [k8s.io/apiserver](https://github.com/kubernetes/apiserver) | `0.30.1` | `0.30.2` |
| [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) | `0.30.1` | `0.30.2` |
| [k8s.io/client-go](https://github.com/kubernetes/client-go) | `0.30.1` | `0.30.2` |
| [k8s.io/klog/v2](https://github.com/kubernetes/klog) | `2.120.1` | `2.130.0` |
| [helm.sh/helm/v3](https://github.com/helm/helm) | `3.15.1` | `3.15.2` |
| [k8s.io/kubelet](https://github.com/kubernetes/kubelet) | `0.30.1` | `0.30.2` |
| [k8s.io/metrics](https://github.com/kubernetes/metrics) | `0.30.1` | `0.30.2` |


Updates `github.com/spf13/cobra` from 1.8.0 to 1.8.1
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.8.0...v1.8.1)

Updates `github.com/vmware-tanzu/velero` from 1.13.2 to 1.14.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.13.2...v1.14.0)

Updates `k8s.io/api` from 0.30.1 to 0.30.2
- [Commits](https://github.com/kubernetes/api/compare/v0.30.1...v0.30.2)

Updates `k8s.io/apiextensions-apiserver` from 0.30.1 to 0.30.2
- [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases)
- [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.30.1...v0.30.2)

Updates `k8s.io/apimachinery` from 0.30.1 to 0.30.2
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.30.1...v0.30.2)

Updates `k8s.io/apiserver` from 0.30.1 to 0.30.2
- [Commits](https://github.com/kubernetes/apiserver/compare/v0.30.1...v0.30.2)

Updates `k8s.io/cli-runtime` from 0.30.1 to 0.30.2
- [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.30.1...v0.30.2)

Updates `k8s.io/client-go` from 0.30.1 to 0.30.2
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.30.1...v0.30.2)

Updates `k8s.io/klog/v2` from 2.120.1 to 2.130.0
- [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.120.1...v2.130.0)

Updates `helm.sh/helm/v3` from 3.15.1 to 3.15.2
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.15.1...v3.15.2)

Updates `k8s.io/kubelet` from 0.30.1 to 0.30.2
- [Commits](https://github.com/kubernetes/kubelet/compare/v0.30.1...v0.30.2)

Updates `k8s.io/metrics` from 0.30.1 to 0.30.2
- [Commits](https://github.com/kubernetes/metrics/compare/v0.30.1...v0.30.2)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-patch
  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: 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: k8s.io/klog/v2
  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/kubelet
  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-06-18 00:20:00 +00:00
dependabot[bot]andlnx01 a9c5baca14 chore(deps): bump goreleaser/goreleaser-action from 5 to 6 (#1562)
Bumps [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) from 5 to 6.
- [Release notes](https://github.com/goreleaser/goreleaser-action/releases)
- [Commits](https://github.com/goreleaser/goreleaser-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: goreleaser/goreleaser-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>
2024-06-13 17:28:22 +12:00
dependabot[bot]andlnx01 5778d4656a chore(deps): bump the security group across 1 directory with 8 updates (#1561)
Bumps the security group with 7 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [github.com/microsoft/go-mssqldb](https://github.com/microsoft/go-mssqldb) | `1.7.1` | `1.7.2` |
| [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) | `3.24.4` | `3.24.5` |
| [github.com/spf13/viper](https://github.com/spf13/viper) | `1.18.2` | `1.19.0` |
| [golang.org/x/mod](https://github.com/golang/mod) | `0.17.0` | `0.18.0` |
| [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) | `0.18.3` | `0.18.4` |
| [sigs.k8s.io/e2e-framework](https://github.com/kubernetes-sigs/e2e-framework) | `0.3.0` | `0.4.0` |
| [golang.org/x/net](https://github.com/golang/net) | `0.25.0` | `0.26.0` |



Updates `github.com/microsoft/go-mssqldb` from 1.7.1 to 1.7.2
- [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.7.1...v1.7.2)

Updates `github.com/shirou/gopsutil/v3` from 3.24.4 to 3.24.5
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.24.4...v3.24.5)

Updates `github.com/spf13/viper` from 1.18.2 to 1.19.0
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.2...v1.19.0)

Updates `golang.org/x/mod` from 0.17.0 to 0.18.0
- [Commits](https://github.com/golang/mod/compare/v0.17.0...v0.18.0)

Updates `sigs.k8s.io/controller-runtime` from 0.18.3 to 0.18.4
- [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.18.3...v0.18.4)

Updates `sigs.k8s.io/e2e-framework` from 0.3.0 to 0.4.0
- [Release notes](https://github.com/kubernetes-sigs/e2e-framework/releases)
- [Changelog](https://github.com/kubernetes-sigs/e2e-framework/blob/main/RELEASE.md)
- [Commits](https://github.com/kubernetes-sigs/e2e-framework/compare/v0.3.0...v0.4.0)

Updates `golang.org/x/net` from 0.25.0 to 0.26.0
- [Commits](https://github.com/golang/net/compare/v0.25.0...v0.26.0)

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

---
updated-dependencies:
- dependency-name: github.com/microsoft/go-mssqldb
  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/spf13/viper
  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: sigs.k8s.io/controller-runtime
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: sigs.k8s.io/e2e-framework
  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
- dependency-name: golang.org/x/text
  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-06-10 00:20:06 +00:00
Xav Paice 905c9771bc update Go to 1.22.3 (#1560) 2024-06-10 10:43:12 +12:00
Evans Mungai ce155270c8 fix: Use correct cron job kind when discovering API versions (#1554)
* fix: Use correct cron job kind when discovering API versions

* Fix failing e2e test
2024-05-31 07:23:56 +01:00
dependabot[bot]andlnx01 91da8f13bb chore(deps): bump azure/docker-login from 1 to 2 (#1552)
Bumps [azure/docker-login](https://github.com/azure/docker-login) from 1 to 2.
- [Release notes](https://github.com/azure/docker-login/releases)
- [Commits](https://github.com/azure/docker-login/compare/v1...v2)

---
updated-dependencies:
- dependency-name: azure/docker-login
  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>
2024-05-28 09:49:06 +01:00
dependabot[bot]andlnx01 1a8aff1d79 chore(deps): bump the security group with 9 updates (#1551)
Bumps the security group with 9 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/containers/image/v5](https://github.com/containers/image) | `5.30.1` | `5.31.0` |
| [github.com/go-logr/logr](https://github.com/go-logr/logr) | `1.4.1` | `1.4.2` |
| [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) | `5.5.5` | `5.6.0` |
| [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.26.0` | `1.27.0` |
| [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) | `1.26.0` | `1.27.0` |
| [golang.org/x/exp](https://github.com/golang/exp) | `0.0.0-20240222234643-814bf88cf225` | `0.0.0-20240506185415-9bf2ced13842` |
| [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) | `0.18.2` | `0.18.3` |
| [github.com/hashicorp/go-version](https://github.com/hashicorp/go-version) | `1.6.0` | `1.7.0` |
| [helm.sh/helm/v3](https://github.com/helm/helm) | `3.15.0` | `3.15.1` |


Updates `github.com/containers/image/v5` from 5.30.1 to 5.31.0
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.30.1...v5.31.0)

Updates `github.com/go-logr/logr` from 1.4.1 to 1.4.2
- [Release notes](https://github.com/go-logr/logr/releases)
- [Changelog](https://github.com/go-logr/logr/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-logr/logr/compare/v1.4.1...v1.4.2)

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

Updates `go.opentelemetry.io/otel` from 1.26.0 to 1.27.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.26.0...v1.27.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.26.0 to 1.27.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.26.0...v1.27.0)

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

Updates `sigs.k8s.io/controller-runtime` from 0.18.2 to 0.18.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.18.2...v0.18.3)

Updates `github.com/hashicorp/go-version` from 1.6.0 to 1.7.0
- [Release notes](https://github.com/hashicorp/go-version/releases)
- [Changelog](https://github.com/hashicorp/go-version/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/go-version/compare/v1.6.0...v1.7.0)

Updates `helm.sh/helm/v3` from 3.15.0 to 3.15.1
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.15.0...v3.15.1)

---
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-logr/logr
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security
- dependency-name: github.com/jackc/pgx/v5
  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
- dependency-name: golang.org/x/exp
  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: github.com/hashicorp/go-version
  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
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-27 17:19:28 -07:00
dependabot[bot]andlnx01 ece20d3c2c chore(deps): bump helm.sh/helm/v3 from 3.15.0 to 3.15.1 in /examples/sdk/helm-template in the security group (#1550)
chore(deps): bump helm.sh/helm/v3

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


Updates `helm.sh/helm/v3` from 3.15.0 to 3.15.1
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.15.0...v3.15.1)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  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-05-27 17:18:57 -07:00
Gerard Nguyen 80e5fac07c feat: New host collector and analyzer for Kernel Configs (#1546)
* new struct and update schemas

* implement Collect function

* add kernel config to collector struct

* generate kernel config analyzer schema

* implement kernel config analyzer

* fail on no match in pass outcome

* run make check-schemas

* fix failed unit test

* update from code review

* add selectedConfigs field

* run make check-schemas
2024-05-27 09:55:39 +10:00
Dexter Yan 51c07b42c3 feat(analyzer): let cluster resource case insensitive to fix name inconsistent (#1547)
* feat(analyzer): let cluster resource case insensitive
2024-05-22 11:37:39 +12:00
bd7bae6302 chore(deps): bump the security group with 9 updates (#1549)
* ---
updated-dependencies:
- 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: helm.sh/helm/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: security
- dependency-name: k8s.io/kubelet
  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>

* Regenerate CR schemas

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Evans Mungai <evans@replicated.com>
2024-05-21 12:07:50 +00:00
dependabot[bot]andlnx01 9d41460d33 chore(deps): bump helm.sh/helm/v3 from 3.14.4 to 3.15.0 in /examples/sdk/helm-template in the security group (#1548)
---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  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-05-21 00:19:08 +00:00
Gerard Nguyen fb0f81d076 bug: [sc-103753] Host analysers are not deduplicating during multiple spec merges #1485 (#1542)
* dedupe host collector and analyzer

* dedup analyzer

* add unit test

* use generic
2024-05-17 09:34:29 +10:00
231 changed files with 24287 additions and 6134 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ labels: 'epic'
# Design Proposal
Link to the [proposal](https://github.com/replicatedhq/troubleshoot/tree/main/design/template.md)
Link to the [proposal](https://github.com/replicatedhq/troubleshoot/tree/main/docs/design/template.md)
# Definition of done
+37 -123
View File
@@ -22,34 +22,17 @@ concurrency:
jobs:
fail_if_pull_request_is_draft:
if: github.event.pull_request.draft == true
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- name: Fails in order to indicate that pull request needs to be marked as ready to review and unit tests workflow needs to pass.
run: exit 1
test:
runs-on: ubuntu-20.04
steps:
- uses: actions/setup-go@v5
with:
go-version: "1.21"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
shell: bash
- uses: actions/checkout@v4
- run: make test
test-integration:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v5
with:
go-version: "1.21"
go-version: "1.23"
- name: setup env
run: |
@@ -61,8 +44,8 @@ jobs:
- uses: replicatedhq/action-k3s@main
id: k3s
with:
version: v1.24.1-k3s1
version: v1.31.2-k3s1
# test-integration includes unit tests
- run: make test-integration
ensure-schemas-are-generated:
@@ -70,7 +53,7 @@ jobs:
steps:
- uses: actions/setup-go@v5
with:
go-version: "1.21"
go-version: "1.23"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
@@ -79,14 +62,17 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: make check-schemas
path: github.com/replicatedhq/troubleshoot
- run: |
cd github.com/replicatedhq/troubleshoot
make check-schemas
compile-preflight:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v5
with:
go-version: "1.21"
go-version: "1.23"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
@@ -99,54 +85,6 @@ jobs:
name: preflight
path: bin/preflight
validate-preflight:
runs-on: ubuntu-latest
needs: compile-preflight
steps:
- uses: replicatedhq/action-k3s@main
id: k3s
with:
version: v1.23.6-k3s1
- name: Download preflight binary
uses: actions/download-artifact@v4
with:
name: preflight
path: bin/
- run: chmod +x bin/preflight
- run: |
set +e
./bin/preflight --interactive=false --format=json https://preflight.replicated.com > result.json
EXIT_CODE=$?
cat result.json
EXIT_STATUS=0
if [ $EXIT_CODE -ne 3 ]; then
echo "Expected exit code of 3 (some checks failed), got $EXIT_CODE"
EXIT_STATUS=1
fi
if grep -q "was not collected" result.json; then
echo "Some files were not collected"
EXIT_STATUS=1
fi
if (( `jq '.pass | length' result.json` < 1 )); then
echo "No passing preflights found"
EXIT_STATUS=1
fi
if (( `jq '.warn | length' result.json` < 1 )); then
echo "No warnings found"
EXIT_STATUS=1
fi
if (( `jq '.fail | length' result.json` < 1 )); then
echo "No failed preflights found"
EXIT_STATUS=1
fi
exit $EXIT_STATUS
validate-preflight-e2e:
runs-on: ubuntu-latest
needs: compile-preflight
@@ -155,7 +93,7 @@ jobs:
- uses: replicatedhq/action-k3s@main
id: k3s
with:
version: v1.23.6-k3s1
version: v1.31.2-k3s1
- name: Download preflight binary
uses: actions/download-artifact@v4
with:
@@ -175,7 +113,7 @@ jobs:
steps:
- uses: actions/setup-go@v5
with:
go-version: "1.21"
go-version: "1.23"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
@@ -188,25 +126,6 @@ jobs:
name: support-bundle
path: bin/support-bundle
validate-supportbundle:
runs-on: ubuntu-latest
needs: compile-supportbundle
steps:
- uses: actions/checkout@v4
- uses: replicatedhq/action-k3s@main
id: k3s
with:
version: v1.23.6-k3s1
- name: Download support-bundle binary
uses: actions/download-artifact@v4
with:
name: support-bundle
path: bin/
- run: chmod +x bin/support-bundle
- run: ./bin/support-bundle ./examples/support-bundle/sample-collectors.yaml
- run: ./bin/support-bundle ./examples/support-bundle/sample-supportbundle.yaml
- run: ./bin/support-bundle https://kots.io
validate-supportbundle-e2e:
runs-on: ubuntu-latest
needs: compile-supportbundle
@@ -215,7 +134,7 @@ jobs:
- uses: replicatedhq/action-k3s@main
id: k3s
with:
version: v1.23.6-k3s1
version: v1.31.2-k3s1
- name: Download support bundle binary
uses: actions/download-artifact@v4
with:
@@ -223,19 +142,12 @@ jobs:
path: bin/
- run: chmod +x bin/support-bundle
- run: make support-bundle-e2e-test
validate-supportbundle-e2e-go-test:
runs-on: ubuntu-latest
needs: compile-supportbundle
steps:
- uses: actions/setup-go@v5
with:
go-version: "1.21"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
shell: bash
# Additional e2e tests for support bundle that run in Go, these create a Kind cluster
validate-supportbundle-e2e-go:
runs-on: ubuntu-latest
needs: compile-supportbundle
steps:
- uses: actions/checkout@v4
- name: Download support bundle binary
uses: actions/download-artifact@v4
@@ -243,6 +155,12 @@ jobs:
name: support-bundle
path: bin/
- run: chmod +x bin/support-bundle
- name: Download preflight binary
uses: actions/download-artifact@v4
with:
name: preflight
path: bin/
- run: chmod +x bin/preflight
- run: make support-bundle-e2e-go-test
compile-collect:
@@ -250,7 +168,7 @@ jobs:
steps:
- uses: actions/setup-go@v5
with:
go-version: "1.21"
go-version: "1.23"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
@@ -283,10 +201,10 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: "1.21"
go-version: "1.23"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
uses: goreleaser/goreleaser-action@v6
with:
version: "v0.183.0"
args: build --rm-dist --snapshot --config deploy/.goreleaser.yaml --single-target
@@ -297,9 +215,8 @@ jobs:
goreleaser:
runs-on: ubuntu-latest
needs:
- validate-preflight
- validate-preflight-e2e
- validate-supportbundle
- validate-supportbundle-e2e
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: Checkout
@@ -307,14 +224,14 @@ jobs:
with:
fetch-depth: 0
- uses: azure/docker-login@v1
- uses: azure/docker-login@v2
with:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- uses: actions/setup-go@v5
with:
go-version: "1.21"
go-version: "1.23"
- uses: sigstore/cosign-installer@v3
with:
@@ -335,7 +252,7 @@ jobs:
COSIGN_KEY: ${{ secrets.COSIGN_KEY }}
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
uses: goreleaser/goreleaser-action@v6
with:
version: "v1.x" # Binary version to install
args: release --rm-dist --config deploy/.goreleaser.yaml
@@ -343,11 +260,11 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update new preflight version in krew-index
uses: rajatjindal/krew-release-bot@v0.0.46
uses: rajatjindal/krew-release-bot@v0.0.47
with:
krew_template_file: deploy/krew/preflight.yaml
- name: Update new support-bundle version in krew-index
uses: rajatjindal/krew-release-bot@v0.0.46
uses: rajatjindal/krew-release-bot@v0.0.47
with:
krew_template_file: deploy/krew/support-bundle.yaml
@@ -356,15 +273,12 @@ jobs:
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
- validate-supportbundle-e2e-go
- ensure-schemas-are-generated
steps:
- run: echo "All PR tests passed"
@@ -386,4 +300,4 @@ jobs:
# 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"
run: echo "Validation succeeded"
-9
View File
@@ -14,15 +14,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.21
- name: Install Go deps
run: go mod download
- name: Install trivy
run: |
wget https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.deb
+3 -1
View File
@@ -2,7 +2,7 @@
run:
allow-parallel-runners: true
timeout: 30s
timeout: 10m
linters:
enable:
@@ -11,3 +11,5 @@ linters:
- gofmt
- gosec
- govet
disable:
- errcheck
+16 -16
View File
@@ -7,6 +7,7 @@ VERSION_PACKAGE = github.com/replicatedhq/troubleshoot/pkg/version
VERSION ?=`git describe --tags --dirty`
DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"`
RUN?=""
GOLANGCI_LINT_VERSION ?= "v1.61.0"
GIT_TREE = $(shell git rev-parse --is-inside-work-tree 2>/dev/null)
ifneq "$(GIT_TREE)" ""
@@ -34,7 +35,8 @@ define LDFLAGS
"
endef
BUILDFLAGS = -tags "netgo containers_image_ostree_stub exclude_graphdriver_devicemapper exclude_graphdriver_btrfs containers_image_openpgp" -installsuffix netgo
BUILDTAGS = "netgo containers_image_ostree_stub exclude_graphdriver_devicemapper exclude_graphdriver_btrfs containers_image_openpgp"
BUILDFLAGS = -tags ${BUILDTAGS} -installsuffix netgo
BUILDPATHS = ./pkg/... ./cmd/... ./internal/...
E2EPATHS = ./test/e2e/...
TESTFLAGS ?= -v -coverprofile cover.out
@@ -57,8 +59,8 @@ test: generate fmt vet
# Go tests that require a K8s instance
# TODOLATER: merge with test, so we get unified coverage reports? it'll add 21~sec to the test job though...
.PHONY: test-integration
test-integration:
go test -v --tags "integration exclude_graphdriver_devicemapper exclude_graphdriver_btrfs" ${BUILDPATHS}
test-integration: generate fmt vet
go test -v --tags="integration exclude_graphdriver_devicemapper exclude_graphdriver_btrfs" ${BUILDPATHS}
.PHONY: preflight-e2e-test
preflight-e2e-test:
@@ -114,6 +116,10 @@ bin/analyze:
bin/collect:
go build ${BUILDFLAGS} ${LDFLAGS} -o bin/collect github.com/replicatedhq/troubleshoot/cmd/collect
build-linux: tidy
@echo "Build cli binaries for Linux"
GOOS=linux GOARCH=amd64 $(MAKE) -j bin/support-bundle bin/preflight bin/analyze bin/collect
.PHONY: fmt
fmt:
go fmt ${BUILDPATHS}
@@ -127,13 +133,15 @@ generate: controller-gen client-gen
$(CONTROLLER_GEN) \
object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/...
$(CLIENT_GEN) \
--output-base=./../../../ \
--output-base=. \
--output-package=github.com/replicatedhq/troubleshoot/pkg/client \
--clientset-name troubleshootclientset \
--input-base github.com/replicatedhq/troubleshoot/pkg/apis \
--input troubleshoot/v1beta1 \
--input troubleshoot/v1beta2 \
-h ./hack/boilerplate.go.txt
cp -r github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset pkg/client
rm -rf github.com
.PHONY: openapischema
openapischema: controller-gen
@@ -141,14 +149,14 @@ openapischema: controller-gen
controller-gen crd +output:dir=./config/crds paths=./pkg/apis/troubleshoot/v1beta2
check-schemas: generate schemas
@if [ -n "$(shell git status --short)" ]; then \
@if [ -n "$$(git status --short)" ]; then \
echo -e "\033[31mThe git repo is dirty :( Ensure all generated files are committed e.g CRD schema files\033[0;m"; \
git status --short; \
exit 1; \
fi
.PHONY: schemas
schemas: fmt vet openapischema bin/schemagen
schemas: openapischema bin/schemagen
./bin/schemagen --output-dir ./schemas
bin/schemagen:
@@ -162,12 +170,12 @@ 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.14.0
go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.2
CONTROLLER_GEN=$(shell which controller-gen)
.PHONY: client-gen
client-gen:
go install k8s.io/code-generator/cmd/client-gen@v0.28.8
go install k8s.io/code-generator/cmd/client-gen@v0.28.13
CLIENT_GEN=$(shell which client-gen)
.PHONY: release
@@ -231,14 +239,6 @@ scan:
--ignore-unfixed \
./
.PHONY: lint
lint:
golangci-lint run --new -c .golangci.yaml ${BUILDPATHS}
.PHONY: lint-and-fix
lint-and-fix:
golangci-lint run --new --fix -c .golangci.yaml ${BUILDPATHS}
.PHONY: watch
watch: npm-install
bin/watch.js
+21
View File
@@ -0,0 +1,21 @@
package cli
import (
"errors"
"syscall"
"github.com/replicatedhq/troubleshoot/internal/util"
)
func checkAndSetChroot(newroot string) error {
if newroot == "" {
return nil
}
if !util.IsRunningAsRoot() {
return errors.New("Can only chroot when run as root")
}
if err := syscall.Chroot(newroot); err != nil {
return err
}
return nil
}
+21
View File
@@ -0,0 +1,21 @@
package cli
import (
"errors"
"syscall"
"github.com/replicatedhq/troubleshoot/internal/util"
)
func checkAndSetChroot(newroot string) error {
if newroot == "" {
return nil
}
if !util.IsRunningAsRoot() {
return errors.New("Can only chroot when run as root")
}
if err := syscall.Chroot(newroot); err != nil {
return err
}
return nil
}
+9
View File
@@ -0,0 +1,9 @@
package cli
import (
"errors"
)
func checkAndSetChroot(newroot string) error {
return errors.New("chroot is only implimented in linux/darwin")
}
+5
View File
@@ -32,6 +32,10 @@ func RootCmd() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
v := viper.GetViper()
if err := checkAndSetChroot(v.GetString("chroot")); err != nil {
return err
}
return runCollect(v, args[0])
},
PostRun: func(cmd *cobra.Command, args []string) {
@@ -53,6 +57,7 @@ func RootCmd() *cobra.Command {
cmd.Flags().String("selector", "", "selector (label query) to filter remote collection nodes on.")
cmd.Flags().Bool("collect-without-permissions", false, "always generate a support bundle, even if it some require additional permissions")
cmd.Flags().Bool("debug", false, "enable debug logging")
cmd.Flags().String("chroot", "", "Chroot to path")
// hidden in favor of the `insecure-skip-tls-verify` flag
cmd.Flags().Bool("allow-insecure-connections", false, "when set, do not verify TLS certs when retrieving spec and reporting results")
+8
View File
@@ -2,6 +2,7 @@ package cli
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
@@ -49,6 +50,13 @@ func runCollect(v *viper.Viper, arg string) error {
}
collectorContent = spec
} else if arg == "-" {
b, err := io.ReadAll(os.Stdin)
if err != nil {
return err
}
collectorContent = b
} else if _, err = os.Stat(arg); err == nil {
b, err := os.ReadFile(arg)
if err != nil {
+2 -1
View File
@@ -50,7 +50,7 @@ that a cluster meets the requirements to run an application.`,
err = preflight.RunPreflights(v.GetBool("interactive"), v.GetString("output"), v.GetString("format"), args)
if !v.GetBool("dry-run") && (v.GetBool("debug") || v.IsSet("v")) {
fmt.Printf("\n%s", traces.GetExporterInstance().GetSummary())
fmt.Fprintf(os.Stderr, "\n%s", traces.GetExporterInstance().GetSummary())
}
return err
@@ -71,6 +71,7 @@ that a cluster meets the requirements to run an application.`,
// Dry run flag should be in cmd.PersistentFlags() flags made available to all subcommands
// Adding here to avoid that
cmd.Flags().Bool("dry-run", false, "print the preflight spec without running preflight checks")
cmd.Flags().Bool("no-uri", false, "When this flag is used, Preflight does not attempt to retrieve the spec referenced by the uri: field`")
k8sutil.AddFlags(cmd.Flags())
+20 -96
View File
@@ -2,10 +2,6 @@ package cli
import (
"fmt"
"io/ioutil"
"os"
"path"
"time"
"github.com/mitchellh/go-wordwrap"
"github.com/pkg/errors"
@@ -19,15 +15,14 @@ import (
var (
selectedResult = 0
table = widgets.NewTable()
isShowingSaved = false
)
func showInteractiveResults(supportBundleName string, analyzeResults []*analyzerunner.AnalyzeResult) error {
func showInteractiveResults(supportBundleName string, analyzeResults []*analyzerunner.AnalyzeResult, archivePath string) error {
if err := ui.Init(); err != nil {
return errors.Wrap(err, "failed to create terminal ui")
}
defer ui.Close()
drawUI(supportBundleName, analyzeResults)
drawUI(supportBundleName, analyzeResults, archivePath)
uiEvents := ui.PollEvents()
for {
@@ -37,29 +32,10 @@ func showInteractiveResults(supportBundleName string, analyzeResults []*analyzer
case "<C-c>":
return nil
case "q":
if isShowingSaved == true {
isShowingSaved = false
ui.Clear()
drawUI(supportBundleName, analyzeResults)
} else {
return nil
}
case "s":
filename, err := save(analyzeResults)
if err != nil {
// show
} else {
showSaved(filename)
go func() {
time.Sleep(time.Second * 5)
isShowingSaved = false
ui.Clear()
drawUI(supportBundleName, analyzeResults)
}()
}
return nil
case "<Resize>":
ui.Clear()
drawUI(supportBundleName, analyzeResults)
drawUI(supportBundleName, analyzeResults, archivePath)
case "<Down>":
if selectedResult < len(analyzeResults)-1 {
selectedResult++
@@ -69,7 +45,7 @@ func showInteractiveResults(supportBundleName string, analyzeResults []*analyzer
}
table.ScrollDown()
ui.Clear()
drawUI(supportBundleName, analyzeResults)
drawUI(supportBundleName, analyzeResults, archivePath)
case "<Up>":
if selectedResult > 0 {
selectedResult--
@@ -79,16 +55,16 @@ func showInteractiveResults(supportBundleName string, analyzeResults []*analyzer
}
table.ScrollUp()
ui.Clear()
drawUI(supportBundleName, analyzeResults)
drawUI(supportBundleName, analyzeResults, archivePath)
}
}
}
}
func drawUI(supportBundleName string, analyzeResults []*analyzerunner.AnalyzeResult) {
func drawUI(supportBundleName string, analyzeResults []*analyzerunner.AnalyzeResult, archivePath string) {
drawGrid(analyzeResults)
drawHeader(supportBundleName)
drawFooter()
drawFooter(archivePath)
}
func drawGrid(analyzeResults []*analyzerunner.AnalyzeResult) {
@@ -101,9 +77,7 @@ func drawHeader(supportBundleName string) {
title := widgets.NewParagraph()
title.Text = fmt.Sprintf("%s Support Bundle Analysis", util.AppName(supportBundleName))
title.TextStyle.Fg = ui.ColorWhite
title.TextStyle.Bg = ui.ColorClear
title.TextStyle.Modifier = ui.ModifierBold
title.TextStyle = ui.NewStyle(ui.ColorWhite, ui.ColorClear, ui.ModifierBold)
title.Border = false
left := termWidth/2 - 2*len(title.Text)/3
@@ -113,11 +87,18 @@ func drawHeader(supportBundleName string) {
ui.Render(title)
}
func drawFooter() {
func drawFooter(archivePath string) {
termWidth, termHeight := ui.TerminalDimensions()
archivePathMsg := widgets.NewParagraph()
archivePathMsg.Text = "Support bundle archive: " + archivePath
archivePathMsg.Border = false
archivePathMsg.TextStyle = ui.NewStyle(ui.ColorWhite, ui.ColorClear, ui.ModifierBold)
archivePathMsg.SetRect(0, termHeight-3, termWidth, termHeight-2)
instructions := widgets.NewParagraph()
instructions.Text = "[q] quit [s] save [↑][↓] scroll"
instructions.Text = "[q] quit [↑][↓] scroll"
instructions.Border = false
left := 0
@@ -126,13 +107,13 @@ func drawFooter() {
bottom := termHeight
instructions.SetRect(left, top, right, bottom)
ui.Render(instructions)
ui.Render(archivePathMsg, instructions)
}
func drawAnalyzersTable(analyzeResults []*analyzerunner.AnalyzeResult) {
termWidth, termHeight := ui.TerminalDimensions()
table.SetRect(0, 3, termWidth/2, termHeight-6)
table.SetRect(0, 3, termWidth/2, termHeight-4)
table.FillRow = true
table.Border = true
table.Rows = [][]string{}
@@ -213,60 +194,3 @@ func drawDetails(analysisResult *analyzerunner.AnalyzeResult) {
message.SetRect(termWidth/2, currentTop, termWidth, currentTop+height)
ui.Render(message)
}
func showSaved(filename string) {
termWidth, termHeight := ui.TerminalDimensions()
savedMessage := widgets.NewParagraph()
savedMessage.Text = fmt.Sprintf("Support Bundle analysis results saved to\n\n%s", filename)
savedMessage.WrapText = true
savedMessage.Border = true
left := termWidth/2 - 20
right := termWidth/2 + 20
top := termHeight/2 - 4
bottom := termHeight/2 + 4
savedMessage.SetRect(left, top, right, bottom)
ui.Render(savedMessage)
isShowingSaved = true
}
func save(analyzeResults []*analyzerunner.AnalyzeResult) (string, error) {
filename := path.Join(util.HomeDir(), fmt.Sprintf("%s-results.txt", "support-bundle"))
_, err := os.Stat(filename)
if err == nil {
os.Remove(filename)
}
results := ""
for _, analyzeResult := range analyzeResults {
result := ""
if analyzeResult.IsPass {
result = "Check PASS\n"
} else if analyzeResult.IsWarn {
result = "Check WARN\n"
} else if analyzeResult.IsFail {
result = "Check FAIL\n"
}
result = result + fmt.Sprintf("Title: %s\n", analyzeResult.Title)
result = result + fmt.Sprintf("Message: %s\n", analyzeResult.Message)
if analyzeResult.URI != "" {
result = result + fmt.Sprintf("URI: %s\n", analyzeResult.URI)
}
result = result + "\n------------\n"
results = results + result
}
if err := ioutil.WriteFile(filename, []byte(results), 0644); err != nil {
return "", errors.Wrap(err, "failed to save preflight results")
}
return filename, nil
}
+1 -1
View File
@@ -64,7 +64,7 @@ For more information on redactors visit https://troubleshoot.sh/docs/redact/
if output == "" {
output = fmt.Sprintf("redacted-support-bundle-%s.tar.gz", time.Now().Format("2006-01-02T15_04_05"))
}
err = collectorResult.ArchiveSupportBundle(bundleDir, output)
err = collectorResult.ArchiveBundle(bundleDir, output)
if err != nil {
return errors.Wrap(err, "failed to create support bundle archive")
}
+25 -5
View File
@@ -18,9 +18,17 @@ func RootCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "support-bundle [urls...]",
Args: cobra.MinimumNArgs(0),
Short: "Generate a support bundle",
Long: `A support bundle is an archive of files, output, metrics and state
from a server that can be used to assist when troubleshooting a Kubernetes cluster.`,
Short: "Generate a support bundle from a Kubernetes cluster or specified sources",
Long: `Generate a support bundle, an archive containing files, output, metrics, and cluster state to aid in troubleshooting Kubernetes clusters.
If no arguments are provided, specs are automatically loaded from the cluster by default.
**Argument Types**:
1. **Secret**: Load specs from a Kubernetes Secret. Format: "secret/namespace-name/secret-name[/data-key]"
2. **ConfigMap**: Load specs from a Kubernetes ConfigMap. Format: "configmap/namespace-name/configmap-name[/data-key]"
3. **File**: Load specs from a local file. Format: Local file path
4. **Standard Input**: Read specs from stdin. Format: "-"
5. **URL**: Load specs from a URL. Supports HTTP and OCI registry URLs.`,
SilenceUsage: true,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
v := viper.GetViper()
@@ -36,6 +44,18 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust
RunE: func(cmd *cobra.Command, args []string) error {
v := viper.GetViper()
// If there are no locations to load specs from passed in the cli args, we should
// load them from the cluster by setting "load-cluster-specs=true". If the caller
// provided "--load-cluster-specs" cli option, we should respect it.
if len(args) == 0 {
// Check if --load-cluster-specs was set by the cli caller to avoid overriding it
flg := cmd.Flags().Lookup("load-cluster-specs")
if flg != nil && !flg.Changed {
// Load specs from the cluster if no spec(s) is(are) provided in the cli args
v.Set("load-cluster-specs", true)
}
}
closer, err := traces.ConfigureTracing("support-bundle")
if err != nil {
// Do not fail running support-bundle if tracing fails
@@ -46,7 +66,7 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust
err = runTroubleshoot(v, args)
if !v.IsSet("dry-run") && (v.GetBool("debug") || v.IsSet("v")) {
fmt.Printf("\n%s", traces.GetExporterInstance().GetSummary())
fmt.Fprintf(os.Stderr, "\n%s", traces.GetExporterInstance().GetSummary())
}
return err
@@ -69,7 +89,7 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust
cmd.Flags().Bool("interactive", true, "enable/disable interactive mode")
cmd.Flags().Bool("collect-without-permissions", true, "always generate a support bundle, even if it some require additional permissions")
cmd.Flags().StringSliceP("selector", "l", []string{"troubleshoot.sh/kind=support-bundle"}, "selector to filter on for loading additional support bundle specs found in secrets within the cluster")
cmd.Flags().Bool("load-cluster-specs", false, "enable/disable loading additional troubleshoot specs found within the cluster. required when no specs are provided on the command line")
cmd.Flags().Bool("load-cluster-specs", false, "enable/disable loading additional troubleshoot specs found within the cluster. Do not load by default unless no specs are provided in the cli args")
cmd.Flags().String("since-time", "", "force pod logs collectors to return logs after a specific date (RFC3339)")
cmd.Flags().String("since", "", "force pod logs collectors to return logs newer than a relative duration like 5s, 2m, or 3h.")
cmd.Flags().StringP("output", "o", "", "specify the output file path for the support bundle")
+34 -11
View File
@@ -9,6 +9,7 @@ import (
"os"
"os/signal"
"path/filepath"
"reflect"
"sync"
"time"
@@ -38,9 +39,6 @@ import (
func runTroubleshoot(v *viper.Viper, args []string) error {
ctx := context.Background()
if !v.GetBool("load-cluster-specs") && len(args) < 1 {
return errors.New("flag load-cluster-specs must be set if no specs are provided on the command line")
}
restConfig, err := k8sutil.GetRESTConfig()
if err != nil {
@@ -112,7 +110,7 @@ func runTroubleshoot(v *viper.Viper, args []string) error {
}
if interactive {
if len(mainBundle.Spec.HostCollectors) > 0 && !util.IsRunningAsRoot() {
if len(mainBundle.Spec.HostCollectors) > 0 && !util.IsRunningAsRoot() && !mainBundle.Spec.RunHostCollectorsInPod {
fmt.Print(cursor.Show())
if util.PromptYesNo(util.HOST_COLLECTORS_RUN_AS_ROOT_PROMPT) {
fmt.Println("Exiting...")
@@ -186,6 +184,7 @@ func runTroubleshoot(v *viper.Viper, args []string) error {
OutputPath: v.GetString("output"),
Redact: v.GetBool("redact"),
FromCLI: true,
RunHostCollectorsInPod: mainBundle.Spec.RunHostCollectorsInPod,
}
nonInteractiveOutput := analysisOutput{}
@@ -200,7 +199,7 @@ func runTroubleshoot(v *viper.Viper, args []string) error {
if len(response.AnalyzerResults) > 0 {
if interactive {
if err := showInteractiveResults(mainBundle.Name, response.AnalyzerResults); err != nil {
if err := showInteractiveResults(mainBundle.Name, response.AnalyzerResults, response.ArchivePath); err != nil {
interactive = false
}
} else {
@@ -227,7 +226,7 @@ the %s Admin Console to begin analysis.`
return nil
}
fmt.Printf("\n%s\n", response.ArchivePath)
fmt.Printf("\nA support bundle was generated and saved at %s. Please send this file to your software vendor for support.\n", response.ArchivePath)
return nil
}
@@ -285,14 +284,21 @@ func loadSupportBundleSpecsFromURIs(ctx context.Context, kinds *loader.Troublesh
}
func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface) (*troubleshootv1beta2.SupportBundle, *troubleshootv1beta2.Redactor, error) {
// Append redactor uris to the args
allArgs := append(args, viper.GetStringSlice("redactors")...)
kinds, err := specs.LoadFromCLIArgs(ctx, client, allArgs, viper.GetViper())
var (
kinds = loader.NewTroubleshootKinds()
vp = viper.GetViper()
redactors = vp.GetStringSlice("redactors")
allArgs = append(args, redactors...)
err error
)
kinds, err = specs.LoadFromCLIArgs(ctx, client, allArgs, vp)
if err != nil {
return nil, nil, err
return nil, nil, errors.Wrap(err, "failed to load specs from CLI args")
}
// Load additional specs from support bundle URIs
// only when no-uri flag is not set and no URLs are provided in the args
if !viper.GetBool("no-uri") {
err := loadSupportBundleSpecsFromURIs(ctx, kinds)
if err != nil {
@@ -305,7 +311,10 @@ func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface)
if len(kinds.CollectorsV1Beta2) == 0 &&
len(kinds.HostCollectorsV1Beta2) == 0 &&
len(kinds.SupportBundlesV1Beta2) == 0 {
return nil, nil, errors.New("no collectors specified to run")
return nil, nil, types.NewExitCodeError(
constants.EXIT_CODE_CATCH_ALL,
errors.New("no collectors specified to run. Use --debug and/or -v=2 to see more information"),
)
}
// Merge specs
@@ -320,10 +329,18 @@ func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface)
Name: "merged-support-bundle-spec",
},
}
var enableRunHostCollectorsInPod bool
for _, sb := range kinds.SupportBundlesV1Beta2 {
sb := sb
mainBundle = supportbundle.ConcatSpec(mainBundle, &sb)
//check if sb has metadata and if it has RunHostCollectorsInPod set to true
if !reflect.DeepEqual(sb.ObjectMeta, metav1.ObjectMeta{}) && sb.Spec.RunHostCollectorsInPod {
enableRunHostCollectorsInPod = sb.Spec.RunHostCollectorsInPod
}
}
mainBundle.Spec.RunHostCollectorsInPod = enableRunHostCollectorsInPod
for _, c := range kinds.CollectorsV1Beta2 {
mainBundle.Spec.Collectors = util.Append(mainBundle.Spec.Collectors, c.Spec.Collectors)
@@ -362,6 +379,12 @@ func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface)
additionalRedactors.Spec.Redactors = util.Append(additionalRedactors.Spec.Redactors, r.Spec.Redactors)
}
// dedupe specs
mainBundle.Spec.Collectors = util.Dedup(mainBundle.Spec.Collectors)
mainBundle.Spec.Analyzers = util.Dedup(mainBundle.Spec.Analyzers)
mainBundle.Spec.HostCollectors = util.Dedup(mainBundle.Spec.HostCollectors)
mainBundle.Spec.HostAnalyzers = util.Dedup(mainBundle.Spec.HostAnalyzers)
return mainBundle, additionalRedactors, nil
}
+74
View File
@@ -343,3 +343,77 @@ spec:
})
}
}
func Test_loadDuplicatedBundleSpecs(t *testing.T) {
spec := testutils.ServeFromFilePath(t, `
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: sb
spec:
collectors:
- helm: {}
analyzers:
- clusterVersion: {}
hostCollectors:
- cpu: {}
hostAnalyzers:
- cpu: {}
`)
args := []string{spec, spec}
ctx := context.Background()
client := testclient.NewSimpleClientset()
sb, _, err := loadSpecs(ctx, args, client)
require.NoError(t, err)
assert.Len(t, sb.Spec.Collectors, 1+2) // default clusterInfo + clusterResources
assert.Len(t, sb.Spec.Analyzers, 1)
assert.Len(t, sb.Spec.HostCollectors, 1)
assert.Len(t, sb.Spec.HostAnalyzers, 1)
}
func Test_loadSpecsFromURL(t *testing.T) {
// Run a webserver to serve the URI spec
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: sb-2
spec:
collectors:
- logs:
name: podlogs/kotsadm
selector:
- app=kotsadm`))
}))
defer srv.Close()
// update URI spec with the server URL
orig := strings.ReplaceAll(templSpec(), "$MY_URI", srv.URL)
// now create a webserver to serve the spec with URI
srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(orig))
}))
defer srv.Close()
fileSpec := testutils.ServeFromFilePath(t, `
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: sb
spec:
collectors:
- helm: {}`)
// test and ensure that URI spec is not loaded
ctx := context.Background()
client := testclient.NewSimpleClientset()
sb, _, err := loadSpecs(ctx, []string{fileSpec, srv.URL}, client)
require.NoError(t, err)
assert.Len(t, sb.Spec.Collectors, 2+2) // default + clusterInfo + clusterResources
assert.NotNil(t, sb.Spec.Collectors[0].Helm) // come from the file spec
assert.NotNil(t, sb.Spec.Collectors[1].ConfigMap) // come from the original spec
assert.Nil(t, sb.Spec.Collectors[1].Logs) // come from the URI spec
}
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
controller-gen.kubebuilder.io/version: v0.16.2
name: analyzers.troubleshoot.replicated.com
spec:
group: troubleshoot.replicated.com
@@ -417,6 +417,12 @@ spec:
type: string
podCapacity:
type: string
resourceAllocatable:
type: string
resourceCapacity:
type: string
resourceName:
type: string
selector:
properties:
matchLabel:
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
controller-gen.kubebuilder.io/version: v0.16.2
name: collectors.troubleshoot.replicated.com
spec:
group: troubleshoot.replicated.com
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
controller-gen.kubebuilder.io/version: v0.16.2
name: preflights.troubleshoot.replicated.com
spec:
group: troubleshoot.replicated.com
@@ -417,6 +417,12 @@ spec:
type: string
podCapacity:
type: string
resourceAllocatable:
type: string
resourceCapacity:
type: string
resourceName:
type: string
selector:
properties:
matchLabel:
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
controller-gen.kubebuilder.io/version: v0.16.2
name: redactors.troubleshoot.replicated.com
spec:
group: troubleshoot.replicated.com
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
controller-gen.kubebuilder.io/version: v0.16.2
name: supportbundles.troubleshoot.replicated.com
spec:
group: troubleshoot.replicated.com
@@ -448,6 +448,12 @@ spec:
type: string
podCapacity:
type: string
resourceAllocatable:
type: string
resourceCapacity:
type: string
resourceName:
type: string
selector:
properties:
matchLabel:
+354 -3
View File
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
controller-gen.kubebuilder.io/version: v0.16.2
name: analyzers.troubleshoot.sh
spec:
group: troubleshoot.sh
@@ -141,6 +141,61 @@ spec:
required:
- outcomes
type: object
clusterContainerStatuses:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
exclude:
type: BoolString
namespaces:
items:
type: string
type: array
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
restartCount:
format: int32
type: integer
strict:
type: BoolString
required:
- outcomes
- restartCount
type: object
clusterPodStatuses:
properties:
annotations:
@@ -673,6 +728,55 @@ spec:
required:
- collectorName
type: object
http:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
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:
- outcomes
type: object
imagePullSecret:
properties:
annotations:
@@ -1117,10 +1221,10 @@ spec:
type: BoolString
filters:
properties:
architecture:
type: string
cpuAllocatable:
type: string
cpuArchitecture:
type: string
cpuCapacity:
type: string
ephemeralStorageAllocatable:
@@ -1135,8 +1239,44 @@ spec:
type: string
podCapacity:
type: string
resourceAllocatable:
type: string
resourceCapacity:
type: string
resourceName:
type: string
selector:
properties:
matchExpressions:
items:
description: |-
A label selector requirement is a selector that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the
selector applies to.
type: string
operator:
description: |-
operator represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: |-
values is an array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
matchLabel:
additionalProperties:
type: string
@@ -2299,6 +2439,117 @@ spec:
required:
- outcomes
type: object
jsonCompare:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
fileName:
type: string
jsonPath:
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
path:
type: string
strict:
type: BoolString
value:
type: string
required:
- outcomes
type: object
kernelConfigs:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
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
selectedConfigs:
items:
type: string
type: array
strict:
type: BoolString
required:
- outcomes
- selectedConfigs
type: object
kernelModules:
properties:
annotations:
@@ -2397,6 +2648,55 @@ spec:
required:
- outcomes
type: object
networkNamespaceConnectivity:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
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:
- outcomes
type: object
subnetAvailable:
properties:
annotations:
@@ -2446,6 +2746,55 @@ spec:
required:
- outcomes
type: object
sysctl:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
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:
- outcomes
type: object
systemPackages:
properties:
annotations:
@@ -2803,6 +3152,8 @@ spec:
type: object
type: object
type: array
uri:
type: string
type: object
status:
description: AnalyzerStatus defines the observed state of Analyzer
File diff suppressed because it is too large Load Diff
+371 -1
View File
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
controller-gen.kubebuilder.io/version: v0.16.2
name: hostcollectors.troubleshoot.sh
spec:
group: troubleshoot.sh
@@ -588,6 +588,117 @@ spec:
required:
- outcomes
type: object
jsonCompare:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
fileName:
type: string
jsonPath:
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
path:
type: string
strict:
type: BoolString
value:
type: string
required:
- outcomes
type: object
kernelConfigs:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
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
selectedConfigs:
items:
type: string
type: array
strict:
type: BoolString
required:
- outcomes
- selectedConfigs
type: object
kernelModules:
properties:
annotations:
@@ -686,6 +797,55 @@ spec:
required:
- outcomes
type: object
networkNamespaceConnectivity:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
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:
- outcomes
type: object
subnetAvailable:
properties:
annotations:
@@ -735,6 +895,55 @@ spec:
required:
- outcomes
type: object
sysctl:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
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:
- outcomes
type: object
systemPackages:
properties:
annotations:
@@ -1129,6 +1338,15 @@ spec:
required:
- paths
type: object
cgroups:
properties:
collectorName:
type: string
exclude:
type: BoolString
mountPoint:
type: string
type: object
copy:
properties:
collectorName:
@@ -1158,6 +1376,19 @@ spec:
required:
- path
type: object
dns:
properties:
collectorName:
type: string
exclude:
type: BoolString
hostnames:
items:
type: string
type: array
required:
- hostnames
type: object
filesystemPerformance:
description: |-
FilesystemPerformance benchmarks sequential write latency on a single file.
@@ -1217,6 +1448,12 @@ spec:
background IOPS feature if enabled, since those must be fixed at 4096.
format: int64
type: integer
runTime:
description: |-
Limit runtime. The test will run until it completes the configured I/O workload or until it
has run for this specified amount of time, whichever occurs first. When the unit is omitted,
the value is interpreted in seconds. Defaults to 120 seconds. Set to "0" to disable.
type: string
sync:
description: Whether to call sync on the file after each
write. Does not apply to background IOPS task.
@@ -1261,11 +1498,34 @@ spec:
type: object
insecureSkipVerify:
type: boolean
proxy:
type: string
timeout:
description: |-
Timeout is the time to wait for a server's response. Its a duration e.g 15s, 2h30m.
Missing value or empty string or means no timeout.
type: string
tls:
properties:
cacert:
type: string
clientCert:
type: string
clientKey:
type: string
secret:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
skipVerify:
type: boolean
type: object
url:
type: string
required:
@@ -1281,11 +1541,34 @@ spec:
type: object
insecureSkipVerify:
type: boolean
proxy:
type: string
timeout:
description: |-
Timeout is the time to wait for a server's response. Its a duration e.g 15s, 2h30m.
Missing value or empty string or means no timeout.
type: string
tls:
properties:
cacert:
type: string
clientCert:
type: string
clientKey:
type: string
secret:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
skipVerify:
type: boolean
type: object
url:
type: string
required:
@@ -1301,11 +1584,34 @@ spec:
type: object
insecureSkipVerify:
type: boolean
proxy:
type: string
timeout:
description: |-
Timeout is the time to wait for a server's response. Its a duration e.g 15s, 2h30m.
Missing value or empty string or means no timeout.
type: string
tls:
properties:
cacert:
type: string
clientCert:
type: string
clientKey:
type: string
secret:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
skipVerify:
type: boolean
type: object
url:
type: string
required:
@@ -1338,6 +1644,42 @@ spec:
exclude:
type: BoolString
type: object
journald:
properties:
collectorName:
type: string
dmesg:
type: boolean
exclude:
type: BoolString
lines:
type: integer
output:
type: string
reverse:
type: boolean
since:
type: string
system:
type: boolean
timeout:
type: string
units:
items:
type: string
type: array
until:
type: string
utc:
type: boolean
type: object
kernelConfigs:
properties:
collectorName:
type: string
exclude:
type: BoolString
type: object
kernelModules:
properties:
collectorName:
@@ -1359,6 +1701,25 @@ spec:
exclude:
type: BoolString
type: object
networkNamespaceConnectivity:
properties:
collectorName:
type: string
exclude:
type: BoolString
fromCIDR:
type: string
port:
type: integer
timeout:
type: string
toCIDR:
type: string
required:
- fromCIDR
- port
- toCIDR
type: object
run:
properties:
args:
@@ -1407,6 +1768,13 @@ spec:
- CIDRRangeAlloc
- desiredCIDR
type: object
sysctl:
properties:
collectorName:
type: string
exclude:
type: BoolString
type: object
systemPackages:
properties:
amzn:
@@ -1562,6 +1930,8 @@ spec:
type: object
type: object
type: array
uri:
type: string
type: object
status:
description: HostCollectorStatus defines the observed state of HostCollector
+453 -1
View File
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
controller-gen.kubebuilder.io/version: v0.16.2
name: hostpreflights.troubleshoot.sh
spec:
group: troubleshoot.sh
@@ -588,6 +588,117 @@ spec:
required:
- outcomes
type: object
jsonCompare:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
fileName:
type: string
jsonPath:
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
path:
type: string
strict:
type: BoolString
value:
type: string
required:
- outcomes
type: object
kernelConfigs:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
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
selectedConfigs:
items:
type: string
type: array
strict:
type: BoolString
required:
- outcomes
- selectedConfigs
type: object
kernelModules:
properties:
annotations:
@@ -686,6 +797,55 @@ spec:
required:
- outcomes
type: object
networkNamespaceConnectivity:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
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:
- outcomes
type: object
subnetAvailable:
properties:
annotations:
@@ -735,6 +895,55 @@ spec:
required:
- outcomes
type: object
sysctl:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
collectorName:
type: string
exclude:
type: BoolString
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:
- outcomes
type: object
systemPackages:
properties:
annotations:
@@ -1129,6 +1338,15 @@ spec:
required:
- paths
type: object
cgroups:
properties:
collectorName:
type: string
exclude:
type: BoolString
mountPoint:
type: string
type: object
copy:
properties:
collectorName:
@@ -1158,6 +1376,19 @@ spec:
required:
- path
type: object
dns:
properties:
collectorName:
type: string
exclude:
type: BoolString
hostnames:
items:
type: string
type: array
required:
- hostnames
type: object
filesystemPerformance:
description: |-
FilesystemPerformance benchmarks sequential write latency on a single file.
@@ -1217,6 +1448,12 @@ spec:
background IOPS feature if enabled, since those must be fixed at 4096.
format: int64
type: integer
runTime:
description: |-
Limit runtime. The test will run until it completes the configured I/O workload or until it
has run for this specified amount of time, whichever occurs first. When the unit is omitted,
the value is interpreted in seconds. Defaults to 120 seconds. Set to "0" to disable.
type: string
sync:
description: Whether to call sync on the file after each
write. Does not apply to background IOPS task.
@@ -1261,11 +1498,34 @@ spec:
type: object
insecureSkipVerify:
type: boolean
proxy:
type: string
timeout:
description: |-
Timeout is the time to wait for a server's response. Its a duration e.g 15s, 2h30m.
Missing value or empty string or means no timeout.
type: string
tls:
properties:
cacert:
type: string
clientCert:
type: string
clientKey:
type: string
secret:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
skipVerify:
type: boolean
type: object
url:
type: string
required:
@@ -1281,11 +1541,34 @@ spec:
type: object
insecureSkipVerify:
type: boolean
proxy:
type: string
timeout:
description: |-
Timeout is the time to wait for a server's response. Its a duration e.g 15s, 2h30m.
Missing value or empty string or means no timeout.
type: string
tls:
properties:
cacert:
type: string
clientCert:
type: string
clientKey:
type: string
secret:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
skipVerify:
type: boolean
type: object
url:
type: string
required:
@@ -1301,11 +1584,34 @@ spec:
type: object
insecureSkipVerify:
type: boolean
proxy:
type: string
timeout:
description: |-
Timeout is the time to wait for a server's response. Its a duration e.g 15s, 2h30m.
Missing value or empty string or means no timeout.
type: string
tls:
properties:
cacert:
type: string
clientCert:
type: string
clientKey:
type: string
secret:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
skipVerify:
type: boolean
type: object
url:
type: string
required:
@@ -1338,6 +1644,42 @@ spec:
exclude:
type: BoolString
type: object
journald:
properties:
collectorName:
type: string
dmesg:
type: boolean
exclude:
type: BoolString
lines:
type: integer
output:
type: string
reverse:
type: boolean
since:
type: string
system:
type: boolean
timeout:
type: string
units:
items:
type: string
type: array
until:
type: string
utc:
type: boolean
type: object
kernelConfigs:
properties:
collectorName:
type: string
exclude:
type: BoolString
type: object
kernelModules:
properties:
collectorName:
@@ -1359,6 +1701,25 @@ spec:
exclude:
type: BoolString
type: object
networkNamespaceConnectivity:
properties:
collectorName:
type: string
exclude:
type: BoolString
fromCIDR:
type: string
port:
type: integer
timeout:
type: string
toCIDR:
type: string
required:
- fromCIDR
- port
- toCIDR
type: object
run:
properties:
args:
@@ -1407,6 +1768,13 @@ spec:
- CIDRRangeAlloc
- desiredCIDR
type: object
sysctl:
properties:
collectorName:
type: string
exclude:
type: BoolString
type: object
systemPackages:
properties:
amzn:
@@ -1663,6 +2031,12 @@ spec:
background IOPS feature if enabled, since those must be fixed at 4096.
format: int64
type: integer
runTime:
description: |-
Limit runtime. The test will run until it completes the configured I/O workload or until it
has run for this specified amount of time, whichever occurs first. When the unit is omitted,
the value is interpreted in seconds. Defaults to 120 seconds. Set to "0" to disable.
type: string
sync:
description: Whether to call sync on the file after each
write. Does not apply to background IOPS task.
@@ -1679,6 +2053,13 @@ spec:
- backgroundWriteIOPSJobs
- enableBackgroundIOPS
type: object
hostOS:
properties:
collectorName:
type: string
exclude:
type: BoolString
type: object
hostServices:
properties:
collectorName:
@@ -1700,11 +2081,34 @@ spec:
type: object
insecureSkipVerify:
type: boolean
proxy:
type: string
timeout:
description: |-
Timeout is the time to wait for a server's response. Its a duration e.g 15s, 2h30m.
Missing value or empty string or means no timeout.
type: string
tls:
properties:
cacert:
type: string
clientCert:
type: string
clientKey:
type: string
secret:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
skipVerify:
type: boolean
type: object
url:
type: string
required:
@@ -1720,11 +2124,34 @@ spec:
type: object
insecureSkipVerify:
type: boolean
proxy:
type: string
timeout:
description: |-
Timeout is the time to wait for a server's response. Its a duration e.g 15s, 2h30m.
Missing value or empty string or means no timeout.
type: string
tls:
properties:
cacert:
type: string
clientCert:
type: string
clientKey:
type: string
secret:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
skipVerify:
type: boolean
type: object
url:
type: string
required:
@@ -1740,11 +2167,34 @@ spec:
type: object
insecureSkipVerify:
type: boolean
proxy:
type: string
timeout:
description: |-
Timeout is the time to wait for a server's response. Its a duration e.g 15s, 2h30m.
Missing value or empty string or means no timeout.
type: string
tls:
properties:
cacert:
type: string
clientCert:
type: string
clientKey:
type: string
secret:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
skipVerify:
type: boolean
type: object
url:
type: string
required:
@@ -1876,6 +2326,8 @@ spec:
type: object
type: object
type: array
uri:
type: string
type: object
status:
description: HostPreflightStatus defines the observed state of HostPreflight
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
controller-gen.kubebuilder.io/version: v0.16.2
name: redactors.troubleshoot.sh
spec:
group: troubleshoot.sh
@@ -75,6 +75,8 @@ spec:
type: object
type: object
type: array
uri:
type: string
type: object
status:
description: RedactorStatus defines the observed state of Redactor
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
controller-gen.kubebuilder.io/version: v0.16.2
name: remotecollectors.troubleshoot.sh
spec:
group: troubleshoot.sh
@@ -171,6 +171,12 @@ spec:
background IOPS feature if enabled, since those must be fixed at 4096.
format: int64
type: integer
runTime:
description: |-
Limit runtime. The test will run until it completes the configured I/O workload or until it
has run for this specified amount of time, whichever occurs first. When the unit is omitted,
the value is interpreted in seconds. Defaults to 120 seconds. Set to "0" to disable.
type: string
sync:
description: Whether to call sync on the file after each
write. Does not apply to background IOPS task.
@@ -187,6 +193,13 @@ spec:
- backgroundWriteIOPSJobs
- enableBackgroundIOPS
type: object
hostOS:
properties:
collectorName:
type: string
exclude:
type: BoolString
type: object
hostServices:
properties:
collectorName:
@@ -208,11 +221,34 @@ spec:
type: object
insecureSkipVerify:
type: boolean
proxy:
type: string
timeout:
description: |-
Timeout is the time to wait for a server's response. Its a duration e.g 15s, 2h30m.
Missing value or empty string or means no timeout.
type: string
tls:
properties:
cacert:
type: string
clientCert:
type: string
clientKey:
type: string
secret:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
skipVerify:
type: boolean
type: object
url:
type: string
required:
@@ -228,11 +264,34 @@ spec:
type: object
insecureSkipVerify:
type: boolean
proxy:
type: string
timeout:
description: |-
Timeout is the time to wait for a server's response. Its a duration e.g 15s, 2h30m.
Missing value or empty string or means no timeout.
type: string
tls:
properties:
cacert:
type: string
clientCert:
type: string
clientKey:
type: string
secret:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
skipVerify:
type: boolean
type: object
url:
type: string
required:
@@ -248,11 +307,34 @@ spec:
type: object
insecureSkipVerify:
type: boolean
proxy:
type: string
timeout:
description: |-
Timeout is the time to wait for a server's response. Its a duration e.g 15s, 2h30m.
Missing value or empty string or means no timeout.
type: string
tls:
properties:
cacert:
type: string
clientCert:
type: string
clientKey:
type: string
secret:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
skipVerify:
type: boolean
type: object
url:
type: string
required:
@@ -388,6 +470,8 @@ spec:
additionalProperties:
type: string
type: object
uri:
type: string
type: object
status:
description: CollectorStatus defines the observed state of Collector
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -37,6 +37,7 @@ preflight [url] [flags]
--kubeconfig string Path to the kubeconfig file to use for CLI requests.
--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, Preflight does not attempt to retrieve the spec referenced by the uri: field`
-o, --output string specify the output file path for the preflight checks
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
--selector string selector (label query) to filter remote collection nodes on.
@@ -54,4 +55,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 25-Jan-2024
###### Auto generated by spf13/cobra on 23-Aug-2024
+1 -1
View File
@@ -34,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 25-Jan-2024
###### Auto generated by spf13/cobra on 23-Aug-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 25-Jan-2024
###### Auto generated by spf13/cobra on 23-Aug-2024
+13 -5
View File
@@ -1,11 +1,19 @@
## support-bundle
Generate a support bundle
Generate a support bundle from a Kubernetes cluster or specified sources
### Synopsis
A support bundle is an archive of files, output, metrics and state
from a server that can be used to assist when troubleshooting a Kubernetes cluster.
Generate a support bundle, an archive containing files, output, metrics, and cluster state to aid in troubleshooting Kubernetes clusters.
If no arguments are provided, specs are automatically loaded from the cluster by default.
**Argument Types**:
1. **Secret**: Load specs from a Kubernetes Secret. Format: "secret/namespace-name/secret-name[/data-key]"
2. **ConfigMap**: Load specs from a Kubernetes ConfigMap. Format: "configmap/namespace-name/configmap-name[/data-key]"
3. **File**: Load specs from a local file. Format: Local file path
4. **Standard Input**: Read specs from stdin. Format: "-"
5. **URL**: Load specs from a URL. Supports HTTP and OCI registry URLs.
```
support-bundle [urls...] [flags]
@@ -32,7 +40,7 @@ support-bundle [urls...] [flags]
--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)
--kubeconfig string Path to the kubeconfig file to use for CLI requests.
--load-cluster-specs enable/disable loading additional troubleshoot specs found within the cluster. required when no specs are provided on the command line
--load-cluster-specs enable/disable loading additional troubleshoot specs found within the cluster. This is the default behavior if no spec is provided as an argument
--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 spec referenced by the uri: field`
@@ -56,4 +64,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 25-Jan-2024
###### Auto generated by spf13/cobra on 23-Aug-2024
+2 -2
View File
@@ -28,6 +28,6 @@ support-bundle analyze [url] [flags]
### SEE ALSO
* [support-bundle](support-bundle.md) - Generate a support bundle
* [support-bundle](support-bundle.md) - Generate a support bundle from a Kubernetes cluster or specified sources
###### Auto generated by spf13/cobra on 25-Jan-2024
###### Auto generated by spf13/cobra on 23-Aug-2024
+3 -3
View File
@@ -13,7 +13,7 @@ be stored in the current directory in the path provided by the --output flag.
The [urls...] argument is a list of either oci://.., http://.., https://.. or local paths to yaml files.
For more information on redactors visit https://troubleshoot.sh/docs/redact/
```
support-bundle redact [urls...] [flags]
@@ -37,6 +37,6 @@ support-bundle redact [urls...] [flags]
### SEE ALSO
* [support-bundle](support-bundle.md) - Generate a support bundle
* [support-bundle](support-bundle.md) - Generate a support bundle from a Kubernetes cluster or specified sources
###### Auto generated by spf13/cobra on 25-Jan-2024
###### Auto generated by spf13/cobra on 23-Aug-2024
+2 -2
View File
@@ -25,6 +25,6 @@ support-bundle version [flags]
### SEE ALSO
* [support-bundle](support-bundle.md) - Generate a support bundle
* [support-bundle](support-bundle.md) - Generate a support bundle from a Kubernetes cluster or specified sources
###### Auto generated by spf13/cobra on 25-Jan-2024
###### Auto generated by spf13/cobra on 23-Aug-2024
+8
View File
@@ -0,0 +1,8 @@
apiVersion: troubleshoot.sh/v1beta2
kind: HostCollector
metadata:
name: sysctl
spec:
collectors:
- sysctl:
collectorName: sysctl
+18
View File
@@ -0,0 +1,18 @@
apiVersion: troubleshoot.sh/v1beta2
kind: HostPreflight
metadata:
name: sysctl
spec:
collectors:
- sysctl:
collectorName: host sysctl
analyzers:
- sysctl:
collectorName: host sysctl
outcomes:
- warn:
when: 'kern.ostype == Darwin'
message: "Running sysctl on a Darwin host"
- pass:
when: 'net.ipv4.conf.default.arp_ignore > 0'
message: "ARP ignore is enabled for the default interfaces interfaces on the host."
+37 -33
View File
@@ -1,6 +1,8 @@
module helm-template
go 1.22.0
go 1.23.0
toolchain go1.23.4
// Always use the local version of troubleshoot so as to build using
// the latest version of the library. This will ensure the example
@@ -9,30 +11,32 @@ replace github.com/replicatedhq/troubleshoot v0.0.0 => ../../../
require (
github.com/replicatedhq/troubleshoot v0.0.0
helm.sh/helm/v3 v3.14.4
helm.sh/helm/v3 v3.16.4
sigs.k8s.io/yaml v1.4.0
)
require (
github.com/BurntSushi/toml v1.3.2 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/BurntSushi/toml v1.4.0 // 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/Masterminds/semver/v3 v3.3.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/cyphar/filepath-securejoin v0.3.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 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.10 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
@@ -42,31 +46,31 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
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.6.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/x448/float16 v0.8.4 // 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.22.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/oauth2 v0.18.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.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
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect
google.golang.org/protobuf v1.35.2 // 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.30.0 // indirect
k8s.io/apiextensions-apiserver v0.30.0 // indirect
k8s.io/apimachinery v0.30.0 // indirect
k8s.io/client-go v0.30.0 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
sigs.k8s.io/controller-runtime v0.17.3 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
k8s.io/api v0.32.0 // indirect
k8s.io/apiextensions-apiserver v0.32.0 // indirect
k8s.io/apimachinery v0.32.0 // indirect
k8s.io/client-go v0.32.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
sigs.k8s.io/controller-runtime v0.19.3 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
)
+82 -134
View File
@@ -1,83 +1,69 @@
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8=
github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/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/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/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/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=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
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=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -87,36 +73,29 @@ 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/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/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/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.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.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=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/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=
@@ -126,112 +105,81 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
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-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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
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=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.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.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.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.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/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.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.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
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.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.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.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.14.4 h1:6FSpEfqyDalHq3kUr4gOMThhgY55kXUEjdQoyODYnrM=
helm.sh/helm/v3 v3.14.4/go.mod h1:Tje7LL4gprZpuBNTbG34d1Xn5NmRT3OWfBRwpOSer9I=
k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
k8s.io/apiextensions-apiserver v0.29.3 h1:9HF+EtZaVpFjStakF4yVufnXGPRppWFEQ87qnO91YeI=
k8s.io/apiextensions-apiserver v0.29.3/go.mod h1:po0XiY5scnpJfFizNGo6puNU6Fq6D70UJY2Cb2KwAVc=
k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y=
k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU=
k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU=
k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg=
k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0=
k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
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/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
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.3 h1:65QmN7r3FWgTxDMz9fvGnO1kbf2nu+acg9p2R9oYYYk=
sigs.k8s.io/controller-runtime v0.17.3/go.mod h1:N0jpP5Lo7lMTF9aL56Z/B2oWBJjey6StQM0jRbKQXtY=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
helm.sh/helm/v3 v3.16.4 h1:rBn/h9MACw+QlhxQTjpl8Ifx+VTWaYsw3rguGBYBzr0=
helm.sh/helm/v3 v3.16.4/go.mod h1:k8QPotUt57wWbi90w3LNmg3/MWcLPigVv+0/X4B8BzA=
k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE=
k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0=
k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0=
k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw=
k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg=
k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8=
k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.19.3 h1:XO2GvC9OPftRst6xWCpTgBZO04S2cbp0Qqkj8bX1sPw=
sigs.k8s.io/controller-runtime v0.19.3/go.mod h1:j4j87DqtsThvwTv5/Tc5NFRyyF/RF0ip4+62tbTSIUM=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
+147 -122
View File
@@ -1,15 +1,19 @@
module github.com/replicatedhq/troubleshoot
go 1.22.0
go 1.23.0
toolchain go1.23.4
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.30.1
github.com/distribution/distribution/v3 v3.0.0-alpha.1
github.com/fatih/color v1.17.0
github.com/go-logr/logr v1.4.1
github.com/cilium/ebpf v0.17.1
github.com/containerd/cgroups/v3 v3.0.5
github.com/containers/image/v5 v5.33.0
github.com/distribution/distribution/v3 v3.0.0-rc.2
github.com/fatih/color v1.18.0
github.com/go-logr/logr v1.4.2
github.com/go-redis/redis/v7 v7.4.1
github.com/go-sql-driver/mysql v1.8.1
github.com/gobwas/glob v0.2.3
@@ -17,152 +21,179 @@ require (
github.com/google/gofuzz v1.2.0
github.com/google/uuid v1.6.0
github.com/gorilla/handlers v1.5.2
github.com/hashicorp/go-getter v1.7.4
github.com/hashicorp/go-getter v1.7.6
github.com/hashicorp/go-multierror v1.1.1
github.com/jackc/pgx/v5 v5.5.5
github.com/jackc/pgx/v5 v5.7.2
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.20
github.com/mholt/archiver/v3 v3.5.1
github.com/microsoft/go-mssqldb v1.7.1
github.com/microsoft/go-mssqldb v1.8.0
github.com/miekg/dns v1.1.62
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.24.4
github.com/spf13/cobra v1.8.0
github.com/shirou/gopsutil/v3 v3.24.5
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.9.0
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.10.0
github.com/tj/go-spin v1.1.0
github.com/vmware-tanzu/velero v1.13.2
go.opentelemetry.io/otel v1.26.0
go.opentelemetry.io/otel/sdk v1.26.0
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
golang.org/x/mod v0.17.0
golang.org/x/sync v0.7.0
github.com/vishvananda/netlink v1.3.0
github.com/vishvananda/netns v0.0.5
github.com/vmware-tanzu/velero v1.15.1
go.opentelemetry.io/otel v1.33.0
go.opentelemetry.io/otel/sdk v1.33.0
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
golang.org/x/mod v0.22.0
golang.org/x/sync v0.10.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.30.0
k8s.io/apiextensions-apiserver v0.30.0
k8s.io/apimachinery v0.30.0
k8s.io/apiserver v0.30.0
k8s.io/cli-runtime v0.30.0
k8s.io/client-go v0.30.0
k8s.io/klog/v2 v2.120.1
oras.land/oras-go v1.2.5
sigs.k8s.io/controller-runtime v0.18.2
sigs.k8s.io/e2e-framework v0.3.0
k8s.io/api v0.32.0
k8s.io/apiextensions-apiserver v0.32.0
k8s.io/apimachinery v0.32.0
k8s.io/apiserver v0.32.0
k8s.io/cli-runtime v0.32.0
k8s.io/client-go v0.32.0
k8s.io/klog/v2 v2.130.1
oras.land/oras-go v1.2.6
sigs.k8s.io/controller-runtime v0.19.3
sigs.k8s.io/e2e-framework v0.5.0
)
require (
cloud.google.com/go/compute/metadata v0.2.3 // indirect
dario.cat/mergo v1.0.0 // indirect
cel.dev/expr v0.18.0 // indirect
cloud.google.com/go/auth v0.9.3 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
cloud.google.com/go/compute/metadata v0.5.1 // indirect
cloud.google.com/go/monitoring v1.21.0 // indirect
dario.cat/mergo v1.0.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 // 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
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/containerd/cgroups/v3 v3.0.2 // indirect
github.com/containerd/errdefs v0.1.0 // indirect
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
github.com/containerd/errdefs v0.3.0 // indirect
github.com/containerd/errdefs/pkg v0.3.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/containerd/platforms v0.2.1 // indirect
github.com/containerd/typeurl/v2 v2.2.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/envoyproxy/go-control-plane v0.13.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // 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.2 // indirect
github.com/google/go-containerregistry v0.20.2 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // 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/huandu/xstrings v1.5.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jmoiron/sqlx v1.4.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/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/moby/sys/user v0.1.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/sys/capability v0.3.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/rubenv/sql-migrate v1.5.2 // indirect
github.com/rubenv/sql-migrate v1.7.0 // indirect
github.com/russross/blackfriday/v2 v2.1.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/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/sylabs/sif/v2 v2.15.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/sylabs/sif/v2 v2.19.1 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/vladimirvivien/gexe v0.2.0 // indirect
github.com/vladimirvivien/gexe v0.3.0 // indirect
github.com/x448/float16 v0.8.4 // 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/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
go.opentelemetry.io/otel/metric v1.26.0 // indirect
go.opentelemetry.io/otel/trace v1.26.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.29.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
go.opentelemetry.io/otel/metric v1.33.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect
go.opentelemetry.io/otel/trace v1.33.0 // indirect
go.uber.org/multierr v1.11.0 // 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.30.0 // indirect
k8s.io/kubectl v0.29.0 // indirect
golang.org/x/tools v0.27.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
k8s.io/component-base v0.32.0 // indirect
k8s.io/kubectl v0.31.3 // indirect
)
require (
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.3.2 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.12.0-rc.3 // indirect
cloud.google.com/go v0.115.1 // indirect
cloud.google.com/go/iam v1.2.1 // indirect
cloud.google.com/go/storage v1.45.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.12.9 // indirect
github.com/andybalholm/brotli v1.0.1 // indirect
github.com/aws/aws-sdk-go v1.48.10 // indirect
github.com/aws/aws-sdk-go v1.55.5 // 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/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/containerd/containerd v1.7.13 // indirect
github.com/containerd/containerd v1.7.23 // 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.9 // indirect
github.com/containers/storage v1.53.0 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/containers/ocicrypt v1.2.0 // indirect
github.com/containers/storage v1.56.0 // indirect
github.com/cyphar/filepath-securejoin v0.3.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v25.0.3+incompatible // indirect
github.com/docker/docker v25.0.5+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.1 // indirect
github.com/docker/cli v27.3.1+incompatible // indirect
github.com/docker/docker v27.3.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // 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.7.0+incompatible // indirect
github.com/evanphx/json-patch v5.9.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.10 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // 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.4 // indirect
@@ -171,34 +202,32 @@ require (
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/googleapis/gax-go/v2 v2.13.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
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.7 // indirect
github.com/klauspost/compress v1.17.11 // 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.15 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/mattn/go-runewidth v0.0.16 // 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.7.1 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // 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
@@ -207,57 +236,53 @@ require (
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/runtime-spec v1.2.0 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
github.com/opencontainers/runtime-spec v1.2.0
github.com/opencontainers/selinux v1.11.1 // indirect
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // 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.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/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cast v1.7.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.5 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/vbatts/tar-split v0.11.6 // 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.4 // indirect
go.opencensus.io v0.24.0 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0
golang.org/x/oauth2 v0.18.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/term v0.20.0 // indirect
golang.org/x/text v0.15.0
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // 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
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.33.0
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sys v0.28.0
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0
golang.org/x/time v0.7.0 // indirect
google.golang.org/api v0.197.0 // indirect
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.68.0 // indirect
google.golang.org/protobuf v1.35.2 // 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.14.4
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/kubelet v0.30.0
k8s.io/metrics v0.30.0
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
helm.sh/helm/v3 v3.16.4
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
k8s.io/kubelet v0.32.0
k8s.io/metrics v0.32.0
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
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.4.1 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/kustomize/api v0.18.0 // indirect
sigs.k8s.io/kustomize/kyaml v0.18.1 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
sigs.k8s.io/yaml v1.4.0
)
+377 -336
View File
File diff suppressed because it is too large Load Diff
+117 -17
View File
@@ -11,6 +11,7 @@ import (
"strings"
"time"
"github.com/fatih/color"
"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/internal/util"
"github.com/replicatedhq/troubleshoot/pkg/constants"
@@ -78,6 +79,7 @@ func LoadFromCLIArgs(ctx context.Context, client kubernetes.Interface, args []st
ctx = context.Background()
}
allURLSpecs := loader.NewTroubleshootKinds()
rawSpecs := []string{}
for _, v := range args {
@@ -174,22 +176,34 @@ func LoadFromCLIArgs(ctx context.Context, client kubernetes.Interface, args []st
if err != nil {
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
}
var rawURLSpec string
headers := map[string]string{}
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)
headers["User-Agent"] = "Replicated_Troubleshoot/v1beta1"
}
rawURLSpec, err = downloadFromHttpURL(ctx, v, headers)
if err != nil {
fmt.Println(color.YellowString("failed to download spec from URI %q: %v\n", v, err))
continue
}
// load URL spec first to remove URI key from the spec
urlSpec, err := loader.LoadSpecs(ctx, loader.LoadOptions{
RawSpec: rawURLSpec,
})
if err != nil {
fmt.Println(color.YellowString("failed to load spec from URI %q: %v\n", v, err))
continue
}
// remove URI key from the spec if any
for i := range urlSpec.SupportBundlesV1Beta2 {
urlSpec.SupportBundlesV1Beta2[i].Spec.Uri = ""
}
allURLSpecs.Add(urlSpec)
}
}
}
@@ -200,14 +214,20 @@ func LoadFromCLIArgs(ctx context.Context, client kubernetes.Interface, args []st
if err != nil {
return nil, err
}
if allURLSpecs.Len() > 0 {
kinds.Add(allURLSpecs)
}
if vp.GetBool("load-cluster-specs") {
clusterKinds, err := LoadFromCluster(ctx, client, vp.GetStringSlice("selector"), vp.GetString("namespace"))
if err != nil {
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
if kinds.IsEmpty() {
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
}
fmt.Println(color.YellowString("failed to load specs from cluster: %v\n", err))
} else {
kinds.Add(clusterKinds)
}
kinds.Add(clusterKinds)
}
return kinds, nil
@@ -251,6 +271,8 @@ func downloadFromHttpURL(ctx context.Context, url string, headers map[string]str
// to list & read secrets and configmaps from all namespaces, we will fallback to trying each
// namespace individually, and eventually default to the configured kubeconfig namespace.
func LoadFromCluster(ctx context.Context, client kubernetes.Interface, selectors []string, ns string) (*loader.TroubleshootKinds, error) {
klog.V(1).Infof("Load troubleshoot specs from the cluster using selectors: %v", selectors)
if reflect.DeepEqual(selectors, []string{"troubleshoot.sh/kind=support-bundle"}) {
// Its the default selector so we append troubleshoot.io/kind=support-bundle to it due to backwards compatibility
selectors = append(selectors, "troubleshoot.io/kind=support-bundle")
@@ -344,3 +366,81 @@ func LoadFromCluster(ctx context.Context, client kubernetes.Interface, selectors
RawSpecs: rawSpecs,
})
}
// LoadAdditionalSpecFromURIs loads additional specs from the URIs provided in the kinds.
// This function will modify kinds in place.
func LoadAdditionalSpecFromURIs(ctx context.Context, kinds *loader.TroubleshootKinds) {
obj := reflect.ValueOf(*kinds)
// iterate over all fields of the TroubleshootKinds
// e.g. SupportBundlesV1Beta2, PreflightsV1Beta2, etc.
for i := 0; i < obj.NumField(); i++ {
field := obj.Field(i)
if field.Kind() != reflect.Slice {
continue
}
// look at each spec in the slice
// e.g. each spec in []PreflightsV1Beta2
for count := 0; count < field.Len(); count++ {
currentSpec := field.Index(count)
specName := currentSpec.Type().Name()
// check if .Spec.Uri exists
specField := currentSpec.FieldByName("Spec")
if !specField.IsValid() {
continue
}
uriField := specField.FieldByName("Uri")
if uriField.Kind() != reflect.String {
continue
}
// download spec from URI
uri := uriField.String()
if uri == "" {
continue
}
rawSpec, err := downloadFromHttpURL(ctx, uri, nil)
if err != nil {
klog.Warningf("failed to download spec from URI %q: %v", uri, err)
continue
}
// load spec from raw spec
uriKinds, err := loader.LoadSpecs(ctx, loader.LoadOptions{RawSpec: string(rawSpec)})
if err != nil {
klog.Warningf("failed to load spec from URI %q: %v", uri, err)
continue
}
// replace original spec with the loaded spec from URI
newSpec := getFirstSpecOf(uriKinds, specName)
if !newSpec.IsValid() {
klog.Warningf("failed to read spec of type %s in URI %s", specName, uri)
continue
}
currentSpec.Set(newSpec)
}
}
}
// dynamically get spec from kinds of given name
// return first element of the spec slice
func getFirstSpecOf(kinds *loader.TroubleshootKinds, name string) reflect.Value {
obj := reflect.ValueOf(*kinds)
for i := 0; i < obj.NumField(); i++ {
field := obj.Field(i)
if field.Kind() != reflect.Slice {
continue
}
if field.Len() > 0 {
if field.Index(0).Type().Name() == name {
// return first element
return field.Index(0)
}
}
}
return reflect.Value{}
}
+79
View File
@@ -1,10 +1,13 @@
package specs
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"os"
"reflect"
"testing"
@@ -224,3 +227,79 @@ spec:
require.NoError(t, err)
require.Len(t, specs.HostCollectorsV1Beta2, 1)
}
func TestLoadFromMultipleURIs(t *testing.T) {
server1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`apiVersion: troubleshoot.sh/v1beta2
kind: HostCollector
metadata:
name: cpu
spec:
collectors:
- cpu: {}
`))
}))
defer server1.Close()
server2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}))
// immediately close the server to force the error
server2.Close()
// Capture stdout
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
client := testclient.NewSimpleClientset()
specs, err := LoadFromCLIArgs(context.Background(), client, []string{server1.URL, server2.URL}, viper.New())
// Restore stdout
w.Close()
os.Stdout = oldStdout
var buf bytes.Buffer
io.Copy(&buf, r)
output := buf.String()
require.NoError(t, err)
require.Len(t, specs.HostCollectorsV1Beta2, 1)
assert.Contains(t, output, "failed to download spec from URI")
}
func TestLoadAdditionalSpecFromURIs(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: Preflight
metadata:
name: preflight-2
spec:
collectors:
- ceph: {}
`))
}))
defer m.Close()
kinds := loader.NewTroubleshootKinds()
kinds.PreflightsV1Beta2 = []troubleshootv1beta2.Preflight{
{
ObjectMeta: metav1.ObjectMeta{
Name: "preflight-1",
},
Spec: troubleshootv1beta2.PreflightSpec{
Uri: m.URL,
Collectors: []*troubleshootv1beta2.Collect{
{
DNS: &troubleshootv1beta2.DNS{},
},
},
},
},
}
LoadAdditionalSpecFromURIs(context.Background(), kinds)
require.Len(t, kinds.PreflightsV1Beta2, 1)
require.Len(t, kinds.PreflightsV1Beta2[0].Spec.Collectors, 1)
require.NotNil(t, kinds.PreflightsV1Beta2[0].Spec.Collectors[0].Ceph)
}
+24
View File
@@ -3,6 +3,7 @@ package util
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"net/url"
"os"
@@ -145,3 +146,26 @@ func PromptYesNo(question string) bool {
}
}
}
func Dedup[T any](objs []T) []T {
seen := make(map[string]bool)
out := []T{}
if len(objs) == 0 {
return objs
}
for _, o := range objs {
data, err := json.Marshal(o)
if err != nil {
out = append(out, o)
continue
}
key := string(data)
if _, ok := seen[key]; !ok {
out = append(out, o)
seen[key] = true
}
}
return out
}
+17 -7
View File
@@ -89,6 +89,11 @@ func HostAnalyze(
if err != nil {
return NewAnalyzeResultError(analyzer, errors.Wrap(err, "analyze"))
}
if len(result) == 0 {
klog.Errorf("no outcome matched for %q host analyzer", analyzer.Title())
}
return result
}
@@ -117,13 +122,10 @@ func Analyze(
return nil, errors.New("nil analyzer")
}
analyzerInst := getAnalyzer(analyzer)
analyzerInst := GetAnalyzer(analyzer)
if analyzerInst == nil {
return []*AnalyzeResult{{
IsFail: true,
Title: "nonexistent analyzer",
Message: "Analyzer not found",
}}, nil
klog.Info("Non-existent analyzer found in the spec. Please double-check the spelling and indentation of the analyzers in the spec.")
return nil, nil
}
_, span := otel.Tracer(constants.LIB_TRACER_NAME).Start(ctx, analyzerInst.Title())
@@ -151,6 +153,10 @@ func Analyze(
results = []*AnalyzeResult{}
}
if len(results) == 0 {
klog.Errorf("no outcome matched for %q analyzer", analyzerInst.Title())
}
return results, nil
}
@@ -182,7 +188,7 @@ type Analyzer interface {
Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error)
}
func getAnalyzer(analyzer *troubleshootv1beta2.Analyze) Analyzer {
func GetAnalyzer(analyzer *troubleshootv1beta2.Analyze) Analyzer {
switch {
case analyzer.ClusterVersion != nil:
return &AnalyzeClusterVersion{analyzer: analyzer.ClusterVersion}
@@ -208,6 +214,8 @@ func getAnalyzer(analyzer *troubleshootv1beta2.Analyze) Analyzer {
return &AnalyzeReplicaSetStatus{analyzer: analyzer.ReplicaSetStatus}
case analyzer.ClusterPodStatuses != nil:
return &AnalyzeClusterPodStatuses{analyzer: analyzer.ClusterPodStatuses}
case analyzer.ClusterContainerStatuses != nil:
return &AnalyzeClusterContainerStatuses{analyzer: analyzer.ClusterContainerStatuses}
case analyzer.ContainerRuntime != nil:
return &AnalyzeContainerRuntime{analyzer: analyzer.ContainerRuntime}
case analyzer.Distribution != nil:
@@ -250,6 +258,8 @@ func getAnalyzer(analyzer *troubleshootv1beta2.Analyze) Analyzer {
return &AnalyzeEvent{analyzer: analyzer.Event}
case analyzer.NodeMetrics != nil:
return &AnalyzeNodeMetrics{analyzer: analyzer.NodeMetrics}
case analyzer.HTTP != nil:
return &AnalyzeHTTPAnalyze{analyzer: analyzer.HTTP}
default:
return nil
}
+260
View File
@@ -0,0 +1,260 @@
package analyzer
import (
"bytes"
"encoding/json"
"fmt"
"path/filepath"
"slices"
"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 AnalyzeClusterContainerStatuses struct {
analyzer *troubleshootv1beta2.ClusterContainerStatuses
}
type podsWithContainers map[string]struct {
name string
namespace string
containerStatuses []corev1.ContainerStatus
}
type matchedContainerInfo struct {
Namespace string
PodName string
ContainerName string
Ready bool
RestartCount int32
Message string
}
func (a *AnalyzeClusterContainerStatuses) Title() string {
if a.analyzer.CheckName != "" {
return a.analyzer.CheckName
}
return "Cluster Container Status"
}
func (a *AnalyzeClusterContainerStatuses) IsExcluded() (bool, error) {
return isExcluded(a.analyzer.Exclude)
}
func (a *AnalyzeClusterContainerStatuses) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
// get all pod list files from clusterResources collector directory
excludeFiles := []string{}
podListFiles, err := findFiles(filepath.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_PODS, "*.json"), excludeFiles)
if err != nil {
return nil, errors.Wrap(err, "failed to read collected pods")
}
// get pods matched analyzer filters
pods, err := a.getPodsMatchingFilters(podListFiles)
if err != nil {
return nil, errors.Wrap(err, "failed to get pods matching filters")
}
results, err := a.analyzeContainerStatuses(pods)
if err != nil {
return nil, errors.Wrap(err, "failed to analyze container statuses")
}
return results, nil
}
func (a *AnalyzeClusterContainerStatuses) getPodsMatchingFilters(podListFiles map[string][]byte) (podsWithContainers, error) {
var podsMatchedNamespace []corev1.Pod
matchedPods := podsWithContainers{}
// filter pods matched namespace selector
for fileName, fileContent := range podListFiles {
// pod list fileName is the namespace name, e.g. default.json
currentNamespace := strings.TrimSuffix(filepath.Base(fileName), ".json")
selectedNamespaces := a.analyzer.Namespaces
if len(selectedNamespaces) > 0 {
if !slices.Contains(selectedNamespaces, currentNamespace) {
continue
}
}
// filter pods by namespace
var podList corev1.PodList
if err := json.Unmarshal(fileContent, &podList); err != nil {
var pods []corev1.Pod
// fallback to old format
if err := json.Unmarshal(fileContent, &pods); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal pods list for namespace %s", currentNamespace)
}
podsMatchedNamespace = append(podsMatchedNamespace, pods...)
} else {
podsMatchedNamespace = append(podsMatchedNamespace, podList.Items...)
}
}
// filter pods by container criteria
for _, pod := range podsMatchedNamespace {
for _, containerStatus := range pod.Status.ContainerStatuses {
if containerStatus.RestartCount < a.analyzer.RestartCount {
continue
}
// check if the pod has already been matched
key := string(pod.UID)
if _, ok := matchedPods[key]; !ok {
matchedPods[key] = struct {
name string
namespace string
containerStatuses []corev1.ContainerStatus
}{
name: pod.Name,
namespace: pod.Namespace,
containerStatuses: []corev1.ContainerStatus{containerStatus},
}
continue
}
entry := matchedPods[key]
entry.containerStatuses = append(entry.containerStatuses, containerStatus)
}
}
return matchedPods, nil
}
func (a *AnalyzeClusterContainerStatuses) analyzeContainerStatuses(podContainers podsWithContainers) ([]*AnalyzeResult, error) {
results := []*AnalyzeResult{}
// for each outcome, iterate over the pods and match the outcome against the container statues
for _, outcome := range a.analyzer.Outcomes {
r := AnalyzeResult{
Title: a.Title(),
IconKey: "kubernetes_container_statuses",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
}
when := ""
switch {
case outcome.Fail != nil:
r.IsFail = true
r.Message = outcome.Fail.Message
r.URI = outcome.Fail.URI
when = outcome.Fail.When
case outcome.Warn != nil:
r.IsWarn = true
r.Message = outcome.Warn.Message
r.URI = outcome.Warn.URI
when = outcome.Warn.When
case outcome.Pass != nil:
r.IsPass = true
r.Message = outcome.Pass.Message
r.URI = outcome.Pass.URI
when = outcome.Pass.When
default:
klog.Warning("unexpected outcome in clusterContainerStatuses analyzer")
continue
}
// empty when indicates final case, let's return the result
if when == "" {
// return collected results if any
if len(results) > 0 {
return results, nil
}
return []*AnalyzeResult{&r}, nil
}
// continue matching with when condition
reason, isEqualityOp, err := parseWhen(when)
if err != nil {
return nil, errors.Wrap(err, "failed to parse when")
}
for _, pod := range podContainers {
matched, matchedContainerInfo := matchContainerReason(reason, pod.name, pod.namespace, pod.containerStatuses)
if matched != isEqualityOp {
continue
}
r.Message = renderContainerMessage(r.Message, &matchedContainerInfo)
results = append(results, &r)
}
}
return results, nil
}
// matchContainerReason iterates over the containerStatuses and returns true on the first reason that matches
func matchContainerReason(reason string, podName string, namespace string, containerStatuses []corev1.ContainerStatus) (bool, matchedContainerInfo) {
var matched bool
info := matchedContainerInfo{}
info.Namespace = namespace
info.PodName = podName
for _, containerStatus := range containerStatuses {
state := containerStatus.State
info.ContainerName = containerStatus.Name
info.Ready = containerStatus.Ready
info.RestartCount = containerStatus.RestartCount
switch {
case containerStatus.LastTerminationState.Terminated != nil && strings.EqualFold(containerStatus.LastTerminationState.Terminated.Reason, reason):
matched = true
info.Message = containerStatus.LastTerminationState.Terminated.Message
case state.Terminated != nil && strings.EqualFold(state.Terminated.Reason, reason):
info.Message = state.Terminated.Message
matched = true
case state.Waiting != nil && strings.EqualFold(state.Waiting.Reason, reason):
matched = true
info.Message = state.Waiting.Message
}
}
return matched, info
}
// parseWhen parses the when string into operator and reason
// return error if reason is not in the expected format
func parseWhen(when string) (string, bool, error) {
parts := strings.Split(strings.TrimSpace(when), " ")
if len(parts) != 2 {
return "", false, errors.Errorf("expected 2 parts in when %q", when)
}
operator := parts[0]
reason := parts[1]
var isEqualityOp bool
switch operator {
case "=", "==", "===":
isEqualityOp = true
case "!=", "!==":
isEqualityOp = false
default:
return "", false, errors.Errorf("unexpected operator %q in containerStatuses reason", operator)
}
return reason, isEqualityOp, nil
}
func renderContainerMessage(message string, info *matchedContainerInfo) string {
if info == nil {
return message
}
out := fmt.Sprintf("Container matched. Container: %s, Namespace: %s, Pod: %s", info.ContainerName, info.Namespace, info.PodName)
tmpl := template.New("container")
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, info)
if err != nil {
klog.V(2).Infof("failed to render message template: %v", err)
return out
}
return strings.TrimSpace(m.String())
}
@@ -0,0 +1,103 @@
package analyzer
import (
"testing"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/require"
)
func Test_analyzeContainerStatuses(t *testing.T) {
tests := []struct {
name string
analyzer troubleshootv1beta2.ClusterContainerStatuses
expectResult []*AnalyzeResult
files map[string][]byte
}{
{
name: "fail when there is OOMKilled container",
analyzer: troubleshootv1beta2.ClusterContainerStatuses{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "oomkilled-container",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "== OOMKilled",
Message: "Container {{ .ContainerName }} from pod {{ .Namespace }}/{{ .PodName }} has OOMKilled",
},
},
},
Namespaces: []string{"message-oomkill-pod"},
},
expectResult: []*AnalyzeResult{
{
IsFail: true,
IsWarn: false,
IsPass: false,
Title: "oomkilled-container",
Message: "Container memory-eater from pod message-oomkill-pod/oom-kill-job3-gbb89 has OOMKilled",
IconKey: "kubernetes_container_statuses",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
},
},
files: map[string][]byte{
"cluster-resources/pods/message-oomkill-pod.json": []byte(messageOOMKillPod),
},
},
{
name: "pass when there is no status detected",
analyzer: troubleshootv1beta2.ClusterContainerStatuses{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "oomkilled-container",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "== OOMKilled",
Message: "Container {{ .ContainerName }} from pod {{ .Namespace }}/{{ .PodName }} has OOMKilled",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "No OOMKilled container found",
},
},
},
Namespaces: []string{"default"},
},
expectResult: []*AnalyzeResult{
{
IsFail: false,
IsWarn: false,
IsPass: true,
Title: "oomkilled-container",
Message: "No OOMKilled container found",
IconKey: "kubernetes_container_statuses",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
},
},
files: map[string][]byte{
"cluster-resources/pods/default.json": []byte(defaultPods),
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
getFiles := func(n string, _ []string) (map[string][]byte, error) {
return test.files, nil
}
a := AnalyzeClusterContainerStatuses{
analyzer: &test.analyzer,
}
actual, err := a.Analyze(nil, getFiles)
req.NoError(err)
req.Equal(test.expectResult, actual)
})
}
}
+60
View File
@@ -0,0 +1,60 @@
package analyzer
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/types"
)
type collectedContent struct {
NodeName string
Data []byte
}
type nodeNames struct {
Nodes []string `json:"nodes"`
}
func retrieveCollectedContents(
getCollectedFileContents func(string) ([]byte, error),
localPath string, remoteNodeBaseDir string, remoteFileName string,
) ([]collectedContent, error) {
var collectedContents []collectedContent
// Try to retrieve local data first
if contents, err := getCollectedFileContents(localPath); err == nil {
collectedContents = append(collectedContents, collectedContent{NodeName: "", Data: contents})
// Return immediately if local content is available
return collectedContents, nil
}
// Local data not available, move to remote collection
nodeListContents, err := getCollectedFileContents(constants.NODE_LIST_FILE)
if err != nil {
if _, ok := err.(*types.NotFoundError); ok {
return collectedContents, nil
}
return nil, errors.Wrap(err, "failed to get node list")
}
var nodeNames nodeNames
if err := json.Unmarshal(nodeListContents, &nodeNames); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal node names")
}
// Collect data for each node
for _, node := range nodeNames.Nodes {
nodeFilePath := fmt.Sprintf("%s/%s/%s", remoteNodeBaseDir, node, remoteFileName)
nodeContents, err := getCollectedFileContents(nodeFilePath)
if err != nil {
return nil, errors.Wrapf(err, "failed to retrieve content for node %s", node)
}
collectedContents = append(collectedContents, collectedContent{NodeName: node, Data: nodeContents})
}
return collectedContents, nil
}
+138
View File
@@ -0,0 +1,138 @@
package analyzer
import (
"encoding/json"
"testing"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRetrieveCollectedContents(t *testing.T) {
tests := []struct {
name string
getCollectedFileContents func(string) ([]byte, error) // Mock function
localPath string
remoteNodeBaseDir string
remoteFileName string
expectedResult []collectedContent
expectedError string
}{
{
name: "successfully retrieve local content",
getCollectedFileContents: func(path string) ([]byte, error) {
if path == "localPath" {
return []byte("localContent"), nil
}
return nil, &types.NotFoundError{Name: path}
},
localPath: "localPath",
remoteNodeBaseDir: "remoteBaseDir",
remoteFileName: "remoteFileName",
expectedResult: []collectedContent{
{
NodeName: "",
Data: []byte("localContent"),
},
},
expectedError: "",
},
{
name: "local content not found, retrieve remote node content successfully",
getCollectedFileContents: func(path string) ([]byte, error) {
if path == constants.NODE_LIST_FILE {
nodeNames := nodeNames{Nodes: []string{"node1", "node2"}}
return json.Marshal(nodeNames)
}
if path == "remoteBaseDir/node1/remoteFileName" {
return []byte("remoteContent1"), nil
}
if path == "remoteBaseDir/node2/remoteFileName" {
return []byte("remoteContent2"), nil
}
return nil, &types.NotFoundError{Name: path}
},
localPath: "localPath",
remoteNodeBaseDir: "remoteBaseDir",
remoteFileName: "remoteFileName",
expectedResult: []collectedContent{
{
NodeName: "node1",
Data: []byte("remoteContent1"),
},
{
NodeName: "node2",
Data: []byte("remoteContent2"),
},
},
expectedError: "",
},
{
name: "fail to retrieve local content and node list",
getCollectedFileContents: func(path string) ([]byte, error) {
return nil, &types.NotFoundError{Name: path}
},
localPath: "localPath",
remoteNodeBaseDir: "remoteBaseDir",
remoteFileName: "remoteFileName",
expectedResult: nil,
expectedError: "",
},
{
name: "fail to retrieve content for one of the nodes",
getCollectedFileContents: func(path string) ([]byte, error) {
if path == constants.NODE_LIST_FILE {
nodeNames := nodeNames{Nodes: []string{"node1", "node2"}}
return json.Marshal(nodeNames)
}
if path == "remoteBaseDir/node1/remoteFileName" {
return []byte("remoteContent1"), nil
}
if path == "remoteBaseDir/node2/remoteFileName" {
return nil, &types.NotFoundError{Name: path}
}
return nil, &types.NotFoundError{Name: path}
},
localPath: "localPath",
remoteNodeBaseDir: "remoteBaseDir",
remoteFileName: "remoteFileName",
expectedResult: nil,
expectedError: "failed to retrieve content for node node2",
},
{
name: "fail to unmarshal node list",
getCollectedFileContents: func(path string) ([]byte, error) {
if path == constants.NODE_LIST_FILE {
return []byte("invalidJSON"), nil
}
return nil, &types.NotFoundError{Name: path}
},
localPath: "localPath",
remoteNodeBaseDir: "remoteBaseDir",
remoteFileName: "remoteFileName",
expectedResult: nil,
expectedError: "failed to unmarshal node names",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result, err := retrieveCollectedContents(
test.getCollectedFileContents,
test.localPath,
test.remoteNodeBaseDir,
test.remoteFileName,
)
if test.expectedError != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), test.expectedError)
} else {
require.NoError(t, err)
assert.Equal(t, test.expectedResult, result)
}
})
}
}
+5 -1
View File
@@ -66,11 +66,15 @@ func (a *AnalyzeCustomResourceDefinition) analyzeCustomResourceDefinition(analyz
}
}
result.IsFail = true
for _, outcome := range analyzer.Outcomes {
if outcome.Fail != nil {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
} else if outcome.Warn != nil {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
}
}
+101
View File
@@ -0,0 +1,101 @@
package analyzer
import (
"encoding/json"
"testing"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestAnalyzeCustomResourceDefinition(t *testing.T) {
getFile := func(_ string) ([]byte, error) {
crdsList := v1beta1.CustomResourceDefinitionList{
Items: []v1beta1.CustomResourceDefinition{
{ObjectMeta: metav1.ObjectMeta{Name: "servicemonitors.monitoring.coreos.com"}},
{ObjectMeta: metav1.ObjectMeta{Name: "probes.monitoring.coreos.com"}},
},
}
return json.Marshal(crdsList)
}
tests := []struct {
name string
analyzer *troubleshootv1beta2.CustomResourceDefinition
expectedResult *AnalyzeResult
}{
{
name: "CRD exists and pass",
analyzer: &troubleshootv1beta2.CustomResourceDefinition{
CustomResourceDefinitionName: "servicemonitors.monitoring.coreos.com",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "The ServiceMonitor CRD is installed and available.",
},
},
},
},
expectedResult: &AnalyzeResult{
Title: "Custom resource definition servicemonitors.monitoring.coreos.com",
IconKey: "kubernetes_custom_resource_definition",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/custom-resource-definition.svg?w=13&h=16",
IsPass: true,
Message: "The ServiceMonitor CRD is installed and available.",
},
},
{
name: "CRD not exists and warn",
analyzer: &troubleshootv1beta2.CustomResourceDefinition{
CustomResourceDefinitionName: "podmonitors.monitoring.coreos.com",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Warn: &troubleshootv1beta2.SingleOutcome{
Message: "The Prometheus PodMonitor CRD was not found in the cluster.",
},
},
},
},
expectedResult: &AnalyzeResult{
Title: "Custom resource definition podmonitors.monitoring.coreos.com",
IconKey: "kubernetes_custom_resource_definition",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/custom-resource-definition.svg?w=13&h=16",
IsWarn: true,
Message: "The Prometheus PodMonitor CRD was not found in the cluster.",
},
},
{
name: "CRD not exists and fail",
analyzer: &troubleshootv1beta2.CustomResourceDefinition{
CustomResourceDefinitionName: "backupstoragelocations.velero.io",
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "The BackupStorageLocation CRD was not found in the cluster.",
},
},
},
},
expectedResult: &AnalyzeResult{
Title: "Custom resource definition backupstoragelocations.velero.io",
IconKey: "kubernetes_custom_resource_definition",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/custom-resource-definition.svg?w=13&h=16",
IsFail: true,
Message: "The BackupStorageLocation CRD was not found in the cluster.",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := &AnalyzeCustomResourceDefinition{
analyzer: tt.analyzer,
}
result, err := a.analyzeCustomResourceDefinition(tt.analyzer, getFile)
assert.NoError(t, err)
assert.Equal(t, tt.expectedResult, result)
})
}
}
+3 -1
View File
@@ -10,6 +10,7 @@ import (
"github.com/replicatedhq/troubleshoot/pkg/constants"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
)
type providers struct {
@@ -75,7 +76,8 @@ func (a *AnalyzeDistribution) IsExcluded() (bool, error) {
func (a *AnalyzeDistribution) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
result, err := a.analyzeDistribution(a.analyzer, getFile)
if err != nil {
return nil, err
klog.Errorf("failed to analyze distribution: %v", err)
return nil, errors.Wrapf(err, "failed to analyze distribution")
}
result.Strict = a.analyzer.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
+387 -2
View File
@@ -2865,6 +2865,391 @@
}
]
}
}
},
{
"apiVersion": "v1",
"kind": "Node",
"metadata": {
"annotations": {
"flannel.alpha.coreos.com/backend-data": "{\"VNI\":1,\"VtepMAC\":\"c6:c2:b9:1b:49:2e\"}",
"flannel.alpha.coreos.com/backend-type": "vxlan",
"flannel.alpha.coreos.com/kube-subnet-manager": "true",
"flannel.alpha.coreos.com/public-ip": "172.31.21.92",
"kubeadm.alpha.kubernetes.io/cri-socket": "unix:///run/containerd/containerd.sock",
"nfd.node.kubernetes.io/feature-labels": "cpu-cpuid.ADX,cpu-cpuid.AESNI,cpu-cpuid.AVX,cpu-cpuid.AVX2,cpu-cpuid.AVX512BW,cpu-cpuid.AVX512CD,cpu-cpuid.AVX512DQ,cpu-cpuid.AVX512F,cpu-cpuid.AVX512VL,cpu-cpuid.AVX512VNNI,cpu-cpuid.CMPXCHG8,cpu-cpuid.FMA3,cpu-cpuid.FXSR,cpu-cpuid.FXSROPT,cpu-cpuid.HYPERVISOR,cpu-cpuid.LAHF,cpu-cpuid.MOVBE,cpu-cpuid.MPX,cpu-cpuid.OSXSAVE,cpu-cpuid.SYSCALL,cpu-cpuid.SYSEE,cpu-cpuid.X87,cpu-cpuid.XGETBV1,cpu-cpuid.XSAVE,cpu-cpuid.XSAVEC,cpu-cpuid.XSAVEOPT,cpu-cpuid.XSAVES,cpu-hardware_multithreading,cpu-model.family,cpu-model.id,cpu-model.vendor_id,kernel-config.NO_HZ,kernel-config.NO_HZ_FULL,kernel-version.full,kernel-version.major,kernel-version.minor,kernel-version.revision,nvidia.com/cuda.driver-version.full,nvidia.com/cuda.driver-version.major,nvidia.com/cuda.driver-version.minor,nvidia.com/cuda.driver-version.revision,nvidia.com/cuda.driver.major,nvidia.com/cuda.driver.minor,nvidia.com/cuda.driver.rev,nvidia.com/cuda.runtime-version.full,nvidia.com/cuda.runtime-version.major,nvidia.com/cuda.runtime-version.minor,nvidia.com/cuda.runtime.major,nvidia.com/cuda.runtime.minor,nvidia.com/gfd.timestamp,nvidia.com/gpu.compute.major,nvidia.com/gpu.compute.minor,nvidia.com/gpu.count,nvidia.com/gpu.family,nvidia.com/gpu.machine,nvidia.com/gpu.memory,nvidia.com/gpu.mode,nvidia.com/gpu.product,nvidia.com/gpu.replicas,nvidia.com/gpu.sharing-strategy,nvidia.com/mig.capable,nvidia.com/mig.strategy,nvidia.com/mps.capable,nvidia.com/vgpu.present,pci-10de.present,pci-1d0f.present,storage-nonrotationaldisk,system-os_release.ID,system-os_release.VERSION_ID,system-os_release.VERSION_ID.major,system-os_release.VERSION_ID.minor",
"node.alpha.kubernetes.io/ttl": "0",
"nvidia.com/gpu-driver-upgrade-enabled": "true",
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
},
"creationTimestamp": "2024-12-18T22:04:55Z",
"labels": {
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
"feature.node.kubernetes.io/cpu-cpuid.ADX": "true",
"feature.node.kubernetes.io/cpu-cpuid.AESNI": "true",
"feature.node.kubernetes.io/cpu-cpuid.AVX": "true",
"feature.node.kubernetes.io/cpu-cpuid.AVX2": "true",
"feature.node.kubernetes.io/cpu-cpuid.AVX512BW": "true",
"feature.node.kubernetes.io/cpu-cpuid.AVX512CD": "true",
"feature.node.kubernetes.io/cpu-cpuid.AVX512DQ": "true",
"feature.node.kubernetes.io/cpu-cpuid.AVX512F": "true",
"feature.node.kubernetes.io/cpu-cpuid.AVX512VL": "true",
"feature.node.kubernetes.io/cpu-cpuid.AVX512VNNI": "true",
"feature.node.kubernetes.io/cpu-cpuid.CMPXCHG8": "true",
"feature.node.kubernetes.io/cpu-cpuid.FMA3": "true",
"feature.node.kubernetes.io/cpu-cpuid.FXSR": "true",
"feature.node.kubernetes.io/cpu-cpuid.FXSROPT": "true",
"feature.node.kubernetes.io/cpu-cpuid.HYPERVISOR": "true",
"feature.node.kubernetes.io/cpu-cpuid.LAHF": "true",
"feature.node.kubernetes.io/cpu-cpuid.MOVBE": "true",
"feature.node.kubernetes.io/cpu-cpuid.MPX": "true",
"feature.node.kubernetes.io/cpu-cpuid.OSXSAVE": "true",
"feature.node.kubernetes.io/cpu-cpuid.SYSCALL": "true",
"feature.node.kubernetes.io/cpu-cpuid.SYSEE": "true",
"feature.node.kubernetes.io/cpu-cpuid.X87": "true",
"feature.node.kubernetes.io/cpu-cpuid.XGETBV1": "true",
"feature.node.kubernetes.io/cpu-cpuid.XSAVE": "true",
"feature.node.kubernetes.io/cpu-cpuid.XSAVEC": "true",
"feature.node.kubernetes.io/cpu-cpuid.XSAVEOPT": "true",
"feature.node.kubernetes.io/cpu-cpuid.XSAVES": "true",
"feature.node.kubernetes.io/cpu-hardware_multithreading": "true",
"feature.node.kubernetes.io/cpu-model.family": "6",
"feature.node.kubernetes.io/cpu-model.id": "85",
"feature.node.kubernetes.io/cpu-model.vendor_id": "Intel",
"feature.node.kubernetes.io/kernel-config.NO_HZ": "true",
"feature.node.kubernetes.io/kernel-config.NO_HZ_FULL": "true",
"feature.node.kubernetes.io/kernel-version.full": "6.8.0-1015-aws",
"feature.node.kubernetes.io/kernel-version.major": "6",
"feature.node.kubernetes.io/kernel-version.minor": "8",
"feature.node.kubernetes.io/kernel-version.revision": "0",
"feature.node.kubernetes.io/pci-10de.present": "true",
"feature.node.kubernetes.io/pci-1d0f.present": "true",
"feature.node.kubernetes.io/storage-nonrotationaldisk": "true",
"feature.node.kubernetes.io/system-os_release.ID": "ubuntu",
"feature.node.kubernetes.io/system-os_release.VERSION_ID": "22.04",
"feature.node.kubernetes.io/system-os_release.VERSION_ID.major": "22",
"feature.node.kubernetes.io/system-os_release.VERSION_ID.minor": "04",
"kubernetes.io/arch": "amd64",
"kubernetes.io/hostname": "ip-172-31-21-92",
"kubernetes.io/os": "linux",
"kurl.sh/cluster": "true",
"node-role.kubernetes.io/control-plane": "",
"node-role.kubernetes.io/master": "",
"node.kubernetes.io/exclude-from-external-load-balancers": "",
"nvidia.com/cuda.driver-version.full": "550.127.08",
"nvidia.com/cuda.driver-version.major": "550",
"nvidia.com/cuda.driver-version.minor": "127",
"nvidia.com/cuda.driver-version.revision": "08",
"nvidia.com/cuda.driver.major": "550",
"nvidia.com/cuda.driver.minor": "127",
"nvidia.com/cuda.driver.rev": "08",
"nvidia.com/cuda.runtime-version.full": "12.4",
"nvidia.com/cuda.runtime-version.major": "12",
"nvidia.com/cuda.runtime-version.minor": "4",
"nvidia.com/cuda.runtime.major": "12",
"nvidia.com/cuda.runtime.minor": "4",
"nvidia.com/gfd.timestamp": "1734575658",
"nvidia.com/gpu-driver-upgrade-state": "upgrade-done",
"nvidia.com/gpu.compute.major": "7",
"nvidia.com/gpu.compute.minor": "5",
"nvidia.com/gpu.count": "1",
"nvidia.com/gpu.deploy.container-toolkit": "true",
"nvidia.com/gpu.deploy.dcgm": "true",
"nvidia.com/gpu.deploy.dcgm-exporter": "true",
"nvidia.com/gpu.deploy.device-plugin": "true",
"nvidia.com/gpu.deploy.driver": "true",
"nvidia.com/gpu.deploy.gpu-feature-discovery": "true",
"nvidia.com/gpu.deploy.node-status-exporter": "true",
"nvidia.com/gpu.deploy.nvsm": "",
"nvidia.com/gpu.deploy.operator-validator": "true",
"nvidia.com/gpu.family": "turing",
"nvidia.com/gpu.machine": "g4dn.xlarge",
"nvidia.com/gpu.memory": "15360",
"nvidia.com/gpu.mode": "compute",
"nvidia.com/gpu.present": "true",
"nvidia.com/gpu.product": "Tesla-T4",
"nvidia.com/gpu.replicas": "1",
"nvidia.com/gpu.sharing-strategy": "none",
"nvidia.com/mig.capable": "false",
"nvidia.com/mig.strategy": "single",
"nvidia.com/mps.capable": "false",
"nvidia.com/vgpu.present": "false"
},
"name": "ip-172-31-21-92",
"resourceVersion": "36620",
"uid": "aed93ed6-f460-4578-a7b0-574574237a0a"
},
"spec": {
"podCIDR": "10.32.0.0/24",
"podCIDRs": [
"10.32.0.0/24"
]
},
"status": {
"addresses": [
{
"address": "172.31.21.92",
"type": "InternalIP"
},
{
"address": "ip-172-31-21-92",
"type": "Hostname"
}
],
"allocatable": {
"cpu": "1",
"ephemeral-storage": "93478772582",
"hugepages-1Gi": "0",
"hugepages-2Mi": "0",
"memory": "16064760Ki",
"nvidia.com/gpu": "1",
"pods": "110"
},
"capacity": {
"cpu": "1",
"ephemeral-storage": "101430960Ki",
"hugepages-1Gi": "0",
"hugepages-2Mi": "0",
"memory": "16167160Ki",
"nvidia.com/gpu": "1",
"pods": "110"
},
"conditions": [
{
"lastHeartbeatTime": "2024-12-19T02:27:57Z",
"lastTransitionTime": "2024-12-19T02:27:57Z",
"message": "Flannel is running on this node",
"reason": "FlannelIsUp",
"status": "False",
"type": "NetworkUnavailable"
},
{
"lastHeartbeatTime": "2024-12-19T02:59:48Z",
"lastTransitionTime": "2024-12-18T22:04:54Z",
"message": "kubelet has sufficient memory available",
"reason": "KubeletHasSufficientMemory",
"status": "False",
"type": "MemoryPressure"
},
{
"lastHeartbeatTime": "2024-12-19T02:59:48Z",
"lastTransitionTime": "2024-12-18T22:04:54Z",
"message": "kubelet has no disk pressure",
"reason": "KubeletHasNoDiskPressure",
"status": "False",
"type": "DiskPressure"
},
{
"lastHeartbeatTime": "2024-12-19T02:59:48Z",
"lastTransitionTime": "2024-12-18T22:04:54Z",
"message": "kubelet has sufficient PID available",
"reason": "KubeletHasSufficientPID",
"status": "False",
"type": "PIDPressure"
},
{
"lastHeartbeatTime": "2024-12-19T02:59:48Z",
"lastTransitionTime": "2024-12-19T02:27:52Z",
"message": "kubelet is posting ready status. AppArmor enabled",
"reason": "KubeletReady",
"status": "True",
"type": "Ready"
}
],
"daemonEndpoints": {
"kubeletEndpoint": {
"Port": 10250
}
},
"images": [
{
"names": [
"nvcr.io/nvidia/driver@sha256:495706c38ad8afcd6c9bca1b436e23d3990631511645933325d0340ed2a87794",
"nvcr.io/nvidia/driver:550.127.08-ubuntu22.04"
],
"sizeBytes": 676042363
},
{
"names": [
"docker.io/replicated/kurl-util:v2024.12.04-0"
],
"sizeBytes": 606430157
},
{
"names": [
"nvcr.io/nvidia/cloud-native/k8s-driver-manager@sha256:b072c5793be65eee556eaff1b9cbbd115a1ef29982be95b2959adfcb4bc72382",
"nvcr.io/nvidia/cloud-native/k8s-driver-manager:v0.7.0"
],
"sizeBytes": 225657726
},
{
"names": [
"nvcr.io/nvidia/gpu-operator@sha256:db8451bc4861b11c2c18a8b439357178b43e9f831dd5908868d0e8dea73804a6",
"nvcr.io/nvidia/gpu-operator:v24.9.1"
],
"sizeBytes": 221606441
},
{
"names": [
"docker.io/replicated/ekco:v0.28.7"
],
"sizeBytes": 194820836
},
{
"names": [
"nvcr.io/nvidia/k8s-device-plugin@sha256:7089559ce6153018806857f5049085bae15b3bf6f1c8bd19d8b12f707d087dea",
"nvcr.io/nvidia/k8s-device-plugin:v0.17.0"
],
"sizeBytes": 187560257
},
{
"names": [
"nvcr.io/nvidia/cloud-native/gpu-operator-validator@sha256:420869a5601e0bff799ffe57ca1883c429ef9071d380050de0223d0a5278517c",
"nvcr.io/nvidia/cloud-native/gpu-operator-validator:v24.9.1"
],
"sizeBytes": 183027141
},
{
"names": [
"docker.io/minio/minio:RELEASE.2024-11-07T00-52-20Z"
],
"sizeBytes": 165802842
},
{
"names": [
"docker.io/envoyproxy/envoy:v1.31.0"
],
"sizeBytes": 156987178
},
{
"names": [
"k8s.gcr.io/etcd:3.5.16-0",
"registry.k8s.io/etcd:3.5.16-0"
],
"sizeBytes": 151015693
},
{
"names": [
"nvcr.io/nvidia/k8s/container-toolkit@sha256:35b40720f7009eec5acfb5318ac118cb4aed4dda39b190d0b75da2e8b3830383",
"nvcr.io/nvidia/k8s/container-toolkit:v1.17.3-ubuntu20.04"
],
"sizeBytes": 132728008
},
{
"names": [
"k8s.gcr.io/kube-apiserver:v1.29.11",
"registry.k8s.io/kube-apiserver:v1.29.11"
],
"sizeBytes": 128856468
},
{
"names": [
"nvcr.io/nvidia/k8s/dcgm-exporter@sha256:3d4e0dfa5fc4d7d12689d29fc6b56cd6c610750e8d187a393882e341fbba6c12",
"nvcr.io/nvidia/k8s/dcgm-exporter:3.3.9-3.6.1-ubuntu22.04"
],
"sizeBytes": 127723011
},
{
"names": [
"k8s.gcr.io/kube-controller-manager:v1.29.11",
"registry.k8s.io/kube-controller-manager:v1.29.11"
],
"sizeBytes": 123998458
},
{
"names": [
"k8s.gcr.io/kube-proxy:v1.29.11",
"registry.k8s.io/kube-proxy:v1.29.11"
],
"sizeBytes": 84241069
},
{
"names": [
"docker.io/flannel/flannel:v0.26.1"
],
"sizeBytes": 83929242
},
{
"names": [
"docker.io/openebs/provisioner-localpv:4.1.0"
],
"sizeBytes": 77454375
},
{
"names": [
"registry.k8s.io/nfd/node-feature-discovery@sha256:19ebca8b3804bfe2ee7324de4873875ab0a9112b51e0ace9dfd7c470beecf4a9",
"registry.k8s.io/nfd/node-feature-discovery:v0.16.6"
],
"sizeBytes": 69405547
},
{
"names": [
"docker.io/openebs/linux-utils:4.1.0"
],
"sizeBytes": 65541306
},
{
"names": [
"k8s.gcr.io/kube-scheduler:v1.29.11",
"registry.k8s.io/kube-scheduler:v1.29.11"
],
"sizeBytes": 61288697
},
{
"names": [
"k8s.gcr.io/coredns/coredns:v1.11.1",
"k8s.gcr.io/coredns:v1.11.1",
"registry.k8s.io/coredns/coredns:v1.11.1",
"registry.k8s.io/coredns:v1.11.1"
],
"sizeBytes": 61240402
},
{
"names": [
"ghcr.io/projectcontour/contour:v1.30.0"
],
"sizeBytes": 53207021
},
{
"names": [
"docker.io/library/haproxy:2.9.7-alpine3.20"
],
"sizeBytes": 26917938
},
{
"names": [
"docker.io/flannel/flannel-cni-plugin:v1.5.1-flannel2"
],
"sizeBytes": 10918841
},
{
"names": [
"k8s.gcr.io/pause:3.9",
"registry.k8s.io/pause:3.9"
],
"sizeBytes": 747356
},
{
"names": [
"k8s.gcr.io/pause:3.6",
"registry.k8s.io/pause:3.6"
],
"sizeBytes": 685844
}
],
"nodeInfo": {
"architecture": "x86_64",
"bootID": "b20715e0-443d-4368-9b23-10a8882c9896",
"containerRuntimeVersion": "containerd://1.6.33",
"kernelVersion": "6.8.0-1015-aws",
"kubeProxyVersion": "v1.29.11",
"kubeletVersion": "v1.29.11",
"machineID": "ec29e5a82f5ff79a06bc334c099a4ca4",
"operatingSystem": "linux",
"osImage": "Ubuntu 22.04.5 LTS",
"systemUUID": "ec29e5a8-2f5f-f79a-06bc-334c099a4ca4"
}
}
}
]
}
}
@@ -0,0 +1,47 @@
{
"kind": "ConfigMapList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "4825753"
},
"items": [
{
"kind": "ConfigMap",
"apiVersion": "v1",
"metadata": {
"name": "kube-root-ca.crt",
"namespace": "kube-public",
"uid": "37a912b2-a666-480f-a1a8-05ab012432e4",
"resourceVersion": "332",
"creationTimestamp": "2023-05-29T23:33:07Z",
"annotations": {
"kubernetes.io/description": "Contains a CA bundle that can be used to verify the kube-apiserver when using internal endpoints such as the internal service IP or kubernetes.default.svc. No other usage is guaranteed across distributions of Kubernetes clusters."
},
"managedFields": [
{
"manager": "kube-controller-manager",
"operation": "Update",
"apiVersion": "v1",
"time": "2023-05-29T23:33:07Z",
"fieldsType": "FieldsV1",
"fieldsV1": {
"f:data": {
".": {},
"f:ca.crt": {}
},
"f:metadata": {
"f:annotations": {
".": {},
"f:kubernetes.io/description": {}
}
}
}
}
]
},
"data": {
"ca.crt": "-----BEGIN CERTIFICATE-----\nMIIDBjCCAe6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwptaW5p\na3ViZUNBMB4XDTIzMDUyODIzMzIzOVoXDTMzMDUyNjIzMzIzOVowFTETMBEGA1UE\nAxMKbWluaWt1YmVDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOpU\nswd15tD+QCelgYlu5MRB260+Ke63gEDTkXIyAr+mR+TjW/v9TPPIP9iidvTUg+jq\nryrf1PwURVx3EUSZWtd9NpAEDT1ov/ggx16xxj2El7KLen0SXQutSF28zjCFXxYG\nMkPxXu+qYsn2mLJX5i1kaCZNffyToCJ0n2bxxx83rOS+fgz11JntAwcgC8V3Mtq6\nIv+2Xb9PSxPs38ef7r15j5KSTOrmWCR5texPFz/WU/YbZ3W42pj9T/EiuGwamfmI\ngevuwv7AxlfjKutp5UQEth5GhY6V4kJyVIUExN3ddEsTPLQD9zvdsP4DlkGpZmiR\n/Ip3rXY/ldxeeGZ0HUUCAwEAAaNhMF8wDgYDVR0PAQH/BAQDAgKkMB0GA1UdJQQW\nMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW\nBBS0XUXhCu/DOH8/0W5L0n4BOw9b6zANBgkqhkiG9w0BAQsFAAOCAQEAHrXOxKdy\n+DvPtvgfzzV3qqmIvTuuFyW0BdZdXV8yo1tZajkEO0B0HvLUyn8ljKgoK5YtCQcr\nzSj6QEewgP+JCBTsWCKzbOhMcDKw1pa6bSeLcQWwMxox+1Zcj7edMPlPcQ3SVLxZ\n6y7fD7BArTKRBKCr8Uwudwox5Vm0URWLRAvb+8jPv9BDuC/uMPJ4UrexL/Q2QQs8\nQkyWYeSk4mBCM3qAahQhYc0WSHbk+a/5iua/y+VUaa208CUbm5glBoAroHDk5eTN\nYstOSLUQAlzTdr4kCEVi+a3+NgmuvzYXWubAdy/PT860aFBJlmVNuDhy8V/bGpnS\nKxmG7B3yJmbmzg==\n-----END CERTIFICATE-----\n"
}
}
]
}
+12
View File
@@ -96,6 +96,18 @@ func TestAnalyzeGoldpinger_podPingsAnalysis(t *testing.T) {
},
},
},
{
name: "no ping errors for one pod",
cao: caoFixture(t, "goldpinger/checkall-one-pod.json"),
want: []*AnalyzeResult{
{
Title: "Pings to \"gp-goldpinger-xn9rg\" pod succeeded",
Message: "Pings to \"gp-goldpinger-xn9rg\" pod from all other pods in the cluster succeeded",
IconKey: "kubernetes",
IsPass: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
+113 -1
View File
@@ -1,6 +1,11 @@
package analyzer
import troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
import (
"fmt"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)
type HostAnalyzer interface {
Title() string
@@ -52,6 +57,14 @@ func GetHostAnalyzer(analyzer *troubleshootv1beta2.HostAnalyze) (HostAnalyzer, b
return &AnalyzeHostOS{analyzer.HostOS}, true
case analyzer.TextAnalyze != nil:
return &AnalyzeHostTextAnalyze{analyzer.TextAnalyze}, true
case analyzer.KernelConfigs != nil:
return &AnalyzeHostKernelConfigs{analyzer.KernelConfigs}, true
case analyzer.JsonCompare != nil:
return &AnalyzeHostJsonCompare{analyzer.JsonCompare}, true
case analyzer.NetworkNamespaceConnectivity != nil:
return &AnalyzeHostNetworkNamespaceConnectivity{analyzer.NetworkNamespaceConnectivity}, true
case analyzer.Sysctl != nil:
return &AnalyzeHostSysctl{analyzer.Sysctl}, true
default:
return nil, false
}
@@ -79,3 +92,102 @@ func (c *resultCollector) get(title string) []*AnalyzeResult {
}
return []*AnalyzeResult{{Title: title, IsWarn: true, Message: "no results"}}
}
func analyzeHostCollectorResults(collectedContent []collectedContent, outcomes []*troubleshootv1beta2.Outcome, checkCondition func(string, []byte) (bool, error), title string) ([]*AnalyzeResult, error) {
var results []*AnalyzeResult
for _, content := range collectedContent {
currentTitle := title
if content.NodeName != "" {
currentTitle = fmt.Sprintf("%s - Node %s", title, content.NodeName)
}
analyzeResult, err := evaluateOutcomes(outcomes, checkCondition, content.Data, currentTitle)
if err != nil {
return nil, errors.Wrap(err, "failed to evaluate outcomes")
}
if analyzeResult != nil {
results = append(results, analyzeResult...)
}
}
return results, nil
}
func evaluateOutcomes(outcomes []*troubleshootv1beta2.Outcome, checkCondition func(string, []byte) (bool, error), data []byte, title string) ([]*AnalyzeResult, error) {
var results []*AnalyzeResult
for _, outcome := range outcomes {
result := AnalyzeResult{
Title: title,
}
switch {
case outcome.Fail != nil:
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
results = append(results, &result)
return results, nil
}
isMatch, err := checkCondition(outcome.Fail.When, data)
if err != nil {
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to compare %s", outcome.Fail.When)
}
if isMatch {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
results = append(results, &result)
return results, nil
}
case outcome.Warn != nil:
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
results = append(results, &result)
return results, nil
}
isMatch, err := checkCondition(outcome.Warn.When, data)
if err != nil {
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to compare %s", outcome.Warn.When)
}
if isMatch {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
results = append(results, &result)
return results, nil
}
case outcome.Pass != nil:
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
results = append(results, &result)
return results, nil
}
isMatch, err := checkCondition(outcome.Pass.When, data)
if err != nil {
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to compare %s", outcome.Pass.When)
}
if isMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
results = append(results, &result)
return results, nil
}
}
}
return nil, nil
}
+294
View File
@@ -0,0 +1,294 @@
package analyzer
import (
"encoding/json"
"testing"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func collectorToBytes(t *testing.T, collector any) []byte {
jsonData, err := json.Marshal(collector)
require.NoError(t, err)
return jsonData
}
func TestAnalyzeHostCollectorResults(t *testing.T) {
tests := []struct {
name string
outcomes []*troubleshootv1beta2.Outcome
collectedContent []collectedContent
expectResult []*AnalyzeResult
}{
{
name: "pass if ubuntu >= 00.1.2",
collectedContent: []collectedContent{
{
NodeName: "node1",
Data: collectorToBytes(t, collect.HostOSInfo{
Name: "myhost",
KernelVersion: "5.4.0-1034-gcp",
PlatformVersion: "00.1.2",
Platform: "ubuntu",
}),
},
},
outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu >= 00.1.2",
Message: "supported distribution matches ubuntu >= 00.1.2",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "unsupported distribution",
},
},
},
expectResult: []*AnalyzeResult{
{
Title: "Host OS Info - Node node1",
IsPass: true,
Message: "supported distribution matches ubuntu >= 00.1.2",
},
},
},
{
name: "fail if ubuntu <= 11.04",
collectedContent: []collectedContent{
{
NodeName: "node1",
Data: collectorToBytes(t, collect.HostOSInfo{
Name: "myhost",
KernelVersion: "5.4.0-1034-gcp",
PlatformVersion: "11.04",
Platform: "ubuntu",
}),
},
{
NodeName: "node2",
Data: collectorToBytes(t, collect.HostOSInfo{
Name: "myhost",
KernelVersion: "5.4.0-1034-gcp",
PlatformVersion: "11.04",
Platform: "ubuntu",
}),
},
},
outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu <= 11.04",
Message: "unsupported ubuntu version 11.04",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "supported distribution",
},
},
},
expectResult: []*AnalyzeResult{
{
Title: "Host OS Info - Node node1",
IsFail: true,
Message: "unsupported ubuntu version 11.04",
},
{
Title: "Host OS Info - Node node2",
IsFail: true,
Message: "unsupported ubuntu version 11.04",
},
},
},
{
name: "title does not include node name if empty",
collectedContent: []collectedContent{
{
NodeName: "",
Data: collectorToBytes(t, collect.HostOSInfo{
Name: "myhost",
KernelVersion: "5.4.0-1034-gcp",
PlatformVersion: "20.04",
Platform: "ubuntu",
}),
},
},
outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu >= 20.04",
Message: "supported distribution matches ubuntu >= 20.04",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "unsupported distribution",
},
},
},
expectResult: []*AnalyzeResult{
{
Title: "Host OS Info", // Ensuring the title does not include node name if it's empty
IsPass: true,
Message: "supported distribution matches ubuntu >= 20.04",
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
a := AnalyzeHostOS{}
// Call the new analyzeHostCollectorResults function with the test data
result, err := analyzeHostCollectorResults(test.collectedContent, test.outcomes, a.CheckCondition, "Host OS Info")
require.NoError(t, err)
assert.Equal(t, test.expectResult, result)
})
}
}
func TestEvaluateOutcomes(t *testing.T) {
tests := []struct {
name string
outcomes []*troubleshootv1beta2.Outcome
checkCondition func(string, []byte) (bool, error)
data []byte
expectedResult []*AnalyzeResult
}{
{
name: "fail condition matches",
outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "failCondition",
Message: "failure condition met",
},
},
},
checkCondition: func(when string, data []byte) (bool, error) {
// Return true if the condition being checked matches "failCondition"
return when == "failCondition", nil
},
data: []byte("someData"),
expectedResult: []*AnalyzeResult{
{
Title: "Test Title",
IsFail: true,
Message: "failure condition met",
},
},
},
{
name: "warn condition matches",
outcomes: []*troubleshootv1beta2.Outcome{
{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "warnCondition",
Message: "warning condition met",
},
},
},
checkCondition: func(when string, data []byte) (bool, error) {
// Return true if the condition being checked matches "warnCondition"
return when == "warnCondition", nil
},
data: []byte("someData"),
expectedResult: []*AnalyzeResult{
{
Title: "Test Title",
IsWarn: true,
Message: "warning condition met",
},
},
},
{
name: "pass condition matches",
outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "passCondition",
Message: "pass condition met",
},
},
},
checkCondition: func(when string, data []byte) (bool, error) {
// Return true if the condition being checked matches "passCondition"
return when == "passCondition", nil
},
data: []byte("someData"),
expectedResult: []*AnalyzeResult{
{
Title: "Test Title",
IsPass: true,
Message: "pass condition met",
},
},
},
{
name: "no condition matches",
outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "failCondition",
Message: "failure condition met",
},
Warn: &troubleshootv1beta2.SingleOutcome{
When: "warnCondition",
Message: "warning condition met",
},
Pass: &troubleshootv1beta2.SingleOutcome{
When: "passCondition",
Message: "pass condition met",
},
},
},
checkCondition: func(when string, data []byte) (bool, error) {
// Always return false to simulate no condition matching
return false, nil
},
data: []byte("someData"),
expectedResult: nil, // No condition matches, so we expect no results
},
{
name: "error in checkCondition",
outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "failCondition",
Message: "failure condition met",
},
},
},
checkCondition: func(when string, data []byte) (bool, error) {
// Simulate an error occurring during condition evaluation
return false, errors.New("mock error")
},
data: []byte("someData"),
expectedResult: []*AnalyzeResult{
{
Title: "Test Title",
IsFail: false, // Error occurred, so no success flag
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result, err := evaluateOutcomes(test.outcomes, test.checkCondition, test.data, "Test Title")
if test.name == "error in checkCondition" {
require.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, test.expectedResult, result)
}
})
}
}
+23 -78
View File
@@ -27,90 +27,24 @@ func (a *AnalyzeHostBlockDevices) IsExcluded() (bool, error) {
func (a *AnalyzeHostBlockDevices) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
result := AnalyzeResult{Title: a.Title()}
contents, err := getCollectedFileContents(collect.HostBlockDevicesPath)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
collect.HostBlockDevicesPath,
collect.NodeInfoBaseDir,
collect.HostBlockDevicesFileName,
)
if err != nil {
return nil, errors.Wrap(err, "failed to get collected file")
return []*AnalyzeResult{&result}, err
}
var devices []collect.BlockDeviceInfo
if err := json.Unmarshal(contents, &devices); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal block devices info")
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze block devices")
}
result := AnalyzeResult{}
result.Title = a.Title()
for _, outcome := range hostAnalyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{&result}, nil
}
isMatch, err := compareHostBlockDevicesConditionalToActual(outcome.Fail.When, hostAnalyzer.MinimumAcceptableSize, hostAnalyzer.IncludeUnmountedPartitions, devices)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Fail.When)
}
if isMatch {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{&result}, nil
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return []*AnalyzeResult{&result}, nil
}
isMatch, err := compareHostBlockDevicesConditionalToActual(outcome.Warn.When, hostAnalyzer.MinimumAcceptableSize, hostAnalyzer.IncludeUnmountedPartitions, devices)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Warn.When)
}
if isMatch {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return []*AnalyzeResult{&result}, nil
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{&result}, nil
}
isMatch, err := compareHostBlockDevicesConditionalToActual(outcome.Pass.When, hostAnalyzer.MinimumAcceptableSize, hostAnalyzer.IncludeUnmountedPartitions, devices)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Pass.When)
}
if isMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{&result}, nil
}
}
}
return []*AnalyzeResult{&result}, nil
return results, nil
}
// <regexp> <op> <count>
@@ -205,3 +139,14 @@ func isEligibleBlockDevice(rx *regexp.Regexp, minimumAcceptableSize uint64, incl
return true
}
func (a *AnalyzeHostBlockDevices) CheckCondition(when string, data []byte) (bool, error) {
var devices []collect.BlockDeviceInfo
if err := json.Unmarshal(data, &devices); err != nil {
return false, errors.Wrap(err, "failed to unmarshal block devices info")
}
return compareHostBlockDevicesConditionalToActual(when, a.hostAnalyzer.MinimumAcceptableSize, a.hostAnalyzer.IncludeUnmountedPartitions, devices)
}
+24 -42
View File
@@ -1,7 +1,7 @@
package analyzer
import (
"path/filepath"
"fmt"
"strings"
"github.com/pkg/errors"
@@ -23,52 +23,34 @@ func (a *AnalyzeHostCertificate) IsExcluded() (bool, error) {
func (a *AnalyzeHostCertificate) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
collectorName := hostAnalyzer.CollectorName
collectorName := a.hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "certificate"
}
name := filepath.Join("host-collectors/certificate", collectorName+".json")
contents, err := getCollectedFileContents(name)
const nodeBaseDir = "host-collectors/certificate"
localPath := fmt.Sprintf("%s/%s", nodeBaseDir, collectorName)
fileName := fmt.Sprintf("%s.json", collectorName)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
nodeBaseDir,
fileName,
)
if err != nil {
return nil, errors.Wrap(err, "failed to get collected file")
}
status := string(contents)
trimmedStatus := strings.TrimSpace(status)
var coll resultCollector
for _, outcome := range hostAnalyzer.Outcomes {
result := &AnalyzeResult{Title: a.Title()}
if outcome.Fail != nil {
if outcome.Fail.When == "" || outcome.Fail.When == trimmedStatus {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" || outcome.Warn.When == trimmedStatus {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" || outcome.Pass.When == trimmedStatus {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
}
}
return []*AnalyzeResult{{Title: a.Title()}}, err
}
return coll.get(a.Title()), nil
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze certificate")
}
return results, nil
}
func (a *AnalyzeHostCertificate) CheckCondition(when string, data []byte) (bool, error) {
return strings.TrimSpace(string(data)) == when, nil
}
+110 -80
View File
@@ -2,6 +2,8 @@ package analyzer
import (
"encoding/json"
"fmt"
"slices"
"strconv"
"strings"
@@ -10,6 +12,16 @@ import (
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
// microarchs holds a list of features present in each microarchitecture.
// ref: https://gitlab.com/x86-psABIs/x86-64-ABI
// ref: https://developers.redhat.com/blog/2021/01/05/building-red-hat-enterprise-linux-9-for-the-x86-64-v2-microarchitecture-level
var microarchs = map[string][]string{
"x86-64": {"cmov", "cx8", "fpu", "fxsr", "mmx", "syscall", "sse", "sse2"},
"x86-64-v2": {"cx16", "lahf_lm", "popcnt", "ssse3", "sse4_1", "sse4_2", "ssse3"},
"x86-64-v3": {"avx", "avx2", "bmi1", "bmi2", "f16c", "fma", "lzcnt", "movbe", "xsave"},
"x86-64-v4": {"avx512f", "avx512bw", "avx512cd", "avx512dq", "avx512vl"},
}
type AnalyzeHostCPU struct {
hostAnalyzer *troubleshootv1beta2.CPUAnalyze
}
@@ -22,104 +34,96 @@ func (a *AnalyzeHostCPU) IsExcluded() (bool, error) {
return isExcluded(a.hostAnalyzer.Exclude)
}
func (a *AnalyzeHostCPU) CheckCondition(when string, data []byte) (bool, error) {
cpuInfo := collect.CPUInfo{}
if err := json.Unmarshal(data, &cpuInfo); err != nil {
return false, fmt.Errorf("failed to unmarshal data into CPUInfo: %v", err)
}
return compareHostCPUConditionalToActual(when, cpuInfo.LogicalCount, cpuInfo.PhysicalCount, cpuInfo.Flags, cpuInfo.MachineArch)
}
func (a *AnalyzeHostCPU) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
result := AnalyzeResult{Title: a.Title()}
contents, err := getCollectedFileContents(collect.HostCPUPath)
// Use the generic function to collect both local and remote data
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
collect.HostCPUPath, // Local path
collect.NodeInfoBaseDir, // Remote base directory
collect.HostCPUFileName, // Remote file name
)
if err != nil {
return nil, errors.Wrap(err, "failed to get collected file")
return []*AnalyzeResult{&result}, err
}
cpuInfo := collect.CPUInfo{}
if err := json.Unmarshal(contents, &cpuInfo); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal cpu info")
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze OS version")
}
result := AnalyzeResult{
Title: a.Title(),
}
return results, nil
for _, outcome := range hostAnalyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{&result}, nil
}
isMatch, err := compareHostCPUConditionalToActual(outcome.Fail.When, cpuInfo.LogicalCount, cpuInfo.PhysicalCount)
if err != nil {
return nil, errors.Wrap(err, "failed to compare")
}
if isMatch {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{&result}, nil
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return []*AnalyzeResult{&result}, nil
}
isMatch, err := compareHostCPUConditionalToActual(outcome.Warn.When, cpuInfo.LogicalCount, cpuInfo.PhysicalCount)
if err != nil {
return nil, errors.Wrap(err, "failed to compare")
}
if isMatch {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return []*AnalyzeResult{&result}, nil
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{&result}, nil
}
isMatch, err := compareHostCPUConditionalToActual(outcome.Pass.When, cpuInfo.LogicalCount, cpuInfo.PhysicalCount)
if err != nil {
return nil, errors.Wrap(err, "failed to compare")
}
if isMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{&result}, nil
}
}
}
return []*AnalyzeResult{&result}, nil
}
func compareHostCPUConditionalToActual(conditional string, logicalCount int, physicalCount int) (res bool, err error) {
func doCompareHostCPUMicroArchitecture(microarch string, flags []string) (res bool, err error) {
specifics := make([]string, 0)
switch microarch {
case "x86-64-v4":
specifics = append(specifics, microarchs["x86-64-v4"]...)
fallthrough
case "x86-64-v3":
specifics = append(specifics, microarchs["x86-64-v3"]...)
fallthrough
case "x86-64-v2":
specifics = append(specifics, microarchs["x86-64-v2"]...)
fallthrough
case "x86-64":
specifics = append(specifics, microarchs["x86-64"]...)
default:
return false, errors.Errorf("troubleshoot does not yet support microarchitecture %q", microarch)
}
for _, flag := range specifics {
if slices.Contains(flags, flag) {
continue
}
return false, nil
}
return true, nil
}
func doCompareHostCPUFlags(expected string, flags []string) (res bool, err error) {
expectedFlags := strings.Split(expected, ",")
if len(expectedFlags) == 0 {
return false, errors.New("expected flags cannot be empty")
}
for _, flag := range expectedFlags {
if slices.Contains(flags, flag) {
continue
}
return false, nil
}
return true, nil
}
func compareHostCPUConditionalToActual(conditional string, logicalCount int, physicalCount int, flags []string, machineArch string) (res bool, err error) {
compareLogical := false
comparePhysical := false
compareUnspecified := false
compareMachineArch := false
comparator := ""
desired := ""
/* When the conditional is in the format of "logical <comparator> <desired>"
example: when: "count < 2"
*/
parts := strings.Split(conditional, " ")
if len(parts) == 3 {
comparator = parts[1]
@@ -130,6 +134,8 @@ func compareHostCPUConditionalToActual(conditional string, logicalCount int, phy
comparePhysical = true
} else if strings.ToLower(parts[0]) == "count" {
compareUnspecified = true
} else if strings.ToLower(parts[0]) == "machinearch" {
compareMachineArch = true
}
} else if len(parts) == 2 {
compareUnspecified = true
@@ -137,7 +143,17 @@ func compareHostCPUConditionalToActual(conditional string, logicalCount int, phy
desired = parts[1]
}
if !compareLogical && !comparePhysical && !compareUnspecified {
// analyze if the cpu supports a specific set of features, aka as micrarchitecture.
if strings.ToLower(comparator) == "supports" {
return doCompareHostCPUMicroArchitecture(desired, flags)
}
// hasFlags allows users to query for specific flags on the CPU.
if strings.ToLower(comparator) == "hasflags" {
return doCompareHostCPUFlags(desired, flags)
}
if !compareLogical && !comparePhysical && !compareUnspecified && !compareMachineArch {
return false, errors.New("unable to parse conditional")
}
@@ -145,6 +161,8 @@ func compareHostCPUConditionalToActual(conditional string, logicalCount int, phy
return doCompareHostCPU(comparator, desired, logicalCount)
} else if comparePhysical {
return doCompareHostCPU(comparator, desired, physicalCount)
} else if compareMachineArch {
return doCompareMachineArch(comparator, desired, machineArch)
} else {
actual := logicalCount
if physicalCount > logicalCount {
@@ -155,6 +173,16 @@ func compareHostCPUConditionalToActual(conditional string, logicalCount int, phy
}
}
func doCompareMachineArch(operator string, desired string, actual string) (bool, error) {
switch operator {
case "=", "==", "===":
return actual == desired, nil
case "!=", "!==":
return actual != desired, nil
}
return false, errors.New("unknown operator")
}
func doCompareHostCPU(operator string, desired string, actual int) (bool, error) {
desiredInt, err := strconv.ParseInt(desired, 10, 64)
if err != nil {
@@ -172,6 +200,8 @@ func doCompareHostCPU(operator string, desired string, actual int) (bool, error)
return actual >= int(desiredInt), nil
case "=", "==", "===":
return actual == int(desiredInt), nil
case "!=", "!==":
return actual != int(desiredInt), nil
}
return false, errors.New("unknown operator")
+33 -1
View File
@@ -81,6 +81,8 @@ func Test_compareHostCPUConditionalToActual(t *testing.T) {
when string
logicalCount int
physicalCount int
flags []string
machineArch string
expected bool
}{
{
@@ -139,12 +141,42 @@ func Test_compareHostCPUConditionalToActual(t *testing.T) {
physicalCount: 4,
expected: true,
},
{
name: "supports x86-64-v2 microarchitecture",
when: "supports x86-64-v2",
flags: []string{""},
expected: false,
},
{
name: "supports x86-64-v2 microarchitecture",
when: "supports x86-64-v2",
flags: []string{"cmov", "cx8", "fpu", "fxsr", "mmx", "syscall", "sse", "sse2", "cx16", "lahf_lm", "popcnt", "ssse3", "sse4_1", "sse4_2", "ssse3"},
expected: true,
},
{
name: "has a non existent flag",
when: "hasFlags cmov,doesNotExist",
flags: []string{"cmov", "cx8", "fpu", "fxsr", "mmx", "syscall", "sse", "sse2", "cx16", "lahf_lm", "popcnt", "ssse3", "sse4_1", "sse4_2", "ssse3"},
expected: false,
},
{
name: "has flags",
when: "hasFlags a,b,c",
flags: []string{"a", "b", "c", "d", "e"},
expected: true,
},
{
name: "machine arch matches",
when: "machineArch == x86_64",
machineArch: "x86_64",
expected: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
actual, err := compareHostCPUConditionalToActual(test.when, test.logicalCount, test.physicalCount)
actual, err := compareHostCPUConditionalToActual(test.when, test.logicalCount, test.physicalCount, test.flags, test.machineArch)
req.NoError(err)
assert.Equal(t, test.expected, actual)
+29 -85
View File
@@ -3,7 +3,6 @@ package analyzer
import (
"encoding/json"
"fmt"
"path/filepath"
"strconv"
"strings"
@@ -28,97 +27,32 @@ func (a *AnalyzeHostDiskUsage) IsExcluded() (bool, error) {
func (a *AnalyzeHostDiskUsage) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
name := filepath.Join("host-collectors/diskUsage", "diskUsage.json")
if hostAnalyzer.CollectorName != "" {
name = filepath.Join("host-collectors/diskUsage", hostAnalyzer.CollectorName+".json")
collectorName := a.hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "diskUsage"
}
contents, err := getCollectedFileContents(name)
const nodeBaseDir = "host-collectors/diskUsage"
localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName)
fileName := fmt.Sprintf("%s.json", collectorName)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
nodeBaseDir,
fileName,
)
if err != nil {
return nil, errors.Wrapf(err, "failed to get collected file %s", name)
return []*AnalyzeResult{{Title: a.Title()}}, err
}
diskUsageInfo := collect.DiskUsageInfo{}
if err := json.Unmarshal(contents, &diskUsageInfo); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal disk usage info from %s", name)
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze disk usage")
}
result := AnalyzeResult{
Title: a.Title(),
}
for _, outcome := range hostAnalyzer.Outcomes {
result := &AnalyzeResult{Title: a.Title()}
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{result}, nil
}
isMatch, err := compareHostDiskUsageConditionalToActual(outcome.Fail.When, diskUsageInfo.TotalBytes, diskUsageInfo.UsedBytes)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %q", outcome.Fail.When)
}
if isMatch {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{result}, nil
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return []*AnalyzeResult{result}, nil
}
isMatch, err := compareHostDiskUsageConditionalToActual(outcome.Warn.When, diskUsageInfo.TotalBytes, diskUsageInfo.UsedBytes)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %q", outcome.Warn.When)
}
if isMatch {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return []*AnalyzeResult{result}, nil
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{result}, nil
}
isMatch, err := compareHostDiskUsageConditionalToActual(outcome.Pass.When, diskUsageInfo.TotalBytes, diskUsageInfo.UsedBytes)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %q", outcome.Pass.When)
}
if isMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{result}, nil
}
}
}
return []*AnalyzeResult{&result}, nil
return results, nil
}
func compareHostDiskUsageConditionalToActual(conditional string, totalBytes uint64, usedBytes uint64) (res bool, err error) {
@@ -202,3 +136,13 @@ func doCompareHostDiskUsagePercent(operator string, desired string, actual float
return false, errors.New("unknown operator")
}
func (a *AnalyzeHostDiskUsage) CheckCondition(when string, data []byte) (bool, error) {
var diskUsageInfo collect.DiskUsageInfo
if err := json.Unmarshal(data, &diskUsageInfo); err != nil {
return false, fmt.Errorf("failed to unmarshal data into DiskUsageInfo: %v", err)
}
return compareHostDiskUsageConditionalToActual(when, diskUsageInfo.TotalBytes, diskUsageInfo.UsedBytes)
}
+131 -96
View File
@@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"log"
"path/filepath"
"strings"
"text/template"
"time"
@@ -30,18 +29,23 @@ func (a *AnalyzeHostFilesystemPerformance) IsExcluded() (bool, error) {
func (a *AnalyzeHostFilesystemPerformance) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
result := &AnalyzeResult{Title: a.Title()}
hostAnalyzer := a.hostAnalyzer
collectorName := a.hostAnalyzer.CollectorName
result := &AnalyzeResult{
Title: a.Title(),
}
collectorName := hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "filesystemPerformance"
}
name := filepath.Join("host-collectors/filesystemPerformance", collectorName+".json")
contents, err := getCollectedFileContents(name)
const nodeBaseDir = "host-collectors/filesystemPerformance"
localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName)
fileName := fmt.Sprintf("%s.json", collectorName)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
nodeBaseDir,
fileName,
)
if err != nil {
if len(hostAnalyzer.Outcomes) >= 1 {
// if the very first outcome is FILE_NOT_COLLECTED', then use that outcome
@@ -64,97 +68,26 @@ func (a *AnalyzeHostFilesystemPerformance) Analyze(
result.URI = hostAnalyzer.Outcomes[0].Pass.URI
return []*AnalyzeResult{result}, nil
}
}
return nil, errors.Wrapf(err, "failed to get collected file %s", name)
}
fioResult := collect.FioResult{}
if err := json.Unmarshal(contents, &fioResult); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal fio results from %s", name)
}
if len(fioResult.Jobs) == 0 {
return nil, errors.Errorf("no jobs found in fio results from %s", name)
}
fioWriteLatency := fioResult.Jobs[0].Sync
fsPerf := fioWriteLatency.FSPerfResults()
if err := json.Unmarshal(contents, &fsPerf); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal filesystem performance results from %s", name)
}
for _, outcome := range hostAnalyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = renderFSPerfOutcome(outcome.Fail.Message, fsPerf)
result.URI = outcome.Fail.URI
return []*AnalyzeResult{result}, nil
}
isMatch, err := compareHostFilesystemPerformanceConditionalToActual(outcome.Fail.When, fsPerf)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %q", outcome.Fail.When)
}
if isMatch {
result.IsFail = true
result.Message = renderFSPerfOutcome(outcome.Fail.Message, fsPerf)
result.URI = outcome.Fail.URI
return []*AnalyzeResult{result}, nil
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = renderFSPerfOutcome(outcome.Warn.Message, fsPerf)
result.URI = outcome.Warn.URI
return []*AnalyzeResult{result}, nil
}
isMatch, err := compareHostFilesystemPerformanceConditionalToActual(outcome.Warn.When, fsPerf)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %q", outcome.Warn.When)
}
if isMatch {
result.IsWarn = true
result.Message = renderFSPerfOutcome(outcome.Warn.Message, fsPerf)
result.URI = outcome.Warn.URI
return []*AnalyzeResult{result}, nil
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = renderFSPerfOutcome(outcome.Pass.Message, fsPerf)
result.URI = outcome.Pass.URI
return []*AnalyzeResult{result}, nil
}
isMatch, err := compareHostFilesystemPerformanceConditionalToActual(outcome.Pass.When, fsPerf)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %q", outcome.Pass.When)
}
if isMatch {
result.IsPass = true
result.Message = renderFSPerfOutcome(outcome.Pass.Message, fsPerf)
result.URI = outcome.Pass.URI
return []*AnalyzeResult{result}, nil
}
return nil, errors.Wrapf(err, "failed to get collected file %s", localPath)
}
}
return []*AnalyzeResult{result}, nil
var results []*AnalyzeResult
for _, content := range collectedContents {
currentTitle := a.Title()
if content.NodeName != "" {
currentTitle = fmt.Sprintf("%s - Node %s", a.Title(), content.NodeName)
}
result, err := a.analyzeSingleNode(content, currentTitle)
if err != nil {
return nil, errors.Wrapf(err, "failed to analyze filesystem performance for %s", currentTitle)
}
if result != nil {
results = append(results, result...)
}
}
return results, nil
}
func compareHostFilesystemPerformanceConditionalToActual(conditional string, fsPerf collect.FSPerfResults) (res bool, err error) {
@@ -251,3 +184,105 @@ func renderFSPerfOutcome(outcome string, fsPerf collect.FSPerfResults) string {
}
return buf.String()
}
func (a *AnalyzeHostFilesystemPerformance) analyzeSingleNode(content collectedContent, currentTitle string) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
result := &AnalyzeResult{
Title: currentTitle,
}
fioResult := collect.FioResult{}
if err := json.Unmarshal(content.Data, &fioResult); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal fio results from %s", currentTitle)
}
var job *collect.FioJobs
for _, j := range fioResult.Jobs {
if j.JobName == collect.FioJobName {
job = &j
break
}
}
if job == nil {
return nil, errors.Errorf("no job named 'fsperf' found in fio results from %s", currentTitle)
}
fioWriteLatency := job.Sync
fsPerf := fioWriteLatency.FSPerfResults()
if err := json.Unmarshal(content.Data, &fsPerf); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal filesystem performance results from %s", currentTitle)
}
for _, outcome := range hostAnalyzer.Outcomes {
switch {
case outcome.Fail != nil:
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = renderFSPerfOutcome(outcome.Fail.Message, fsPerf)
result.URI = outcome.Fail.URI
return []*AnalyzeResult{result}, nil
}
isMatch, err := compareHostFilesystemPerformanceConditionalToActual(outcome.Fail.When, fsPerf)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %q", outcome.Fail.When)
}
if isMatch {
result.IsFail = true
result.Message = renderFSPerfOutcome(outcome.Fail.Message, fsPerf)
result.URI = outcome.Fail.URI
return []*AnalyzeResult{result}, nil
}
case outcome.Warn != nil:
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = renderFSPerfOutcome(outcome.Warn.Message, fsPerf)
result.URI = outcome.Warn.URI
return []*AnalyzeResult{result}, nil
}
isMatch, err := compareHostFilesystemPerformanceConditionalToActual(outcome.Warn.When, fsPerf)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %q", outcome.Warn.When)
}
if isMatch {
result.IsWarn = true
result.Message = renderFSPerfOutcome(outcome.Warn.Message, fsPerf)
result.URI = outcome.Warn.URI
return []*AnalyzeResult{result}, nil
}
case outcome.Pass != nil:
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = renderFSPerfOutcome(outcome.Pass.Message, fsPerf)
result.URI = outcome.Pass.URI
return []*AnalyzeResult{result}, nil
}
isMatch, err := compareHostFilesystemPerformanceConditionalToActual(outcome.Pass.When, fsPerf)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %q", outcome.Pass.When)
}
if isMatch {
result.IsPass = true
result.Message = renderFSPerfOutcome(outcome.Pass.Message, fsPerf)
result.URI = outcome.Pass.URI
return []*AnalyzeResult{result}, nil
}
default:
return nil, errors.New("unexpected outcome")
}
}
return []*AnalyzeResult{result}, nil
}
+64 -33
View File
@@ -3,7 +3,6 @@ package analyzer
import (
"encoding/json"
"fmt"
"path/filepath"
"strconv"
"strings"
@@ -32,13 +31,65 @@ func (a *AnalyzeHostHTTP) IsExcluded() (bool, error) {
func (a *AnalyzeHostHTTP) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
name := filepath.Join("host-collectors/http", "result.json")
if hostAnalyzer.CollectorName != "" {
name = filepath.Join("host-collectors/http", hostAnalyzer.CollectorName+".json")
collectorName := a.hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "result"
}
contents, err := getCollectedFileContents(name)
const nodeBaseDir = "host-collectors/http"
localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName)
fileName := fmt.Sprintf("%s.json", collectorName)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
nodeBaseDir,
fileName,
)
if err != nil {
return []*AnalyzeResult{{Title: a.Title()}}, err
}
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze http request")
}
return results, nil
}
func compareHostHTTPConditionalToActual(conditional string, result *httpResult) (res bool, err error) {
if conditional == "error" {
return result.Error != nil, nil
}
parts := strings.Split(conditional, " ")
if len(parts) != 3 {
return false, fmt.Errorf("Failed to parse conditional: got %d parts", len(parts))
}
if parts[0] != "statusCode" {
return false, errors.New(`Conditional must begin with keyword "statusCode"`)
}
if parts[1] != "=" && parts[1] != "==" && parts[1] != "===" {
return false, errors.New(`Only supported operator is "=="`)
}
i, err := strconv.Atoi(parts[2])
if err != nil {
return false, err
}
if result.Response == nil {
return false, err
}
return result.Response.Status == i, nil
}
func analyzeHTTPResult(analyzer *troubleshootv1beta2.HTTPAnalyze, fileName string, getCollectedFileContents getCollectedFileContents, title string) ([]*AnalyzeResult, error) {
contents, err := getCollectedFileContents(fileName)
if err != nil {
return nil, errors.Wrap(err, "failed to get collected file")
}
@@ -49,10 +100,10 @@ func (a *AnalyzeHostHTTP) Analyze(
}
result := &AnalyzeResult{
Title: a.Title(),
Title: title,
}
for _, outcome := range hostAnalyzer.Outcomes {
for _, outcome := range analyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
@@ -123,31 +174,11 @@ func (a *AnalyzeHostHTTP) Analyze(
return []*AnalyzeResult{result}, nil
}
func compareHostHTTPConditionalToActual(conditional string, result *httpResult) (res bool, err error) {
if conditional == "error" {
return result.Error != nil, nil
}
func (a *AnalyzeHostHTTP) CheckCondition(when string, data []byte) (bool, error) {
parts := strings.Split(conditional, " ")
if len(parts) != 3 {
return false, fmt.Errorf("Failed to parse conditional: got %d parts", len(parts))
var httpInfo httpResult
if err := json.Unmarshal(data, &httpInfo); err != nil {
return false, fmt.Errorf("failed to unmarshal data into httpResult: %v", err)
}
if parts[0] != "statusCode" {
return false, errors.New(`Conditional must begin with keyword "statusCode"`)
}
if parts[1] != "=" && parts[1] != "==" && parts[1] != "===" {
return false, errors.New(`Only supported operator is "=="`)
}
i, err := strconv.Atoi(parts[2])
if err != nil {
return false, err
}
if result.Response == nil {
return false, err
}
return result.Response.Status == i, nil
return compareHostHTTPConditionalToActual(when, &httpInfo)
}
+30 -70
View File
@@ -3,7 +3,6 @@ package analyzer
import (
"encoding/json"
"fmt"
"path"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
@@ -25,82 +24,43 @@ func (a *AnalyzeHostHTTPLoadBalancer) IsExcluded() (bool, error) {
func (a *AnalyzeHostHTTPLoadBalancer) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
collectorName := hostAnalyzer.CollectorName
collectorName := a.hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "httpLoadBalancer"
}
fullPath := path.Join("host-collectors/httpLoadBalancer", fmt.Sprintf("%s.json", collectorName))
collected, err := getCollectedFileContents(fullPath)
const nodeBaseDir = "host-collectors/httpLoadBalancer"
localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName)
fileName := fmt.Sprintf("%s.json", collectorName)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
nodeBaseDir,
fileName,
)
if err != nil {
return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath)
}
actual := collect.NetworkStatusResult{}
if err := json.Unmarshal(collected, &actual); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal collected")
return []*AnalyzeResult{{Title: a.Title()}}, err
}
var coll resultCollector
for _, outcome := range hostAnalyzer.Outcomes {
result := &AnalyzeResult{Title: a.Title()}
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
continue
}
if string(actual.Status) == outcome.Fail.When {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
continue
}
if string(actual.Status) == outcome.Warn.When {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
continue
}
if string(actual.Status) == outcome.Pass.When {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
}
}
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze HTTP Load Balancer")
}
return coll.get(a.Title()), nil
return results, nil
}
func (a *AnalyzeHostHTTPLoadBalancer) CheckCondition(when string, data []byte) (bool, error) {
var httpLoadBalancer collect.NetworkStatusResult
if err := json.Unmarshal(data, &httpLoadBalancer); err != nil {
return false, fmt.Errorf("failed to unmarshal data into NetworkStatusResult: %v", err)
}
if string(httpLoadBalancer.Status) == when {
return true, nil
}
return false, nil
}
+112
View File
@@ -0,0 +1,112 @@
package analyzer
import (
"encoding/json"
"testing"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAnalyzeHTTPLoadBalancer(t *testing.T) {
tests := []struct {
name string
info *collect.NetworkStatusResult
hostAnalyzer *troubleshootv1beta2.HTTPLoadBalancerAnalyze
result []*AnalyzeResult
expectErr bool
}{
{
name: "connection refused, fail",
info: &collect.NetworkStatusResult{
Status: collect.NetworkStatusConnectionRefused,
},
hostAnalyzer: &troubleshootv1beta2.HTTPLoadBalancerAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "connection-refused",
Message: "Connection was refused",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "connected",
Message: "Connection was successful",
},
},
{
Warn: &troubleshootv1beta2.SingleOutcome{
Message: "Unexpected HTTP connection status",
},
},
},
},
result: []*AnalyzeResult{
{
Title: "HTTP Load Balancer",
IsFail: true,
Message: "Connection was refused",
},
},
},
{
name: "connected, success",
info: &collect.NetworkStatusResult{
Status: collect.NetworkStatusConnected,
},
hostAnalyzer: &troubleshootv1beta2.HTTPLoadBalancerAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "connection-refused",
Message: "Connection was refused",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "connected",
Message: "Connection was successful",
},
},
{
Warn: &troubleshootv1beta2.SingleOutcome{
Message: "Unexpected HTTP connection status",
},
},
},
},
result: []*AnalyzeResult{
{
Title: "HTTP Load Balancer",
IsPass: true,
Message: "Connection was successful",
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
b, err := json.Marshal(test.info)
if err != nil {
t.Fatal(err)
}
getCollectedFileContents := func(filename string) ([]byte, error) {
return b, nil
}
result, err := (&AnalyzeHostHTTPLoadBalancer{test.hostAnalyzer}).Analyze(getCollectedFileContents, nil)
if test.expectErr {
req.Error(err)
} else {
req.NoError(err)
}
assert.Equal(t, test.result, result)
})
}
}
+22 -82
View File
@@ -27,94 +27,24 @@ func (a *AnalyzeHostIPV4Interfaces) IsExcluded() (bool, error) {
func (a *AnalyzeHostIPV4Interfaces) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
result := AnalyzeResult{Title: a.Title()}
contents, err := getCollectedFileContents(collect.HostIPV4InterfacesPath)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
collect.HostIPV4InterfacesPath,
collect.NodeInfoBaseDir,
collect.HostIPV4FileName,
)
if err != nil {
return nil, errors.Wrap(err, "failed to get collected file")
return []*AnalyzeResult{&result}, err
}
var ipv4Interfaces []net.Interface
if err := json.Unmarshal(contents, &ipv4Interfaces); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal ipv4Interfaces")
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze IPv4 interfaces")
}
var coll resultCollector
for _, outcome := range hostAnalyzer.Outcomes {
result := &AnalyzeResult{Title: a.Title()}
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
continue
}
isMatch, err := compareHostIPV4InterfacesConditionalToActual(outcome.Fail.When, ipv4Interfaces)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Fail.When)
}
if isMatch {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
continue
}
isMatch, err := compareHostIPV4InterfacesConditionalToActual(outcome.Warn.When, ipv4Interfaces)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Warn.When)
}
if isMatch {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
continue
}
isMatch, err := compareHostIPV4InterfacesConditionalToActual(outcome.Pass.When, ipv4Interfaces)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Pass.When)
}
if isMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
}
}
}
return coll.get(a.Title()), nil
return results, nil
}
func compareHostIPV4InterfacesConditionalToActual(conditional string, ipv4Interfaces []net.Interface) (res bool, err error) {
@@ -153,3 +83,13 @@ func compareHostIPV4InterfacesConditionalToActual(conditional string, ipv4Interf
return false, fmt.Errorf("Unknown operator %q. Supported operators are: <, <=, ==, >=, >", operator)
}
func (a *AnalyzeHostIPV4Interfaces) CheckCondition(when string, data []byte) (bool, error) {
var ipv4Interfaces []net.Interface
if err := json.Unmarshal(data, &ipv4Interfaces); err != nil {
return false, fmt.Errorf("failed to unmarshal data into []net.Interface: %v", err)
}
return compareHostIPV4InterfacesConditionalToActual(when, ipv4Interfaces)
}
+28
View File
@@ -0,0 +1,28 @@
package analyzer
import (
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)
type AnalyzeHostJsonCompare struct {
hostAnalyzer *troubleshootv1beta2.JsonCompare
}
func (a *AnalyzeHostJsonCompare) Title() string {
return jsonCompareTitle(a.hostAnalyzer)
}
func (a *AnalyzeHostJsonCompare) IsExcluded() (bool, error) {
return isExcluded(a.hostAnalyzer.Exclude)
}
func (a *AnalyzeHostJsonCompare) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
result, err := analyzeJsonCompare(a.hostAnalyzer, getCollectedFileContents, a.Title())
if err != nil {
return nil, err
}
result.Strict = a.hostAnalyzer.Strict.BoolOrDefaultFalse()
return []*AnalyzeResult{result}, nil
}
+123
View File
@@ -0,0 +1,123 @@
package analyzer
import (
"encoding/json"
"fmt"
"regexp"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"k8s.io/klog/v2"
)
type AnalyzeHostKernelConfigs struct {
hostAnalyzer *troubleshootv1beta2.KernelConfigsAnalyze
}
var kConfigRegex = regexp.MustCompile("^(CONFIG_[A-Z0-9_]+)=([ymn]+)$")
func (a *AnalyzeHostKernelConfigs) Title() string {
return hostAnalyzerTitleOrDefault(a.hostAnalyzer.AnalyzeMeta, "Kernel Configs")
}
func (a *AnalyzeHostKernelConfigs) IsExcluded() (bool, error) {
return isExcluded(a.hostAnalyzer.Exclude)
}
func (a *AnalyzeHostKernelConfigs) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
collect.HostKernelConfigsPath,
collect.NodeInfoBaseDir,
collect.HostKernelConfigsFileName,
)
if err != nil {
return []*AnalyzeResult{{Title: a.Title()}}, err
}
var results []*AnalyzeResult
for _, content := range collectedContents {
currentTitle := a.Title()
if content.NodeName != "" {
currentTitle = fmt.Sprintf("%s - Node %s", a.Title(), content.NodeName)
}
result, err := a.analyzeSingleNode(content, currentTitle)
if err != nil {
return nil, errors.Wrapf(err, "failed to analyze kernel configs for %s", currentTitle)
}
if result != nil {
results = append(results, result...)
}
}
return results, nil
}
func addMissingKernelConfigs(message string, missingConfigs []string) string {
if message == "" && len(missingConfigs) == 0 {
return message
}
return strings.ReplaceAll(message, "{{ .ConfigsNotFound }}", strings.Join(missingConfigs, ", "))
}
func (a *AnalyzeHostKernelConfigs) analyzeSingleNode(content collectedContent, currentTitle string) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
kConfigs := collect.KConfigs{}
if err := json.Unmarshal(content.Data, &kConfigs); err != nil {
return nil, errors.Wrap(err, "failed to read kernel configs")
}
var configsNotFound []string
for _, config := range hostAnalyzer.SelectedConfigs {
matches := kConfigRegex.FindStringSubmatch(config)
// zero tolerance for invalid kernel config
if matches == nil || len(matches) < 3 {
return nil, errors.Errorf("invalid kernel config: %s", config)
}
key := matches[1]
values := matches[2] // values can contain multiple values in any order y, m, n
// check if the kernel config exists
if _, ok := kConfigs[key]; !ok {
configsNotFound = append(configsNotFound, config)
continue
}
// check if the kernel config value matches
if !strings.Contains(values, kConfigs[key]) {
klog.V(2).Infof("collected kernel config %s=%s does not in expected values %s", key, kConfigs[key], values)
configsNotFound = append(configsNotFound, config)
}
}
var results []*AnalyzeResult
for _, outcome := range hostAnalyzer.Outcomes {
result := &AnalyzeResult{
Title: currentTitle,
Strict: a.hostAnalyzer.Strict.BoolOrDefaultFalse(),
}
if outcome.Pass != nil && len(configsNotFound) == 0 {
result.IsPass = true
result.Message = outcome.Pass.Message
results = append(results, result)
break
}
if outcome.Fail != nil && len(configsNotFound) > 0 {
result.IsFail = true
result.Message = addMissingKernelConfigs(outcome.Fail.Message, configsNotFound)
results = append(results, result)
break
}
}
return results, nil
}
+129
View File
@@ -0,0 +1,129 @@
package analyzer
import (
"testing"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
)
func TestAnalyzeKernelConfigs(t *testing.T) {
tests := []struct {
name string
selectedConfigs []string
outcomes []*troubleshootv1beta2.Outcome
results []*AnalyzeResult
expectErr bool
}{
{
name: "all pass",
selectedConfigs: []string{"CONFIG_CGROUP_FREEZER=y", "CONFIG_NETFILTER_XTABLES=m"},
outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "required kernel configs are available",
},
},
},
results: []*AnalyzeResult{
{
Title: "Kernel Configs",
IsPass: true,
Message: "required kernel configs are available",
},
},
expectErr: false,
},
{
name: "has fail",
selectedConfigs: []string{"CONFIG_UTS_NS=y"},
outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "missing kernel config(s): {{ .ConfigsNotFound }}",
},
},
},
results: []*AnalyzeResult{
{
Title: "Kernel Configs",
IsFail: true,
Message: "missing kernel config(s): CONFIG_UTS_NS=y",
},
},
expectErr: false,
},
{
name: "kernel config disabled",
selectedConfigs: []string{"CONFIG_CGROUP_FREEZER=n"},
outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "missing kernel config(s): {{ .ConfigsNotFound }}",
},
},
},
results: []*AnalyzeResult{
{
Title: "Kernel Configs",
IsFail: true,
Message: "missing kernel config(s): CONFIG_CGROUP_FREEZER=n",
},
},
expectErr: false,
},
{
name: "invalid kernel config",
selectedConfigs: []string{"foobar=n"},
expectErr: true,
},
{
name: "select multiple kernel config values",
selectedConfigs: []string{"CONFIG_BRIDGE=my"},
outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "required kernel configs are available",
},
},
},
results: []*AnalyzeResult{
{
Title: "Kernel Configs",
IsPass: true,
Message: "required kernel configs are available",
},
},
expectErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockKernelFile := func(_ string) ([]byte, error) {
return []byte(`{"CONFIG_CGROUP_FREEZER": "y", "CONFIG_NETFILTER_XTABLES": "m", "CONFIG_BRIDGE": "y"}`), nil
}
analyzer := AnalyzeHostKernelConfigs{
hostAnalyzer: &troubleshootv1beta2.KernelConfigsAnalyze{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "Kernel Configs",
},
SelectedConfigs: tt.selectedConfigs,
Outcomes: tt.outcomes,
},
}
results, err := analyzer.Analyze(mockKernelFile, nil)
if tt.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.results, results)
}
})
}
}
+32 -87
View File
@@ -26,95 +26,36 @@ func (a *AnalyzeHostMemory) IsExcluded() (bool, error) {
func (a *AnalyzeHostMemory) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
result := AnalyzeResult{Title: a.Title()}
contents, err := getCollectedFileContents(collect.HostMemoryPath)
// Use the generic function to collect both local and remote data
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
collect.HostMemoryPath, // Local path
collect.NodeInfoBaseDir, // Remote base directory
collect.HostMemoryFileName, // Remote file name
)
if err != nil {
return nil, errors.Wrap(err, "failed to get collected file")
return []*AnalyzeResult{&result}, err
}
memoryInfo := collect.MemoryInfo{}
if err := json.Unmarshal(contents, &memoryInfo); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal memory info")
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze OS version")
}
result := AnalyzeResult{
Title: a.Title(),
}
for _, outcome := range hostAnalyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{&result}, nil
}
isMatch, err := compareHostMemoryConditionalToActual(outcome.Fail.When, memoryInfo.Total)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Fail.When)
}
if isMatch {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{&result}, nil
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return []*AnalyzeResult{&result}, nil
}
isMatch, err := compareHostMemoryConditionalToActual(outcome.Warn.When, memoryInfo.Total)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Warn.When)
}
if isMatch {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return []*AnalyzeResult{&result}, nil
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{&result}, nil
}
isMatch, err := compareHostMemoryConditionalToActual(outcome.Pass.When, memoryInfo.Total)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Pass.When)
}
if isMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{&result}, nil
}
}
}
return []*AnalyzeResult{&result}, nil
return results, nil
}
func compareHostMemoryConditionalToActual(conditional string, total uint64) (res bool, err error) {
parts := strings.Split(conditional, " ")
// checkCondition checks the condition of the when clause
func (a *AnalyzeHostMemory) CheckCondition(when string, data []byte) (bool, error) {
var memInfo collect.MemoryInfo
if err := json.Unmarshal(data, &memInfo); err != nil {
return false, fmt.Errorf("failed to unmarshal data into MemoryInfo: %v", err)
}
parts := strings.Split(when, " ")
if len(parts) != 2 {
return false, fmt.Errorf("Expected 2 parts in conditional, got %d", len(parts))
}
@@ -129,19 +70,23 @@ func compareHostMemoryConditionalToActual(conditional string, total uint64) (res
if !ok {
return false, fmt.Errorf("could not parse quantity %q", desired)
}
if desiredInt < 0 {
return false, fmt.Errorf("desired value must be a positive integer, got %d", desiredInt)
}
switch operator {
case "<":
return total < uint64(desiredInt), nil
return memInfo.Total < uint64(desiredInt), nil
case "<=":
return total <= uint64(desiredInt), nil
return memInfo.Total <= uint64(desiredInt), nil
case ">":
return total > uint64(desiredInt), nil
return memInfo.Total > uint64(desiredInt), nil
case ">=":
return total >= uint64(desiredInt), nil
return memInfo.Total >= uint64(desiredInt), nil
case "=", "==", "===":
return total == uint64(desiredInt), nil
return memInfo.Total == uint64(desiredInt), nil
default:
return false, fmt.Errorf("unsupported operator: %q", operator)
}
return false, errors.New("unknown operator")
}
+167 -108
View File
@@ -2,91 +2,110 @@ package analyzer
import (
"encoding/json"
"fmt"
"testing"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_doCompareHostMemory(t *testing.T) {
func TestAnalyzeHostMemoryCheckCondition(t *testing.T) {
tests := []struct {
name string
conditional string
actual uint64
expected bool
expectErr bool
}{
{
name: "< 16Gi when actual is 8Gi",
conditional: "< 16Gi",
actual: 8 * 1024 * 1024 * 1024,
actual: 8 * 1024 * 1024 * 1024, // 8GiB
expected: true,
expectErr: false,
},
{
name: "< 8Gi when actual is 8Gi",
conditional: "< 8Gi",
actual: 8 * 1024 * 1024 * 1024,
actual: 8 * 1024 * 1024 * 1024, // 8GiB
expected: false,
expectErr: false,
},
{
name: "<= 8Gi when actual is 8Gi",
conditional: "<= 8Gi",
actual: 8 * 1024 * 1024 * 1024,
actual: 8 * 1024 * 1024 * 1024, // 8GiB
expected: true,
expectErr: false,
},
{
name: "<= 8Gi when actual is 16Gi",
conditional: "<= 8Gi",
actual: 16 * 1024 * 1024 * 1024,
actual: 16 * 1024 * 1024 * 1024, // 16GiB
expected: false,
expectErr: false,
},
{
name: "== 8Gi when actual is 16Gi",
conditional: "== 8Gi",
actual: 16 * 1024 * 1024 * 1024,
actual: 16 * 1024 * 1024 * 1024, // 16GiB
expected: false,
expectErr: false,
},
{
name: "== 8Gi when actual is 8Gi",
conditional: "== 8Gi",
actual: 8 * 1024 * 1024 * 1024,
actual: 8 * 1024 * 1024 * 1024, // 8GiB
expected: true,
expectErr: false,
},
{
name: "== 8000000000 when actual is 8000000000",
conditional: "== 8000000000",
actual: 8 * 1000 * 1000 * 1000,
actual: 8 * 1000 * 1000 * 1000, // 8GB in decimal
expected: true,
expectErr: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
// Create the AnalyzeHostMemory object
analyzeHostMemory := AnalyzeHostMemory{}
actual, err := compareHostMemoryConditionalToActual(test.conditional, test.actual)
req.NoError(err)
assert.Equal(t, test.expected, actual)
// Simulate the memory info as JSON-encoded data
memInfo := collect.MemoryInfo{
Total: test.actual,
}
rawData, err := json.Marshal(memInfo)
require.NoError(t, err)
// Call the CheckCondition method
result, err := analyzeHostMemory.CheckCondition(test.conditional, rawData)
if test.expectErr {
require.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, test.expected, result)
}
})
}
}
func TestAnalyzeHostMemory(t *testing.T) {
tests := []struct {
name string
memoryInfo *collect.MemoryInfo
hostAnalyzer *troubleshootv1beta2.MemoryAnalyze
result []*AnalyzeResult
expectErr bool
name string
hostAnalyzer *troubleshootv1beta2.MemoryAnalyze
getCollectedFileContents func(string) ([]byte, error)
expectedResults []*AnalyzeResult
expectedError string
}{
{
name: "Pass on memory available",
memoryInfo: &collect.MemoryInfo{
Total: 8 * 1024 * 1024 * 1024,
},
name: "Pass on memory available (local)",
hostAnalyzer: &troubleshootv1beta2.MemoryAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
@@ -97,19 +116,27 @@ func TestAnalyzeHostMemory(t *testing.T) {
},
},
},
result: []*AnalyzeResult{
getCollectedFileContents: func(path string) ([]byte, error) {
// Simulate local memory content retrieval
if path == collect.HostMemoryPath {
memoryInfo := collect.MemoryInfo{
Total: 8 * 1024 * 1024 * 1024, // 8GiB
}
return json.Marshal(memoryInfo)
}
return nil, errors.New("file not found")
},
expectedResults: []*AnalyzeResult{
{
Title: "Amount of Memory",
IsPass: true,
Message: "System has at least 4Gi of memory",
},
},
expectedError: "",
},
{
name: "Fail on memory available",
memoryInfo: &collect.MemoryInfo{
Total: 8 * 1024 * 1024 * 1024,
},
name: "Fail on memory available (remote node)",
hostAnalyzer: &troubleshootv1beta2.MemoryAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
@@ -120,27 +147,33 @@ func TestAnalyzeHostMemory(t *testing.T) {
},
},
},
result: []*AnalyzeResult{
getCollectedFileContents: func(path string) ([]byte, error) {
// Simulate remote node list and memory content retrieval
if path == constants.NODE_LIST_FILE {
nodeNames := nodeNames{Nodes: []string{"node1"}}
return json.Marshal(nodeNames)
}
if path == fmt.Sprintf("%s/node1/%s", collect.NodeInfoBaseDir, collect.HostMemoryFileName) {
memoryInfo := collect.MemoryInfo{
Total: 8 * 1024 * 1024 * 1024, // 8GiB for remote node
}
return json.Marshal(memoryInfo)
}
return nil, errors.New("file not found")
},
expectedResults: []*AnalyzeResult{
{
Title: "Amount of Memory",
Title: "Amount of Memory - Node node1",
IsFail: true,
Message: "System requires at least 16Gi of memory",
},
},
expectedError: "",
},
{
name: "Warn on memory available",
memoryInfo: &collect.MemoryInfo{
Total: 8 * 1024 * 1024 * 1024,
},
name: "Warn on memory available (remote node)",
hostAnalyzer: &troubleshootv1beta2.MemoryAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "< 4Gi",
Message: "System requires at least 4Gi of memory",
},
},
{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "<= 8Gi",
@@ -149,97 +182,123 @@ func TestAnalyzeHostMemory(t *testing.T) {
},
},
},
result: []*AnalyzeResult{
getCollectedFileContents: func(path string) ([]byte, error) {
// Simulate remote node list and memory content retrieval
if path == constants.NODE_LIST_FILE {
nodeNames := nodeNames{Nodes: []string{"node1"}}
return json.Marshal(nodeNames)
}
if path == fmt.Sprintf("%s/node1/%s", collect.NodeInfoBaseDir, collect.HostMemoryFileName) {
memoryInfo := collect.MemoryInfo{
Total: 8 * 1024 * 1024 * 1024, // 8GiB for remote node
}
return json.Marshal(memoryInfo)
}
return nil, errors.New("file not found")
},
expectedResults: []*AnalyzeResult{
{
Title: "Amount of Memory",
Title: "Amount of Memory - Node node1",
IsWarn: true,
Message: "System performs best with more than 8Gi of memory",
},
},
expectedError: "",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
b, err := json.Marshal(test.memoryInfo)
if err != nil {
t.Fatal(err)
}
getCollectedFileContents := func(filename string) ([]byte, error) {
return b, nil
}
result, err := (&AnalyzeHostMemory{test.hostAnalyzer}).Analyze(getCollectedFileContents, nil)
if test.expectErr {
req.Error(err)
} else {
req.NoError(err)
}
assert.Equal(t, test.result, result)
})
}
}
func TestHostMemoryAnalyze(t *testing.T) {
tt := []struct {
name string
memoryInfo collect.MemoryInfo
outcomes []*troubleshootv1beta2.Outcome
results []*AnalyzeResult
wantErr bool
}{
{
name: "fix for empty pass predicate",
memoryInfo: collect.MemoryInfo{
Total: 16 * 1024 * 1024 * 1024,
},
outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "< 8Gi",
Message: "oops",
name: "Pass on empty pass predicate (local)",
hostAnalyzer: &troubleshootv1beta2.MemoryAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "< 8Gi",
Message: "System requires at least 8Gi of memory",
},
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "",
Message: "it passed",
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "",
Message: "Memory is sufficient",
},
},
},
},
results: []*AnalyzeResult{
getCollectedFileContents: func(path string) ([]byte, error) {
// Simulate local memory content retrieval
if path == collect.HostMemoryPath {
memoryInfo := collect.MemoryInfo{
Total: 16 * 1024 * 1024 * 1024, // 16GiB
}
return json.Marshal(memoryInfo)
}
return nil, errors.New("file not found")
},
expectedResults: []*AnalyzeResult{
{
Title: "Amount of Memory",
IsPass: true,
Message: "Memory is sufficient",
},
},
expectedError: "",
},
{
name: "Fix for empty pass predicate",
hostAnalyzer: &troubleshootv1beta2.MemoryAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "< 8Gi",
Message: "oops",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "",
Message: "it passed",
},
},
},
},
getCollectedFileContents: func(path string) ([]byte, error) {
// Simulate local memory content retrieval
if path == collect.HostMemoryPath {
memoryInfo := collect.MemoryInfo{
Total: 16 * 1024 * 1024 * 1024, // 16GiB
}
return json.Marshal(memoryInfo)
}
return nil, errors.New("file not found")
},
expectedResults: []*AnalyzeResult{
{
Title: "Amount of Memory",
IsPass: true,
Message: "it passed",
Title: "Memory Test",
},
},
expectedError: "",
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
fn := func(_ string) ([]byte, error) {
return json.Marshal(&tc.memoryInfo)
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
// Set up the AnalyzeHostMemory object
analyzeHostMemory := AnalyzeHostMemory{
hostAnalyzer: test.hostAnalyzer,
}
analyzer := AnalyzeHostMemory{
hostAnalyzer: &troubleshootv1beta2.MemoryAnalyze{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "Memory Test",
},
Outcomes: tc.outcomes,
},
// Call the Analyze function
results, err := analyzeHostMemory.Analyze(test.getCollectedFileContents, nil)
// Check for errors and compare results
if test.expectedError != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), test.expectedError)
} else {
require.NoError(t, err)
assert.Equal(t, test.expectedResults, results)
}
results, err := analyzer.Analyze(fn, nil)
if tc.wantErr {
require.NotNil(t, err)
return
}
require.Nil(t, err)
require.Equal(t, tc.results, results)
})
}
}
@@ -0,0 +1,80 @@
package analyzer
import (
"encoding/json"
"fmt"
"path/filepath"
util "github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
type AnalyzeHostNetworkNamespaceConnectivity struct {
hostAnalyzer *troubleshootv1beta2.NetworkNamespaceConnectivityAnalyze
}
func (a *AnalyzeHostNetworkNamespaceConnectivity) Title() string {
return hostAnalyzerTitleOrDefault(a.hostAnalyzer.AnalyzeMeta, "Network Namespace Connectivity")
}
func (a *AnalyzeHostNetworkNamespaceConnectivity) IsExcluded() (bool, error) {
return isExcluded(a.hostAnalyzer.Exclude)
}
func (a *AnalyzeHostNetworkNamespaceConnectivity) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
collectedPath := filepath.Join("host-collectors/system", "networkNamespaceConnectivity.json")
fileName := "networkNamespaceConnectivity.json"
if hostAnalyzer.CollectorName != "" {
collectedPath = filepath.Join("host-collectors/system", hostAnalyzer.CollectorName+".json")
fileName = hostAnalyzer.CollectorName + ".json"
}
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
collectedPath,
collect.NodeInfoBaseDir,
fileName,
)
if err != nil {
return nil, fmt.Errorf("failed to retrieve collected contents: %w", err)
}
var results []*AnalyzeResult
for _, collected := range collectedContents {
var info collect.NetworkNamespaceConnectivityInfo
if err := json.Unmarshal(collected.Data, &info); err != nil {
return nil, fmt.Errorf("failed to unmarshal disk usage info: %w", err)
}
for _, outcome := range hostAnalyzer.Outcomes {
result := &AnalyzeResult{Title: a.Title()}
if outcome.Pass != nil && info.Success {
result.IsPass = true
result.Message, err = util.RenderTemplate(outcome.Pass.Message, &info)
if err != nil {
return nil, fmt.Errorf("failed to render template on outcome message: %w", err)
}
results = append(results, result)
break
}
if outcome.Fail != nil && !info.Success {
result.IsFail = true
result.Message, err = util.RenderTemplate(outcome.Fail.Message, &info)
if err != nil {
return nil, fmt.Errorf("failed to render template on outcome message: %w", err)
}
results = append(results, result)
break
}
}
}
return results, nil
}
+60 -76
View File
@@ -27,103 +27,87 @@ func (a *AnalyzeHostOS) IsExcluded() (bool, error) {
func (a *AnalyzeHostOS) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
result := AnalyzeResult{Title: a.Title()}
result := AnalyzeResult{}
result.Title = a.Title()
contents, err := getCollectedFileContents(collect.HostOSInfoPath)
// Use the generic function to collect both local and remote data
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
collect.HostOSInfoPath, // Local path
collect.NodeInfoBaseDir, // Remote base directory
collect.HostInfoFileName, // Remote file name
)
if err != nil {
return []*AnalyzeResult{&result}, errors.Wrap(err, "failed to get collected file")
return []*AnalyzeResult{&result}, err
}
var osInfo collect.HostOSInfo
if err := json.Unmarshal(contents, &osInfo); err != nil {
return []*AnalyzeResult{&result}, errors.Wrap(err, "failed to unmarshal host os info")
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze OS version")
}
return analyzeOSVersionResult(osInfo, a.hostAnalyzer.Outcomes, a.Title())
return results, nil
}
func analyzeOSVersionResult(osInfo collect.HostOSInfo, outcomes []*troubleshootv1beta2.Outcome, title string) ([]*AnalyzeResult, error) {
// checkCondition checks the condition of the when clause
func (a *AnalyzeHostOS) CheckCondition(when string, data []byte) (bool, error) {
if title == "" {
title = "Host OS Info"
var osInfo collect.HostOSInfo
if err := json.Unmarshal(data, &osInfo); err != nil {
return false, fmt.Errorf("failed to unmarshal data into HostOSInfo: %v", err)
}
for _, outcome := range outcomes {
parts := strings.Split(when, " ")
if len(parts) < 3 {
return false, errors.New("when condition must have at least 3 parts")
}
expectedVer := fixVersion(parts[2])
toleratedVer, err := semver.ParseTolerant(expectedVer)
if err != nil {
return false, errors.Wrapf(err, "failed to parse version: %s", expectedVer)
}
when = fmt.Sprintf("%s %v", parts[1], toleratedVer)
whenRange, err := semver.ParseRange(when)
if err != nil {
return false, errors.Wrapf(err, "failed to parse version range: %s", when)
}
result := AnalyzeResult{
Title: title,
}
when := ""
message := ""
uri := ""
if outcome.Fail != nil {
result.IsFail = true
when = outcome.Fail.When
message = outcome.Fail.Message
uri = outcome.Fail.URI
} else if outcome.Warn != nil {
result.IsWarn = true
when = outcome.Warn.When
message = outcome.Warn.Message
uri = outcome.Warn.URI
} else if outcome.Pass != nil {
result.IsPass = true
when = outcome.Pass.When
message = outcome.Pass.Message
uri = outcome.Pass.URI
} else {
return nil, errors.New("empty outcome")
}
result.Message = message
result.URI = uri
// When is usually empty as the final case and should be treated as true
if when == "" {
return []*AnalyzeResult{&result}, nil
}
parts := strings.Split(when, " ")
platform := parts[0]
expectedVer := fixVersion(parts[2])
toleratedVer, err := semver.ParseTolerant(expectedVer)
// Match the kernel version regardless of the platform
if parts[0] == "kernelVersion" {
fixedKernelVer := fixVersion(osInfo.KernelVersion)
toleratedKernelVer, err := semver.ParseTolerant(fixedKernelVer)
if err != nil {
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to parse tolerant: %s", expectedVer)
return false, errors.Wrapf(err, "failed to parse tolerant: %v", fixedKernelVer)
}
when = fmt.Sprintf("%s %v", parts[1], toleratedVer)
whenRange, err := semver.ParseRange(when)
if err != nil {
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to parse range: %s", when)
if whenRange(toleratedKernelVer) {
return true, nil
}
}
kernelInfo := fmt.Sprintf("%s-%s-kernel", osInfo.Platform, osInfo.PlatformVersion)
if len(strings.Split(platform, "-")) == 3 && strings.Split(platform, "-")[2] == "kernel" {
if platform == kernelInfo {
fixedKernelVer := fixVersion(osInfo.KernelVersion)
toleratedKernelVer, err := semver.ParseTolerant(fixedKernelVer)
if err != nil {
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to parse tolerant: %v", fixedKernelVer)
}
if whenRange(toleratedKernelVer) {
return []*AnalyzeResult{&result}, nil
}
}
} else if platform == osInfo.Platform {
fixedDistVer := fixVersion(osInfo.PlatformVersion)
toleratedDistVer, err := semver.ParseTolerant(fixedDistVer)
// Match platform version and kernel version, such as "centos-8.2-kernel == 8.2"
platform := parts[0]
kernelInfo := fmt.Sprintf("%s-%s-kernel", osInfo.Platform, osInfo.PlatformVersion)
if len(strings.Split(platform, "-")) == 3 && strings.Split(platform, "-")[2] == "kernel" {
if platform == kernelInfo {
fixedKernelVer := fixVersion(osInfo.KernelVersion)
toleratedKernelVer, err := semver.ParseTolerant(fixedKernelVer)
if err != nil {
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to parse tolerant: %v", fixedDistVer)
return false, errors.Wrapf(err, "failed to parse tolerant: %v", fixedKernelVer)
}
if whenRange(toleratedDistVer) {
return []*AnalyzeResult{&result}, nil
if whenRange(toleratedKernelVer) {
return true, nil
}
}
} else if platform == osInfo.Platform {
fixedDistVer := fixVersion(osInfo.PlatformVersion)
toleratedDistVer, err := semver.ParseTolerant(fixedDistVer)
if err != nil {
return false, errors.Wrapf(err, "failed to parse tolerant: %v", fixedDistVer)
}
if whenRange(toleratedDistVer) {
return true, nil
}
}
return []*AnalyzeResult{}, nil
return false, nil
}
var rx = regexp.MustCompile(`^[0-9]+\.?[0-9]*\.?[0-9]*`)
+248 -217
View File
@@ -2,30 +2,106 @@ package analyzer
import (
"encoding/json"
"fmt"
"testing"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAnalyzeHostOS(t *testing.T) {
func TestAnalyzeHostOSCheckCondition(t *testing.T) {
tests := []struct {
name string
hostInfo collect.HostOSInfo
hostAnalyzer *troubleshootv1beta2.HostOSAnalyze
result []*AnalyzeResult
expectErr bool
name string
conditional string
osInfo collect.HostOSInfo
expected bool
expectErr bool
}{
{
name: "pass if ubuntu >= 0.1.2",
hostInfo: collect.HostOSInfo{
Name: "myhost",
KernelVersion: "5.4.0-1034-gcp",
PlatformVersion: "00.1.2",
Platform: "ubuntu",
name: "kernelVersion == 4.15 when actual is 4.15",
conditional: "kernelVersion == 4.15",
osInfo: collect.HostOSInfo{
KernelVersion: "4.15.0",
},
expected: true,
expectErr: false,
},
{
name: "kernelVersion < 4.15 when actual is 4.16",
conditional: "kernelVersion < 4.15",
osInfo: collect.HostOSInfo{
KernelVersion: "4.16.0",
},
expected: false,
expectErr: false,
},
{
name: "centos == 8.2 when actual is 8.2",
conditional: "centos == 8.2",
osInfo: collect.HostOSInfo{
Platform: "centos",
PlatformVersion: "8.2",
},
expected: true,
expectErr: false,
},
{
name: "ubuntu == 20.04 when actual is 18.04",
conditional: "ubuntu == 20.04",
osInfo: collect.HostOSInfo{
Platform: "ubuntu",
PlatformVersion: "18.04",
},
expected: false,
expectErr: false,
},
{
name: "invalid conditional format",
conditional: "invalid conditional",
osInfo: collect.HostOSInfo{
Platform: "ubuntu",
PlatformVersion: "18.04",
},
expected: false,
expectErr: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
// Create the AnalyzeHostOS object
analyzeHostOS := AnalyzeHostOS{}
// Simulate the OS info as JSON-encoded data
rawData, err := json.Marshal(test.osInfo)
require.NoError(t, err)
// Call the CheckCondition method
result, err := analyzeHostOS.CheckCondition(test.conditional, rawData)
if test.expectErr {
require.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, test.expected, result)
}
})
}
}
func TestAnalyzeHostOS(t *testing.T) {
tests := []struct {
name string
hostAnalyzer *troubleshootv1beta2.HostOSAnalyze
getCollectedFileContents func(string) ([]byte, error)
result []*AnalyzeResult
expectErr bool
}{
{
name: "successfully retrieve local content and analyze",
hostAnalyzer: &troubleshootv1beta2.HostOSAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
@@ -41,6 +117,17 @@ func TestAnalyzeHostOS(t *testing.T) {
},
},
},
getCollectedFileContents: func(path string) ([]byte, error) {
if path == collect.HostOSInfoPath {
return json.Marshal(collect.HostOSInfo{
Name: "myhost",
KernelVersion: "5.4.0-1034-gcp",
PlatformVersion: "00.1.2",
Platform: "ubuntu",
})
}
return nil, errors.New("file not found")
},
result: []*AnalyzeResult{
{
Title: "Host OS Info",
@@ -48,22 +135,16 @@ func TestAnalyzeHostOS(t *testing.T) {
Message: "supported distribution matches ubuntu >= 00.1.2",
},
},
expectErr: false,
},
{
name: "pass if ubuntu >= 1.0.2",
hostInfo: collect.HostOSInfo{
Name: "myhost",
KernelVersion: "5.4.0-1034-gcp",
PlatformVersion: "1.0.2",
Platform: "ubuntu",
},
name: "local content not found, retrieve and analyze remote content",
hostAnalyzer: &troubleshootv1beta2.HostOSAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu >= 1.0.2",
Message: "supported distribution matches ubuntu >= 1.0.2",
When: "ubuntu == 18.04",
Message: "supported remote ubuntu 18.04",
},
},
{
@@ -73,53 +154,75 @@ func TestAnalyzeHostOS(t *testing.T) {
},
},
},
getCollectedFileContents: func(path string) ([]byte, error) {
if path == constants.NODE_LIST_FILE {
return json.Marshal(nodeNames{Nodes: []string{"node1"}})
}
if path == fmt.Sprintf("%s/node1/%s", collect.NodeInfoBaseDir, collect.HostInfoFileName) {
return json.Marshal(collect.HostOSInfo{
Name: "nodehost",
KernelVersion: "4.15.0-1034-aws",
PlatformVersion: "18.04",
Platform: "ubuntu",
})
}
return nil, errors.New("file not found")
},
result: []*AnalyzeResult{
{
Title: "Host OS Info",
Title: "Host OS Info - Node node1",
IsPass: true,
Message: "supported distribution matches ubuntu >= 1.0.2",
Message: "supported remote ubuntu 18.04",
},
},
expectErr: false,
},
{
name: "pass if ubuntu >= 1.2.0",
hostInfo: collect.HostOSInfo{
Name: "myhost",
KernelVersion: "5.4.0-1034-gcp",
PlatformVersion: "1.2.0",
Platform: "ubuntu",
},
name: "fail to retrieve both local and remote content",
hostAnalyzer: &troubleshootv1beta2.HostOSAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu >= 1.0.2",
Message: "supported distribution matches ubuntu >= 1.2.0",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "unsupported distribution",
Message: "failed analysis",
},
},
},
},
getCollectedFileContents: func(path string) ([]byte, error) {
return nil, errors.New("file not found")
},
result: []*AnalyzeResult{
{
Title: "Host OS Info",
IsPass: true,
Message: "supported distribution matches ubuntu >= 1.2.0",
Title: "Host OS Info",
},
},
expectErr: true,
},
{
name: "pass if ubuntu-1.2.0-kernel >= 1.2.0",
hostInfo: collect.HostOSInfo{
Name: "myhost",
KernelVersion: "1.2.0-1034-gcp",
PlatformVersion: "1.2.0",
Platform: "centos",
name: "error during remote content analysis",
hostAnalyzer: &troubleshootv1beta2.HostOSAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "analysis failed",
},
},
},
},
getCollectedFileContents: func(path string) ([]byte, error) {
if path == constants.NODE_LIST_FILE {
return json.Marshal(nodeNames{Nodes: []string{"node1"}})
}
if path == fmt.Sprintf("%s/node1/%s", collect.NodeInfoBaseDir, collect.HostInfoFileName) {
return nil, errors.New("file not found")
}
return nil, errors.New("file not found")
},
result: nil,
expectErr: true,
},
{
name: "pass if centos-1.2.0-kernel >= 1.2.0",
hostAnalyzer: &troubleshootv1beta2.HostOSAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
@@ -135,211 +238,139 @@ func TestAnalyzeHostOS(t *testing.T) {
},
},
},
getCollectedFileContents: func(path string) ([]byte, error) {
if path == constants.NODE_LIST_FILE {
return json.Marshal(nodeNames{Nodes: []string{"node1"}})
}
if path == fmt.Sprintf("%s/node1/%s", collect.NodeInfoBaseDir, collect.HostInfoFileName) {
return json.Marshal(collect.HostOSInfo{
Name: "nodehost",
KernelVersion: "1.2.0-1034-aws",
PlatformVersion: "1.2.0",
Platform: "centos",
})
}
return nil, errors.New("file not found")
},
result: []*AnalyzeResult{
{
Title: "Host OS Info",
Title: "Host OS Info - Node node1",
IsPass: true,
Message: "supported kernel matches centos-1.2.0-kernel >= 1.2.0",
},
},
expectErr: false,
},
{
name: "pass if ubuntu-0.1.2-kernel >= 0.1.2",
hostInfo: collect.HostOSInfo{
Name: "myhost",
KernelVersion: "0.01.2-1034-gcp",
PlatformVersion: "8.2",
Platform: "centos",
},
name: "warn if ubuntu <= 16.04",
hostAnalyzer: &troubleshootv1beta2.HostOSAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "centos-8.2-kernel >= 0.01.2",
Message: "supported kernel matches centos-8.2-kernel >= 0.01.2",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "unsupported distribution",
},
},
},
},
result: []*AnalyzeResult{
{
Title: "Host OS Info",
IsPass: true,
Message: "supported kernel matches centos-8.2-kernel >= 0.01.2",
},
},
},
{
name: "fail if ubuntu <= 11.04",
hostInfo: collect.HostOSInfo{
Name: "myhost",
KernelVersion: "5.4.0-1034-gcp",
PlatformVersion: "11.04",
Platform: "ubuntu",
},
hostAnalyzer: &troubleshootv1beta2.HostOSAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu <= 11.04",
Message: "unsupported ubuntu version 11.04",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "supported distribution",
},
},
},
},
result: []*AnalyzeResult{
{
Title: "Host OS Info",
IsFail: true,
Message: "unsupported ubuntu version 11.04",
},
},
},
{
name: "fail if none of the kernel distribution versions match",
hostInfo: collect.HostOSInfo{
Name: "my-host",
KernelVersion: "4.4",
PlatformVersion: "18.04",
Platform: "ubuntu",
},
hostAnalyzer: &troubleshootv1beta2.HostOSAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "centos-18.04-kernel > 4.15",
Message: "supported distribution matches centos-18.04-kernel >= 4.15",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu-18.04-kernel > 4.15",
Message: "supported distribution matches ubuntu-18.04-kernel >= 4.15",
},
},
{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu-16.04-kernel == 4.15",
Message: "supported distribution matches ubuntu-16.04-kernel == 4.15 ",
When: "ubuntu <= 16.04",
Message: "System performs best with Ubuntu version higher than 16.04",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "None matched, centos-18.04-kernel >= 4.15,ubuntu-18.04-kernel >= 4.15, supported distribution",
Pass: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu > 16.04",
Message: "Ubuntu version is sufficient",
},
},
},
},
getCollectedFileContents: func(path string) ([]byte, error) {
if path == collect.HostOSInfoPath {
return json.Marshal(collect.HostOSInfo{
Name: "myhost",
KernelVersion: "4.15.0-1234-gcp",
PlatformVersion: "16.04",
Platform: "ubuntu",
})
}
return nil, errors.New("file not found")
},
result: []*AnalyzeResult{
{
Title: "Host OS Info",
IsWarn: true,
Message: "System performs best with Ubuntu version higher than 16.04",
},
},
expectErr: false,
},
{
name: "analyze multiple nodes with different OS info",
hostAnalyzer: &troubleshootv1beta2.HostOSAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu == 18.04",
Message: "supported ubuntu version",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "unsupported ubuntu version",
},
},
},
},
getCollectedFileContents: func(path string) ([]byte, error) {
if path == constants.NODE_LIST_FILE {
return json.Marshal(nodeNames{Nodes: []string{"node1", "node2"}})
}
if path == fmt.Sprintf("%s/node1/%s", collect.NodeInfoBaseDir, collect.HostInfoFileName) {
return json.Marshal(collect.HostOSInfo{
Name: "nodehost",
KernelVersion: "4.15.0-1034-aws",
PlatformVersion: "18.04",
Platform: "ubuntu",
})
}
if path == fmt.Sprintf("%s/node2/%s", collect.NodeInfoBaseDir, collect.HostInfoFileName) {
return json.Marshal(collect.HostOSInfo{
Name: "nodehost",
KernelVersion: "4.15.0-1034-aws",
PlatformVersion: "16.04",
Platform: "ubuntu",
})
}
return nil, errors.New("file not found")
},
result: []*AnalyzeResult{
{
Title: "Host OS Info - Node node1",
IsPass: true,
Message: "supported ubuntu version",
},
{
Title: "Host OS Info - Node node2",
IsFail: true,
Message: "None matched, centos-18.04-kernel >= 4.15,ubuntu-18.04-kernel >= 4.15, supported distribution",
},
},
},
{
name: "test if centos kernel > 4.15",
hostInfo: collect.HostOSInfo{
Name: "my-host",
KernelVersion: "4.15",
PlatformVersion: "18.04",
Platform: "centos",
},
hostAnalyzer: &troubleshootv1beta2.HostOSAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "centos-18.04-kernel >= 4.15",
Message: "supported distribution matches centos-18.04-kernel >= 4.15",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu-18.04-kernel > 4.15",
Message: "supported distribution matches ubuntu-18.04-kernel >= 4.15",
},
},
{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu-16.04-kernel == 4.15",
Message: "supported distribution matches ubuntu-16.04-kernel == 4.15 ",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "None matched, centos-18.04-kernel >= 4.15,ubuntu-18.04-kernel >= 4.15, supported distribution",
},
},
},
},
result: []*AnalyzeResult{
{
Title: "Host OS Info",
IsPass: true,
Message: "supported distribution matches centos-18.04-kernel >= 4.15",
},
},
},
{
name: "test ubuntu 16 kernel >= 4.15-abc",
hostInfo: collect.HostOSInfo{
Name: "my-host",
KernelVersion: "4.14-abc",
PlatformVersion: "16.04",
Platform: "ubuntu",
},
hostAnalyzer: &troubleshootv1beta2.HostOSAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "ubuntu-16.04-kernel >= 4.14",
Message: "supported distribution match 4.14",
},
},
},
},
result: []*AnalyzeResult{
{
Title: "Host OS Info",
IsPass: true,
Message: "supported distribution match 4.14",
Message: "unsupported ubuntu version",
},
},
expectErr: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
b, err := json.Marshal(test.hostInfo)
if err != nil {
t.Fatal(err)
// Set up the AnalyzeHostOS object with the custom hostAnalyzer per test
analyzeHostOS := AnalyzeHostOS{
hostAnalyzer: test.hostAnalyzer,
}
getCollectedFileContents := func(filename string) ([]byte, error) {
return b, nil
}
// Call the Analyze function
results, err := analyzeHostOS.Analyze(test.getCollectedFileContents, nil)
result, err := (&AnalyzeHostOS{test.hostAnalyzer}).Analyze(getCollectedFileContents, nil)
// Check for errors and compare results
if test.expectErr {
req.Error(err)
require.Error(t, err)
} else {
req.NoError(err)
require.NoError(t, err)
assert.Equal(t, test.result, results)
}
assert.Equal(t, test.result, result)
})
}
}
+22 -82
View File
@@ -25,94 +25,24 @@ func (a *AnalyzeHostServices) IsExcluded() (bool, error) {
func (a *AnalyzeHostServices) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
result := AnalyzeResult{Title: a.Title()}
contents, err := getCollectedFileContents(collect.HostServicesPath)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
collect.HostServicesPath,
collect.NodeInfoBaseDir,
collect.HostServicesFileName,
)
if err != nil {
return nil, errors.Wrap(err, "failed to get collected file")
return []*AnalyzeResult{&result}, err
}
var services []collect.ServiceInfo
if err := json.Unmarshal(contents, &services); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal systemctl service info")
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze host services")
}
var coll resultCollector
for _, outcome := range hostAnalyzer.Outcomes {
result := &AnalyzeResult{Title: a.Title()}
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
continue
}
isMatch, err := compareHostServicesConditionalToActual(outcome.Fail.When, services)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Fail.When)
}
if isMatch {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
continue
}
isMatch, err := compareHostServicesConditionalToActual(outcome.Warn.When, services)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Warn.When)
}
if isMatch {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
continue
}
isMatch, err := compareHostServicesConditionalToActual(outcome.Pass.When, services)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Pass.When)
}
if isMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
}
}
}
return coll.get(a.Title()), nil
return results, nil
}
// <service> <op> <state>
@@ -187,3 +117,13 @@ func isServiceMatch(serviceName string, matchName string) bool {
return false
}
func (a *AnalyzeHostServices) CheckCondition(when string, data []byte) (bool, error) {
var services []collect.ServiceInfo
if err := json.Unmarshal(data, &services); err != nil {
return false, fmt.Errorf("failed to unmarshal data into ServiceInfo: %v", err)
}
return compareHostServicesConditionalToActual(when, services)
}
+26 -49
View File
@@ -2,7 +2,7 @@ package analyzer
import (
"encoding/json"
"path/filepath"
"fmt"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
@@ -24,61 +24,38 @@ func (a *AnalyzeHostSubnetAvailable) IsExcluded() (bool, error) {
func (a *AnalyzeHostSubnetAvailable) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
name := filepath.Join("host-collectors/subnetAvailable", "result.json")
if hostAnalyzer.CollectorName != "" {
name = filepath.Join("host-collectors/subnetAvailable", hostAnalyzer.CollectorName+".json")
collectorName := a.hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "result"
}
contents, err := getCollectedFileContents(name)
const nodeBaseDir = "host-collectors/subnetAvailable"
localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName)
fileName := fmt.Sprintf("%s.json", collectorName)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
nodeBaseDir,
fileName,
)
if err != nil {
return nil, errors.Wrap(err, "failed to get collected file")
return []*AnalyzeResult{{Title: a.Title()}}, err
}
isSubnetAvailable := &collect.SubnetAvailableResult{}
if err := json.Unmarshal(contents, isSubnetAvailable); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal subnetAvailable result")
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze HTTP Load Balancer")
}
result := &AnalyzeResult{
Title: a.Title(),
}
for _, outcome := range hostAnalyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{result}, nil
}
if string(isSubnetAvailable.Status) == outcome.Fail.When {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{result}, nil
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{result}, nil
}
if string(isSubnetAvailable.Status) == outcome.Pass.When {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return results, nil
}
return []*AnalyzeResult{result}, nil
}
}
func (a *AnalyzeHostSubnetAvailable) CheckCondition(when string, data []byte) (bool, error) {
isSubnetAvailable := &collect.SubnetAvailableResult{}
if err := json.Unmarshal(data, isSubnetAvailable); err != nil {
return false, errors.Wrap(err, "failed to unmarshal subnetAvailable result")
}
return []*AnalyzeResult{result}, nil
return string(isSubnetAvailable.Status) == when, nil
}
+107
View File
@@ -0,0 +1,107 @@
package analyzer
import (
"encoding/json"
"testing"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAnalyzeSubnetAvailable(t *testing.T) {
tests := []struct {
name string
info *collect.SubnetAvailableResult
hostAnalyzer *troubleshootv1beta2.SubnetAvailableAnalyze
result []*AnalyzeResult
expectedErr bool
}{
{
name: "subnet available",
info: &collect.SubnetAvailableResult{
CIDRRangeAlloc: "10.0.0.0/8",
DesiredCIDR: 22,
Status: collect.SubnetStatusAvailable,
},
hostAnalyzer: &troubleshootv1beta2.SubnetAvailableAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "a-subnet-is-available",
Message: "available /22 subnet found",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "no-subnet-available",
Message: "failed to find available subnet",
},
},
},
},
result: []*AnalyzeResult{
{
Title: "Subnet Available",
IsPass: true,
Message: "available /22 subnet found",
},
},
},
{
name: "subnet not available",
info: &collect.SubnetAvailableResult{
CIDRRangeAlloc: "10.0.0.0/8",
DesiredCIDR: 22,
Status: collect.SubnetStatusNoneAvailable,
},
hostAnalyzer: &troubleshootv1beta2.SubnetAvailableAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "a-subnet-is-available",
Message: "available /22 subnet found",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "no-subnet-available",
Message: "failed to find available subnet",
},
},
},
},
result: []*AnalyzeResult{
{
Title: "Subnet Available",
IsFail: true,
Message: "failed to find available subnet",
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
b, err := json.Marshal(test.info)
if err != nil {
t.Fatal(err)
}
getCollectedFileContents := func(filename string) ([]byte, error) {
return b, nil
}
result, err := (&AnalyzeHostSubnetAvailable{test.hostAnalyzer}).Analyze(getCollectedFileContents, nil)
if test.expectedErr {
req.Error(err)
} else {
req.NoError(err)
}
assert.Equal(t, test.result, result)
})
}
}
+106
View File
@@ -0,0 +1,106 @@
package analyzer
import (
"encoding/json"
"fmt"
"strconv"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
// Ensure `AnalyzeHostSysctl` implements `HostAnalyzer` interface at compile time.
var _ HostAnalyzer = (*AnalyzeHostSysctl)(nil)
type AnalyzeHostSysctl struct {
hostAnalyzer *troubleshootv1beta2.HostSysctlAnalyze
}
func (a *AnalyzeHostSysctl) Title() string {
return hostAnalyzerTitleOrDefault(a.hostAnalyzer.AnalyzeMeta, "Sysctl")
}
func (a *AnalyzeHostSysctl) IsExcluded() (bool, error) {
return isExcluded(a.hostAnalyzer.Exclude)
}
func (a *AnalyzeHostSysctl) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
result := AnalyzeResult{Title: a.Title()}
// Use the generic function to collect both local and remote data
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
collect.HostSysctlPath, // Local path
collect.NodeInfoBaseDir, // Remote base directory
collect.HostSysctlFileName, // Remote file name
)
if err != nil {
return []*AnalyzeResult{&result}, err
}
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze sysctl output")
}
return results, nil
}
// checkCondition checks the condition of the when clause
func (a *AnalyzeHostSysctl) CheckCondition(when string, data []byte) (bool, error) {
sysctl := map[string]string{}
if err := json.Unmarshal(data, &sysctl); err != nil {
return false, errors.Wrap(err, "failed to unmarshal data")
}
// <1:key> <2:operator> <3:value>
matches := sysctlWhenRX.FindStringSubmatch(when)
if len(matches) < 4 {
return false, fmt.Errorf("expected 3 parts in when %q", when)
}
param := matches[1]
expected := matches[3]
opString := matches[2]
operator, err := ParseComparisonOperator(opString)
if err != nil {
return false, errors.Wrap(err, fmt.Sprintf("failed to parse comparison operator %q", opString))
}
if _, ok := sysctl[param]; !ok {
return false, fmt.Errorf("kernel parameter %q does not exist on collected sysctl output", param)
}
switch operator {
case Equal:
return expected == sysctl[param], nil
}
// operator used is an inequality operator, the only valid inputs should be ints, if not we'll error out
value, err := strconv.Atoi(sysctl[param])
if err != nil {
return false, fmt.Errorf("collected sysctl param %q has value %q, cannot be used with provided operator %q", param, sysctl[param], opString)
}
expectedInt, err := strconv.Atoi(expected)
if err != nil {
return false, fmt.Errorf("expected value for sysctl param %q has value %q, cannot be used with provided operator %q", param, expected, opString)
}
switch operator {
case LessThan:
return value < expectedInt, nil
case LessThanOrEqual:
return value <= expectedInt, nil
case GreaterThan:
return value > expectedInt, nil
case GreaterThanOrEqual:
return value >= expectedInt, nil
default:
return false, fmt.Errorf("unsupported operator %q", opString)
}
}
+321
View File
@@ -0,0 +1,321 @@
package analyzer
import (
"encoding/json"
"fmt"
"testing"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAnalyzeHostSysctlCheckCondition(t *testing.T) {
tests := []struct {
name string
conditional string
collected string
expected bool
expectErr string
}{
{
name: "errors out if we can't unmarshal data",
conditional: "net.ipv4.conf.all.arp_filter = 0",
collected: `{not JSON}`,
expected: false,
expectErr: "failed to unmarshal data",
},
{
name: "errors out if the matched conditional is missing elements",
conditional: "net.ipv4.conf.all.arp_filter =",
collected: `{}`,
expected: false,
expectErr: `expected 3 parts in when "net.ipv4.conf.all.arp_filter ="`,
},
{
name: "errors out if the parameter in the condition was not collected",
conditional: "net.ipv4.conf.all.arp_filter = 0",
collected: `{"net.ipv4.conf.all.arp_ignore": "0"}`,
expected: false,
expectErr: `"net.ipv4.conf.all.arp_filter" does not exist on collected sysctl`,
},
{
name: "errors out if the collected parameter does not support inequalities",
conditional: "net.ipv4.tcp_available_congestion_control > 0",
collected: `{"net.ipv4.tcp_available_congestion_control": "reno cubic"}`,
expected: false,
expectErr: `has value "reno cubic", cannot be used with provided operator ">"`,
},
{
name: "errors out if the provided value for the conditional does not support inequalities",
conditional: "net.ipv4.conf.all.arp_filter > broken",
collected: `{"net.ipv4.conf.all.arp_filter": "0"}`,
expected: false,
expectErr: `has value "broken", cannot be used with provided operator ">"`,
},
{
name: "errors out if the provided operator is unsupported",
conditional: "net.ipv4.conf.all.arp_filter <== 0",
collected: `{"net.ipv4.conf.all.arp_filter": "0"}`,
expected: false,
expectErr: `failed to parse comparison operator "<=="`,
},
{
name: "equals with ints",
conditional: "net.ipv4.conf.all.arp_filter = 0",
collected: `{"net.ipv4.conf.all.arp_filter": "0"}`,
expected: true,
},
{
name: "equals with different data types",
conditional: "net.ipv4.conf.all.arp_filter = will be false",
collected: `{"net.ipv4.conf.all.arp_filter": "0"}`,
expected: false,
},
{
name: "equals with strings",
conditional: "net.ipv4.tcp_available_congestion_control = reno cubic",
collected: `{"net.ipv4.tcp_available_congestion_control": "reno cubic"}`,
expected: true,
},
{
name: "triple equals works",
conditional: "net.ipv4.tcp_available_congestion_control === reno cubic",
collected: `{"net.ipv4.tcp_available_congestion_control": "reno cubic"}`,
expected: true,
},
{
name: "double equals works",
conditional: "net.ipv4.tcp_available_congestion_control == reno cubic",
collected: `{"net.ipv4.tcp_available_congestion_control": "reno cubic"}`,
expected: true,
},
{
name: "lower than succeeds",
conditional: "net.ipv4.conf.default.arp_ignore < 1",
collected: `{"net.ipv4.conf.default.arp_ignore": "0"}`,
expected: true,
},
{
name: "lower than fails",
conditional: "net.ipv4.conf.default.arp_ignore < 1",
collected: `{"net.ipv4.conf.default.arp_ignore": "1"}`,
expected: false,
},
{
name: "lower than or equals succeeds",
conditional: "net.ipv4.conf.default.arp_ignore <= 1",
collected: `{"net.ipv4.conf.default.arp_ignore": "1"}`,
expected: true,
},
{
name: "lower than or equals fails",
conditional: "net.ipv4.conf.default.arp_ignore <= 1",
collected: `{"net.ipv4.conf.default.arp_ignore": "2"}`,
expected: false,
},
{
name: "higher than succeeds",
conditional: "net.ipv4.conf.default.arp_ignore > 1",
collected: `{"net.ipv4.conf.default.arp_ignore": "2"}`,
expected: true,
},
{
name: "higher than fails",
conditional: "net.ipv4.conf.default.arp_ignore > 1",
collected: `{"net.ipv4.conf.default.arp_ignore": "1"}`,
expected: false,
},
{
name: "higher than or equals succeeds",
conditional: "net.ipv4.conf.default.arp_ignore >= 1",
collected: `{"net.ipv4.conf.default.arp_ignore": "1"}`,
expected: true,
},
{
name: "higher than or equals fails",
conditional: "net.ipv4.conf.default.arp_ignore >= 2",
collected: `{"net.ipv4.conf.default.arp_ignore": "1"}`,
expected: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
analyzeHostSysctl := AnalyzeHostSysctl{}
// JSON encoded sysctl collected output
data := []byte(test.collected)
// Call the CheckCondition method
result, err := analyzeHostSysctl.CheckCondition(test.conditional, data)
if test.expectErr != "" {
req.ErrorContains(err, test.expectErr)
} else {
req.NoError(err)
}
assert.Equal(t, test.expected, result)
})
}
}
func TestAnalyzeHostSysctl(t *testing.T) {
tests := []struct {
name string
hostAnalyzer *troubleshootv1beta2.HostSysctlAnalyze
getCollectedFileContents func(string) ([]byte, error)
expectedResults []*AnalyzeResult
expectedError string
}{
{
name: "Pass on successful condition (local)",
hostAnalyzer: &troubleshootv1beta2.HostSysctlAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "net.ipv4.conf.default.arp_ignore >= 1",
Message: "ARP ignore is enabled",
},
},
},
},
getCollectedFileContents: func(path string) ([]byte, error) {
// Simulate local sysctl content retrieval
if path == collect.HostSysctlPath {
data := map[string]string{
"net.ipv4.conf.default.arp_ignore": "2",
}
return json.Marshal(data)
}
return nil, errors.New("file not found")
},
expectedResults: []*AnalyzeResult{
{
Title: "Sysctl",
IsPass: true,
Message: "ARP ignore is enabled",
},
},
expectedError: "",
},
{
name: "Fail on condition (remote node)",
hostAnalyzer: &troubleshootv1beta2.HostSysctlAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "net.ipv4.conf.default.arp_filter = 0",
Message: "ARP filter is disabled, please enable it via `sysctl net.ipv4.conf.default.arp_filter=1`",
},
},
},
},
getCollectedFileContents: func(path string) ([]byte, error) {
// Simulate remote node list and sysctl content retrieval
if path == constants.NODE_LIST_FILE {
nodeNames := nodeNames{Nodes: []string{"node1"}}
return json.Marshal(nodeNames)
}
if path == fmt.Sprintf("%s/node1/%s", collect.NodeInfoBaseDir, collect.HostSysctlFileName) {
data := map[string]string{
"net.ipv4.conf.default.arp_filter": "0",
}
return json.Marshal(data)
}
return nil, errors.New("file not found")
},
expectedResults: []*AnalyzeResult{
{
Title: "Sysctl - Node node1",
IsFail: true,
Message: "ARP filter is disabled, please enable it via `sysctl net.ipv4.conf.default.arp_filter=1`",
},
},
expectedError: "",
},
{
name: "Warn on condition(remote node)",
hostAnalyzer: &troubleshootv1beta2.HostSysctlAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "net.ipv4.tcp_available_congestion_control = reno cubic",
Message: "Unexpected TCP congestion control algorithm available",
},
},
},
},
getCollectedFileContents: func(path string) ([]byte, error) {
// Simulate remote node list and sysctl content retrieval
if path == constants.NODE_LIST_FILE {
nodeNames := nodeNames{Nodes: []string{"node1"}}
return json.Marshal(nodeNames)
}
if path == fmt.Sprintf("%s/node1/%s", collect.NodeInfoBaseDir, collect.HostSysctlFileName) {
data := map[string]string{
"net.ipv4.tcp_available_congestion_control": "reno cubic",
}
return json.Marshal(data)
}
return nil, errors.New("file not found")
},
expectedResults: []*AnalyzeResult{
{
Title: "Sysctl - Node node1",
IsWarn: true,
Message: "Unexpected TCP congestion control algorithm available",
},
},
expectedError: "",
},
{
name: "Return error if collection fails",
hostAnalyzer: &troubleshootv1beta2.HostSysctlAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "net.ipv4.tcp_available_congestion_control = reno cubic",
Message: "Unexpected TCP congestion control algorithm available",
},
},
},
},
getCollectedFileContents: func(path string) ([]byte, error) {
return nil, errors.New("file not found")
},
expectedResults: []*AnalyzeResult{
{
Title: "Sysctl",
},
},
expectedError: "file not found",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
analyzeHostSysctl := AnalyzeHostSysctl{
hostAnalyzer: test.hostAnalyzer,
}
results, err := analyzeHostSysctl.Analyze(test.getCollectedFileContents, nil)
if test.expectedError != "" {
req.ErrorContains(err, test.expectedError)
} else {
req.NoError(err)
}
req.Equal(test.expectedResults, results)
})
}
}
+95 -79
View File
@@ -32,93 +32,40 @@ func (a *AnalyzeHostSystemPackages) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
packagesFileName := "host-collectors/system/packages.json"
if a.hostAnalyzer.CollectorName != "" {
packagesFileName = fmt.Sprintf("host-collectors/system/%s-packages.json", a.hostAnalyzer.CollectorName)
collectorName := hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "packages"
}
contents, err := getCollectedFileContents(packagesFileName)
localPath := fmt.Sprintf("%s/%s-packages.json", collect.NodeInfoBaseDir, collectorName)
packagesFileName := fmt.Sprintf("%s-packages.json", collectorName)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
collect.NodeInfoBaseDir,
packagesFileName,
)
if err != nil {
return nil, errors.Wrap(err, "failed to get collected file")
return []*AnalyzeResult{{Title: a.Title()}}, err
}
var info collect.SystemPackagesInfo
if err := json.Unmarshal(contents, &info); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal system packages info")
}
allResults := []*AnalyzeResult{}
for _, pkg := range info.Packages {
templateMap := getSystemPackageTemplateMap(pkg, info.OS, info.OSVersion)
for _, outcome := range hostAnalyzer.Outcomes {
r := AnalyzeResult{}
when := ""
if outcome.Fail != nil {
r.IsFail = true
r.Message = outcome.Fail.Message
r.URI = outcome.Fail.URI
when = outcome.Fail.When
} else if outcome.Warn != nil {
r.IsWarn = true
r.Message = outcome.Warn.Message
r.URI = outcome.Warn.URI
when = outcome.Warn.When
} else if outcome.Pass != nil {
r.IsPass = true
r.Message = outcome.Pass.Message
r.URI = outcome.Pass.URI
when = outcome.Pass.When
} else {
println("error: found an empty outcome in a systemPackages analyzer") // don't stop
continue
}
match, err := compareSystemPackagesConditionalToActual(when, templateMap)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", when)
}
if !match {
continue
}
tmpl := template.New("package")
// template the title
titleTmpl, err := tmpl.Parse(a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to create new title template")
}
var t bytes.Buffer
err = titleTmpl.Execute(&t, templateMap)
if err != nil {
return nil, errors.Wrap(err, "failed to execute title template")
}
r.Title = t.String()
// template the message
msgTmpl, err := tmpl.Parse(r.Message)
if err != nil {
return nil, errors.Wrap(err, "failed to create new message template")
}
var m bytes.Buffer
err = msgTmpl.Execute(&m, templateMap)
if err != nil {
return nil, errors.Wrap(err, "failed to execute message template")
}
r.Message = m.String()
// add to results, break and check the next pod
allResults = append(allResults, &r)
break
var results []*AnalyzeResult
for _, content := range collectedContents {
currentTitle := a.Title()
if content.NodeName != "" {
currentTitle = fmt.Sprintf("%s - Node %s", a.Title(), content.NodeName)
}
result, err := a.analyzeSingleNode(content, currentTitle)
if err != nil {
return nil, errors.Wrapf(err, "failed to analyze system packages for %s", currentTitle)
}
if result != nil {
results = append(results, result...)
}
}
return allResults, nil
return results, nil
}
func getSystemPackageTemplateMap(pkg collect.SystemPackage, osName string, osVersion string) map[string]interface{} {
@@ -231,3 +178,72 @@ func compareSystemPackagesConditionalToActual(conditional string, templateMap ma
return t, nil
}
func (a *AnalyzeHostSystemPackages) analyzeSingleNode(content collectedContent, currentTitle string) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
var info collect.SystemPackagesInfo
if err := json.Unmarshal(content.Data, &info); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal system packages info from %s", currentTitle)
}
allResults := []*AnalyzeResult{}
for _, pkg := range info.Packages {
templateMap := getSystemPackageTemplateMap(pkg, info.OS, info.OSVersion)
for _, outcome := range hostAnalyzer.Outcomes {
r := AnalyzeResult{}
when := ""
switch {
case outcome.Fail != nil:
r.IsFail = true
r.Message = outcome.Fail.Message
r.URI = outcome.Fail.URI
when = outcome.Fail.When
case outcome.Warn != nil:
r.IsWarn = true
r.Message = outcome.Warn.Message
r.URI = outcome.Warn.URI
when = outcome.Warn.When
case outcome.Pass != nil:
r.IsPass = true
r.Message = outcome.Pass.Message
r.URI = outcome.Pass.URI
when = outcome.Pass.When
default:
return nil, errors.New("invalid outcome")
}
match, err := compareSystemPackagesConditionalToActual(when, templateMap)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", when)
}
if !match {
continue
}
tmpl := template.New("package")
r.Title = currentTitle
// template the message
msgTmpl, err := tmpl.Parse(r.Message)
if err != nil {
return nil, errors.Wrap(err, "failed to create new message template")
}
var m bytes.Buffer
err = msgTmpl.Execute(&m, templateMap)
if err != nil {
return nil, errors.Wrap(err, "failed to execute message template")
}
r.Message = m.String()
// add to results, break and check the next pod
allResults = append(allResults, &r)
break
}
}
return allResults, nil
}
+27 -72
View File
@@ -2,7 +2,7 @@ package analyzer
import (
"encoding/json"
"path/filepath"
"fmt"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
@@ -24,84 +24,39 @@ func (a *AnalyzeHostTCPConnect) IsExcluded() (bool, error) {
func (a *AnalyzeHostTCPConnect) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
collectorName := hostAnalyzer.CollectorName
collectorName := a.hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "connect"
}
fullPath := filepath.Join("host-collectors/connect", collectorName+".json")
collected, err := getCollectedFileContents(fullPath)
const nodeBaseDir = "host-collectors/connect"
localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName)
fileName := fmt.Sprintf("%s.json", collectorName)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
nodeBaseDir,
fileName,
)
if err != nil {
return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath)
}
actual := collect.NetworkStatusResult{}
if err := json.Unmarshal(collected, &actual); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal collected")
return []*AnalyzeResult{{Title: a.Title()}}, err
}
var coll resultCollector
result := &AnalyzeResult{Title: a.Title()}
for _, outcome := range hostAnalyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
break
}
if string(actual.Status) == outcome.Fail.When {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
break
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
break
}
if string(actual.Status) == outcome.Warn.When {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
break
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
break
}
if string(actual.Status) == outcome.Pass.When {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
break
}
}
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze tcp connect")
}
return coll.get(a.Title()), nil
return results, nil
}
func (a *AnalyzeHostTCPConnect) CheckCondition(when string, data []byte) (bool, error) {
var tcpConnect collect.NetworkStatusResult
if err := json.Unmarshal(data, &tcpConnect); err != nil {
return false, fmt.Errorf("failed to unmarshal data into NetworkStatusResult: %v", err)
}
return string(tcpConnect.Status) == when, nil
}
+30 -71
View File
@@ -3,7 +3,6 @@ package analyzer
import (
"encoding/json"
"fmt"
"path"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
@@ -25,85 +24,45 @@ func (a *AnalyzeHostTCPLoadBalancer) IsExcluded() (bool, error) {
func (a *AnalyzeHostTCPLoadBalancer) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
collectorName := hostAnalyzer.CollectorName
collectorName := a.hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "tcpLoadBalancer"
}
fullPath := path.Join("host-collectors/tcpLoadBalancer", fmt.Sprintf("%s.json", collectorName))
const nodeBaseDir = "host-collectors/tcpLoadBalancer"
localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName)
fileName := fmt.Sprintf("%s.json", collectorName)
collected, err := getCollectedFileContents(fullPath)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
nodeBaseDir,
fileName,
)
if err != nil {
return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath)
}
actual := collect.NetworkStatusResult{}
if err := json.Unmarshal(collected, &actual); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal collected")
return []*AnalyzeResult{{Title: a.Title()}}, err
}
var coll resultCollector
result := &AnalyzeResult{Title: a.Title()}
for _, outcome := range hostAnalyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
break
}
if string(actual.Status) == outcome.Fail.When {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
break
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
break
}
if string(actual.Status) == outcome.Warn.When {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
break
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
break
}
if string(actual.Status) == outcome.Pass.When {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
break
}
}
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze TCP Load Balancer")
}
return coll.get(a.Title()), nil
return results, nil
}
func (a *AnalyzeHostTCPLoadBalancer) CheckCondition(when string, data []byte) (bool, error) {
var tcpLoadBalancer collect.NetworkStatusResult
if err := json.Unmarshal(data, &tcpLoadBalancer); err != nil {
return false, fmt.Errorf("failed to unmarshal data into NetworkStatusResult: %v", err)
}
if string(tcpLoadBalancer.Status) == when {
return true, nil
}
return false, nil
}
+29 -66
View File
@@ -3,7 +3,6 @@ package analyzer
import (
"encoding/json"
"fmt"
"path"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
@@ -27,76 +26,40 @@ func (a *AnalyzeHostTCPPortStatus) IsExcluded() (bool, error) {
func (a *AnalyzeHostTCPPortStatus) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
fullPath := path.Join("host-collectors/tcpPortStatus", "tcpPortStatus.json")
if hostAnalyzer.CollectorName != "" {
fullPath = path.Join("host-collectors/tcpPortStatus", fmt.Sprintf("%s.json", hostAnalyzer.CollectorName))
collectorName := a.hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "tcpPortStatus"
}
collected, err := getCollectedFileContents(fullPath)
const nodeBaseDir = "host-collectors/tcpPortStatus"
localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName)
fileName := fmt.Sprintf("%s.json", collectorName)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
nodeBaseDir,
fileName,
)
if err != nil {
return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath)
}
actual := collect.NetworkStatusResult{}
if err := json.Unmarshal(collected, &actual); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal collected")
return []*AnalyzeResult{{Title: a.Title()}}, err
}
result := &AnalyzeResult{Title: a.Title()}
for _, outcome := range hostAnalyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{result}, nil
}
if string(actual.Status) == outcome.Fail.When {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{result}, nil
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return []*AnalyzeResult{result}, nil
}
if string(actual.Status) == outcome.Warn.When {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return []*AnalyzeResult{result}, nil
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{result}, nil
}
if string(actual.Status) == outcome.Pass.When {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{result}, nil
}
}
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze tcp port status")
}
return []*AnalyzeResult{result}, nil
return results, nil
}
func (a *AnalyzeHostTCPPortStatus) CheckCondition(when string, data []byte) (bool, error) {
var tcpPortStatus collect.NetworkStatusResult
if err := json.Unmarshal(data, &tcpPortStatus); err != nil {
return false, fmt.Errorf("failed to unmarshal data into NetworkStatusResult: %v", err)
}
return string(tcpPortStatus.Status) == when, nil
}
+22 -82
View File
@@ -32,95 +32,25 @@ func (a *AnalyzeHostTime) IsExcluded() (bool, error) {
func (a *AnalyzeHostTime) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
result := AnalyzeResult{Title: a.Title()}
contents, err := getCollectedFileContents(collect.HostTimePath)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
collect.HostTimePath,
collect.NodeInfoBaseDir,
collect.HostTimeFileName,
)
if err != nil {
return nil, errors.Wrap(err, "failed to get collected file")
return []*AnalyzeResult{&result}, err
}
timeInfo := collect.TimeInfo{}
if err := json.Unmarshal(contents, &timeInfo); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal time info")
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze OS version")
}
var coll resultCollector
return results, nil
for _, outcome := range hostAnalyzer.Outcomes {
result := &AnalyzeResult{Title: a.Title()}
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
continue
}
isMatch, err := compareHostTimeStatusToActual(outcome.Fail.When, timeInfo)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Fail.When)
}
if isMatch {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
coll.push(result)
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
continue
}
isMatch, err := compareHostTimeStatusToActual(outcome.Warn.When, timeInfo)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Warn.When)
}
if isMatch {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
coll.push(result)
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
continue
}
isMatch, err := compareHostTimeStatusToActual(outcome.Pass.When, timeInfo)
if err != nil {
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Pass.When)
}
if isMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
coll.push(result)
}
}
}
return coll.get(a.Title()), nil
}
func compareHostTimeStatusToActual(status string, timeInfo collect.TimeInfo) (res bool, err error) {
@@ -160,3 +90,13 @@ func compareHostTimeStatusToActual(status string, timeInfo collect.TimeInfo) (re
return false, fmt.Errorf("Unknown keyword: %s", parts[0])
}
func (a *AnalyzeHostTime) CheckCondition(when string, data []byte) (bool, error) {
var timeInfo collect.TimeInfo
if err := json.Unmarshal(data, &timeInfo); err != nil {
return false, fmt.Errorf("failed to unmarshal data into TimeInfo: %v", err)
}
return compareHostTimeStatusToActual(when, timeInfo)
}
-48
View File
@@ -222,54 +222,6 @@ func TestAnalyzeHostTime(t *testing.T) {
},
},
},
{
name: "multiple issues",
timeInfo: &collect.TimeInfo{
Timezone: "PST",
NTPSynchronized: false,
NTPActive: true,
},
hostAnalyzer: &troubleshootv1beta2.TimeAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "timezone != UTC",
Message: "timezone is not set to UTC",
},
},
{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "ntp == unsynchronized+active",
Message: "System clock not yet synchronized",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "timezone == UTC",
Message: "Timezone is set to UTC",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "ntp == synchronized+active",
Message: "System clock is synchronized",
},
},
},
},
result: []*AnalyzeResult{
{
Title: "Time",
IsFail: true,
Message: "timezone is not set to UTC",
},
{
Title: "Time",
IsWarn: true,
Message: "System clock not yet synchronized",
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
+29 -66
View File
@@ -3,7 +3,6 @@ package analyzer
import (
"encoding/json"
"fmt"
"path"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
@@ -27,76 +26,40 @@ func (a *AnalyzeHostUDPPortStatus) IsExcluded() (bool, error) {
func (a *AnalyzeHostUDPPortStatus) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
fullPath := path.Join("host-collectors/udpPortStatus", "udpPortStatus.json")
if hostAnalyzer.CollectorName != "" {
fullPath = path.Join("host-collectors/udpPortStatus", fmt.Sprintf("%s.json", hostAnalyzer.CollectorName))
collectorName := a.hostAnalyzer.CollectorName
if collectorName == "" {
collectorName = "udpPortStatus"
}
collected, err := getCollectedFileContents(fullPath)
const nodeBaseDir = "host-collectors/udpPortStatus"
localPath := fmt.Sprintf("%s/%s.json", nodeBaseDir, collectorName)
fileName := fmt.Sprintf("%s.json", collectorName)
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
localPath,
nodeBaseDir,
fileName,
)
if err != nil {
return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath)
}
actual := collect.NetworkStatusResult{}
if err := json.Unmarshal(collected, &actual); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal collected")
return []*AnalyzeResult{{Title: a.Title()}}, err
}
result := &AnalyzeResult{Title: a.Title()}
for _, outcome := range hostAnalyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{result}, nil
}
if string(actual.Status) == outcome.Fail.When {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return []*AnalyzeResult{result}, nil
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return []*AnalyzeResult{result}, nil
}
if string(actual.Status) == outcome.Warn.When {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return []*AnalyzeResult{result}, nil
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{result}, nil
}
if string(actual.Status) == outcome.Pass.When {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return []*AnalyzeResult{result}, nil
}
}
results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title())
if err != nil {
return nil, errors.Wrap(err, "failed to analyze udp port status")
}
return []*AnalyzeResult{result}, nil
return results, nil
}
func (a *AnalyzeHostUDPPortStatus) CheckCondition(when string, data []byte) (bool, error) {
var udpPort collect.NetworkStatusResult
if err := json.Unmarshal(data, &udpPort); err != nil {
return false, fmt.Errorf("failed to unmarshal data into NetworkStatusResult: %v", err)
}
return string(udpPort.Status) == when, nil
}
+30
View File
@@ -0,0 +1,30 @@
package analyzer
import (
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)
type AnalyzeHTTPAnalyze struct {
analyzer *troubleshootv1beta2.HTTPAnalyze
}
func (a *AnalyzeHTTPAnalyze) Title() string {
checkName := a.analyzer.CheckName
if checkName == "" {
checkName = a.analyzer.CollectorName
}
return checkName
}
func (a *AnalyzeHTTPAnalyze) IsExcluded() (bool, error) {
return isExcluded(a.analyzer.Exclude)
}
func (a *AnalyzeHTTPAnalyze) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
fileName := "result.json"
if a.analyzer.CollectorName != "" {
fileName = a.analyzer.CollectorName + ".json"
}
return analyzeHTTPResult(a.analyzer, fileName, getFile, a.Title())
}
+13 -6
View File
@@ -21,9 +21,16 @@ type AnalyzeJsonCompare struct {
}
func (a *AnalyzeJsonCompare) Title() string {
title := a.analyzer.CheckName
return jsonCompareTitle(a.analyzer)
}
func jsonCompareTitle(analyser *troubleshootv1beta2.JsonCompare) string {
title := analyser.CheckName
if title == "" {
title = a.analyzer.CollectorName
title = analyser.CollectorName
}
if title == "" {
title = "Json Compare"
}
return title
@@ -34,7 +41,7 @@ func (a *AnalyzeJsonCompare) IsExcluded() (bool, error) {
}
func (a *AnalyzeJsonCompare) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
result, err := a.analyzeJsonCompare(a.analyzer, getFile)
result, err := analyzeJsonCompare(a.analyzer, getFile, a.Title())
if err != nil {
return nil, err
}
@@ -42,7 +49,7 @@ func (a *AnalyzeJsonCompare) Analyze(getFile getCollectedFileContents, findFiles
return []*AnalyzeResult{result}, nil
}
func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.JsonCompare, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
func analyzeJsonCompare(analyzer *troubleshootv1beta2.JsonCompare, getCollectedFileContents func(string) ([]byte, error), title string) (*AnalyzeResult, error) {
fullPath := filepath.Join(analyzer.CollectorName, analyzer.FileName)
collected, err := getCollectedFileContents(fullPath)
if err != nil {
@@ -97,7 +104,7 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js
}
result := &AnalyzeResult{
Title: a.Title(),
Title: title,
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
}
@@ -173,7 +180,7 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js
}
return &AnalyzeResult{
Title: a.Title(),
Title: title,
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
IsFail: true,
+1 -5
View File
@@ -808,11 +808,7 @@ func Test_jsonCompare(t *testing.T) {
return test.fileContents, nil
}
a := AnalyzeJsonCompare{
analyzer: &test.analyzer,
}
actual, err := a.analyzeJsonCompare(&test.analyzer, getCollectedFileContents)
actual, err := analyzeJsonCompare(&test.analyzer, getCollectedFileContents, test.analyzer.CollectorName)
if !test.isError {
req.NoError(err)
req.Equal(test.expectResult, *actual)
+18 -14
View File
@@ -17,22 +17,23 @@ import (
)
var Filemap = map[string]string{
"Deployment": constants.CLUSTER_RESOURCES_DEPLOYMENTS,
"StatefulSet": constants.CLUSTER_RESOURCES_STATEFULSETS,
"NetworkPolicy": constants.CLUSTER_RESOURCES_NETWORK_POLICY,
"Pod": constants.CLUSTER_RESOURCES_PODS,
"Ingress": constants.CLUSTER_RESOURCES_INGRESS,
"Service": constants.CLUSTER_RESOURCES_SERVICES,
"ResourceQuota": constants.CLUSTER_RESOURCES_RESOURCE_QUOTA,
"Job": constants.CLUSTER_RESOURCES_JOBS,
"PersistentVolumeClaim": constants.CLUSTER_RESOURCES_PVCS,
"deployment": constants.CLUSTER_RESOURCES_DEPLOYMENTS,
"statefulset": constants.CLUSTER_RESOURCES_STATEFULSETS,
"networkpolicy": constants.CLUSTER_RESOURCES_NETWORK_POLICY,
"pod": constants.CLUSTER_RESOURCES_PODS,
"ingress": constants.CLUSTER_RESOURCES_INGRESS,
"service": constants.CLUSTER_RESOURCES_SERVICES,
"resourcequota": constants.CLUSTER_RESOURCES_RESOURCE_QUOTA,
"job": constants.CLUSTER_RESOURCES_JOBS,
"persistentvolumeclaim": constants.CLUSTER_RESOURCES_PVCS,
"pvc": constants.CLUSTER_RESOURCES_PVCS,
"ReplicaSet": constants.CLUSTER_RESOURCES_REPLICASETS,
"Namespace": fmt.Sprintf("%s.json", constants.CLUSTER_RESOURCES_NAMESPACES),
"PersistentVolume": fmt.Sprintf("%s.json", constants.CLUSTER_RESOURCES_PVS),
"replicaset": constants.CLUSTER_RESOURCES_REPLICASETS,
"configmap": constants.CLUSTER_RESOURCES_CONFIGMAPS,
"namespace": fmt.Sprintf("%s.json", constants.CLUSTER_RESOURCES_NAMESPACES),
"persistentvolume": fmt.Sprintf("%s.json", constants.CLUSTER_RESOURCES_PVS),
"pv": fmt.Sprintf("%s.json", constants.CLUSTER_RESOURCES_PVS),
"Node": fmt.Sprintf("%s.json", constants.CLUSTER_RESOURCES_NODES),
"StorageClass": fmt.Sprintf("%s.json", constants.CLUSTER_RESOURCES_STORAGE_CLASS),
"node": fmt.Sprintf("%s.json", constants.CLUSTER_RESOURCES_NODES),
"storageclass": fmt.Sprintf("%s.json", constants.CLUSTER_RESOURCES_STORAGE_CLASS),
}
type AnalyzeClusterResource struct {
@@ -66,6 +67,9 @@ func FindResource(kind string, clusterScoped bool, namespace string, name string
var datapath string
// lowercase the kind to avoid case sensitivity
kind = strings.ToLower(kind)
resourceLocation, ok := Filemap[kind]
if !ok {
+14 -4
View File
@@ -21,13 +21,13 @@ func Test_findResource(t *testing.T) {
resourceExists: true,
analyzer: troubleshootv1beta2.ClusterResource{
CollectorName: "Check namespaced resource",
Kind: "Deployment",
Kind: "deployment",
Namespace: "kube-system",
Name: "coredns",
},
},
{
name: "check default fallthrough",
name: "check default fallthrough with case insensitivity",
resourceExists: true,
analyzer: troubleshootv1beta2.ClusterResource{
CollectorName: "Check namespaced resource",
@@ -40,7 +40,7 @@ func Test_findResource(t *testing.T) {
resourceExists: true,
analyzer: troubleshootv1beta2.ClusterResource{
CollectorName: "Check namespaced resource",
Kind: "Node",
Kind: "node",
ClusterScoped: true,
Name: "repldev-marc",
},
@@ -50,11 +50,21 @@ func Test_findResource(t *testing.T) {
resourceExists: false,
analyzer: troubleshootv1beta2.ClusterResource{
CollectorName: "Check namespaced resource",
Kind: "Node",
Kind: "node",
ClusterScoped: true,
Name: "resource-does-not-exist",
},
},
{
name: "configmap does exist",
resourceExists: true,
analyzer: troubleshootv1beta2.ClusterResource{
CollectorName: "Check namespaced resource",
Kind: "configmap",
Namespace: "kube-public",
Name: "kube-root-ca.crt",
},
},
}
for _, test := range tests {
+107 -29
View File
@@ -8,22 +8,30 @@ import (
"strings"
"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/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
)
type AnalyzeNodeResources struct {
analyzer *troubleshootv1beta2.NodeResources
}
type NodeResourceMsg struct {
*troubleshootv1beta2.NodeResourceFilters
NodeCount int
}
func (a *AnalyzeNodeResources) Title() string {
title := a.analyzer.CheckName
if title == "" {
title = "Node Resources"
}
return title
}
@@ -41,6 +49,7 @@ func (a *AnalyzeNodeResources) Analyze(getFile getCollectedFileContents, findFil
}
func (a *AnalyzeNodeResources) analyzeNodeResources(analyzer *troubleshootv1beta2.NodeResources, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
collected, err := getCollectedFileContents(fmt.Sprintf("%s/%s.json", constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_NODES))
if err != nil {
return nil, errors.Wrap(err, "failed to get contents of nodes.json")
@@ -70,42 +79,55 @@ func (a *AnalyzeNodeResources) analyzeNodeResources(analyzer *troubleshootv1beta
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
}
nodeMsg := NodeResourceMsg{
analyzer.Filters, len(matchingNodes),
}
for _, outcome := range analyzer.Outcomes {
if outcome.Fail != nil {
isWhenMatch, err := compareNodeResourceConditionalToActual(outcome.Fail.When, matchingNodes)
isWhenMatch, err := compareNodeResourceConditionalToActual(outcome.Fail.When, matchingNodes, analyzer.Filters)
if err != nil {
return nil, errors.Wrap(err, "failed to parse when")
}
if isWhenMatch {
result.IsFail = true
result.Message = outcome.Fail.Message
result.Message, err = util.RenderTemplate(outcome.Fail.Message, nodeMsg)
if err != nil {
return nil, errors.Wrap(err, "failed to render message template")
}
result.URI = outcome.Fail.URI
return result, nil
}
} else if outcome.Warn != nil {
isWhenMatch, err := compareNodeResourceConditionalToActual(outcome.Warn.When, matchingNodes)
isWhenMatch, err := compareNodeResourceConditionalToActual(outcome.Warn.When, matchingNodes, analyzer.Filters)
if err != nil {
return nil, errors.Wrap(err, "failed to parse when")
}
if isWhenMatch {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.Message, err = util.RenderTemplate(outcome.Warn.Message, nodeMsg)
if err != nil {
return nil, errors.Wrap(err, "failed to render message template")
}
result.URI = outcome.Warn.URI
return result, nil
}
} else if outcome.Pass != nil {
isWhenMatch, err := compareNodeResourceConditionalToActual(outcome.Pass.When, matchingNodes)
isWhenMatch, err := compareNodeResourceConditionalToActual(outcome.Pass.When, matchingNodes, analyzer.Filters)
if err != nil {
return nil, errors.Wrap(err, "failed to parse when")
}
if isWhenMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.Message, err = util.RenderTemplate(outcome.Pass.Message, nodeMsg)
if err != nil {
return nil, errors.Wrap(err, "failed to render message template")
}
result.URI = outcome.Pass.URI
return result, nil
@@ -116,7 +138,7 @@ func (a *AnalyzeNodeResources) analyzeNodeResources(analyzer *troubleshootv1beta
return result, nil
}
func compareNodeResourceConditionalToActual(conditional string, matchingNodes []corev1.Node) (res bool, err error) {
func compareNodeResourceConditionalToActual(conditional string, matchingNodes []corev1.Node, filters *troubleshootv1beta2.NodeResourceFilters) (res bool, err error) {
res = false
err = nil
@@ -169,6 +191,11 @@ func compareNodeResourceConditionalToActual(conditional string, matchingNodes []
function := match[1]
property := match[2]
resourceName := ""
if filters != nil {
resourceName = filters.ResourceName
}
var actualValue interface{}
@@ -176,11 +203,11 @@ func compareNodeResourceConditionalToActual(conditional string, matchingNodes []
case "count":
actualValue = len(matchingNodes)
case "min":
actualValue = findMin(matchingNodes, property)
actualValue = findMin(matchingNodes, property, resourceName)
case "max":
actualValue = findMax(matchingNodes, property)
actualValue = findMax(matchingNodes, property, resourceName)
case "sum":
actualValue = findSum(matchingNodes, property)
actualValue = findSum(matchingNodes, property, resourceName)
case "nodeCondition":
operatorChecker := regexp.MustCompile(`={1,3}`)
if !operatorChecker.MatchString(operator) {
@@ -290,7 +317,7 @@ func compareNodeResourceConditionalToActual(conditional string, matchingNodes []
return
}
func getQuantity(node corev1.Node, property string) *resource.Quantity {
func getQuantity(node corev1.Node, property string, resourceName string) *resource.Quantity {
switch property {
case "cpuCapacity":
return node.Status.Capacity.Cpu()
@@ -308,15 +335,27 @@ func getQuantity(node corev1.Node, property string) *resource.Quantity {
return node.Status.Capacity.StorageEphemeral()
case "ephemeralStorageAllocatable":
return node.Status.Allocatable.StorageEphemeral()
case "resourceCapacity":
capacity, ok := node.Status.Capacity[corev1.ResourceName(resourceName)]
if !ok {
return nil
}
return &capacity
case "resourceAllocatable":
allocatable, ok := node.Status.Allocatable[corev1.ResourceName(resourceName)]
if !ok {
return nil
}
return &allocatable
}
return nil
}
func findSum(nodes []corev1.Node, property string) *resource.Quantity {
func findSum(nodes []corev1.Node, property string, resourceName string) *resource.Quantity {
sum := resource.Quantity{}
for _, node := range nodes {
if quant := getQuantity(node, property); quant != nil {
if quant := getQuantity(node, property, resourceName); quant != nil {
sum.Add(*quant)
}
}
@@ -324,11 +363,11 @@ func findSum(nodes []corev1.Node, property string) *resource.Quantity {
return &sum
}
func findMin(nodes []corev1.Node, property string) *resource.Quantity {
func findMin(nodes []corev1.Node, property string, resourceName string) *resource.Quantity {
var min *resource.Quantity
for _, node := range nodes {
if quant := getQuantity(node, property); quant != nil {
if quant := getQuantity(node, property, resourceName); quant != nil {
if min == nil {
min = quant
} else if quant.Cmp(*min) == -1 {
@@ -340,11 +379,11 @@ func findMin(nodes []corev1.Node, property string) *resource.Quantity {
return min
}
func findMax(nodes []corev1.Node, property string) *resource.Quantity {
func findMax(nodes []corev1.Node, property string, resourceName string) *resource.Quantity {
var max *resource.Quantity
for _, node := range nodes {
if quant := getQuantity(node, property); quant != nil {
if quant := getQuantity(node, property, resourceName); quant != nil {
if max == nil {
max = quant
} else if quant.Cmp(*max) == 1 {
@@ -361,20 +400,59 @@ func nodeMatchesFilters(node corev1.Node, filters *troubleshootv1beta2.NodeResou
return true, nil
}
// all filters must pass for this to pass
if filters.Selector != nil {
for k, v := range filters.Selector.MatchLabel {
l, found := node.Labels[k]
if !found {
if filters.ResourceName != "" {
capacity, capacityExists := node.Status.Capacity[corev1.ResourceName(filters.ResourceName)]
allocatable, allocatableExists := node.Status.Allocatable[corev1.ResourceName(filters.ResourceName)]
if !capacityExists && !allocatableExists {
return false, nil
}
if filters.ResourceCapacity != "" {
parsed, err := resource.ParseQuantity(filters.ResourceCapacity)
if err != nil {
return false, errors.Wrap(err, "failed to parse resource capacity")
}
// Compare the capacity value with the parsed value
if capacity.Cmp(parsed) == -1 {
return false, nil
} else if l != v {
}
}
if filters.ResourceAllocatable != "" {
parsed, err := resource.ParseQuantity(filters.ResourceAllocatable)
if err != nil {
return false, errors.Wrap(err, "failed to parse resource allocatable")
}
// Compare the allocatable value with the parsed value
if allocatable.Cmp(parsed) == -1 {
return false, nil
}
}
}
if filters.Architecture != "" {
parsed := filters.Architecture
// all filters must pass for this to pass
if filters.Selector != nil {
selector, err := metav1.LabelSelectorAsSelector(
&metav1.LabelSelector{
MatchLabels: filters.Selector.MatchLabel,
MatchExpressions: filters.Selector.MatchExpressions,
},
)
if err != nil {
return false, errors.Wrap(err, "failed to create label selector")
}
found := selector.Matches(labels.Set(node.Labels))
if !found {
return false, nil
}
}
if filters.CPUArchitecture != "" {
parsed := filters.CPUArchitecture
if !strings.EqualFold(node.Status.NodeInfo.Architecture, parsed) {
return false, nil
+558 -5
View File
@@ -3,12 +3,13 @@ package analyzer
import (
"testing"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)
func Test_compareNodeResourceConditionalToActual(t *testing.T) {
@@ -33,6 +34,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
"ephemeral-storage": resource.MustParse("19316009748"),
"memory": resource.MustParse("16Ki"),
"pods": resource.MustParse("14"),
"nvidia.com/gpu": resource.MustParse("1"),
},
},
},
@@ -56,6 +58,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
"ephemeral-storage": resource.MustParse("12316009748"),
"memory": resource.MustParse("7848976Ki"),
"pods": resource.MustParse("12"),
"nvidia.com/gpu": resource.MustParse("1"),
},
},
},
@@ -64,6 +67,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
tests := []struct {
name string
conditional string
filters *troubleshootv1beta2.NodeResourceFilters
totalNodeCount int
matchingNodes []corev1.Node
expected bool
@@ -365,13 +369,58 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
expected: false,
isError: true,
},
{
name: "GPU min(resourceAllocatable) == 1 (true)",
conditional: "min(resourceAllocatable) == 1",
filters: &troubleshootv1beta2.NodeResourceFilters{
ResourceName: "nvidia.com/gpu",
},
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "GPU max(resourceAllocatable) > 1 (false)",
conditional: "max(resourceAllocatable) > 1",
filters: &troubleshootv1beta2.NodeResourceFilters{
ResourceName: "nvidia.com/gpu",
},
matchingNodes: nodeData,
totalNodeCount: 0,
expected: false,
isError: false,
},
{
name: "GPU count() == 2 (true)",
conditional: "count() == 2",
filters: &troubleshootv1beta2.NodeResourceFilters{
ResourceName: "nvidia.com/gpu",
ResourceAllocatable: "1",
},
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "GPU count() == 1 (false)",
conditional: "count() == 1",
filters: &troubleshootv1beta2.NodeResourceFilters{
ResourceName: "gpu.intel.com/i915",
},
matchingNodes: nodeData,
totalNodeCount: 0,
expected: false,
isError: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := require.New(t)
actual, err := compareNodeResourceConditionalToActual(test.conditional, test.matchingNodes)
actual, err := compareNodeResourceConditionalToActual(test.conditional, test.matchingNodes, test.filters)
if test.isError {
req.Error(err)
} else {
@@ -403,6 +452,7 @@ func Test_nodeMatchesFilters(t *testing.T) {
"hugepages-2Mi": resource.MustParse("0"),
"memory": resource.MustParse("7951376Ki"),
"pods": resource.MustParse("29"),
"nvidia.com/gpu": resource.MustParse("1"),
},
Allocatable: corev1.ResourceList{
"attachable-volumes-aws-ebs": resource.MustParse("25"),
@@ -412,6 +462,7 @@ func Test_nodeMatchesFilters(t *testing.T) {
"hugepages-2Mi": resource.MustParse("0"),
"memory": resource.MustParse("7848976Ki"),
"pods": resource.MustParse("29"),
"nvidia.com/gpu": resource.MustParse("1"),
},
},
}
@@ -445,7 +496,7 @@ func Test_nodeMatchesFilters(t *testing.T) {
name: "true when cpu arch is amd64",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Architecture: "amd64",
CPUArchitecture: "amd64",
},
expectResult: true,
},
@@ -453,7 +504,7 @@ func Test_nodeMatchesFilters(t *testing.T) {
name: "false when cpu arch is not amd64",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Architecture: "armhf",
CPUArchitecture: "armhf",
},
expectResult: false,
},
@@ -501,6 +552,156 @@ func Test_nodeMatchesFilters(t *testing.T) {
},
expectResult: true,
},
{
name: "true when the label expression matches with operator In",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"value"},
},
},
},
},
expectResult: true,
},
{
name: "false when the label expression does not match with operator In",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"value2"},
},
},
},
},
expectResult: false,
},
{
name: "true when the label expression matches with operator NotIn",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label",
Operator: metav1.LabelSelectorOpNotIn,
Values: []string{"value2"},
},
},
},
},
expectResult: true,
},
{
name: "false when the label expression does not match with operator NotIn",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label",
Operator: metav1.LabelSelectorOpNotIn,
Values: []string{"value"},
},
},
},
},
expectResult: false,
},
{
name: "true when the label expression matches with operator Exists",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label",
Operator: metav1.LabelSelectorOpExists,
},
},
},
},
expectResult: true,
},
{
name: "false when the label expression matches with operator Exists",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label2",
Operator: metav1.LabelSelectorOpExists,
},
},
},
},
expectResult: false,
},
{
name: "true when the label expression matches with operator DoesNotExist",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label2",
Operator: metav1.LabelSelectorOpDoesNotExist,
},
},
},
},
expectResult: true,
},
{
name: "false when the label expression does not match with operator DoesNotExist",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label",
Operator: metav1.LabelSelectorOpDoesNotExist,
},
},
},
},
expectResult: false,
},
{
name: "true when allocatable gpu is available",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
ResourceName: "nvidia.com/gpu",
ResourceAllocatable: "1",
},
expectResult: true,
},
{
name: "true when gpu capacity is available",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
ResourceName: "nvidia.com/gpu",
ResourceCapacity: "1",
},
expectResult: true,
},
{
name: "false when no gpu is available",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
ResourceName: "gpu.intel.com/i915",
},
expectResult: false,
},
}
for _, test := range tests {
@@ -751,7 +952,44 @@ func Test_analyzeNodeResources(t *testing.T) {
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
Architecture: "amd64",
CPUArchitecture: "amd64",
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "amd64-exists",
Message: "There is a node with at least 8 cores on amd64 arch",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "at least 8 cores on amd64 with message templating", // filter for a node with enough amd64 cores with message templating
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "amd64-exists",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "max(cpuCapacity) < 8",
Message: "There isn't a node with 8 or more cores on {{ .CPUArchitecture }} arch",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "max(cpuCapacity) >= 8",
Message: "There is a node with at least 8 cores on {{ .CPUArchitecture }} arch",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
CPUArchitecture: "amd64",
},
},
want: &AnalyzeResult{
@@ -898,6 +1136,162 @@ func Test_analyzeNodeResources(t *testing.T) {
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "8 cores in nodes with at least 8gb of ram with message templating", // validate that filtering based on memory capacity works with message templating
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "memory filter",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "sum(cpuCapacity) < 8",
Message: "less than 8 CPUs in nodes with {{ .MemoryCapacity }} of ram",
URI: "",
},
},
{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "sum(cpuCapacity) = 8",
Message: "exactly 8 CPUs total in nodes with {{ .MemoryCapacity }} of ram",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "sum(cpuCapacity) > 8",
Message: "more than 8 CPUs in nodes with {{ .MemoryCapacity }} of ram",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
MemoryCapacity: "8Gi",
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "memory filter",
Message: "more than 8 CPUs in nodes with 8Gi of ram",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "at least 1 node on arm64 with message templating", // filter for arm64 nodes with message templating
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "arm64-exists",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "count() < 3",
Message: "This application requires at least 3 nodes. {{ .CPUArchitecture }}, it should only return the {{ .NodeCount }} nodes that match that filter",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "count() >= 3",
Message: "There are {{ .NodeCount }} nodes that match that filter",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
CPUArchitecture: "arm64",
},
},
want: &AnalyzeResult{
IsPass: false,
IsFail: true,
IsWarn: false,
Title: "arm64-exists",
Message: "This application requires at least 3 nodes. arm64, it should only return the 0 nodes that match that filter",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "at least 1 node on amd64 with message templating", // filter for amd64 nodes with message templating
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "amd64-exists",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "count() < 3",
Message: "This application requires at least 3 nodes. {{ .CPUArchitecture }}, it should only return the {{ .NodeCount }} nodes that match that filter",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "count() >= 3",
Message: "There are {{ .NodeCount }} nodes that match that filter",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
CPUArchitecture: "amd64",
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "amd64-exists",
Message: "There are 6 nodes that match that filter",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "Only 5 Nodes with amd64 and 2 CPU with message templating", // filter for amd64 and 2 CPU nodes with message templating
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "amd64-exists",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "count() < 3",
Message: "This application requires at least 3 nodes. {{ .CPUArchitecture }}, it should only return the {{ .NodeCount }} nodes that match that filter",
URI: "",
},
},
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "count() >= 3",
Message: "There are {{ .NodeCount }} nodes that match that filter {{ .CPUArchitecture }} and {{ .CPUCapacity }} CPU cores",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
CPUArchitecture: "amd64",
CPUCapacity: "2",
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "amd64-exists",
Message: "There are 5 nodes that match that filter amd64 and 2 CPU cores",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "no pass or fail", // validate that the pass message is not always shown
@@ -926,6 +1320,165 @@ func Test_analyzeNodeResources(t *testing.T) {
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "1 GPU in nodes", // validate that the pass message is not always shown
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "GPU filter",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "count() >= 1",
Message: "There is a node with at least 1 GPU",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
ResourceName: "nvidia.com/gpu",
ResourceAllocatable: "1",
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "GPU filter",
Message: "There is a node with at least 1 GPU",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "1 GPU in nodes filtered by ResourceAllocatable", // validate that the pass message is not always shown
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "GPU filter by ResourceAllocatable",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "count() >= 1",
Message: "There is a node with at least 1 GPU",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
ResourceName: "nvidia.com/gpu",
ResourceAllocatable: "1",
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "GPU filter by ResourceAllocatable",
Message: "There is a node with at least 1 GPU",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "1 GPU in nodes filtered by ResourceCapacity", // validate that the pass message is not always shown
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "GPU filter by ResourceCapacity",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "count() >= 1",
Message: "There is a node with at least 1 GPU",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
ResourceName: "nvidia.com/gpu",
ResourceCapacity: "1",
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "GPU filter by ResourceCapacity",
Message: "There is a node with at least 1 GPU",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "Sum 1 GPU in nodes", // validate that the pass message is not always shown
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "GPU sum",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "sum(resourceAllocatable) >= 1",
Message: "There is a node with at least 1 GPU",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
ResourceName: "nvidia.com/gpu",
ResourceAllocatable: "1",
},
},
want: &AnalyzeResult{
IsPass: true,
IsFail: false,
IsWarn: false,
Title: "GPU sum",
Message: "There is a node with at least 1 GPU",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
{
name: "Count 0 Intel GPU in nodes", // validate that the pass message is not always shown
analyzer: &troubleshootv1beta2.NodeResources{
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
CheckName: "GPU Intel Count",
},
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "count() >= 1",
Message: "There is a node with at least 1 Intel GPU",
URI: "",
},
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "There is no node with at least 1 Intel GPU",
URI: "",
},
},
},
Filters: &troubleshootv1beta2.NodeResourceFilters{
ResourceName: "gpu.intel.com/i915",
},
},
want: &AnalyzeResult{
IsPass: false,
IsFail: true,
IsWarn: false,
Title: "GPU Intel Count",
Message: "There is no node with at least 1 Intel GPU",
URI: "",
IconKey: "kubernetes_node_resources",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/node-resources.svg?w=16&h=18",
},
},
}
getExampleNodeContents := func(nodeName string) ([]byte, error) {
+51 -39
View File
@@ -115,54 +115,66 @@ func analyzeRegexPattern(pattern string, collected []byte, outcomes []*troublesh
return nil, errors.Wrapf(err, "failed to compile regex: %s", pattern)
}
var failOutcome *troubleshootv1beta2.SingleOutcome
var passOutcome *troubleshootv1beta2.SingleOutcome
for _, outcome := range outcomes {
if outcome.Fail != nil {
failOutcome = outcome.Fail
} else if outcome.Pass != nil {
passOutcome = outcome.Pass
}
}
result := AnalyzeResult{
Title: checkName,
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
}
reMatch := re.MatchString(string(collected))
failWhen := false
if failOutcome != nil && failOutcome.When != "" {
failWhen, err = strconv.ParseBool(failOutcome.When)
if err != nil {
return nil, errors.Wrapf(err, "failed to process when statement: %s", failOutcome.When)
}
}
passWhen := true
if passOutcome != nil && passOutcome.When != "" {
passWhen, err = strconv.ParseBool(passOutcome.When)
if err != nil {
return nil, errors.Wrapf(err, "failed to process when statement: %s", passOutcome.When)
}
}
isMatch := re.MatchString(string(collected))
if passWhen == failWhen {
return nil, errors.Wrap(err, "outcome when conditions for fail and pass are equal")
}
for _, outcome := range outcomes {
if outcome.Fail != nil {
if reMatch == passWhen {
result.IsPass = true
if passOutcome != nil {
result.Message = passOutcome.Message
result.URI = passOutcome.URI
// if the outcome.Fail.When is not set, default to false
if outcome.Fail.When == "" {
outcome.Fail.When = "false"
}
failWhen, err := strconv.ParseBool(outcome.Fail.When)
if err != nil {
return nil, errors.Wrapf(err, "failed to process when statement: %s", outcome.Fail.When)
}
if isMatch == failWhen {
result.IsFail = true
result.IsWarn = false
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
}
} else if outcome.Warn != nil {
// if the outcome.Warn.When is not set, default to false
if outcome.Warn.When == "" {
outcome.Warn.When = "false"
}
warnWhen, err := strconv.ParseBool(outcome.Warn.When)
if err != nil {
return nil, errors.Wrapf(err, "failed to process when statement: %s", outcome.Warn.When)
}
if isMatch == warnWhen {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
}
} else if outcome.Pass != nil {
// if the outcome.Pass.When is not set, default to true
if outcome.Pass.When == "" {
outcome.Pass.When = "true"
}
passWhen, err := strconv.ParseBool(outcome.Pass.When)
if err != nil {
return nil, errors.Wrapf(err, "failed to process when statement: %s", outcome.Pass.When)
}
if isMatch == passWhen {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
}
}
return &result, nil
}
result.IsFail = true
if failOutcome != nil {
result.Message = failOutcome.Message
result.URI = failOutcome.URI
}
return &result, nil
}
+84
View File
@@ -222,6 +222,40 @@ func Test_textAnalyze(t *testing.T) {
"text-collector-6/cfile-6.txt": []byte("A different message"),
},
},
{
name: "warn case 1",
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "success",
},
},
{
Warn: &troubleshootv1beta2.SingleOutcome{
Message: "warning",
},
},
},
CollectorName: "text-collector-6",
FileName: "cfile-6.txt",
RegexPattern: "([a-zA-Z0-9\\-_:*\\s])*succe([a-zA-Z0-9\\-_:*\\s!])*",
},
expectResult: []AnalyzeResult{
{
IsPass: false,
IsWarn: true,
IsFail: false,
Title: "text-collector-6",
Message: "warning",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
"text-collector-6/cfile-6.txt": []byte("A different message"),
},
},
{
name: "multiple results case 1",
analyzer: troubleshootv1beta2.TextAnalyze{
@@ -303,6 +337,56 @@ func Test_textAnalyze(t *testing.T) {
"text-collector-2/cfile-3.txt": []byte("Yes it all succeeded"),
},
},
{
name: "multiple results with both warn and fail case 1, only fail",
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "pass",
},
},
{
Warn: &troubleshootv1beta2.SingleOutcome{
Message: "warning",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "fail",
},
},
},
CollectorName: "text-collector-1",
FileName: "cfile",
RegexPattern: "succeeded",
},
expectResult: []AnalyzeResult{
{
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "text-collector-1",
Message: "pass",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-1",
Message: "fail",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
"text-collector-1/cfile-1.txt": []byte("Yes it all succeeded"),
"text-collector-1/cfile-2.txt": []byte("no success here"),
"text-collector-2/cfile-3.txt": []byte("Yes it all succeeded"),
},
},
{
name: "Fail on error case 1", // regexes are not case insensitive by default
analyzer: troubleshootv1beta2.TextAnalyze{
@@ -93,6 +93,9 @@ type NodeResourceFilters struct {
EphemeralStorageCapacity string `json:"ephemeralStorageCapacity,omitempty" yaml:"ephemeralStorageCapacity,omitempty"`
EphemeralStorageAllocatable string `json:"ephemeralStorageAllocatable,omitempty" yaml:"ephemeralStorageAllocatable,omitempty"`
Selector *NodeResourceSelectors `json:"selector,omitempty" yaml:"selector,omitempty"`
ResourceName string `json:"resourceName,omitempty" yaml:"resourceName,omitempty"`
ResourceAllocatable string `json:"resourceAllocatable,omitempty" yaml:"resourceAllocatable,omitempty"`
ResourceCapacity string `json:"resourceCapacity,omitempty" yaml:"resourceCapacity,omitempty"`
}
type NodeResourceSelectors struct {
@@ -1,6 +1,8 @@
package v1beta2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/replicatedhq/troubleshoot/pkg/multitype"
)
@@ -103,6 +105,13 @@ type ClusterPodStatuses struct {
Namespaces []string `json:"namespaces,omitempty" yaml:"namespaces,omitempty"`
}
type ClusterContainerStatuses struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
Namespaces []string `json:"namespaces,omitempty" yaml:"namespaces,omitempty"`
RestartCount int32 `json:"restartCount" yaml:"restartCount"`
}
type ContainerRuntime struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
@@ -120,7 +129,7 @@ type NodeResources struct {
}
type NodeResourceFilters struct {
Architecture string `json:"architecture,omitempty" yaml:"cpuArchitecture,omitempty"`
CPUArchitecture string `json:"cpuArchitecture,omitempty" yaml:"cpuArchitecture,omitempty"`
CPUCapacity string `json:"cpuCapacity,omitempty" yaml:"cpuCapacity,omitempty"`
CPUAllocatable string `json:"cpuAllocatable,omitempty" yaml:"cpuAllocatable,omitempty"`
MemoryCapacity string `json:"memoryCapacity,omitempty" yaml:"memoryCapacity,omitempty"`
@@ -130,10 +139,14 @@ type NodeResourceFilters struct {
EphemeralStorageCapacity string `json:"ephemeralStorageCapacity,omitempty" yaml:"ephemeralStorageCapacity,omitempty"`
EphemeralStorageAllocatable string `json:"ephemeralStorageAllocatable,omitempty" yaml:"ephemeralStorageAllocatable,omitempty"`
Selector *NodeResourceSelectors `json:"selector,omitempty" yaml:"selector,omitempty"`
ResourceName string `json:"resourceName,omitempty" yaml:"resourceName,omitempty"`
ResourceAllocatable string `json:"resourceAllocatable,omitempty" yaml:"resourceAllocatable,omitempty"`
ResourceCapacity string `json:"resourceCapacity,omitempty" yaml:"resourceCapacity,omitempty"`
}
type NodeResourceSelectors struct {
MatchLabel map[string]string `json:"matchLabel,omitempty" yaml:"matchLabel,omitempty"`
MatchLabel map[string]string `json:"matchLabel,omitempty" yaml:"matchLabel,omitempty"`
MatchExpressions []metav1.LabelSelectorRequirement `json:"matchExpressions,omitempty" yaml:"matchExpressions,omitempty"`
}
type TextAnalyze struct {
@@ -271,6 +284,7 @@ type Analyze struct {
JobStatus *JobStatus `json:"jobStatus,omitempty" yaml:"jobStatus,omitempty"`
ReplicaSetStatus *ReplicaSetStatus `json:"replicasetStatus,omitempty" yaml:"replicasetStatus,omitempty"`
ClusterPodStatuses *ClusterPodStatuses `json:"clusterPodStatuses,omitempty" yaml:"clusterPodStatuses,omitempty"`
ClusterContainerStatuses *ClusterContainerStatuses `json:"clusterContainerStatuses,omitempty" yaml:"clusterContainerStatuses,omitempty"`
ContainerRuntime *ContainerRuntime `json:"containerRuntime,omitempty" yaml:"containerRuntime,omitempty"`
Distribution *Distribution `json:"distribution,omitempty" yaml:"distribution,omitempty"`
NodeResources *NodeResources `json:"nodeResources,omitempty" yaml:"nodeResources,omitempty"`
@@ -292,4 +306,5 @@ type Analyze struct {
Goldpinger *GoldpingerAnalyze `json:"goldpinger,omitempty" yaml:"goldpinger,omitempty"`
Event *EventAnalyze `json:"event,omitempty" yaml:"event,omitempty"`
NodeMetrics *NodeMetricsAnalyze `json:"nodeMetrics,omitempty" yaml:"nodeMetrics,omitempty"`
HTTP *HTTPAnalyze `json:"http,omitempty" yaml:"http,omitempty"`
}
@@ -24,6 +24,7 @@ import (
type AnalyzerSpec struct {
Analyzers []*Analyze `json:"analyzers,omitempty"`
HostAnalyzers []*HostAnalyze `json:"hostAnalyzers,omitempty" yaml:"hostAnalyzers,omitempty"`
Uri string `json:"uri,omitempty" yaml:"uri,omitempty"`
}
// AnalyzerStatus defines the observed state of Analyzer

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