mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-08-27 00:37:20 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
088f0321e7 | ||
|
|
aeaac7a70f | ||
|
|
6b368f2221 | ||
|
|
cb5db1733a | ||
|
|
c2f72ecd0c | ||
|
|
f3bad5f409 | ||
|
|
f438467003 | ||
|
|
a374b0a3ab | ||
|
|
6aaba59ebd | ||
|
|
f18b5d754e | ||
|
|
2ede46ed58 | ||
|
|
07d1747d81 | ||
|
|
db871e6889 | ||
|
|
dc4403811b | ||
|
|
6f7acec7b3 | ||
|
|
123d17ab4a | ||
|
|
867c70619c | ||
|
|
c9830de736 | ||
|
|
ca5ff88964 | ||
|
|
e6aff48f1b | ||
|
|
76c52d2b93 | ||
|
|
bece801ba8 | ||
|
|
72701fa4d2 | ||
|
|
7eae0fe687 | ||
|
|
6f839b389d | ||
|
|
ebe98ff2f1 | ||
|
|
59624e88fd | ||
|
|
15446996f9 | ||
|
|
c4237b6c40 | ||
|
|
d0685ea81a | ||
|
|
742e92f1ee | ||
|
|
553d709043 | ||
|
|
29435b1c15 | ||
|
|
a9b889e033 | ||
|
|
4a84e26f21 | ||
|
|
2eb3aa9883 | ||
|
|
ffd357c1e8 | ||
|
|
09287f624f | ||
|
|
cd7f2dc74d | ||
|
|
8132936e3e | ||
|
|
b49d1050c9 | ||
|
|
27f867bd4e | ||
|
|
f4d2cfb749 | ||
|
|
8620e001d7 | ||
|
|
bb9a2931a1 | ||
|
|
7c6788c0c5 | ||
|
|
e24ca642aa | ||
|
|
7dc9d6dfe4 | ||
|
|
118b2edc0a | ||
|
|
84d51481da | ||
|
|
fad7a26156 | ||
|
|
772d867093 | ||
|
|
5daf6d6c89 | ||
|
|
8b491b5702 | ||
|
|
18efea6616 | ||
|
|
48fc8b2e84 | ||
|
|
e2adfa3774 | ||
|
|
41134ca621 | ||
|
|
05a852d68c | ||
|
|
90d04b1f7d | ||
|
|
de0a2c631c | ||
|
|
2def517d39 | ||
|
|
fe6b1c7448 | ||
|
|
710366c8e5 | ||
|
|
96529106c7 | ||
|
|
39b371991e | ||
|
|
d4c1e9cc04 | ||
|
|
70a0024a76 | ||
|
|
62984cba40 | ||
|
|
1447e18c56 | ||
|
|
37a5cce8ce | ||
|
|
80de5e3d12 | ||
|
|
c1eddca1e5 | ||
|
|
19a04dc24e | ||
|
|
5d51207f4d | ||
|
|
8d727ea31f | ||
|
|
b46f89af7f | ||
|
|
5ad108fab6 | ||
|
|
361e12e691 | ||
|
|
e542f4fd0a | ||
|
|
e4363a1e50 | ||
|
|
b5996d06ba | ||
|
|
b1b6299ed5 | ||
|
|
7681cc249c | ||
|
|
cad5e8179c | ||
|
|
4c77bcb7cc | ||
|
|
534931600b | ||
|
|
acaa2edd30 | ||
|
|
a86b5c4441 | ||
|
|
bd275dfbab | ||
|
|
7093fa0865 | ||
|
|
3012b870bd | ||
|
|
f44a3d3986 | ||
|
|
53113c0170 | ||
|
|
b0907d7139 | ||
|
|
fef12be8f1 | ||
|
|
e5e26eea14 |
+5
-1
@@ -14,4 +14,8 @@
|
||||
## RULES
|
||||
|
||||
* @replicatedhq/troubleshoot
|
||||
*.md @replicatedhq/cre
|
||||
*.md @replicatedhq/cre
|
||||
go.mod
|
||||
go.sum
|
||||
/examples/sdk/helm-template/go.mod
|
||||
/examples/sdk/helm-template/go.sum
|
||||
|
||||
+21
-3
@@ -10,10 +10,28 @@ updates:
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "go"
|
||||
- "type::chore"
|
||||
- "type::security"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
groups:
|
||||
security:
|
||||
update-types:
|
||||
- "patch"
|
||||
- "minor"
|
||||
- package-ecosystem: "gomod" # See documentation for possible values
|
||||
directory: "/examples/sdk/helm-template" # Location of package manifests
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "go"
|
||||
- "type::security"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
security:
|
||||
update-types:
|
||||
- "patch"
|
||||
- "minor"
|
||||
|
||||
# Maintain dependencies for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
# Workflow files stored in the
|
||||
@@ -22,6 +40,6 @@ updates:
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "github-actions"
|
||||
- "type::chore"
|
||||
- "type::security"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
name: Automated PRs Manager
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 */6 * * *" # every 6 hours
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
list-prs:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
prs: ${{ steps.list-prs.outputs.prs }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.REPLICATED_GH_PAT }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: List PRs
|
||||
id: list-prs
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# list prs that are less than 24h old and exclude prs from forks
|
||||
|
||||
dependabot_prs=$(
|
||||
gh pr list \
|
||||
--author 'dependabot[bot]' \
|
||||
--json url,createdAt,headRefName,headRepository,headRepositoryOwner \
|
||||
-q '.[] | select((.createdAt | fromdateiso8601 > now - 24*60*60) and .headRepositoryOwner.login == "replicatedhq" and .headRepository.name == "troubleshoot")'
|
||||
)
|
||||
|
||||
prs=$(echo "$dependabot_prs" | jq -sc '. | unique')
|
||||
echo "prs=$prs" >> "$GITHUB_OUTPUT"
|
||||
|
||||
process-prs:
|
||||
needs: list-prs
|
||||
runs-on: ubuntu-latest
|
||||
if: needs.list-prs.outputs.prs != '[]'
|
||||
strategy:
|
||||
matrix:
|
||||
pr: ${{ fromJson(needs.list-prs.outputs.prs) }}
|
||||
fail-fast: false
|
||||
max-parallel: 1
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.REPLICATED_GH_PAT }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ matrix.pr.headRefName }}
|
||||
|
||||
- name: Process PR
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "Ensuring required labels..."
|
||||
gh pr edit "${{ matrix.pr.url }}" --add-label "type::security"
|
||||
|
||||
echo "Checking status of tests..."
|
||||
run_id=$(gh run list --branch "${{ matrix.pr.headRefName }}" --workflow build-test-deploy --limit 1 --json databaseId -q '.[0].databaseId')
|
||||
|
||||
# If there are still pending jobs, skip.
|
||||
|
||||
num_of_pending_jobs=$(gh run view "$run_id" --json jobs -q '.jobs[] | select(.conclusion == "") | .name' | wc -l)
|
||||
if [ "$num_of_pending_jobs" -gt 0 ]; then
|
||||
echo "There are still pending jobs. Skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If all checks passed, approve and merge.
|
||||
if gh run view "$run_id" --json jobs -q '.jobs[] | select(.name == "validate-success") | .conclusion' | grep -q "success"; then
|
||||
if gh pr checks "${{ matrix.pr.url }}"; then
|
||||
echo "All tests passed. Approving and merging."
|
||||
echo -e "LGTM :thumbsup: \n\nThis PR was automatically approved and merged by the [automated-prs-manager](https://github.com/replicatedhq/troubleshoot/blob/main/.github/workflows/automated-prs-manager.yaml) GitHub action" > body.txt
|
||||
gh pr review --approve "${{ matrix.pr.url }}" --body-file body.txt
|
||||
sleep 10
|
||||
gh pr merge --auto --squash "${{ matrix.pr.url }}"
|
||||
exit 0
|
||||
else
|
||||
echo "Some checks did not pass. Skipping."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# If more than half of the jobs are successful, re-run the failed jobs.
|
||||
|
||||
num_of_jobs=$(gh run view "$run_id" --json jobs -q '.jobs[].name ' | wc -l)
|
||||
num_of_successful_jobs=$(gh run view "$run_id" --json jobs -q '.jobs[] | select(.conclusion == "success") | .name' | wc -l)
|
||||
|
||||
if [ "$num_of_successful_jobs" -gt $((num_of_jobs / 2)) ]; then
|
||||
echo "More than half of the jobs are successful. Re-running failed jobs."
|
||||
gh run rerun "$run_id" --failed
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Less than half of the jobs are successful. Skipping."
|
||||
@@ -1,3 +1,5 @@
|
||||
name: build-test-deploy
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
@@ -28,9 +30,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
|
||||
- name: setup env
|
||||
run: |
|
||||
@@ -45,9 +47,9 @@ jobs:
|
||||
test-integration:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
|
||||
- name: setup env
|
||||
run: |
|
||||
@@ -59,16 +61,16 @@ jobs:
|
||||
- uses: replicatedhq/action-k3s@main
|
||||
id: k3s
|
||||
with:
|
||||
version: v1.23.6-k3s1
|
||||
version: v1.24.1-k3s1
|
||||
|
||||
- run: make test-integration
|
||||
|
||||
ensure-schemas-are-generated:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
- name: setup env
|
||||
run: |
|
||||
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
||||
@@ -82,9 +84,9 @@ jobs:
|
||||
compile-preflight:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
- name: setup env
|
||||
run: |
|
||||
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
||||
@@ -92,7 +94,7 @@ jobs:
|
||||
shell: bash
|
||||
- uses: actions/checkout@v4
|
||||
- run: make generate preflight
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: preflight
|
||||
path: bin/preflight
|
||||
@@ -106,7 +108,7 @@ jobs:
|
||||
with:
|
||||
version: v1.23.6-k3s1
|
||||
- name: Download preflight binary
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: preflight
|
||||
path: bin/
|
||||
@@ -155,7 +157,7 @@ jobs:
|
||||
with:
|
||||
version: v1.23.6-k3s1
|
||||
- name: Download preflight binary
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: preflight
|
||||
path: bin/
|
||||
@@ -171,9 +173,9 @@ jobs:
|
||||
compile-supportbundle:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
- name: setup env
|
||||
run: |
|
||||
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
||||
@@ -181,7 +183,7 @@ jobs:
|
||||
shell: bash
|
||||
- uses: actions/checkout@v4
|
||||
- run: make generate support-bundle
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: support-bundle
|
||||
path: bin/support-bundle
|
||||
@@ -196,7 +198,7 @@ jobs:
|
||||
with:
|
||||
version: v1.23.6-k3s1
|
||||
- name: Download support-bundle binary
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: support-bundle
|
||||
path: bin/
|
||||
@@ -215,7 +217,7 @@ jobs:
|
||||
with:
|
||||
version: v1.23.6-k3s1
|
||||
- name: Download support bundle binary
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: support-bundle
|
||||
path: bin/
|
||||
@@ -226,9 +228,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: compile-supportbundle
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
- name: setup env
|
||||
run: |
|
||||
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
||||
@@ -236,7 +238,7 @@ jobs:
|
||||
shell: bash
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download support bundle binary
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: support-bundle
|
||||
path: bin/
|
||||
@@ -246,9 +248,9 @@ jobs:
|
||||
compile-collect:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
- name: setup env
|
||||
run: |
|
||||
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
||||
@@ -256,7 +258,7 @@ jobs:
|
||||
shell: bash
|
||||
- uses: actions/checkout@v4
|
||||
- run: make generate collect
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: collect
|
||||
path: bin/collect
|
||||
@@ -279,9 +281,9 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
@@ -310,9 +312,9 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USER }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
|
||||
- uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
@@ -348,3 +350,40 @@ jobs:
|
||||
uses: rajatjindal/krew-release-bot@v0.0.46
|
||||
with:
|
||||
krew_template_file: deploy/krew/support-bundle.yaml
|
||||
|
||||
|
||||
# summary jobs, these jobs will only run if all the other jobs have succeeded
|
||||
validate-pr-tests:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- test
|
||||
- test-integration
|
||||
- run-examples
|
||||
- compile-collect
|
||||
- validate-preflight
|
||||
- validate-preflight-e2e
|
||||
- validate-supportbundle
|
||||
- validate-supportbundle-e2e
|
||||
- validate-supportbundle-e2e-go-test
|
||||
- ensure-schemas-are-generated
|
||||
steps:
|
||||
- run: echo "All PR tests passed"
|
||||
|
||||
|
||||
# this job will validate that the validation did not fail and that all pr-tests succeed
|
||||
# it is used for the github branch protection rule
|
||||
validate-success:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- validate-pr-tests
|
||||
if: always()
|
||||
steps:
|
||||
# https://docs.github.com/en/actions/learn-github-actions/contexts#needs-context
|
||||
# if the validate-pr-tests job was not successful, this job will fail
|
||||
- name: fail if validate-pr-tests job was not successful
|
||||
if: needs.validate-pr-tests.result != 'success'
|
||||
run: exit 1
|
||||
# if the validate-pr-tests job was successful, this job will succeed
|
||||
- name: succeed if validate-pr-tests job succeeded
|
||||
if: needs.validate-pr-tests.result == 'success'
|
||||
run: echo "Validation succeeded"
|
||||
@@ -22,6 +22,6 @@ jobs:
|
||||
severity: 'HIGH,CRITICAL'
|
||||
|
||||
- name: Upload Trivy scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
@@ -16,7 +16,9 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21
|
||||
|
||||
- name: Install Go deps
|
||||
run: go mod download
|
||||
@@ -30,7 +32,7 @@ jobs:
|
||||
run: trivy fs --scanners license --skip-dirs ".github" . | tee license-report.txt
|
||||
|
||||
- name: Upload license report artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: license-report
|
||||
path: license-report.txt
|
||||
|
||||
@@ -10,3 +10,4 @@ linters:
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- gosec
|
||||
- govet
|
||||
|
||||
+4
-4
@@ -23,12 +23,12 @@ To get started we recommend:
|
||||
|
||||
1. Go (v1.20 or later)
|
||||
2. A Kubernetes cluster (we recommend <https://k3d.io/>. This requires Docker v20.10.5 or later)
|
||||
3. Fork and clone the repo to $GOPATH/src/github.com/replicatedhq/
|
||||
4. Run `make support-bundle preflight` to generate binaries
|
||||
5. Run `make run-troubleshoot` to generate a support bundle with the `sample-troubleshoot.yaml` in the root of the repo
|
||||
3. Fork and clone repo
|
||||
4. Run `make clean build` to generate binaries
|
||||
5. Run `make run-support-bundle` to generate a support bundle with the `sample-troubleshoot.yaml` in the root of the repo
|
||||
|
||||
> Note: recent versions of Go support easy cross-compilation. For example, to cross-compile a Linux binary from MacOS:
|
||||
> `GOOS=linux GOARCH=amd64 make support-bundle preflight`
|
||||
> `GOOS=linux GOARCH=amd64 make clean build`
|
||||
|
||||
6. Install [golangci-lint] linter and run `make lint` to execute additional code linters.
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ BUILDPATHS = ./pkg/... ./cmd/... ./internal/...
|
||||
E2EPATHS = ./test/e2e/...
|
||||
TESTFLAGS ?= -v -coverprofile cover.out
|
||||
|
||||
.DEFAULT: all
|
||||
all: test build
|
||||
.DEFAULT_GOAL := all
|
||||
all: clean build test
|
||||
|
||||
.PHONY: ffi
|
||||
ffi: fmt vet
|
||||
@@ -80,28 +80,38 @@ support-bundle-e2e-go-test:
|
||||
go test ${BUILDFLAGS} ${E2EPATHS} -v; \
|
||||
fi
|
||||
|
||||
# Build all binaries in parallel ( -j )
|
||||
build:
|
||||
@echo "Build cli binaries"
|
||||
$(MAKE) -j support-bundle preflight analyze collect
|
||||
rebuild: clean build
|
||||
|
||||
mod-tidy:
|
||||
# Build all binaries in parallel ( -j )
|
||||
build: tidy
|
||||
@echo "Build cli binaries"
|
||||
$(MAKE) -j bin/support-bundle bin/preflight bin/analyze bin/collect
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -f bin/analyze
|
||||
@rm -f bin/support-bundle
|
||||
@rm -f bin/collect
|
||||
@rm -f bin/preflight
|
||||
@rm -f bin/troubleshoot.h
|
||||
@rm -f bin/troubleshoot.so
|
||||
@rm -f bin/schemagen
|
||||
@rm -f bin/docsgen
|
||||
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
go mod tidy
|
||||
|
||||
.PHONY: support-bundle
|
||||
support-bundle:
|
||||
bin/support-bundle:
|
||||
go build ${BUILDFLAGS} ${LDFLAGS} -o bin/support-bundle github.com/replicatedhq/troubleshoot/cmd/troubleshoot
|
||||
|
||||
.PHONY: preflight
|
||||
preflight:
|
||||
bin/preflight:
|
||||
go build ${BUILDFLAGS} ${LDFLAGS} -o bin/preflight github.com/replicatedhq/troubleshoot/cmd/preflight
|
||||
|
||||
.PHONY: analyze
|
||||
analyze:
|
||||
bin/analyze:
|
||||
go build ${BUILDFLAGS} ${LDFLAGS} -o bin/analyze github.com/replicatedhq/troubleshoot/cmd/analyze
|
||||
|
||||
.PHONY: collect
|
||||
collect:
|
||||
bin/collect:
|
||||
go build ${BUILDFLAGS} ${LDFLAGS} -o bin/collect github.com/replicatedhq/troubleshoot/cmd/collect
|
||||
|
||||
.PHONY: fmt
|
||||
@@ -138,27 +148,27 @@ check-schemas: generate schemas
|
||||
fi
|
||||
|
||||
.PHONY: schemas
|
||||
schemas: fmt vet openapischema
|
||||
go build ${LDFLAGS} -o bin/schemagen github.com/replicatedhq/troubleshoot/cmd/schemagen
|
||||
schemas: fmt vet openapischema bin/schemagen
|
||||
./bin/schemagen --output-dir ./schemas
|
||||
|
||||
bin/schemagen:
|
||||
go build ${LDFLAGS} -o bin/schemagen github.com/replicatedhq/troubleshoot/cmd/schemagen
|
||||
|
||||
.PHONY: docs
|
||||
docs: fmt vet
|
||||
go build ${LDFLAGS} -o bin/docsgen github.com/replicatedhq/troubleshoot/cmd/docsgen
|
||||
docs: fmt vet bin/docsgen
|
||||
./bin/docsgen
|
||||
|
||||
bin/docsgen:
|
||||
go build ${LDFLAGS} -o bin/docsgen github.com/replicatedhq/troubleshoot/cmd/docsgen
|
||||
|
||||
controller-gen:
|
||||
go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.11.2
|
||||
go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.14.0
|
||||
CONTROLLER_GEN=$(shell which controller-gen)
|
||||
|
||||
.PHONY: client-gen
|
||||
client-gen:
|
||||
ifeq (, $(shell which client-gen 2>/dev/null))
|
||||
go install k8s.io/code-generator/cmd/client-gen@v0.28.2
|
||||
CLIENT_GEN=$(shell go env GOPATH)/bin/client-gen
|
||||
else
|
||||
go install k8s.io/code-generator/cmd/client-gen@v0.28.8
|
||||
CLIENT_GEN=$(shell which client-gen)
|
||||
endif
|
||||
|
||||
.PHONY: release
|
||||
release: export GITHUB_TOKEN = $(shell echo ${GITHUB_TOKEN_TROUBLESHOOT})
|
||||
@@ -180,15 +190,15 @@ local-release:
|
||||
docker push localhost:32000/preflight:alpha
|
||||
|
||||
.PHONY: run-preflight
|
||||
run-preflight: preflight
|
||||
run-preflight: bin/preflight
|
||||
./bin/preflight ./examples/preflight/sample-preflight.yaml
|
||||
|
||||
.PHONY: run-troubleshoot
|
||||
run-troubleshoot: support-bundle
|
||||
.PHONY: run-support-bundle
|
||||
run-support-bundle: bin/support-bundle
|
||||
./bin/support-bundle ./examples/support-bundle/sample-supportbundle.yaml
|
||||
|
||||
.PHONY: run-analyze
|
||||
run-analyze: analyze
|
||||
run-analyze: bin/analyze
|
||||
./bin/analyze --analyzers ./examples/support-bundle/sample-analyzers.yaml ./support-bundle.tar.gz
|
||||
|
||||
.PHONY: init-sbom
|
||||
@@ -212,20 +222,6 @@ sbom: sbom/assets/troubleshoot-sbom.tgz
|
||||
cosign sign-blob -key cosign.key sbom/assets/troubleshoot-sbom.tgz > sbom/assets/troubleshoot-sbom.tgz.sig
|
||||
cosign public-key -key cosign.key -outfile sbom/assets/key.pub
|
||||
|
||||
longhorn:
|
||||
git clone https://github.com/longhorn/longhorn-manager.git
|
||||
cd longhorn-manager && git checkout v1.2.2 && cd ..
|
||||
rm -rf pkg/longhorn
|
||||
mv longhorn-manager/k8s/pkg pkg/longhorn
|
||||
mv longhorn-manager/types pkg/longhorn/types
|
||||
mv longhorn-manager/util pkg/longhorn/util
|
||||
rm -rf pkg/longhorn/util/daemon
|
||||
rm -rf pkg/longhorn/util/server
|
||||
find pkg/longhorn -type f | xargs sed -i "s/github.com\/longhorn\/longhorn-manager\/k8s\/pkg/github.com\/replicatedhq\/troubleshoot\/pkg\/longhorn/g"
|
||||
find pkg/longhorn -type f | xargs sed -i "s/github.com\/longhorn\/longhorn-manager\/types/github.com\/replicatedhq\/troubleshoot\/pkg\/longhorn\/types/g"
|
||||
find pkg/longhorn -type f | xargs sed -i "s/github.com\/longhorn\/longhorn-manager\/util/github.com\/replicatedhq\/troubleshoot\/pkg\/longhorn\/util/g"
|
||||
rm -rf longhorn-manager
|
||||
|
||||
.PHONY: scan
|
||||
scan:
|
||||
trivy fs \
|
||||
@@ -236,11 +232,11 @@ scan:
|
||||
./
|
||||
|
||||
.PHONY: lint
|
||||
lint: fmt vet
|
||||
lint:
|
||||
golangci-lint run --new -c .golangci.yaml ${BUILDPATHS}
|
||||
|
||||
.PHONY: lint-and-fix
|
||||
lint-and-fix: fmt vet
|
||||
lint-and-fix:
|
||||
golangci-lint run --new --fix -c .golangci.yaml ${BUILDPATHS}
|
||||
|
||||
.PHONY: watch
|
||||
@@ -256,3 +252,35 @@ watchrsync: npm-install
|
||||
npm-install:
|
||||
npm --version 2>&1 >/dev/null || ( echo "npm not installed; install npm to set up watchrsync" && exit 1 )
|
||||
npm list gaze-run-interrupt || npm install install --no-save gaze-run-interrupt@~2.0.0
|
||||
|
||||
|
||||
######## Lagacy make targets ###########
|
||||
# Deprecated: These can be removed
|
||||
.PHONY: support-bundle
|
||||
support-bundle: clean bin/support-bundle
|
||||
|
||||
.PHONY: preflight
|
||||
preflight: clean bin/preflight
|
||||
|
||||
.PHONY: analyze
|
||||
analyze: clean bin/analyze
|
||||
|
||||
.PHONY: collect
|
||||
collect: clean bin/collect
|
||||
|
||||
.PHONY: run-troubleshoot
|
||||
run-troubleshoot: run-support-bundle
|
||||
|
||||
longhorn:
|
||||
git clone https://github.com/longhorn/longhorn-manager.git
|
||||
cd longhorn-manager && git checkout v1.2.2 && cd ..
|
||||
rm -rf pkg/longhorn
|
||||
mv longhorn-manager/k8s/pkg pkg/longhorn
|
||||
mv longhorn-manager/types pkg/longhorn/types
|
||||
mv longhorn-manager/util pkg/longhorn/util
|
||||
rm -rf pkg/longhorn/util/daemon
|
||||
rm -rf pkg/longhorn/util/server
|
||||
find pkg/longhorn -type f | xargs sed -i "s/github.com\/longhorn\/longhorn-manager\/k8s\/pkg/github.com\/replicatedhq\/troubleshoot\/pkg\/longhorn/g"
|
||||
find pkg/longhorn -type f | xargs sed -i "s/github.com\/longhorn\/longhorn-manager\/types/github.com\/replicatedhq\/troubleshoot\/pkg\/longhorn\/types/g"
|
||||
find pkg/longhorn -type f | xargs sed -i "s/github.com\/longhorn\/longhorn-manager\/util/github.com\/replicatedhq\/troubleshoot\/pkg\/longhorn\/util/g"
|
||||
rm -rf longhorn-manager
|
||||
|
||||
@@ -27,7 +27,7 @@ func runAnalyzers(v *viper.Viper, bundlePath string) error {
|
||||
} else {
|
||||
if !util.IsURL(specPath) {
|
||||
// TODO: Better error message when we do not have a file/url etc
|
||||
return fmt.Errorf("%s is not a URL and was not found (err %s)", specPath, err)
|
||||
return fmt.Errorf("%s is not a URL and was not found", specPath)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", specPath, nil)
|
||||
|
||||
@@ -58,7 +58,7 @@ func runCollect(v *viper.Viper, arg string) error {
|
||||
collectorContent = b
|
||||
} else {
|
||||
if !util.IsURL(arg) {
|
||||
return fmt.Errorf("%s is not a URL and was not found (err %s)", arg, err)
|
||||
return fmt.Errorf("%s is not a URL and was not found", arg)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", arg, nil)
|
||||
|
||||
@@ -91,7 +91,7 @@ func downloadAnalyzerSpec(specPath string) (string, error) {
|
||||
specContent = string(b)
|
||||
} else {
|
||||
if !util.IsURL(specPath) {
|
||||
return "", fmt.Errorf("%s is not a URL and was not found (err %s)", specPath, err)
|
||||
return "", fmt.Errorf("%s is not a URL and was not found", specPath)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", specPath, nil)
|
||||
|
||||
+68
-43
@@ -106,6 +106,22 @@ func runTroubleshoot(v *viper.Viper, args []string) error {
|
||||
})
|
||||
}
|
||||
|
||||
if interactive {
|
||||
c := color.New()
|
||||
c.Println(fmt.Sprintf("\r%s\r", cursor.ClearEntireLine()))
|
||||
}
|
||||
|
||||
if interactive {
|
||||
if len(mainBundle.Spec.HostCollectors) > 0 && !util.IsRunningAsRoot() {
|
||||
fmt.Print(cursor.Show())
|
||||
if util.PromptYesNo(util.HOST_COLLECTORS_RUN_AS_ROOT_PROMPT) {
|
||||
fmt.Println("Exiting...")
|
||||
return nil
|
||||
}
|
||||
fmt.Print(cursor.Hide())
|
||||
}
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
collectorCB := func(c chan interface{}, msg string) { c <- msg }
|
||||
progressChan := make(chan interface{})
|
||||
@@ -174,11 +190,6 @@ func runTroubleshoot(v *viper.Viper, args []string) error {
|
||||
|
||||
nonInteractiveOutput := analysisOutput{}
|
||||
|
||||
if interactive {
|
||||
c := color.New()
|
||||
c.Println(fmt.Sprintf("\r%s\r", cursor.ClearEntireLine()))
|
||||
}
|
||||
|
||||
response, err := supportbundle.CollectSupportBundleFromSpec(&mainBundle.Spec, additionalRedactors, createOpts)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to run collect and analyze process")
|
||||
@@ -233,31 +244,44 @@ the %s Admin Console to begin analysis.`
|
||||
}
|
||||
|
||||
// loadSupportBundleSpecsFromURIs loads support bundle specs from URIs
|
||||
func loadSupportBundleSpecsFromURIs(ctx context.Context, kinds *loader.TroubleshootKinds) (*loader.TroubleshootKinds, error) {
|
||||
remoteRawSpecs := []string{}
|
||||
func loadSupportBundleSpecsFromURIs(ctx context.Context, kinds *loader.TroubleshootKinds) error {
|
||||
moreKinds := loader.NewTroubleshootKinds()
|
||||
|
||||
// iterate through original kinds and replace any support bundle spec with provided uri spec
|
||||
for _, s := range kinds.SupportBundlesV1Beta2 {
|
||||
if s.Spec.Uri != "" && util.IsURL(s.Spec.Uri) {
|
||||
// We are using LoadSupportBundleSpec function here since it handles prompting
|
||||
// users to accept insecure connections
|
||||
// There is an opportunity to refactor this code in favour of the Loader APIs
|
||||
// TODO: Pass ctx to LoadSupportBundleSpec
|
||||
rawSpec, err := supportbundle.LoadSupportBundleSpec(s.Spec.Uri)
|
||||
if err != nil {
|
||||
// In the event a spec can't be loaded, we'll just skip it and print a warning
|
||||
klog.Warningf("unable to load support bundle from URI: %q: %v", s.Spec.Uri, err)
|
||||
continue
|
||||
}
|
||||
remoteRawSpecs = append(remoteRawSpecs, string(rawSpec))
|
||||
if s.Spec.Uri == "" || !util.IsURL(s.Spec.Uri) {
|
||||
moreKinds.SupportBundlesV1Beta2 = append(moreKinds.SupportBundlesV1Beta2, s)
|
||||
continue
|
||||
}
|
||||
|
||||
// We are using LoadSupportBundleSpec function here since it handles prompting
|
||||
// users to accept insecure connections
|
||||
// There is an opportunity to refactor this code in favour of the Loader APIs
|
||||
// TODO: Pass ctx to LoadSupportBundleSpec
|
||||
rawSpec, err := supportbundle.LoadSupportBundleSpec(s.Spec.Uri)
|
||||
if err != nil {
|
||||
// add back original spec
|
||||
moreKinds.SupportBundlesV1Beta2 = append(moreKinds.SupportBundlesV1Beta2, s)
|
||||
// In the event a spec can't be loaded, we'll just skip it and print a warning
|
||||
klog.Warningf("unable to load support bundle from URI: %q: %v", s.Spec.Uri, err)
|
||||
continue
|
||||
}
|
||||
k, err := loader.LoadSpecs(ctx, loader.LoadOptions{RawSpec: string(rawSpec)})
|
||||
if err != nil {
|
||||
// add back original spec
|
||||
moreKinds.SupportBundlesV1Beta2 = append(moreKinds.SupportBundlesV1Beta2, s)
|
||||
klog.Warningf("unable to load spec: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// finally append the uri spec
|
||||
moreKinds.SupportBundlesV1Beta2 = append(moreKinds.SupportBundlesV1Beta2, k.SupportBundlesV1Beta2...)
|
||||
|
||||
}
|
||||
|
||||
if len(remoteRawSpecs) == 0 {
|
||||
return kinds, nil
|
||||
}
|
||||
kinds.SupportBundlesV1Beta2 = moreKinds.SupportBundlesV1Beta2
|
||||
|
||||
return loader.LoadSpecs(ctx, loader.LoadOptions{
|
||||
RawSpecs: remoteRawSpecs,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface) (*troubleshootv1beta2.SupportBundle, *troubleshootv1beta2.Redactor, error) {
|
||||
@@ -270,11 +294,9 @@ func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface)
|
||||
|
||||
// Load additional specs from support bundle URIs
|
||||
if !viper.GetBool("no-uri") {
|
||||
moreKinds, err := loadSupportBundleSpecsFromURIs(ctx, kinds)
|
||||
err := loadSupportBundleSpecsFromURIs(ctx, kinds)
|
||||
if err != nil {
|
||||
klog.Warningf("unable to load support bundles from URIs: %v", err)
|
||||
} else {
|
||||
kinds.Add(moreKinds)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,25 +326,28 @@ func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface)
|
||||
}
|
||||
|
||||
for _, c := range kinds.CollectorsV1Beta2 {
|
||||
mainBundle.Spec.Collectors = append(mainBundle.Spec.Collectors, c.Spec.Collectors...)
|
||||
mainBundle.Spec.Collectors = util.Append(mainBundle.Spec.Collectors, c.Spec.Collectors)
|
||||
}
|
||||
|
||||
for _, hc := range kinds.HostCollectorsV1Beta2 {
|
||||
mainBundle.Spec.HostCollectors = append(mainBundle.Spec.HostCollectors, hc.Spec.Collectors...)
|
||||
mainBundle.Spec.HostCollectors = util.Append(mainBundle.Spec.HostCollectors, hc.Spec.Collectors)
|
||||
}
|
||||
|
||||
// Ensure cluster info and cluster resources collectors are in the merged spec
|
||||
// We need to add them here so when we --dry-run, these collectors are included.
|
||||
// supportbundle.runCollectors duplicates this bit. We'll need to refactor it out later
|
||||
// when its clearer what other code depends on this logic e.g KOTS
|
||||
mainBundle.Spec.Collectors = collect.EnsureCollectorInList(
|
||||
mainBundle.Spec.Collectors,
|
||||
troubleshootv1beta2.Collect{ClusterInfo: &troubleshootv1beta2.ClusterInfo{}},
|
||||
)
|
||||
mainBundle.Spec.Collectors = collect.EnsureCollectorInList(
|
||||
mainBundle.Spec.Collectors,
|
||||
troubleshootv1beta2.Collect{ClusterResources: &troubleshootv1beta2.ClusterResources{}},
|
||||
)
|
||||
if !(len(mainBundle.Spec.HostCollectors) > 0 && len(mainBundle.Spec.Collectors) == 0) {
|
||||
// Always add default collectors unless we only have host collectors
|
||||
// We need to add them here so when we --dry-run, these collectors
|
||||
// are included. supportbundle.runCollectors duplicates this bit.
|
||||
// We'll need to refactor it out later when its clearer what other
|
||||
// code depends on this logic e.g KOTS
|
||||
mainBundle.Spec.Collectors = collect.EnsureCollectorInList(
|
||||
mainBundle.Spec.Collectors,
|
||||
troubleshootv1beta2.Collect{ClusterInfo: &troubleshootv1beta2.ClusterInfo{}},
|
||||
)
|
||||
mainBundle.Spec.Collectors = collect.EnsureCollectorInList(
|
||||
mainBundle.Spec.Collectors,
|
||||
troubleshootv1beta2.Collect{ClusterResources: &troubleshootv1beta2.ClusterResources{}},
|
||||
)
|
||||
}
|
||||
|
||||
additionalRedactors := &troubleshootv1beta2.Redactor{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
@@ -334,7 +359,7 @@ func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface)
|
||||
},
|
||||
}
|
||||
for _, r := range kinds.RedactorsV1Beta2 {
|
||||
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, r.Spec.Redactors...)
|
||||
additionalRedactors.Spec.Redactors = util.Append(additionalRedactors.Spec.Redactors, r.Spec.Redactors)
|
||||
}
|
||||
|
||||
return mainBundle, additionalRedactors, nil
|
||||
|
||||
@@ -2,19 +2,24 @@ package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/internal/testutils"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/httputil"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/loader"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
testclient "k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
var orig = `
|
||||
func templSpec() string {
|
||||
return `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
metadata:
|
||||
@@ -26,6 +31,7 @@ spec:
|
||||
name: kube-root-ca.crt
|
||||
namespace: default
|
||||
`
|
||||
}
|
||||
|
||||
func Test_loadSupportBundleSpecsFromURIs(t *testing.T) {
|
||||
// Run a webserver to serve the spec
|
||||
@@ -41,18 +47,85 @@ spec:
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
orig := strings.ReplaceAll(orig, "$MY_URI", srv.URL)
|
||||
orig := strings.ReplaceAll(templSpec(), "$MY_URI", srv.URL)
|
||||
|
||||
ctx := context.Background()
|
||||
kinds, err := loader.LoadSpecs(ctx, loader.LoadOptions{RawSpec: orig})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, kinds)
|
||||
|
||||
moreKinds, err := loadSupportBundleSpecsFromURIs(ctx, kinds)
|
||||
assert.Len(t, kinds.SupportBundlesV1Beta2, 1)
|
||||
assert.NotNil(t, kinds.SupportBundlesV1Beta2[0].Spec.Collectors[0].ConfigMap)
|
||||
err = loadSupportBundleSpecsFromURIs(ctx, kinds)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, moreKinds.SupportBundlesV1Beta2, 1)
|
||||
assert.NotNil(t, moreKinds.SupportBundlesV1Beta2[0].Spec.Collectors[0].ClusterInfo)
|
||||
require.Len(t, kinds.SupportBundlesV1Beta2, 1)
|
||||
assert.NotNil(t, kinds.SupportBundlesV1Beta2[0].Spec.Collectors[0].ClusterInfo)
|
||||
}
|
||||
|
||||
func Test_loadMultipleSupportBundleSpecsWithNoURIs(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := testclient.NewSimpleClientset()
|
||||
specs := []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
metadata:
|
||||
name: sb-1
|
||||
spec:
|
||||
collectors:
|
||||
- clusterInfo:{}`), testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
metadata:
|
||||
name: sb-2
|
||||
spec:
|
||||
collectors:
|
||||
- clusterInfo: {}`)}
|
||||
|
||||
sb, _, err := loadSpecs(ctx, specs, client)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, sb.Spec.Collectors, 2)
|
||||
}
|
||||
|
||||
func Test_loadMultipleSupportBundleSpecsWithURIs(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := testclient.NewSimpleClientset()
|
||||
|
||||
specFile := testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
metadata:
|
||||
name: sb-file
|
||||
spec:
|
||||
collectors:
|
||||
- logs:
|
||||
name: podlogs/kotsadm
|
||||
selector:
|
||||
- app=kotsadm
|
||||
`)
|
||||
|
||||
// Run a webserver to serve the 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-uri
|
||||
spec:
|
||||
collectors:
|
||||
- clusterInfo: {}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
orig := strings.ReplaceAll(templSpec(), "$MY_URI", srv.URL)
|
||||
specUri := testutils.ServeFromFilePath(t, orig)
|
||||
specs := []string{specFile, specUri}
|
||||
|
||||
sb, _, err := loadSpecs(ctx, specs, client)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, sb.Spec.Collectors[0].Logs)
|
||||
assert.Nil(t, sb.Spec.Collectors[1].ConfigMap) // original spec gone
|
||||
assert.NotNil(t, sb.Spec.Collectors[1].ClusterInfo) // new spec from URI
|
||||
}
|
||||
|
||||
func Test_loadSupportBundleSpecsFromURIs_TimeoutError(t *testing.T) {
|
||||
@@ -63,7 +136,7 @@ func Test_loadSupportBundleSpecsFromURIs_TimeoutError(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
kinds, err := loader.LoadSpecs(ctx, loader.LoadOptions{
|
||||
RawSpec: strings.ReplaceAll(orig, "$MY_URI", srv.URL),
|
||||
RawSpec: strings.ReplaceAll(templSpec(), "$MY_URI", srv.URL),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -76,8 +149,197 @@ func Test_loadSupportBundleSpecsFromURIs_TimeoutError(t *testing.T) {
|
||||
httputil.GetHttpClient().Timeout = before
|
||||
}()
|
||||
|
||||
kindsAfter, err := loadSupportBundleSpecsFromURIs(ctx, kinds)
|
||||
beforeJSON, err := json.Marshal(kinds)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, kinds, kindsAfter)
|
||||
err = loadSupportBundleSpecsFromURIs(ctx, kinds)
|
||||
require.NoError(t, err)
|
||||
|
||||
afterJSON, err := json.Marshal(kinds)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.JSONEq(t, string(beforeJSON), string(afterJSON))
|
||||
}
|
||||
|
||||
func Test_loadSupportBundleSpecs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
expected troubleshootv1beta2.SupportBundleSpec
|
||||
}{
|
||||
{
|
||||
name: "empty collectors array in spec, default collectors added",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
spec:
|
||||
collectors: []
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
Collectors: []*troubleshootv1beta2.Collect{
|
||||
{
|
||||
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
|
||||
},
|
||||
{
|
||||
ClusterResources: &troubleshootv1beta2.ClusterResources{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no collectors defined in spec, default collectors added",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
spec:
|
||||
analyzers: []
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
Collectors: []*troubleshootv1beta2.Collect{
|
||||
{
|
||||
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
|
||||
},
|
||||
{
|
||||
ClusterResources: &troubleshootv1beta2.ClusterResources{},
|
||||
},
|
||||
},
|
||||
Analyzers: []*troubleshootv1beta2.Analyze{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "collectors present but defaults missing, default collectors added",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
spec:
|
||||
collectors:
|
||||
- logs: {}
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
Collectors: []*troubleshootv1beta2.Collect{
|
||||
{
|
||||
Logs: &troubleshootv1beta2.Logs{},
|
||||
},
|
||||
{
|
||||
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
|
||||
},
|
||||
{
|
||||
ClusterResources: &troubleshootv1beta2.ClusterResources{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty support bundle spec adds default collectors",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
Collectors: []*troubleshootv1beta2.Collect{
|
||||
{
|
||||
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
|
||||
},
|
||||
{
|
||||
ClusterResources: &troubleshootv1beta2.ClusterResources{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sb spec with host collectors does not add default collectors",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
spec:
|
||||
hostCollectors:
|
||||
- cpu: {}
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
HostCollectors: []*troubleshootv1beta2.HostCollect{
|
||||
{
|
||||
CPU: &troubleshootv1beta2.CPU{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "host collector spec with collectors does not add default collectors",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostCollector
|
||||
spec:
|
||||
collectors:
|
||||
- cpu: {}
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
HostCollectors: []*troubleshootv1beta2.HostCollect{
|
||||
{
|
||||
CPU: &troubleshootv1beta2.CPU{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sb spec with host and in-cluster collectors adds default collectors",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
spec:
|
||||
hostCollectors:
|
||||
- cpu: {}
|
||||
collectors:
|
||||
- logs: {}
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
HostCollectors: []*troubleshootv1beta2.HostCollect{
|
||||
{
|
||||
CPU: &troubleshootv1beta2.CPU{},
|
||||
},
|
||||
},
|
||||
Collectors: []*troubleshootv1beta2.Collect{
|
||||
{
|
||||
Logs: &troubleshootv1beta2.Logs{},
|
||||
},
|
||||
{
|
||||
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
|
||||
},
|
||||
{
|
||||
ClusterResources: &troubleshootv1beta2.ClusterResources{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sb spec with host collectors and empty in-cluster collectors does not default collectors",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
spec:
|
||||
hostCollectors:
|
||||
- cpu: {}
|
||||
collectors: []
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
HostCollectors: []*troubleshootv1beta2.HostCollect{
|
||||
{
|
||||
CPU: &troubleshootv1beta2.CPU{},
|
||||
},
|
||||
},
|
||||
Collectors: []*troubleshootv1beta2.Collect{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := testclient.NewSimpleClientset()
|
||||
|
||||
sb, _, err := loadSpecs(ctx, test.args, client)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected, sb.Spec)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.2
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: analyzers.troubleshoot.replicated.com
|
||||
spec:
|
||||
group: troubleshoot.replicated.com
|
||||
@@ -21,14 +20,19 @@ spec:
|
||||
description: Analyzer is the Schema for the analyzers API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
|
||||
@@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.2
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: collectors.troubleshoot.replicated.com
|
||||
spec:
|
||||
group: troubleshoot.replicated.com
|
||||
@@ -21,14 +20,19 @@ spec:
|
||||
description: Collector is the Schema for the collectors API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -164,10 +168,10 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
timeout:
|
||||
description: A Duration represents the elapsed time
|
||||
between two instants as an int64 nanosecond count.
|
||||
The representation limits the largest representable
|
||||
duration to approximately 290 years.
|
||||
description: |-
|
||||
A Duration represents the elapsed time between two instants
|
||||
as an int64 nanosecond count. The representation limits the
|
||||
largest representable duration to approximately 290 years.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
@@ -188,10 +192,10 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
timeout:
|
||||
description: A Duration represents the elapsed time
|
||||
between two instants as an int64 nanosecond count.
|
||||
The representation limits the largest representable
|
||||
duration to approximately 290 years.
|
||||
description: |-
|
||||
A Duration represents the elapsed time between two instants
|
||||
as an int64 nanosecond count. The representation limits the
|
||||
largest representable duration to approximately 290 years.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
@@ -210,10 +214,10 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
timeout:
|
||||
description: A Duration represents the elapsed time
|
||||
between two instants as an int64 nanosecond count.
|
||||
The representation limits the largest representable
|
||||
duration to approximately 290 years.
|
||||
description: |-
|
||||
A Duration represents the elapsed time between two instants
|
||||
as an int64 nanosecond count. The representation limits the
|
||||
largest representable duration to approximately 290 years.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
|
||||
@@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.2
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: preflights.troubleshoot.replicated.com
|
||||
spec:
|
||||
group: troubleshoot.replicated.com
|
||||
@@ -21,14 +20,19 @@ spec:
|
||||
description: Preflight is the Schema for the preflights API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -836,10 +840,10 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
timeout:
|
||||
description: A Duration represents the elapsed time
|
||||
between two instants as an int64 nanosecond count.
|
||||
The representation limits the largest representable
|
||||
duration to approximately 290 years.
|
||||
description: |-
|
||||
A Duration represents the elapsed time between two instants
|
||||
as an int64 nanosecond count. The representation limits the
|
||||
largest representable duration to approximately 290 years.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
@@ -860,10 +864,10 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
timeout:
|
||||
description: A Duration represents the elapsed time
|
||||
between two instants as an int64 nanosecond count.
|
||||
The representation limits the largest representable
|
||||
duration to approximately 290 years.
|
||||
description: |-
|
||||
A Duration represents the elapsed time between two instants
|
||||
as an int64 nanosecond count. The representation limits the
|
||||
largest representable duration to approximately 290 years.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
@@ -882,10 +886,10 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
timeout:
|
||||
description: A Duration represents the elapsed time
|
||||
between two instants as an int64 nanosecond count.
|
||||
The representation limits the largest representable
|
||||
duration to approximately 290 years.
|
||||
description: |-
|
||||
A Duration represents the elapsed time between two instants
|
||||
as an int64 nanosecond count. The representation limits the
|
||||
largest representable duration to approximately 290 years.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
|
||||
@@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.2
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: redactors.troubleshoot.replicated.com
|
||||
spec:
|
||||
group: troubleshoot.replicated.com
|
||||
@@ -21,14 +20,19 @@ spec:
|
||||
description: Redactor is the Schema for the redaction API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
|
||||
@@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.2
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: supportbundles.troubleshoot.replicated.com
|
||||
spec:
|
||||
group: troubleshoot.replicated.com
|
||||
@@ -21,14 +20,19 @@ spec:
|
||||
description: SupportBundle is the Schema for the SupportBundles API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -867,10 +871,10 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
timeout:
|
||||
description: A Duration represents the elapsed time
|
||||
between two instants as an int64 nanosecond count.
|
||||
The representation limits the largest representable
|
||||
duration to approximately 290 years.
|
||||
description: |-
|
||||
A Duration represents the elapsed time between two instants
|
||||
as an int64 nanosecond count. The representation limits the
|
||||
largest representable duration to approximately 290 years.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
@@ -891,10 +895,10 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
timeout:
|
||||
description: A Duration represents the elapsed time
|
||||
between two instants as an int64 nanosecond count.
|
||||
The representation limits the largest representable
|
||||
duration to approximately 290 years.
|
||||
description: |-
|
||||
A Duration represents the elapsed time between two instants
|
||||
as an int64 nanosecond count. The representation limits the
|
||||
largest representable duration to approximately 290 years.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
@@ -913,10 +917,10 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
timeout:
|
||||
description: A Duration represents the elapsed time
|
||||
between two instants as an int64 nanosecond count.
|
||||
The representation limits the largest representable
|
||||
duration to approximately 290 years.
|
||||
description: |-
|
||||
A Duration represents the elapsed time between two instants
|
||||
as an int64 nanosecond count. The representation limits the
|
||||
largest representable duration to approximately 290 years.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
|
||||
@@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.2
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: analyzers.troubleshoot.sh
|
||||
spec:
|
||||
group: troubleshoot.sh
|
||||
@@ -21,14 +20,19 @@ spec:
|
||||
description: Analyzer is the Schema for the analyzers API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -559,6 +563,116 @@ spec:
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
event:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
kind:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
reason:
|
||||
type: string
|
||||
regex:
|
||||
type: string
|
||||
strict:
|
||||
type: BoolString
|
||||
required:
|
||||
- collectorName
|
||||
- outcomes
|
||||
- reason
|
||||
type: object
|
||||
goldpinger:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
filePath:
|
||||
type: string
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
strict:
|
||||
type: BoolString
|
||||
required:
|
||||
- collectorName
|
||||
type: object
|
||||
imagePullSecret:
|
||||
properties:
|
||||
annotations:
|
||||
@@ -931,6 +1045,66 @@ spec:
|
||||
- collectorName
|
||||
- outcomes
|
||||
type: object
|
||||
nodeMetrics:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
filters:
|
||||
properties:
|
||||
pvc:
|
||||
properties:
|
||||
nameRegex:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
strict:
|
||||
type: BoolString
|
||||
required:
|
||||
- collectorName
|
||||
- outcomes
|
||||
type: object
|
||||
nodeResources:
|
||||
properties:
|
||||
annotations:
|
||||
|
||||
+11763
-3954
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.2
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: hostcollectors.troubleshoot.sh
|
||||
spec:
|
||||
group: troubleshoot.sh
|
||||
@@ -21,14 +20,19 @@ spec:
|
||||
description: HostCollector is the Schema for the collectors API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -1155,9 +1159,9 @@ spec:
|
||||
- path
|
||||
type: object
|
||||
filesystemPerformance:
|
||||
description: FilesystemPerformance benchmarks sequential write
|
||||
latency on a single file. The optional background IOPS feature
|
||||
attempts to mimic real-world conditions by running read and
|
||||
description: |-
|
||||
FilesystemPerformance benchmarks sequential write latency on a single file.
|
||||
The optional background IOPS feature attempts to mimic real-world conditions by running read and
|
||||
write workloads prior to and during benchmark execution.
|
||||
properties:
|
||||
backgroundIOPSWarmupSeconds:
|
||||
@@ -1165,34 +1169,33 @@ spec:
|
||||
write workloads prior to starting the benchmarks.
|
||||
type: integer
|
||||
backgroundReadIOPS:
|
||||
description: The target read IOPS to run while benchmarking.
|
||||
This is a limit and there is no guarantee it will be reached.
|
||||
This is the total IOPS for all background read jobs.
|
||||
description: |-
|
||||
The target read IOPS to run while benchmarking. This is a limit and there is no guarantee
|
||||
it will be reached. This is the total IOPS for all background read jobs.
|
||||
type: integer
|
||||
backgroundReadIOPSJobs:
|
||||
description: Number of threads to use for background read
|
||||
IOPS. This should be set high enough to reach the target
|
||||
specified in BackgrounReadIOPS.
|
||||
description: |-
|
||||
Number of threads to use for background read IOPS. This should be set high enough to reach
|
||||
the target specified in BackgrounReadIOPS.
|
||||
type: integer
|
||||
backgroundWriteIOPS:
|
||||
description: The target write IOPS to run while benchmarking.
|
||||
This is a limit and there is no guarantee it will be reached.
|
||||
This is the total IOPS for all background write jobs.
|
||||
description: |-
|
||||
The target write IOPS to run while benchmarking. This is a limit and there is no guarantee
|
||||
it will be reached. This is the total IOPS for all background write jobs.
|
||||
type: integer
|
||||
backgroundWriteIOPSJobs:
|
||||
description: 'Number of threads to use for background write
|
||||
IOPS. This should be set high enough to reach the target
|
||||
specified in BackgroundWriteIOPS. Example: If BackgroundWriteIOPS
|
||||
is 100 and write latency is 10ms then a single job would
|
||||
barely be able to reach 100 IOPS so this should be at
|
||||
least 2.'
|
||||
description: |-
|
||||
Number of threads to use for background write IOPS. This should be set high enough to reach
|
||||
the target specified in BackgroundWriteIOPS.
|
||||
Example: If BackgroundWriteIOPS is 100 and write latency is 10ms then a single job would
|
||||
barely be able to reach 100 IOPS so this should be at least 2.
|
||||
type: integer
|
||||
collectorName:
|
||||
type: string
|
||||
datasync:
|
||||
description: Whether to call datasync on the file after
|
||||
each write. Skipped if Sync is also true. Does not apply
|
||||
to background IOPS task.
|
||||
description: |-
|
||||
Whether to call datasync on the file after each write. Skipped if Sync is also true. Does not
|
||||
apply to background IOPS task.
|
||||
type: boolean
|
||||
directory:
|
||||
description: The directory where the benchmark will create
|
||||
@@ -1204,16 +1207,14 @@ spec:
|
||||
exclude:
|
||||
type: BoolString
|
||||
fileSize:
|
||||
description: The size of the file used in the benchmark.
|
||||
The number of IO operations for the benchmark will be
|
||||
FileSize / OperationSizeBytes. Accepts valid Kubernetes
|
||||
resource units such as Mi.
|
||||
description: |-
|
||||
The size of the file used in the benchmark. The number of IO operations for the benchmark
|
||||
will be FileSize / OperationSizeBytes. Accepts valid Kubernetes resource units such as Mi.
|
||||
type: string
|
||||
operationSize:
|
||||
description: The size of each write operation performed
|
||||
while benchmarking. This does not apply to the background
|
||||
IOPS feature if enabled, since those must be fixed at
|
||||
4096.
|
||||
description: |-
|
||||
The size of each write operation performed while benchmarking. This does not apply to the
|
||||
background IOPS feature if enabled, since those must be fixed at 4096.
|
||||
format: int64
|
||||
type: integer
|
||||
sync:
|
||||
@@ -1261,9 +1262,9 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
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.
|
||||
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
|
||||
url:
|
||||
type: string
|
||||
@@ -1281,9 +1282,9 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
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.
|
||||
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
|
||||
url:
|
||||
type: string
|
||||
@@ -1301,9 +1302,9 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
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.
|
||||
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
|
||||
url:
|
||||
type: string
|
||||
@@ -1386,6 +1387,8 @@ spec:
|
||||
type: object
|
||||
outputDir:
|
||||
type: string
|
||||
timeout:
|
||||
type: string
|
||||
required:
|
||||
- args
|
||||
- command
|
||||
|
||||
@@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.2
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: hostpreflights.troubleshoot.sh
|
||||
spec:
|
||||
group: troubleshoot.sh
|
||||
@@ -21,14 +20,19 @@ spec:
|
||||
description: HostPreflight is the Schema for the hostpreflights API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -1155,9 +1159,9 @@ spec:
|
||||
- path
|
||||
type: object
|
||||
filesystemPerformance:
|
||||
description: FilesystemPerformance benchmarks sequential write
|
||||
latency on a single file. The optional background IOPS feature
|
||||
attempts to mimic real-world conditions by running read and
|
||||
description: |-
|
||||
FilesystemPerformance benchmarks sequential write latency on a single file.
|
||||
The optional background IOPS feature attempts to mimic real-world conditions by running read and
|
||||
write workloads prior to and during benchmark execution.
|
||||
properties:
|
||||
backgroundIOPSWarmupSeconds:
|
||||
@@ -1165,34 +1169,33 @@ spec:
|
||||
write workloads prior to starting the benchmarks.
|
||||
type: integer
|
||||
backgroundReadIOPS:
|
||||
description: The target read IOPS to run while benchmarking.
|
||||
This is a limit and there is no guarantee it will be reached.
|
||||
This is the total IOPS for all background read jobs.
|
||||
description: |-
|
||||
The target read IOPS to run while benchmarking. This is a limit and there is no guarantee
|
||||
it will be reached. This is the total IOPS for all background read jobs.
|
||||
type: integer
|
||||
backgroundReadIOPSJobs:
|
||||
description: Number of threads to use for background read
|
||||
IOPS. This should be set high enough to reach the target
|
||||
specified in BackgrounReadIOPS.
|
||||
description: |-
|
||||
Number of threads to use for background read IOPS. This should be set high enough to reach
|
||||
the target specified in BackgrounReadIOPS.
|
||||
type: integer
|
||||
backgroundWriteIOPS:
|
||||
description: The target write IOPS to run while benchmarking.
|
||||
This is a limit and there is no guarantee it will be reached.
|
||||
This is the total IOPS for all background write jobs.
|
||||
description: |-
|
||||
The target write IOPS to run while benchmarking. This is a limit and there is no guarantee
|
||||
it will be reached. This is the total IOPS for all background write jobs.
|
||||
type: integer
|
||||
backgroundWriteIOPSJobs:
|
||||
description: 'Number of threads to use for background write
|
||||
IOPS. This should be set high enough to reach the target
|
||||
specified in BackgroundWriteIOPS. Example: If BackgroundWriteIOPS
|
||||
is 100 and write latency is 10ms then a single job would
|
||||
barely be able to reach 100 IOPS so this should be at
|
||||
least 2.'
|
||||
description: |-
|
||||
Number of threads to use for background write IOPS. This should be set high enough to reach
|
||||
the target specified in BackgroundWriteIOPS.
|
||||
Example: If BackgroundWriteIOPS is 100 and write latency is 10ms then a single job would
|
||||
barely be able to reach 100 IOPS so this should be at least 2.
|
||||
type: integer
|
||||
collectorName:
|
||||
type: string
|
||||
datasync:
|
||||
description: Whether to call datasync on the file after
|
||||
each write. Skipped if Sync is also true. Does not apply
|
||||
to background IOPS task.
|
||||
description: |-
|
||||
Whether to call datasync on the file after each write. Skipped if Sync is also true. Does not
|
||||
apply to background IOPS task.
|
||||
type: boolean
|
||||
directory:
|
||||
description: The directory where the benchmark will create
|
||||
@@ -1204,16 +1207,14 @@ spec:
|
||||
exclude:
|
||||
type: BoolString
|
||||
fileSize:
|
||||
description: The size of the file used in the benchmark.
|
||||
The number of IO operations for the benchmark will be
|
||||
FileSize / OperationSizeBytes. Accepts valid Kubernetes
|
||||
resource units such as Mi.
|
||||
description: |-
|
||||
The size of the file used in the benchmark. The number of IO operations for the benchmark
|
||||
will be FileSize / OperationSizeBytes. Accepts valid Kubernetes resource units such as Mi.
|
||||
type: string
|
||||
operationSize:
|
||||
description: The size of each write operation performed
|
||||
while benchmarking. This does not apply to the background
|
||||
IOPS feature if enabled, since those must be fixed at
|
||||
4096.
|
||||
description: |-
|
||||
The size of each write operation performed while benchmarking. This does not apply to the
|
||||
background IOPS feature if enabled, since those must be fixed at 4096.
|
||||
format: int64
|
||||
type: integer
|
||||
sync:
|
||||
@@ -1261,9 +1262,9 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
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.
|
||||
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
|
||||
url:
|
||||
type: string
|
||||
@@ -1281,9 +1282,9 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
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.
|
||||
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
|
||||
url:
|
||||
type: string
|
||||
@@ -1301,9 +1302,9 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
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.
|
||||
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
|
||||
url:
|
||||
type: string
|
||||
@@ -1386,6 +1387,8 @@ spec:
|
||||
type: object
|
||||
outputDir:
|
||||
type: string
|
||||
timeout:
|
||||
type: string
|
||||
required:
|
||||
- args
|
||||
- command
|
||||
@@ -1602,44 +1605,43 @@ spec:
|
||||
- path
|
||||
type: object
|
||||
filesystemPerformance:
|
||||
description: RemoteFilesystemPerformance benchmarks sequential
|
||||
write latency on a single file. The optional background IOPS
|
||||
feature attempts to mimic real-world conditions by running
|
||||
read and write workloads prior to and during benchmark execution.
|
||||
description: |-
|
||||
RemoteFilesystemPerformance benchmarks sequential write latency on a single file.
|
||||
The optional background IOPS feature attempts to mimic real-world conditions by running read and
|
||||
write workloads prior to and during benchmark execution.
|
||||
properties:
|
||||
backgroundIOPSWarmupSeconds:
|
||||
description: How long to run the background IOPS read and
|
||||
write workloads prior to starting the benchmarks.
|
||||
type: integer
|
||||
backgroundReadIOPS:
|
||||
description: The target read IOPS to run while benchmarking.
|
||||
This is a limit and there is no guarantee it will be reached.
|
||||
This is the total IOPS for all background read jobs.
|
||||
description: |-
|
||||
The target read IOPS to run while benchmarking. This is a limit and there is no guarantee
|
||||
it will be reached. This is the total IOPS for all background read jobs.
|
||||
type: integer
|
||||
backgroundReadIOPSJobs:
|
||||
description: Number of threads to use for background read
|
||||
IOPS. This should be set high enough to reach the target
|
||||
specified in BackgrounReadIOPS.
|
||||
description: |-
|
||||
Number of threads to use for background read IOPS. This should be set high enough to reach
|
||||
the target specified in BackgrounReadIOPS.
|
||||
type: integer
|
||||
backgroundWriteIOPS:
|
||||
description: The target write IOPS to run while benchmarking.
|
||||
This is a limit and there is no guarantee it will be reached.
|
||||
This is the total IOPS for all background write jobs.
|
||||
description: |-
|
||||
The target write IOPS to run while benchmarking. This is a limit and there is no guarantee
|
||||
it will be reached. This is the total IOPS for all background write jobs.
|
||||
type: integer
|
||||
backgroundWriteIOPSJobs:
|
||||
description: 'Number of threads to use for background write
|
||||
IOPS. This should be set high enough to reach the target
|
||||
specified in BackgroundWriteIOPS. Example: If BackgroundWriteIOPS
|
||||
is 100 and write latency is 10ms then a single job would
|
||||
barely be able to reach 100 IOPS so this should be at
|
||||
least 2.'
|
||||
description: |-
|
||||
Number of threads to use for background write IOPS. This should be set high enough to reach
|
||||
the target specified in BackgroundWriteIOPS.
|
||||
Example: If BackgroundWriteIOPS is 100 and write latency is 10ms then a single job would
|
||||
barely be able to reach 100 IOPS so this should be at least 2.
|
||||
type: integer
|
||||
collectorName:
|
||||
type: string
|
||||
datasync:
|
||||
description: Whether to call datasync on the file after
|
||||
each write. Skipped if Sync is also true. Does not apply
|
||||
to background IOPS task.
|
||||
description: |-
|
||||
Whether to call datasync on the file after each write. Skipped if Sync is also true. Does not
|
||||
apply to background IOPS task.
|
||||
type: boolean
|
||||
directory:
|
||||
description: The directory where the benchmark will create
|
||||
@@ -1651,16 +1653,14 @@ spec:
|
||||
exclude:
|
||||
type: BoolString
|
||||
fileSize:
|
||||
description: The size of the file used in the benchmark.
|
||||
The number of IO operations for the benchmark will be
|
||||
FileSize / OperationSizeBytes. Accepts valid Kubernetes
|
||||
resource units such as Mi.
|
||||
description: |-
|
||||
The size of the file used in the benchmark. The number of IO operations for the benchmark
|
||||
will be FileSize / OperationSizeBytes. Accepts valid Kubernetes resource units such as Mi.
|
||||
type: string
|
||||
operationSize:
|
||||
description: The size of each write operation performed
|
||||
while benchmarking. This does not apply to the background
|
||||
IOPS feature if enabled, since those must be fixed at
|
||||
4096.
|
||||
description: |-
|
||||
The size of each write operation performed while benchmarking. This does not apply to the
|
||||
background IOPS feature if enabled, since those must be fixed at 4096.
|
||||
format: int64
|
||||
type: integer
|
||||
sync:
|
||||
@@ -1701,9 +1701,9 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
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.
|
||||
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
|
||||
url:
|
||||
type: string
|
||||
@@ -1721,9 +1721,9 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
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.
|
||||
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
|
||||
url:
|
||||
type: string
|
||||
@@ -1741,9 +1741,9 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
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.
|
||||
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
|
||||
url:
|
||||
type: string
|
||||
|
||||
+11969
-3993
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.2
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: redactors.troubleshoot.sh
|
||||
spec:
|
||||
group: troubleshoot.sh
|
||||
@@ -21,14 +20,19 @@ spec:
|
||||
description: Redactor is the Schema for the redaction API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
|
||||
@@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.2
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: remotecollectors.troubleshoot.sh
|
||||
spec:
|
||||
group: troubleshoot.sh
|
||||
@@ -21,14 +20,19 @@ spec:
|
||||
description: RemoteCollector is the Schema for the remote collectors API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -109,44 +113,43 @@ spec:
|
||||
- path
|
||||
type: object
|
||||
filesystemPerformance:
|
||||
description: RemoteFilesystemPerformance benchmarks sequential
|
||||
write latency on a single file. The optional background IOPS
|
||||
feature attempts to mimic real-world conditions by running
|
||||
read and write workloads prior to and during benchmark execution.
|
||||
description: |-
|
||||
RemoteFilesystemPerformance benchmarks sequential write latency on a single file.
|
||||
The optional background IOPS feature attempts to mimic real-world conditions by running read and
|
||||
write workloads prior to and during benchmark execution.
|
||||
properties:
|
||||
backgroundIOPSWarmupSeconds:
|
||||
description: How long to run the background IOPS read and
|
||||
write workloads prior to starting the benchmarks.
|
||||
type: integer
|
||||
backgroundReadIOPS:
|
||||
description: The target read IOPS to run while benchmarking.
|
||||
This is a limit and there is no guarantee it will be reached.
|
||||
This is the total IOPS for all background read jobs.
|
||||
description: |-
|
||||
The target read IOPS to run while benchmarking. This is a limit and there is no guarantee
|
||||
it will be reached. This is the total IOPS for all background read jobs.
|
||||
type: integer
|
||||
backgroundReadIOPSJobs:
|
||||
description: Number of threads to use for background read
|
||||
IOPS. This should be set high enough to reach the target
|
||||
specified in BackgrounReadIOPS.
|
||||
description: |-
|
||||
Number of threads to use for background read IOPS. This should be set high enough to reach
|
||||
the target specified in BackgrounReadIOPS.
|
||||
type: integer
|
||||
backgroundWriteIOPS:
|
||||
description: The target write IOPS to run while benchmarking.
|
||||
This is a limit and there is no guarantee it will be reached.
|
||||
This is the total IOPS for all background write jobs.
|
||||
description: |-
|
||||
The target write IOPS to run while benchmarking. This is a limit and there is no guarantee
|
||||
it will be reached. This is the total IOPS for all background write jobs.
|
||||
type: integer
|
||||
backgroundWriteIOPSJobs:
|
||||
description: 'Number of threads to use for background write
|
||||
IOPS. This should be set high enough to reach the target
|
||||
specified in BackgroundWriteIOPS. Example: If BackgroundWriteIOPS
|
||||
is 100 and write latency is 10ms then a single job would
|
||||
barely be able to reach 100 IOPS so this should be at
|
||||
least 2.'
|
||||
description: |-
|
||||
Number of threads to use for background write IOPS. This should be set high enough to reach
|
||||
the target specified in BackgroundWriteIOPS.
|
||||
Example: If BackgroundWriteIOPS is 100 and write latency is 10ms then a single job would
|
||||
barely be able to reach 100 IOPS so this should be at least 2.
|
||||
type: integer
|
||||
collectorName:
|
||||
type: string
|
||||
datasync:
|
||||
description: Whether to call datasync on the file after
|
||||
each write. Skipped if Sync is also true. Does not apply
|
||||
to background IOPS task.
|
||||
description: |-
|
||||
Whether to call datasync on the file after each write. Skipped if Sync is also true. Does not
|
||||
apply to background IOPS task.
|
||||
type: boolean
|
||||
directory:
|
||||
description: The directory where the benchmark will create
|
||||
@@ -158,16 +161,14 @@ spec:
|
||||
exclude:
|
||||
type: BoolString
|
||||
fileSize:
|
||||
description: The size of the file used in the benchmark.
|
||||
The number of IO operations for the benchmark will be
|
||||
FileSize / OperationSizeBytes. Accepts valid Kubernetes
|
||||
resource units such as Mi.
|
||||
description: |-
|
||||
The size of the file used in the benchmark. The number of IO operations for the benchmark
|
||||
will be FileSize / OperationSizeBytes. Accepts valid Kubernetes resource units such as Mi.
|
||||
type: string
|
||||
operationSize:
|
||||
description: The size of each write operation performed
|
||||
while benchmarking. This does not apply to the background
|
||||
IOPS feature if enabled, since those must be fixed at
|
||||
4096.
|
||||
description: |-
|
||||
The size of each write operation performed while benchmarking. This does not apply to the
|
||||
background IOPS feature if enabled, since those must be fixed at 4096.
|
||||
format: int64
|
||||
type: integer
|
||||
sync:
|
||||
@@ -208,9 +209,9 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
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.
|
||||
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
|
||||
url:
|
||||
type: string
|
||||
@@ -228,9 +229,9 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
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.
|
||||
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
|
||||
url:
|
||||
type: string
|
||||
@@ -248,9 +249,9 @@ spec:
|
||||
insecureSkipVerify:
|
||||
type: boolean
|
||||
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.
|
||||
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
|
||||
url:
|
||||
type: string
|
||||
|
||||
+11970
-3992
File diff suppressed because it is too large
Load Diff
@@ -42,7 +42,7 @@ An initially unintended benefit of using the Aggregation Layer is that any HostC
|
||||
* [microk8s implementation](https://github.com/canonical/microk8s/blob/master/build-scripts/patches/0000-Kubelite-integration.patch) - bundles slightly modified binaries
|
||||
* [k0s uses upstream binaries statically compiled](https://docs.k0sproject.io/v1.23.8+k0s.0/architecture/) - bundles statically compiled binaries that self extract and uses a process monitor to run them
|
||||
|
||||
2. Can you in fact push metadat like "Status" into an api-server or do we have to write directly to etcd?
|
||||
2. Can you in fact push metadata like "Status" into an api-server or do we have to write directly to etcd?
|
||||
|
||||
* If we can't push to the api-server is just writing the information directly into etcd something we can do and have a reasonable expectation of compatibility?
|
||||
|
||||
|
||||
+1
-1
@@ -54,4 +54,4 @@ preflight [url] [flags]
|
||||
* [preflight oci-fetch](preflight_oci-fetch.md) - Fetch a preflight from an OCI registry and print it to standard out
|
||||
* [preflight version](preflight_version.md) - Print the current version and exit
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Aug-2023
|
||||
###### Auto generated by spf13/cobra on 25-Jan-2024
|
||||
|
||||
@@ -9,7 +9,8 @@ preflight oci-fetch [URI] [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for oci-fetch
|
||||
-h, --help help for oci-fetch
|
||||
-v, --v Level number for the log level verbosity
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -33,4 +34,4 @@ preflight oci-fetch [URI] [flags]
|
||||
|
||||
* [preflight](preflight.md) - Run and retrieve preflight checks in a cluster
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Aug-2023
|
||||
###### Auto generated by spf13/cobra on 25-Jan-2024
|
||||
|
||||
@@ -37,4 +37,4 @@ preflight version [flags]
|
||||
|
||||
* [preflight](preflight.md) - Run and retrieve preflight checks in a cluster
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Aug-2023
|
||||
###### Auto generated by spf13/cobra on 25-Jan-2024
|
||||
|
||||
@@ -27,6 +27,7 @@ support-bundle [urls...] [flags]
|
||||
--cpuprofile string File path to write cpu profiling data
|
||||
--debug enable debug logging. This is equivalent to --v=0
|
||||
--disable-compression If true, opt-out of response compression for all requests to the server
|
||||
--dry-run print support bundle spec without collecting anything
|
||||
-h, --help help for support-bundle
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--interactive enable/disable interactive mode (default true)
|
||||
@@ -34,7 +35,7 @@ support-bundle [urls...] [flags]
|
||||
--load-cluster-specs enable/disable loading additional troubleshoot specs found within the cluster. required when no specs are provided on the command line
|
||||
--memprofile string File path to write memory profiling data
|
||||
-n, --namespace string If present, the namespace scope for this CLI request
|
||||
--no-uri When this flag is used, Troubleshoot does not attempt to retrieve the bundle referenced by the uri: field in the spec.`
|
||||
--no-uri When this flag is used, Troubleshoot does not attempt to retrieve the spec referenced by the uri: field`
|
||||
-o, --output string specify the output file path for the support bundle
|
||||
--redact enable/disable default redactions (default true)
|
||||
--redactors strings names of the additional redactors to use
|
||||
@@ -55,4 +56,4 @@ support-bundle [urls...] [flags]
|
||||
* [support-bundle redact](support-bundle_redact.md) - Redact information from a generated support bundle archive
|
||||
* [support-bundle version](support-bundle_version.md) - Print the current version and exit
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Aug-2023
|
||||
###### Auto generated by spf13/cobra on 25-Jan-2024
|
||||
|
||||
@@ -30,4 +30,4 @@ support-bundle analyze [url] [flags]
|
||||
|
||||
* [support-bundle](support-bundle.md) - Generate a support bundle
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Aug-2023
|
||||
###### Auto generated by spf13/cobra on 25-Jan-2024
|
||||
|
||||
@@ -39,4 +39,4 @@ support-bundle redact [urls...] [flags]
|
||||
|
||||
* [support-bundle](support-bundle.md) - Generate a support bundle
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Aug-2023
|
||||
###### Auto generated by spf13/cobra on 25-Jan-2024
|
||||
|
||||
@@ -27,4 +27,4 @@ support-bundle version [flags]
|
||||
|
||||
* [support-bundle](support-bundle.md) - Generate a support bundle
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Aug-2023
|
||||
###### Auto generated by spf13/cobra on 25-Jan-2024
|
||||
|
||||
@@ -75,6 +75,9 @@ spec:
|
||||
- pass:
|
||||
when: "== oke"
|
||||
message: OKE is a supported distribution
|
||||
- pass:
|
||||
when: "== kind"
|
||||
message: Kind is a supported distribution
|
||||
- warn:
|
||||
message: Unable to determine the distribution of Kubernetes
|
||||
- nodeResources:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module helm-template
|
||||
|
||||
go 1.20
|
||||
go 1.22.0
|
||||
|
||||
// Always use the local version of troubleshoot so as to build using
|
||||
// the latest version of the library. This will ensure the example
|
||||
@@ -9,29 +9,28 @@ replace github.com/replicatedhq/troubleshoot v0.0.0 => ../../../
|
||||
|
||||
require (
|
||||
github.com/replicatedhq/troubleshoot v0.0.0
|
||||
helm.sh/helm/v3 v3.12.3
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
helm.sh/helm/v3 v3.14.4
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
|
||||
github.com/go-logr/logr v1.2.4 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-openapi/swag v0.22.10 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.3.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
@@ -44,30 +43,30 @@ require (
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.12.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/term v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
golang.org/x/crypto v0.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
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.28.2 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.28.1 // indirect
|
||||
k8s.io/apimachinery v0.28.2 // indirect
|
||||
k8s.io/client-go v0.28.2 // indirect
|
||||
k8s.io/klog/v2 v2.100.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.16.2 // indirect
|
||||
k8s.io/api v0.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.2.3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
@@ -14,39 +14,45 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE=
|
||||
github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI=
|
||||
github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.10 h1:4y86NVn7Z2yYd6pfS4Z+Nyh3aAUL3Nul+LMbhFKy0gA=
|
||||
github.com/go-openapi/swag v0.22.10/go.mod h1:Cnn8BYtRlx6BNE3DPN86f/xkapGIcLWzh3CLEb4C1jI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
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 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
|
||||
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
@@ -61,6 +67,7 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -80,21 +87,25 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
|
||||
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
|
||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||
github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY=
|
||||
github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw=
|
||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@@ -104,7 +115,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -120,23 +132,22 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
|
||||
golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
|
||||
golang.org/x/net v0.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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -149,38 +160,39 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.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.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
|
||||
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -194,27 +206,32 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
helm.sh/helm/v3 v3.12.3 h1:5y1+Sbty12t48T/t/CGNYUIME5BJ0WKfmW/sobYqkFg=
|
||||
helm.sh/helm/v3 v3.12.3/go.mod h1:KPKQiX9IP5HX7o5YnnhViMnNuKiL/lJBVQ47GHe1R0k=
|
||||
k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw=
|
||||
k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg=
|
||||
k8s.io/apiextensions-apiserver v0.28.1 h1:l2ThkBRjrWpw4f24uq0Da2HaEgqJZ7pcgiEUTKSmQZw=
|
||||
k8s.io/apiextensions-apiserver v0.28.1/go.mod h1:sVvrI+P4vxh2YBBcm8n2ThjNyzU4BQGilCQ/JAY5kGs=
|
||||
k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ=
|
||||
k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU=
|
||||
k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY=
|
||||
k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY=
|
||||
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
|
||||
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ=
|
||||
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.16.2 h1:mwXAVuEk3EQf478PQwQ48zGOXvW27UJc8NHktQVuIPU=
|
||||
sigs.k8s.io/controller-runtime v0.16.2/go.mod h1:vpMu3LpI5sYWtujJOa2uPK61nB5rbwlN7BAB8aSLvGU=
|
||||
helm.sh/helm/v3 v3.14.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.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
||||
@@ -1,62 +1,64 @@
|
||||
module github.com/replicatedhq/troubleshoot
|
||||
|
||||
go 1.20
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412
|
||||
github.com/apparentlymart/go-cidr v1.1.0
|
||||
github.com/blang/semver/v4 v4.0.0
|
||||
github.com/containers/image/v5 v5.25.0
|
||||
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/go-logr/logr v1.2.4
|
||||
github.com/containers/image/v5 v5.30.0
|
||||
github.com/distribution/distribution/v3 v3.0.0-alpha.1
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/go-logr/logr v1.4.1
|
||||
github.com/go-redis/redis/v7 v7.4.1
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/godbus/dbus/v5 v5.1.0
|
||||
github.com/google/gofuzz v1.2.0
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
github.com/hashicorp/go-getter v1.7.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/handlers v1.5.2
|
||||
github.com/hashicorp/go-getter v1.7.4
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/jackc/pgx/v5 v5.4.3
|
||||
github.com/jackc/pgx/v5 v5.5.5
|
||||
github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e
|
||||
github.com/manifoldco/promptui v0.9.0
|
||||
github.com/mattn/go-isatty v0.0.19
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microsoft/go-mssqldb v1.6.0
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b
|
||||
github.com/microsoft/go-mssqldb v1.7.1
|
||||
github.com/opencontainers/image-spec v1.1.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/replicatedhq/termui/v3 v3.1.1-0.20200811145416-f40076d26851
|
||||
github.com/segmentio/ksuid v1.0.4
|
||||
github.com/shirou/gopsutil/v3 v3.23.9
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/shirou/gopsutil/v3 v3.24.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.17.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/spf13/viper v1.18.2
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tj/go-spin v1.1.0
|
||||
github.com/vmware-tanzu/velero v1.12.0
|
||||
go.opentelemetry.io/otel v1.19.0
|
||||
go.opentelemetry.io/otel/sdk v1.19.0
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
|
||||
golang.org/x/mod v0.12.0
|
||||
golang.org/x/sync v0.4.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
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/api v0.28.2
|
||||
k8s.io/apiextensions-apiserver v0.28.1
|
||||
k8s.io/apimachinery v0.28.2
|
||||
k8s.io/apiserver v0.28.1
|
||||
k8s.io/cli-runtime v0.28.2
|
||||
k8s.io/client-go v0.28.2
|
||||
k8s.io/klog/v2 v2.100.1
|
||||
oras.land/oras-go v1.2.3
|
||||
sigs.k8s.io/controller-runtime v0.16.2
|
||||
k8s.io/api v0.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.0
|
||||
sigs.k8s.io/e2e-framework v0.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/MakeNowJust/heredoc v1.0.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
@@ -64,18 +66,24 @@ require (
|
||||
github.com/Masterminds/squirrel v1.5.4 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/chai2010/gettext-go v1.0.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/docker/distribution v2.8.2+incompatible // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.0.2 // indirect
|
||||
github.com/containerd/errdefs v0.1.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/distribution/reference v0.5.0 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
||||
github.com/go-gorp/gorp/v3 v3.0.5 // indirect
|
||||
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-containerregistry v0.19.0 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/gosuri/uitable v0.0.4 // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
@@ -85,83 +93,86 @@ require (
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.0 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/sys/user v0.1.0 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/rubenv/sql-migrate v1.3.1 // indirect
|
||||
github.com/rubenv/sql-migrate v1.5.2 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/sylabs/sif/v2 v2.11.1 // indirect
|
||||
github.com/sylabs/sif/v2 v2.15.1 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
||||
github.com/vladimirvivien/gexe v0.2.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.19.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.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
|
||||
k8s.io/component-base v0.28.1 // indirect
|
||||
k8s.io/kubectl v0.28.1 // indirect
|
||||
golang.org/x/tools v0.18.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
k8s.io/component-base v0.30.0 // indirect
|
||||
k8s.io/kubectl v0.29.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.110.7 // indirect
|
||||
cloud.google.com/go/compute v1.23.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.1 // indirect
|
||||
cloud.google.com/go/storage v1.30.1 // indirect
|
||||
cloud.google.com/go v0.110.10 // indirect
|
||||
cloud.google.com/go/compute v1.23.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.5 // indirect
|
||||
cloud.google.com/go/storage v1.35.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/Microsoft/hcsshim v0.10.0-rc.7 // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/Microsoft/hcsshim v0.12.0-rc.3 // indirect
|
||||
github.com/andybalholm/brotli v1.0.1 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.253 // indirect
|
||||
github.com/aws/aws-sdk-go v1.48.10 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
github.com/containerd/containerd v1.7.0 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
||||
github.com/containerd/containerd v1.7.13 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
|
||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
|
||||
github.com/containers/ocicrypt v1.1.7 // indirect
|
||||
github.com/containers/storage v1.46.0 // indirect
|
||||
github.com/containers/ocicrypt v1.1.9 // indirect
|
||||
github.com/containers/storage v1.53.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/docker/cli v23.0.1+incompatible // indirect
|
||||
github.com/docker/docker v23.0.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.7.0 // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/cli v25.0.3+incompatible // indirect
|
||||
github.com/docker/docker v25.0.5+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.1 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-openapi/swag v0.22.10 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-intervals v0.0.2 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
@@ -173,83 +184,81 @@ require (
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8 // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/moby/sys/mountinfo v0.6.2 // indirect
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
|
||||
github.com/moby/sys/mountinfo v0.7.1 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect
|
||||
github.com/nwaples/rardecode v1.1.2 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/runc v1.1.5 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.2.0 // indirect
|
||||
github.com/opencontainers/selinux v1.11.0 // indirect
|
||||
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.16.0 // indirect
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.10.1 // indirect
|
||||
github.com/prometheus/client_golang v1.18.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.45.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/spf13/afero v1.10.0 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/vbatts/tar-split v0.11.3 // indirect
|
||||
github.com/vbatts/tar-split v0.11.5 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xlab/treeprint v1.2.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/oauth2 v0.12.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/term v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/net v0.24.0
|
||||
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
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.143.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect
|
||||
google.golang.org/grpc v1.58.3 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
google.golang.org/api v0.153.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/grpc v1.59.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
helm.sh/helm/v3 v3.12.3
|
||||
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
|
||||
k8s.io/metrics v0.28.2
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2
|
||||
helm.sh/helm/v3 v3.14.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
|
||||
periph.io/x/host/v3 v3.8.2
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
replace (
|
||||
|
||||
+18
-14
@@ -167,25 +167,29 @@ func LoadFromCLIArgs(ctx context.Context, client kubernetes.Interface, args []st
|
||||
rawSpecs = append(rawSpecs, content...)
|
||||
} else {
|
||||
if !util.IsURL(v) {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, fmt.Errorf("%s is not a URL and was not found (err %s)", v, err))
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, fmt.Errorf("%s is not a URL and was not found", v))
|
||||
}
|
||||
|
||||
// Download preflight specs
|
||||
rawSpec, err := downloadFromHttpURL(ctx, v, map[string]string{"User-Agent": "Replicated_Preflight/v1beta2"})
|
||||
parsedURL, err := url.ParseRequestURI(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
|
||||
}
|
||||
rawSpecs = append(rawSpecs, rawSpec)
|
||||
|
||||
// Download support bundle specs
|
||||
rawSpec, err = downloadFromHttpURL(ctx, v, map[string]string{
|
||||
"User-Agent": "Replicated_Troubleshoot/v1beta1",
|
||||
"Bundle-Upload-Host": fmt.Sprintf("%s://%s", u.Scheme, u.Host),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if parsedURL.Host == "kots.io" {
|
||||
// To download specs from kots.io, we need to set the User-Agent header
|
||||
rawSpec, err := downloadFromHttpURL(ctx, v, map[string]string{
|
||||
"User-Agent": "Replicated_Troubleshoot/v1beta1",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawSpecs = append(rawSpecs, rawSpec)
|
||||
} else {
|
||||
rawSpec, err := downloadFromHttpURL(ctx, v, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawSpecs = append(rawSpecs, rawSpec)
|
||||
}
|
||||
rawSpecs = append(rawSpecs, rawSpec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package specs
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -10,6 +12,7 @@ import (
|
||||
"github.com/replicatedhq/troubleshoot/internal/testutils"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/loader"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -201,3 +204,23 @@ spec:
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFromURI(t *testing.T) {
|
||||
m := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`apiVersion: troubleshoot.sh/v1beta2
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostCollector
|
||||
metadata:
|
||||
name: cpu
|
||||
spec:
|
||||
collectors:
|
||||
- cpu: {}
|
||||
`))
|
||||
}))
|
||||
defer m.Close()
|
||||
|
||||
client := testclient.NewSimpleClientset()
|
||||
specs, err := LoadFromCLIArgs(context.Background(), client, []string{m.URL}, viper.New())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, specs.HostCollectorsV1Beta2, 1)
|
||||
}
|
||||
|
||||
@@ -15,16 +15,25 @@ import (
|
||||
|
||||
func GetTestFixture(t *testing.T, path string) string {
|
||||
t.Helper()
|
||||
p := path
|
||||
if !filepath.IsAbs(path) {
|
||||
p = filepath.Join("../../testdata", path)
|
||||
}
|
||||
b, err := os.ReadFile(p)
|
||||
|
||||
b, err := os.ReadFile(TestFixtureFilePath(t, path))
|
||||
require.NoError(t, err)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// FileDir returns the directory of the current source file.
|
||||
func TestFixtureFilePath(t *testing.T, path string) string {
|
||||
t.Helper()
|
||||
|
||||
if !filepath.IsAbs(path) {
|
||||
p, err := filepath.Abs(filepath.Join(FileDir(), "../../testdata", path))
|
||||
require.NoError(t, err)
|
||||
return p
|
||||
} else {
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
// FileDir returns the directory of this source file
|
||||
func FileDir() string {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
return filepath.Dir(filename)
|
||||
@@ -32,9 +41,13 @@ func FileDir() string {
|
||||
|
||||
// Generates a temporary filename
|
||||
func TempFilename(prefix string) string {
|
||||
return filepath.Join(os.TempDir(), generateTempName(prefix))
|
||||
}
|
||||
|
||||
func generateTempName(prefix string) string {
|
||||
randBytes := make([]byte, 16)
|
||||
rand.Read(randBytes)
|
||||
return filepath.Join(os.TempDir(), fmt.Sprintf("%s_%s", prefix, hex.EncodeToString(randBytes)))
|
||||
return fmt.Sprintf("%s_%s", prefix, hex.EncodeToString(randBytes))
|
||||
}
|
||||
|
||||
func CreateTestFile(t *testing.T, path string) {
|
||||
@@ -69,3 +82,11 @@ func AsJSON(t *testing.T, v interface{}) string {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func ServeFromFilePath(t *testing.T, data string) string {
|
||||
t.Helper()
|
||||
|
||||
path := filepath.Join(t.TempDir(), generateTempName("testfile"))
|
||||
CreateTestFileWithData(t, path, data)
|
||||
return path
|
||||
}
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/user"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
const HOST_COLLECTORS_RUN_AS_ROOT_PROMPT = "Some host collectors need to be run as root.\nDo you want to exit and rerun the command using sudo?"
|
||||
|
||||
func HomeDir() string {
|
||||
if h := os.Getenv("HOME"); h != "" {
|
||||
return h
|
||||
@@ -52,3 +59,89 @@ func EstimateNumberOfLines(text string) int {
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Append appends elements in src to target.
|
||||
// We have this function because of how the
|
||||
// builtin append() function works. It treats
|
||||
// target nil slices the same as empty slices.
|
||||
func Append[T any](target []T, src []T) []T {
|
||||
// Do nothing only if src is nil
|
||||
if src == nil {
|
||||
return target
|
||||
}
|
||||
|
||||
// In case target is nil, we need to initialize it
|
||||
// since append() will not do it for us when len(src) == 0
|
||||
if target == nil {
|
||||
target = []T{}
|
||||
}
|
||||
return append(target, src...)
|
||||
}
|
||||
|
||||
// IsInCluster returns true if the code is running within a process
|
||||
// inside a kubernetes pod
|
||||
func IsInCluster() bool {
|
||||
// This is a best effort check, it's not guaranteed to be accurate
|
||||
host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
|
||||
if len(host) == 0 || len(port) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// RenderTemplate renders a template and returns the result as a string
|
||||
func RenderTemplate(tpl string, data interface{}) (string, error) {
|
||||
// Create a new template and parse the letter into it
|
||||
t, err := template.New("data").Parse(tpl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Create a new buffer
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
// Execute the template and write the bytes to the buffer
|
||||
err = t.Execute(buf, data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Return the string representation of the buffer
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func IsRunningAsRoot() bool {
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the user ID is 0 (root's UID)
|
||||
return currentUser.Uid == "0"
|
||||
}
|
||||
|
||||
func PromptYesNo(question string) bool {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
for {
|
||||
fmt.Printf("%s (yes/no): ", question)
|
||||
|
||||
response, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
fmt.Println("Error reading response:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
response = strings.TrimSpace(response)
|
||||
response = strings.ToLower(response)
|
||||
|
||||
if response == "yes" || response == "y" {
|
||||
return true
|
||||
} else if response == "no" || response == "n" {
|
||||
return false
|
||||
} else {
|
||||
fmt.Println("Please type 'yes' or 'no'.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,3 +194,111 @@ func Test_EstimateNumberOfLines(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppend(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
target []string
|
||||
src []string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "empty target",
|
||||
target: []string{},
|
||||
src: []string{"a", "b", "c"},
|
||||
want: []string{"a", "b", "c"},
|
||||
},
|
||||
{
|
||||
name: "empty src",
|
||||
target: []string{"a", "b", "c"},
|
||||
src: []string{},
|
||||
want: []string{"a", "b", "c"},
|
||||
},
|
||||
{
|
||||
name: "non-empty target and src",
|
||||
target: []string{"a", "b", "c"},
|
||||
src: []string{"d", "e", "f"},
|
||||
want: []string{"a", "b", "c", "d", "e", "f"},
|
||||
},
|
||||
{
|
||||
name: "nil target",
|
||||
target: nil,
|
||||
src: []string{"a", "b", "c"},
|
||||
want: []string{"a", "b", "c"},
|
||||
},
|
||||
{
|
||||
name: "nil src",
|
||||
target: []string{"a", "b", "c"},
|
||||
src: nil,
|
||||
want: []string{"a", "b", "c"},
|
||||
},
|
||||
{
|
||||
name: "nil target and empty src",
|
||||
target: nil,
|
||||
src: []string{},
|
||||
want: []string{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := Append(tt.target, tt.src)
|
||||
assert.Equal(t, tt.want, got, "Append() = %v, want %v", got, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderTemplate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
tpl string
|
||||
data interface{}
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty template and data",
|
||||
tpl: "",
|
||||
data: nil,
|
||||
want: "",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty template with data",
|
||||
tpl: "",
|
||||
data: map[string]string{"Name": "World"},
|
||||
want: "",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty data with template with no keys",
|
||||
tpl: "Hello, World!",
|
||||
data: nil,
|
||||
want: "Hello, World!",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "simple template",
|
||||
tpl: "Hello, {{ .Name }}!",
|
||||
data: map[string]string{"Name": "World"},
|
||||
want: "Hello, World!",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "template with missing key",
|
||||
tpl: "Hello, {{ .Name }}!",
|
||||
data: map[string]string{"Name2": "World"},
|
||||
want: "Hello, <no value>!",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := RenderTemplate(tt.tpl, tt.data)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("RenderTemplate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.want, got, "RenderTemplate() = %v, want %v", got, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,6 +244,12 @@ func getAnalyzer(analyzer *troubleshootv1beta2.Analyze) Analyzer {
|
||||
return &AnalyzeClusterResource{analyzer: analyzer.ClusterResource}
|
||||
case analyzer.Certificates != nil:
|
||||
return &AnalyzeCertificates{analyzer: analyzer.Certificates}
|
||||
case analyzer.Goldpinger != nil:
|
||||
return &AnalyzeGoldpinger{analyzer: analyzer.Goldpinger}
|
||||
case analyzer.Event != nil:
|
||||
return &AnalyzeEvent{analyzer: analyzer.Event}
|
||||
case analyzer.NodeMetrics != nil:
|
||||
return &AnalyzeNodeMetrics{analyzer: analyzer.NodeMetrics}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func analyzeClusterVersion(analyzer *troubleshootv1beta2.ClusterVersion, getColl
|
||||
return nil, errors.Wrap(err, "failed to parse cluster_version.json")
|
||||
}
|
||||
|
||||
k8sVersion, err := semver.Make(strings.TrimLeft(collectorClusterVersion.String, "v"))
|
||||
k8sVersion, err := parseK8sVersionString(collectorClusterVersion.String)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse semver from cluster_version.json")
|
||||
}
|
||||
@@ -58,6 +58,16 @@ func title(checkName string) string {
|
||||
return checkName
|
||||
}
|
||||
|
||||
func parseK8sVersionString(version string) (semver.Version, error) {
|
||||
// Workaround for https://github.com/aws/containers-roadmap/issues/1404
|
||||
// for EKS, replace pre-release gitVersion string with the release version
|
||||
if strings.Contains(version, "-eks-") {
|
||||
version = strings.Split(version, "-")[0]
|
||||
}
|
||||
|
||||
return semver.Make(strings.TrimLeft(version, "v"))
|
||||
}
|
||||
|
||||
func analyzeClusterVersionResult(k8sVersion semver.Version, outcomes []*troubleshootv1beta2.Outcome, checkName string) (*AnalyzeResult, error) {
|
||||
for _, outcome := range outcomes {
|
||||
when := ""
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/blang/semver/v4"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_analyzeClusterVersionResult(t *testing.T) {
|
||||
@@ -103,3 +104,41 @@ func Test_analyzeClusterVersionResult(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseVersionString(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rawVersion string
|
||||
want semver.Version
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid version",
|
||||
rawVersion: "1.17.0",
|
||||
want: semver.MustParse("1.17.0"),
|
||||
},
|
||||
{
|
||||
name: "valid version with v prefix",
|
||||
rawVersion: "v1.17.0",
|
||||
want: semver.MustParse("1.17.0"),
|
||||
},
|
||||
{
|
||||
name: "invalid version",
|
||||
rawVersion: "v1.17",
|
||||
want: semver.Version{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "EKS version",
|
||||
rawVersion: "v1.25.16-eks-8cb36c9",
|
||||
want: semver.MustParse("1.25.16"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parseK8sVersionString(tt.rawVersion)
|
||||
assert.Equal(t, tt.wantErr, err != nil, "parseVersionString() error = %v, wantErr %v", err, tt.wantErr)
|
||||
assert.Equal(t, tt.want, got, "parseVersionString() = %v, want %v", got, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package analyzer
|
||||
|
||||
import "fmt"
|
||||
|
||||
type ComparisonOperator int
|
||||
|
||||
const (
|
||||
Unknown ComparisonOperator = iota
|
||||
Equal
|
||||
NotEqual
|
||||
GreaterThan
|
||||
GreaterThanOrEqual
|
||||
LessThan
|
||||
LessThanOrEqual
|
||||
)
|
||||
|
||||
func ParseComparisonOperator(s string) (ComparisonOperator, error) {
|
||||
switch s {
|
||||
case "=", "==", "===":
|
||||
return Equal, nil
|
||||
case "!=", "!==":
|
||||
return NotEqual, nil
|
||||
case "<":
|
||||
return LessThan, nil
|
||||
case ">":
|
||||
return GreaterThan, nil
|
||||
case "<=":
|
||||
return LessThanOrEqual, nil
|
||||
case ">=":
|
||||
return GreaterThanOrEqual, nil
|
||||
}
|
||||
|
||||
return Unknown, fmt.Errorf("unknown operator: %s", s)
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseComparisonOperator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want ComparisonOperator
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "equal",
|
||||
input: "=",
|
||||
want: Equal,
|
||||
},
|
||||
{
|
||||
name: "equal",
|
||||
input: "==",
|
||||
want: Equal,
|
||||
},
|
||||
{
|
||||
name: "equal",
|
||||
input: "===",
|
||||
want: Equal,
|
||||
},
|
||||
{
|
||||
name: "not equal",
|
||||
input: "!=",
|
||||
want: NotEqual,
|
||||
},
|
||||
{
|
||||
name: "not equal",
|
||||
input: "!==",
|
||||
want: NotEqual,
|
||||
},
|
||||
{
|
||||
name: "less than",
|
||||
input: "<",
|
||||
want: LessThan,
|
||||
},
|
||||
{
|
||||
name: "greater than",
|
||||
input: ">",
|
||||
want: GreaterThan,
|
||||
},
|
||||
{
|
||||
name: "less than or equal",
|
||||
input: "<=",
|
||||
want: LessThanOrEqual,
|
||||
},
|
||||
{
|
||||
name: "greater than or equal",
|
||||
input: ">=",
|
||||
want: GreaterThanOrEqual,
|
||||
},
|
||||
{
|
||||
name: "invalid operator 1",
|
||||
input: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid operator 2",
|
||||
input: "gibberish",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseComparisonOperator(tt.input)
|
||||
assert.Equal(t, tt.want, got, "ParseOperator() = %v, want %v", got, tt.want)
|
||||
assert.Equalf(t, tt.wantErr, err != nil, "ParseOperator() error = %v, wantErr %v", err, tt.wantErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
+60
-29
@@ -13,40 +13,46 @@ import (
|
||||
)
|
||||
|
||||
type providers struct {
|
||||
microk8s bool
|
||||
dockerDesktop bool
|
||||
eks bool
|
||||
gke bool
|
||||
digitalOcean bool
|
||||
openShift bool
|
||||
tanzu bool
|
||||
kurl bool
|
||||
aks bool
|
||||
ibm bool
|
||||
minikube bool
|
||||
rke2 bool
|
||||
k3s bool
|
||||
oke bool
|
||||
microk8s bool
|
||||
dockerDesktop bool
|
||||
eks bool
|
||||
gke bool
|
||||
digitalOcean bool
|
||||
openShift bool
|
||||
tanzu bool
|
||||
kurl bool
|
||||
aks bool
|
||||
ibm bool
|
||||
minikube bool
|
||||
rke2 bool
|
||||
k3s bool
|
||||
oke bool
|
||||
kind bool
|
||||
k0s bool
|
||||
embeddedCluster bool
|
||||
}
|
||||
|
||||
type Provider int
|
||||
|
||||
const (
|
||||
unknown Provider = iota
|
||||
microk8s Provider = iota
|
||||
dockerDesktop Provider = iota
|
||||
eks Provider = iota
|
||||
gke Provider = iota
|
||||
digitalOcean Provider = iota
|
||||
openShift Provider = iota
|
||||
tanzu Provider = iota
|
||||
kurl Provider = iota
|
||||
aks Provider = iota
|
||||
ibm Provider = iota
|
||||
minikube Provider = iota
|
||||
rke2 Provider = iota
|
||||
k3s Provider = iota
|
||||
oke Provider = iota
|
||||
unknown Provider = iota
|
||||
microk8s Provider = iota
|
||||
dockerDesktop Provider = iota
|
||||
eks Provider = iota
|
||||
gke Provider = iota
|
||||
digitalOcean Provider = iota
|
||||
openShift Provider = iota
|
||||
tanzu Provider = iota
|
||||
kurl Provider = iota
|
||||
aks Provider = iota
|
||||
ibm Provider = iota
|
||||
minikube Provider = iota
|
||||
rke2 Provider = iota
|
||||
k3s Provider = iota
|
||||
oke Provider = iota
|
||||
kind Provider = iota
|
||||
k0s Provider = iota
|
||||
embeddedCluster Provider = iota
|
||||
)
|
||||
|
||||
type AnalyzeDistribution struct {
|
||||
@@ -132,6 +138,15 @@ func ParseNodesForProviders(nodes []corev1.Node) (providers, string) {
|
||||
foundProviders.oke = true
|
||||
stringProvider = "oke"
|
||||
}
|
||||
if k == "node.k0sproject.io/role" {
|
||||
foundProviders.k0s = true
|
||||
stringProvider = "k0s"
|
||||
}
|
||||
|
||||
if k == "kots.io/embedded-cluster-role" {
|
||||
foundProviders.embeddedCluster = true
|
||||
stringProvider = "embedded-cluster"
|
||||
}
|
||||
}
|
||||
|
||||
for k := range node.ObjectMeta.Annotations {
|
||||
@@ -162,6 +177,10 @@ func ParseNodesForProviders(nodes []corev1.Node) (providers, string) {
|
||||
foundProviders.ibm = true
|
||||
stringProvider = "ibm"
|
||||
}
|
||||
if strings.HasPrefix(node.Spec.ProviderID, "kind:") {
|
||||
foundProviders.kind = true
|
||||
stringProvider = "kind"
|
||||
}
|
||||
}
|
||||
|
||||
if foundMaster {
|
||||
@@ -335,6 +354,12 @@ func compareDistributionConditionalToActual(conditional string, actual providers
|
||||
isMatch = actual.k3s
|
||||
case oke:
|
||||
isMatch = actual.oke
|
||||
case kind:
|
||||
isMatch = actual.kind
|
||||
case k0s:
|
||||
isMatch = actual.k0s
|
||||
case embeddedCluster:
|
||||
isMatch = actual.embeddedCluster
|
||||
}
|
||||
|
||||
switch parts[0] {
|
||||
@@ -377,6 +402,12 @@ func mustNormalizeDistributionName(raw string) Provider {
|
||||
return k3s
|
||||
case "oke":
|
||||
return oke
|
||||
case "kind":
|
||||
return kind
|
||||
case "k0s":
|
||||
return k0s
|
||||
case "embeddedcluster":
|
||||
return embeddedCluster
|
||||
}
|
||||
|
||||
return unknown
|
||||
|
||||
@@ -39,6 +39,30 @@ func Test_compareDistributionConditionalToActual(t *testing.T) {
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "== kind when kind is found",
|
||||
conditional: "== kind",
|
||||
input: providers{
|
||||
kind: true,
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "== k0s when k0s is found",
|
||||
conditional: "== k0s",
|
||||
input: providers{
|
||||
k0s: true,
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "== embedded-cluster when embedded-cluster is found",
|
||||
conditional: "== embedded-cluster",
|
||||
input: providers{
|
||||
embeddedCluster: true,
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
@@ -73,6 +97,66 @@ func Test_mustNormalizeDistributionName(t *testing.T) {
|
||||
raw: "Docker-Desktop",
|
||||
expected: dockerDesktop,
|
||||
},
|
||||
{
|
||||
raw: "embedded-cluster",
|
||||
expected: embeddedCluster,
|
||||
},
|
||||
{
|
||||
raw: "k0s",
|
||||
expected: k0s,
|
||||
},
|
||||
{
|
||||
raw: "kind",
|
||||
expected: kind,
|
||||
},
|
||||
{
|
||||
raw: "k3s",
|
||||
expected: k3s,
|
||||
},
|
||||
{
|
||||
raw: "ibm",
|
||||
expected: ibm,
|
||||
},
|
||||
{
|
||||
raw: "ibmcloud",
|
||||
expected: ibm,
|
||||
},
|
||||
{
|
||||
raw: "ibm cloud",
|
||||
expected: ibm,
|
||||
},
|
||||
{
|
||||
raw: "gke",
|
||||
expected: gke,
|
||||
},
|
||||
{
|
||||
raw: "aks",
|
||||
expected: aks,
|
||||
},
|
||||
{
|
||||
raw: "eks",
|
||||
expected: eks,
|
||||
},
|
||||
{
|
||||
raw: "oke",
|
||||
expected: oke,
|
||||
},
|
||||
{
|
||||
raw: "rke2",
|
||||
expected: rke2,
|
||||
},
|
||||
{
|
||||
raw: "dockerdesktop",
|
||||
expected: dockerDesktop,
|
||||
},
|
||||
{
|
||||
raw: "docker desktop",
|
||||
expected: dockerDesktop,
|
||||
},
|
||||
{
|
||||
raw: "docker-desktop",
|
||||
expected: dockerDesktop,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/constants"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type AnalyzeEvent struct {
|
||||
analyzer *troubleshootv1beta2.EventAnalyze
|
||||
}
|
||||
|
||||
type eventFilter struct {
|
||||
kind string
|
||||
reason string
|
||||
msgRegex string
|
||||
}
|
||||
|
||||
func (a *AnalyzeEvent) Title() string {
|
||||
if a.analyzer.CheckName != "" {
|
||||
return a.analyzer.CheckName
|
||||
}
|
||||
if a.analyzer.CollectorName != "" {
|
||||
return a.analyzer.CollectorName
|
||||
}
|
||||
return "Event"
|
||||
}
|
||||
|
||||
func (a *AnalyzeEvent) IsExcluded() (bool, error) {
|
||||
return isExcluded(a.analyzer.Exclude)
|
||||
}
|
||||
|
||||
func (a *AnalyzeEvent) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
||||
// required check
|
||||
if a.analyzer.Reason == "" {
|
||||
return nil, errors.New("reason is required")
|
||||
}
|
||||
|
||||
// read collected events based on namespace
|
||||
namespace := getNamespace(a.analyzer.Namespace)
|
||||
fullPath := path.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_EVENTS, namespace)
|
||||
fullPath = fmt.Sprintf("%s.json", fullPath)
|
||||
fileContent, err := getFile(fullPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read collected events for namespace: %s", namespace)
|
||||
}
|
||||
|
||||
events, err := convertToEventList(fileContent)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read collected events")
|
||||
}
|
||||
|
||||
// filter if there's single event matched with the given criteria
|
||||
// match: Reason && Kind (optional) && MessageRegex (optional)
|
||||
// e.g. Reason: Unhealthy. Kind: Pod. Message: Readiness probe failed:...
|
||||
event := getEvent(events, eventFilter{
|
||||
kind: a.analyzer.Kind,
|
||||
reason: a.analyzer.Reason,
|
||||
msgRegex: a.analyzer.RegexPattern,
|
||||
})
|
||||
|
||||
return analyzeEventResult(event, a.analyzer.Outcomes, a.Title())
|
||||
|
||||
}
|
||||
|
||||
func getNamespace(namespace string) string {
|
||||
if namespace == "" {
|
||||
return corev1.NamespaceDefault
|
||||
}
|
||||
return namespace
|
||||
}
|
||||
|
||||
func convertToEventList(data []byte) (*corev1.EventList, error) {
|
||||
var eventList corev1.EventList
|
||||
err := json.Unmarshal(data, &eventList)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert []byte to corev1.EventList: %w", err)
|
||||
}
|
||||
return &eventList, nil
|
||||
}
|
||||
|
||||
func getEvent(events *corev1.EventList, filter eventFilter) *corev1.Event {
|
||||
var (
|
||||
re *regexp.Regexp
|
||||
errParseRegex error
|
||||
)
|
||||
|
||||
if filter.msgRegex != "" {
|
||||
re, errParseRegex = regexp.Compile(filter.msgRegex)
|
||||
if errParseRegex != nil {
|
||||
klog.V(2).Infof("failed to read message regex: %v", errParseRegex)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, event := range events.Items {
|
||||
if !matchReason(event.Reason, filter.reason) {
|
||||
continue
|
||||
}
|
||||
if !matchKind(event.InvolvedObject.Kind, filter.kind) {
|
||||
continue
|
||||
}
|
||||
if re == nil || re.MatchString(event.Message) {
|
||||
klog.V(2).Infof("event matched: %v for reason: %s kind: %s messageRegex: %s ", event, filter.reason, filter.kind, filter.msgRegex)
|
||||
return &event
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func matchReason(actual, expected string) bool {
|
||||
// not possible to have empty reason
|
||||
if expected == "" {
|
||||
return false
|
||||
}
|
||||
return strings.EqualFold(actual, expected)
|
||||
}
|
||||
|
||||
func matchKind(actual, expected string) bool {
|
||||
// kind is optional
|
||||
if expected == "" {
|
||||
return true
|
||||
}
|
||||
return strings.EqualFold(actual, expected)
|
||||
}
|
||||
|
||||
func analyzeEventResult(event *corev1.Event, outcomes []*troubleshootv1beta2.Outcome, checkName string) ([]*AnalyzeResult, error) {
|
||||
|
||||
results := []*AnalyzeResult{}
|
||||
|
||||
// for now, only support single outcome
|
||||
// we will return when there's a matched event
|
||||
willReturn := event != nil
|
||||
|
||||
result := &AnalyzeResult{
|
||||
Title: checkName,
|
||||
IconKey: "kubernetes_event",
|
||||
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
|
||||
}
|
||||
|
||||
for _, o := range outcomes {
|
||||
if o.Fail != nil {
|
||||
toReturn, err := strconv.ParseBool(o.Fail.When)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse when condition: %s", o.Fail.When)
|
||||
}
|
||||
if toReturn == willReturn {
|
||||
result.IsFail = true
|
||||
result.Message = decorateMessage(o.Fail.Message, event)
|
||||
result.URI = o.Fail.URI
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if o.Warn != nil {
|
||||
toReturn, err := strconv.ParseBool(o.Warn.When)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse when condition: %s", o.Warn.When)
|
||||
}
|
||||
if toReturn == willReturn {
|
||||
result.IsWarn = true
|
||||
result.Message = decorateMessage(o.Warn.Message, event)
|
||||
result.URI = o.Warn.URI
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if o.Pass != nil {
|
||||
toReturn, err := strconv.ParseBool(o.Pass.When)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse when condition: %s", o.Pass.When)
|
||||
}
|
||||
if toReturn == willReturn {
|
||||
result.IsPass = true
|
||||
result.Message = decorateMessage(o.Pass.Message, event)
|
||||
result.URI = o.Pass.URI
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
results = append(results, result)
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func decorateMessage(message string, event *corev1.Event) string {
|
||||
if event == nil {
|
||||
return message
|
||||
}
|
||||
out := fmt.Sprintf("Event matched. Reason: %s Name: %s Message: %s", event.Reason, event.InvolvedObject.Name, event.Message)
|
||||
|
||||
tmpl := template.New("event")
|
||||
msgTmpl, err := tmpl.Parse(message)
|
||||
if err != nil {
|
||||
klog.V(2).Infof("failed to parse message template: %v", err)
|
||||
return out
|
||||
}
|
||||
|
||||
var m bytes.Buffer
|
||||
err = msgTmpl.Execute(&m, event)
|
||||
if err != nil {
|
||||
klog.V(2).Infof("failed to render message template: %v", err)
|
||||
return out
|
||||
}
|
||||
|
||||
return strings.TrimSpace(m.String())
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestAnalyzeEvent(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
analyzer troubleshootv1beta2.EventAnalyze
|
||||
expectResult []AnalyzeResult
|
||||
files map[string][]byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "reason is required",
|
||||
analyzer: troubleshootv1beta2.EventAnalyze{
|
||||
CollectorName: "event-collector-0",
|
||||
Kind: "Pod",
|
||||
Namespace: "default",
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "true",
|
||||
Message: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: errors.New("reason is required"),
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/events/default.json": []byte(`
|
||||
{
|
||||
"kind": "EventList",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"resourceVersion": "722"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "nginx-rc",
|
||||
"namespace": "default",
|
||||
"creationTimestamp": "2022-01-01T00:00:00Z"
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"name": "nginx-rc-12345",
|
||||
"namespace": "default"
|
||||
},
|
||||
"reason": "OOMKilled",
|
||||
"message": "The container was killed due to an out-of-memory condition.",
|
||||
"type": "Warning"
|
||||
}
|
||||
]
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fail when OOMKilled event is present",
|
||||
analyzer: troubleshootv1beta2.EventAnalyze{
|
||||
CollectorName: "event-collector-1",
|
||||
Kind: "Pod",
|
||||
Namespace: "default",
|
||||
Reason: "OOMKilled",
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "true",
|
||||
Message: "Detect OOMKilled event with {{ .InvolvedObject.Kind }}-{{ .InvolvedObject.Name }} with message {{ .Message }}",
|
||||
},
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "false",
|
||||
Message: "No OOMKilled event detected",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectResult: []AnalyzeResult{
|
||||
{
|
||||
Title: "event-collector-1",
|
||||
IconKey: "kubernetes_event",
|
||||
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
|
||||
IsFail: true,
|
||||
IsWarn: false,
|
||||
IsPass: false,
|
||||
Message: "Detect OOMKilled event with Pod-nginx-rc-12345 with message The container was killed due to an out-of-memory condition.",
|
||||
},
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/events/default.json": []byte(`
|
||||
{
|
||||
"kind": "EventList",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"resourceVersion": "722"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "nginx-rc",
|
||||
"namespace": "default",
|
||||
"creationTimestamp": "2022-01-01T00:00:00Z"
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"name": "nginx-rc-12345",
|
||||
"namespace": "default"
|
||||
},
|
||||
"reason": "OOMKilled",
|
||||
"message": "The container was killed due to an out-of-memory condition.",
|
||||
"type": "Warning"
|
||||
}
|
||||
]
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "pass when no FailedMount event is present",
|
||||
analyzer: troubleshootv1beta2.EventAnalyze{
|
||||
CollectorName: "event-collector-2",
|
||||
Kind: "Pod",
|
||||
Namespace: "default",
|
||||
Reason: "FailedMount",
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "false",
|
||||
Message: "No FailedMount event detected",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectResult: []AnalyzeResult{
|
||||
{
|
||||
Title: "event-collector-2",
|
||||
IconKey: "kubernetes_event",
|
||||
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
|
||||
IsFail: false,
|
||||
IsWarn: false,
|
||||
IsPass: true,
|
||||
Message: "No FailedMount event detected",
|
||||
},
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/events/default.json": []byte(`
|
||||
{
|
||||
"kind": "EventList",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"resourceVersion": "722"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "nginx-rc-1",
|
||||
"namespace": "default",
|
||||
"creationTimestamp": "2022-01-01T00:00:00Z"
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"name": "nginx-rc-12345",
|
||||
"namespace": "default"
|
||||
},
|
||||
"reason": "Created",
|
||||
"message": "Created container",
|
||||
"type": "Normal"
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "nginx-rc-2",
|
||||
"namespace": "default",
|
||||
"creationTimestamp": "2022-01-01T00:00:00Z"
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"name": "nginx-rc-67890",
|
||||
"namespace": "default"
|
||||
},
|
||||
"reason": "Started",
|
||||
"message": "Started container",
|
||||
"type": "Normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
getFile := func(n string) ([]byte, error) {
|
||||
if b, ok := test.files[n]; ok {
|
||||
return b, nil
|
||||
}
|
||||
return nil, errors.New("file not found")
|
||||
}
|
||||
|
||||
findFiles := func(n string, _ []string) (map[string][]byte, error) {
|
||||
return nil, errors.New("method not implemented")
|
||||
}
|
||||
|
||||
a := &AnalyzeEvent{
|
||||
analyzer: &test.analyzer,
|
||||
}
|
||||
actual, err := a.Analyze(getFile, findFiles)
|
||||
if test.err != nil {
|
||||
req.EqualError(err, test.err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
req.NoError(err)
|
||||
unPointered := []AnalyzeResult{}
|
||||
for _, v := range actual {
|
||||
unPointered = append(unPointered, *v)
|
||||
}
|
||||
req.ElementsMatch(test.expectResult, unPointered)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeEventResult(t *testing.T) {
|
||||
event := &corev1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo-pod",
|
||||
},
|
||||
Reason: "Unhealthy",
|
||||
Message: "foo-message",
|
||||
Type: "Warning",
|
||||
}
|
||||
|
||||
outcomes := []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "true",
|
||||
Message: "No unhealthy pods allowed",
|
||||
},
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "false",
|
||||
Message: "No unhealthy pod detected",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
checkName := "Test Event"
|
||||
|
||||
expectedResults := []*AnalyzeResult{
|
||||
{
|
||||
Title: "Test Event",
|
||||
IconKey: "kubernetes_event",
|
||||
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
|
||||
IsFail: true,
|
||||
IsWarn: false,
|
||||
IsPass: false,
|
||||
Message: "No unhealthy pods allowed",
|
||||
},
|
||||
}
|
||||
|
||||
results, err := analyzeEventResult(event, outcomes, checkName)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != len(expectedResults) {
|
||||
t.Fatalf("unexpected number of results, got %d, want %d", len(results), len(expectedResults))
|
||||
}
|
||||
|
||||
for i, result := range results {
|
||||
expectedResult := expectedResults[i]
|
||||
|
||||
if result.Title != expectedResult.Title {
|
||||
t.Errorf("unexpected title, got %s, want %s", result.Title, expectedResult.Title)
|
||||
}
|
||||
|
||||
if result.IsFail != expectedResult.IsFail {
|
||||
t.Errorf("unexpected IsFail value, got %v, want %v", result.IsFail, expectedResult.IsFail)
|
||||
}
|
||||
|
||||
if result.IsWarn != expectedResult.IsWarn {
|
||||
t.Errorf("unexpected IsWarn value, got %v, want %v", result.IsWarn, expectedResult.IsWarn)
|
||||
}
|
||||
|
||||
if result.IsPass != expectedResult.IsPass {
|
||||
t.Errorf("unexpected IsPass value, got %v, want %v", result.IsPass, expectedResult.IsPass)
|
||||
}
|
||||
|
||||
if result.Message != expectedResult.Message {
|
||||
t.Errorf("unexpected message, got %s, want %s", result.Message, expectedResult.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/constants"
|
||||
)
|
||||
|
||||
type AnalyzeGoldpinger struct {
|
||||
analyzer *troubleshootv1beta2.GoldpingerAnalyze
|
||||
}
|
||||
|
||||
type checkAllOutput struct {
|
||||
Hosts []struct {
|
||||
HostIP string `json:"hostIP"`
|
||||
PodIP string `json:"podIP"`
|
||||
PodName string `json:"podName"`
|
||||
} `json:"hosts"`
|
||||
Responses map[string]struct {
|
||||
HostIP string `json:"hostIP"`
|
||||
PodIP string `json:"podIP"`
|
||||
OK bool `json:"OK"`
|
||||
Response struct {
|
||||
PodResults map[string]struct {
|
||||
HostIP string `json:"hostIP"`
|
||||
OK bool `json:"OK"`
|
||||
PingTime string `json:"pingTime"`
|
||||
PodIP string `json:"podIP"`
|
||||
Response struct {
|
||||
BootTime string `json:"boot_time"`
|
||||
} `json:"response"`
|
||||
Error string `json:"error"`
|
||||
ResponseTimeMS int `json:"response-time-ms"`
|
||||
StatusCode int `json:"status-code"`
|
||||
} `json:"podResults"`
|
||||
} `json:"response"`
|
||||
} `json:"responses"`
|
||||
}
|
||||
|
||||
func (a *AnalyzeGoldpinger) Title() string {
|
||||
title := a.analyzer.CheckName
|
||||
if title == "" {
|
||||
title = a.collectorName()
|
||||
}
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
func (a *AnalyzeGoldpinger) IsExcluded() (bool, error) {
|
||||
return isExcluded(a.analyzer.Exclude)
|
||||
}
|
||||
|
||||
func (a *AnalyzeGoldpinger) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
||||
caoFilePath := constants.GP_CHECK_ALL_RESULTS_PATH
|
||||
// To allow analysing older support bundles, we can provide a custom file path
|
||||
if a.analyzer.FilePath != "" {
|
||||
caoFilePath = a.analyzer.FilePath
|
||||
}
|
||||
|
||||
collected, err := getFile(caoFilePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read collected file path: %q", caoFilePath)
|
||||
}
|
||||
|
||||
var cao checkAllOutput
|
||||
err = json.Unmarshal(collected, &cao)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal collected goldpinger output")
|
||||
}
|
||||
|
||||
return a.podPingsAnalysis(&cao), nil
|
||||
}
|
||||
|
||||
func (a *AnalyzeGoldpinger) collectorName() string {
|
||||
if a.analyzer.CollectorName != "" {
|
||||
return a.analyzer.CollectorName
|
||||
}
|
||||
return "goldpinger"
|
||||
}
|
||||
|
||||
func (a *AnalyzeGoldpinger) podPingsAnalysis(cao *checkAllOutput) []*AnalyzeResult {
|
||||
results := []*AnalyzeResult{}
|
||||
|
||||
for _, host := range cao.Hosts {
|
||||
// Check if the pod from a host has any ping errors from other pods
|
||||
targetPod := host.PodName
|
||||
pingsSucceeded := true
|
||||
for srcPod, resp := range cao.Responses {
|
||||
res := &AnalyzeResult{
|
||||
IconKey: "kubernetes",
|
||||
Strict: a.analyzer.Strict.BoolOrDefaultFalse(),
|
||||
}
|
||||
|
||||
// Get ping result for the pod
|
||||
podResult, ok := resp.Response.PodResults[targetPod]
|
||||
if !ok {
|
||||
// Pod not found in ping results from the source pod
|
||||
res.IsWarn = true
|
||||
res.Title = fmt.Sprintf("Missing ping results for %q pod", targetPod)
|
||||
res.Message = fmt.Sprintf("Ping result for %q pod from %q pod is missing", targetPod, srcPod)
|
||||
pingsSucceeded = false
|
||||
|
||||
results = append(results, res)
|
||||
continue
|
||||
}
|
||||
|
||||
if !podResult.OK {
|
||||
// Ping was not successful
|
||||
res.IsFail = true
|
||||
res.Title = fmt.Sprintf("Ping from %q pod to %q pod failed", srcPod, targetPod)
|
||||
res.Message = fmt.Sprintf("Ping error: %s", podResult.Error)
|
||||
pingsSucceeded = false
|
||||
|
||||
results = append(results, res)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// If all pings succeeded, add a pass result
|
||||
if pingsSucceeded {
|
||||
results = append(results, &AnalyzeResult{
|
||||
IconKey: "kubernetes",
|
||||
Strict: a.analyzer.Strict.BoolOrDefaultFalse(),
|
||||
IsPass: true,
|
||||
Title: fmt.Sprintf("Pings to %q pod succeeded", targetPod),
|
||||
Message: fmt.Sprintf("Pings to %q pod from all other pods in the cluster succeeded", targetPod),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/internal/testutils"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUnmarshallingCheckAllResults(t *testing.T) {
|
||||
s := testutils.GetTestFixture(t, "goldpinger/checkall-with-error.json")
|
||||
var res checkAllOutput
|
||||
err := json.Unmarshal([]byte(s), &res)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, res.Hosts, 3)
|
||||
assert.Equal(t, "goldpinger-tbdsb", res.Hosts[1].PodName)
|
||||
assert.Equal(t, "10.32.2.2", res.Responses["goldpinger-4hctt"].Response.PodResults["goldpinger-jj9mw"].PodIP)
|
||||
assert.Equal(t,
|
||||
`Get "http://10.32.0.9:80/ping": context deadline exceeded`,
|
||||
res.Responses["goldpinger-tbdsb"].Response.PodResults["goldpinger-4hctt"].Error,
|
||||
)
|
||||
}
|
||||
|
||||
func TestAnalyzeGoldpinger_podPingsAnalysis(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
cao *checkAllOutput
|
||||
want []*AnalyzeResult
|
||||
}{
|
||||
{
|
||||
name: "no ping errors",
|
||||
cao: caoFixture(t, "goldpinger/checkall-success.json"),
|
||||
want: []*AnalyzeResult{
|
||||
{
|
||||
Title: "Pings to \"goldpinger-kpz4g\" pod succeeded",
|
||||
Message: "Pings to \"goldpinger-kpz4g\" pod from all other pods in the cluster succeeded",
|
||||
IconKey: "kubernetes",
|
||||
IsPass: true,
|
||||
},
|
||||
{
|
||||
Title: "Pings to \"goldpinger-k6d2j\" pod succeeded",
|
||||
Message: "Pings to \"goldpinger-k6d2j\" pod from all other pods in the cluster succeeded",
|
||||
IconKey: "kubernetes",
|
||||
IsPass: true,
|
||||
},
|
||||
{
|
||||
Title: "Pings to \"goldpinger-5ck4d\" pod succeeded",
|
||||
Message: "Pings to \"goldpinger-5ck4d\" pod from all other pods in the cluster succeeded",
|
||||
IconKey: "kubernetes",
|
||||
IsPass: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with some ping errors",
|
||||
cao: caoFixture(t, "goldpinger/checkall-with-error.json"),
|
||||
want: []*AnalyzeResult{
|
||||
{
|
||||
Title: "Ping from \"goldpinger-jj9mw\" pod to \"goldpinger-4hctt\" pod failed",
|
||||
Message: "Ping error: Get \"http://10.32.0.9:80/ping\": context deadline exceeded",
|
||||
IconKey: "kubernetes",
|
||||
IsFail: true,
|
||||
},
|
||||
{
|
||||
Title: "Ping from \"goldpinger-jj9mw\" pod to \"goldpinger-tbdsb\" pod failed",
|
||||
Message: "Ping error: Get \"http://10.32.1.2:80/ping\": context deadline exceeded",
|
||||
IconKey: "kubernetes",
|
||||
IsFail: true,
|
||||
},
|
||||
{
|
||||
Title: "Ping from \"goldpinger-4hctt\" pod to \"goldpinger-jj9mw\" pod failed",
|
||||
Message: "Ping error: Get \"http://10.32.2.2:80/ping\": context deadline exceeded",
|
||||
IconKey: "kubernetes",
|
||||
IsFail: true,
|
||||
},
|
||||
{
|
||||
Title: "Ping from \"goldpinger-4hctt\" pod to \"goldpinger-tbdsb\" pod failed",
|
||||
Message: "Ping error: Get \"http://10.32.1.2:80/ping\": context deadline exceeded",
|
||||
IconKey: "kubernetes",
|
||||
IsFail: true,
|
||||
},
|
||||
{
|
||||
Title: "Ping from \"goldpinger-tbdsb\" pod to \"goldpinger-4hctt\" pod failed",
|
||||
Message: "Ping error: Get \"http://10.32.0.9:80/ping\": context deadline exceeded",
|
||||
IconKey: "kubernetes",
|
||||
IsFail: true,
|
||||
},
|
||||
{
|
||||
Title: "Ping from \"goldpinger-tbdsb\" pod to \"goldpinger-jj9mw\" pod failed",
|
||||
Message: "Ping error: Get \"http://10.32.2.2:80/ping\": context deadline exceeded",
|
||||
IconKey: "kubernetes",
|
||||
IsFail: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &AnalyzeGoldpinger{
|
||||
analyzer: &troubleshootv1beta2.GoldpingerAnalyze{},
|
||||
}
|
||||
|
||||
got := a.podPingsAnalysis(tt.cao)
|
||||
// Check existence of each want. Maps are not ordered, so we can't just compare
|
||||
for _, want := range tt.want {
|
||||
assert.Contains(t, got, want)
|
||||
}
|
||||
assert.Len(t, got, len(tt.want))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func caoFixture(t *testing.T, path string) *checkAllOutput {
|
||||
t.Helper()
|
||||
|
||||
s := testutils.GetTestFixture(t, path)
|
||||
var res checkAllOutput
|
||||
err := json.Unmarshal([]byte(s), &res)
|
||||
require.NoError(t, err)
|
||||
|
||||
return &res
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
util "github.com/replicatedhq/troubleshoot/internal/util"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
iutils "github.com/replicatedhq/troubleshoot/pkg/interfaceutils"
|
||||
"k8s.io/client-go/util/jsonpath"
|
||||
@@ -54,6 +55,8 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js
|
||||
return nil, errors.Wrap(err, "failed to parse collected data as json")
|
||||
}
|
||||
|
||||
originalActual := actual
|
||||
|
||||
if analyzer.Path != "" {
|
||||
actual, err = iutils.GetAtPath(actual, analyzer.Path)
|
||||
if err != nil {
|
||||
@@ -112,6 +115,11 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js
|
||||
}
|
||||
}
|
||||
|
||||
outcome.Fail.Message, err = util.RenderTemplate(outcome.Fail.Message, originalActual)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to render template on outcome message")
|
||||
}
|
||||
|
||||
if when == equal {
|
||||
result.IsFail = true
|
||||
result.Message = outcome.Fail.Message
|
||||
@@ -128,6 +136,11 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js
|
||||
}
|
||||
}
|
||||
|
||||
outcome.Warn.Message, err = util.RenderTemplate(outcome.Warn.Message, originalActual)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to render template on outcome message")
|
||||
}
|
||||
|
||||
if when == equal {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
@@ -144,6 +157,11 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js
|
||||
}
|
||||
}
|
||||
|
||||
outcome.Pass.Message, err = util.RenderTemplate(outcome.Pass.Message, originalActual)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to render template on outcome message")
|
||||
}
|
||||
|
||||
if when == equal {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
|
||||
@@ -756,6 +756,48 @@ func Test_jsonCompare(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
},
|
||||
{
|
||||
name: "basic comparison with outcome message templated",
|
||||
analyzer: troubleshootv1beta2.JsonCompare{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
Message: "Status: {{ .morestuff.status }}, Info: {{ .morestuff.info }}",
|
||||
When: "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
Message: "Status: {{ .morestuff.status }}, Info: {{ .morestuff.info }}",
|
||||
When: "false",
|
||||
},
|
||||
},
|
||||
},
|
||||
CollectorName: "json-compare",
|
||||
FileName: "json-compare.json",
|
||||
Path: "morestuff.status",
|
||||
Value: `"ready"`,
|
||||
},
|
||||
expectResult: AnalyzeResult{
|
||||
IsPass: false,
|
||||
IsWarn: false,
|
||||
IsFail: true,
|
||||
Title: "json-compare",
|
||||
Message: "Status: notready, Info: morestuff is not ready",
|
||||
IconKey: "kubernetes_text_analyze",
|
||||
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||
},
|
||||
fileContents: []byte(`{
|
||||
"stuff": {
|
||||
"status": "ready",
|
||||
"info": "this stuff is ready"
|
||||
},
|
||||
"morestuff": {
|
||||
"status": "notready",
|
||||
"info": "morestuff is not ready"
|
||||
}
|
||||
}`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -0,0 +1,316 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"k8s.io/klog/v2"
|
||||
kubeletv1alpha1 "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
|
||||
)
|
||||
|
||||
type AnalyzeNodeMetrics struct {
|
||||
analyzer *troubleshootv1beta2.NodeMetricsAnalyze
|
||||
}
|
||||
|
||||
type nodeMetricsComparisonResults struct {
|
||||
PVC pvcTemplateData
|
||||
}
|
||||
|
||||
type pvcTemplateData struct {
|
||||
UsedPercentage float64
|
||||
ConcatenatedNames string
|
||||
Names []string
|
||||
}
|
||||
|
||||
type pvcUsageStats struct {
|
||||
PvcName string
|
||||
Used float64
|
||||
}
|
||||
|
||||
func (a *AnalyzeNodeMetrics) Title() string {
|
||||
title := a.analyzer.CheckName
|
||||
if title == "" {
|
||||
title = a.analyzer.CollectorName
|
||||
}
|
||||
if title == "" {
|
||||
title = "Node Metrics"
|
||||
}
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
func (a *AnalyzeNodeMetrics) IsExcluded() (bool, error) {
|
||||
return isExcluded(a.analyzer.Exclude)
|
||||
}
|
||||
|
||||
func (a *AnalyzeNodeMetrics) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
||||
// Gather all collected node-metrics files
|
||||
collected, err := findFiles(filepath.Join("node-metrics", "*.json"), nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read collected pods")
|
||||
}
|
||||
|
||||
// Unmarshal all collected node-metrics files
|
||||
summaries := []kubeletv1alpha1.Summary{}
|
||||
for _, fileContent := range collected {
|
||||
summary := kubeletv1alpha1.Summary{}
|
||||
if err := json.Unmarshal(fileContent, &summary); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal node metrics")
|
||||
}
|
||||
|
||||
summaries = append(summaries, summary)
|
||||
}
|
||||
|
||||
// Run through all outcomes to generate results
|
||||
result, err := a.compareCollectedMetricsWithOutcomes(summaries)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compare node metrics with outcomes")
|
||||
}
|
||||
if result == nil {
|
||||
return []*AnalyzeResult{}, nil
|
||||
}
|
||||
result.Strict = a.analyzer.Strict.BoolOrDefaultFalse()
|
||||
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
|
||||
func (a *AnalyzeNodeMetrics) compareCollectedMetricsWithOutcomes(summaries []kubeletv1alpha1.Summary) (*AnalyzeResult, error) {
|
||||
for _, outcome := range a.analyzer.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 result, nil
|
||||
} else {
|
||||
isMatch, out, err := a.compareNodeMetricConditionalsToStats(outcome.Fail.When, summaries)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compare node metrics conditional with summary stats")
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsFail = true
|
||||
result.Message = renderTemplate(outcome.Fail.Message, out)
|
||||
result.URI = outcome.Fail.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
} else if outcome.Warn != nil {
|
||||
if outcome.Warn.When == "" {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
result.URI = outcome.Warn.URI
|
||||
|
||||
return result, nil
|
||||
} else {
|
||||
isMatch, out, err := a.compareNodeMetricConditionalsToStats(outcome.Warn.When, summaries)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compare node metrics conditional with summary stats")
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsWarn = true
|
||||
result.Message = renderTemplate(outcome.Warn.Message, out)
|
||||
result.URI = outcome.Warn.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
} else if outcome.Pass != nil {
|
||||
if outcome.Pass.When == "" {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return result, nil
|
||||
} else {
|
||||
isMatch, out, err := a.compareNodeMetricConditionalsToStats(outcome.Pass.When, summaries)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compare node metrics conditional with summary stats")
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsPass = true
|
||||
result.Message = renderTemplate(outcome.Pass.Message, out)
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (a *AnalyzeNodeMetrics) findPVCUsageStats(summaries []kubeletv1alpha1.Summary) ([]pvcUsageStats, error) {
|
||||
// We just collect usage percentages for now. If other stats are needed, we can add them.
|
||||
stats := []pvcUsageStats{}
|
||||
var nameRegex *regexp.Regexp
|
||||
var ns string
|
||||
var err error
|
||||
|
||||
pvcFilter := a.analyzer.Filters.PVC
|
||||
if pvcFilter != nil {
|
||||
if pvcFilter.NameRegex != "" {
|
||||
nameRegex, err = regexp.Compile(pvcFilter.NameRegex)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compile PVC name regex")
|
||||
}
|
||||
}
|
||||
|
||||
ns = pvcFilter.Namespace
|
||||
}
|
||||
|
||||
// Analyze PVCs
|
||||
for _, summary := range summaries {
|
||||
for i := range summary.Pods {
|
||||
pod := summary.Pods[i]
|
||||
if ns != "" && ns != pod.PodRef.Namespace {
|
||||
klog.V(2).Infof("Skipping pvcs in %s/%s pod due to namespace filter", pod.PodRef.Namespace, pod.PodRef.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
for j := range pod.VolumeStats {
|
||||
volume := pod.VolumeStats[j]
|
||||
|
||||
// This is a persistent volume
|
||||
if volume.PVCRef != nil {
|
||||
if nameRegex != nil && !nameRegex.MatchString(volume.PVCRef.Name) {
|
||||
klog.V(2).Infof("Skipping pvc %s/%s due to name regex filter", volume.PVCRef.Namespace, volume.PVCRef.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
// Calculate the usage
|
||||
pvcName := fmt.Sprintf("%s/%s", volume.PVCRef.Namespace, volume.PVCRef.Name)
|
||||
|
||||
used := volume.UsedBytes
|
||||
capacity := volume.CapacityBytes
|
||||
if used != nil && capacity != nil {
|
||||
pvcUsedPercentage := float64(*used) / float64(*capacity) * 100
|
||||
stats = append(stats, pvcUsageStats{
|
||||
PvcName: pvcName,
|
||||
Used: pvcUsedPercentage,
|
||||
})
|
||||
klog.V(2).Infof("PVC usage for %s: %0.2f%%", pvcName, pvcUsedPercentage)
|
||||
} else {
|
||||
klog.V(2).Infof("Missing capacity or used bytes for PVC %s", pvcName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// compareNodeMetricConditionalsToStats compares the conditional with the collected node metrics
|
||||
// and returns true if the conditional is met. At the moment we only support comparing PVC usage
|
||||
func (a *AnalyzeNodeMetrics) compareNodeMetricConditionalsToStats(conditional string, summaries []kubeletv1alpha1.Summary) (bool, nodeMetricsComparisonResults, error) {
|
||||
klog.V(2).Infof("Comparing node metrics with conditional: %s", conditional)
|
||||
parts := strings.Split(strings.TrimSpace(conditional), " ")
|
||||
out := nodeMetricsComparisonResults{}
|
||||
|
||||
if len(parts) != 3 {
|
||||
return false, out, errors.New("unable to parse conditional")
|
||||
}
|
||||
|
||||
switch parts[0] {
|
||||
case "pvcUsedPercentage":
|
||||
// e.g pvcUsedPercentage >= 50.4
|
||||
|
||||
klog.V(2).Infof("Analyzing volume usage stats for PVCs")
|
||||
|
||||
op, err := ParseComparisonOperator(parts[1])
|
||||
if err != nil {
|
||||
return false, out, errors.Wrap(err, "failed to parse comparison operator")
|
||||
}
|
||||
|
||||
expected, err := strconv.ParseFloat(parts[2], 64)
|
||||
if err != nil {
|
||||
return false, out, errors.Wrap(err, "failed to parse bool")
|
||||
}
|
||||
|
||||
// Pick all PVCs from all summaries. Filters will be applied here
|
||||
pvcUsageStats, err := a.findPVCUsageStats(summaries)
|
||||
if err != nil {
|
||||
return false, out, errors.Wrap(err, "failed to find PVC usage stats")
|
||||
}
|
||||
matchedPVCs := []string{}
|
||||
|
||||
for _, pvcUsage := range pvcUsageStats {
|
||||
value := pvcUsage.Used
|
||||
switch op {
|
||||
case Equal:
|
||||
if value == expected {
|
||||
matchedPVCs = append(matchedPVCs, pvcUsage.PvcName)
|
||||
}
|
||||
case NotEqual:
|
||||
if value != expected {
|
||||
matchedPVCs = append(matchedPVCs, pvcUsage.PvcName)
|
||||
}
|
||||
case LessThan:
|
||||
if value < expected {
|
||||
matchedPVCs = append(matchedPVCs, pvcUsage.PvcName)
|
||||
}
|
||||
case GreaterThan:
|
||||
if value > expected {
|
||||
matchedPVCs = append(matchedPVCs, pvcUsage.PvcName)
|
||||
}
|
||||
case LessThanOrEqual:
|
||||
if value <= expected {
|
||||
matchedPVCs = append(matchedPVCs, pvcUsage.PvcName)
|
||||
}
|
||||
case GreaterThanOrEqual:
|
||||
if value >= expected {
|
||||
matchedPVCs = append(matchedPVCs, pvcUsage.PvcName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Concatenate all matched PVC names
|
||||
out.PVC = pvcTemplateData{
|
||||
Names: matchedPVCs,
|
||||
ConcatenatedNames: strings.Join(matchedPVCs, ", "),
|
||||
}
|
||||
return len(matchedPVCs) > 0, out, nil
|
||||
}
|
||||
|
||||
return false, out, errors.New("unknown node metric conditional")
|
||||
}
|
||||
|
||||
func renderTemplate(tmpMsg string, data any) string {
|
||||
if data == nil {
|
||||
return tmpMsg
|
||||
}
|
||||
|
||||
t, err := template.New("msg").Parse(tmpMsg)
|
||||
if err != nil {
|
||||
klog.V(2).Infof("Failed to parse template: %s", err)
|
||||
return tmpMsg
|
||||
}
|
||||
|
||||
var m bytes.Buffer
|
||||
err = t.Execute(&m, data)
|
||||
if err != nil {
|
||||
klog.V(2).Infof("Failed to execute template: %s", err)
|
||||
return tmpMsg
|
||||
}
|
||||
|
||||
return m.String()
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
kubeletv1alpha1 "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
|
||||
utilptr "k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
func TestAnalyzeNodeMetrics_findPVCUsageStats(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
analyzer troubleshootv1beta2.NodeMetricsAnalyze
|
||||
summaries []kubeletv1alpha1.Summary
|
||||
want []pvcUsageStats
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no summaries",
|
||||
summaries: []kubeletv1alpha1.Summary{},
|
||||
want: []pvcUsageStats{},
|
||||
},
|
||||
{
|
||||
name: "one summary",
|
||||
summaries: []kubeletv1alpha1.Summary{
|
||||
{
|
||||
Pods: []kubeletv1alpha1.PodStats{
|
||||
{
|
||||
PodRef: kubeletv1alpha1.PodReference{
|
||||
Namespace: "default",
|
||||
Name: "my-pod",
|
||||
},
|
||||
VolumeStats: []kubeletv1alpha1.VolumeStats{
|
||||
{
|
||||
Name: "volume-1",
|
||||
PVCRef: &kubeletv1alpha1.PVCReference{
|
||||
Namespace: "default",
|
||||
Name: "my-pvc",
|
||||
},
|
||||
FsStats: kubeletv1alpha1.FsStats{
|
||||
AvailableBytes: utilptr.To(uint64(20)),
|
||||
UsedBytes: utilptr.To(uint64(80)),
|
||||
CapacityBytes: utilptr.To(uint64(100)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []pvcUsageStats{
|
||||
{
|
||||
Used: 80,
|
||||
PvcName: "default/my-pvc",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "one summary with namespace filter",
|
||||
analyzer: troubleshootv1beta2.NodeMetricsAnalyze{
|
||||
Filters: troubleshootv1beta2.NodeMetricsAnalyzeFilters{
|
||||
PVC: &troubleshootv1beta2.PVCRef{
|
||||
Namespace: "another-namespace",
|
||||
},
|
||||
},
|
||||
},
|
||||
summaries: []kubeletv1alpha1.Summary{
|
||||
{
|
||||
Pods: []kubeletv1alpha1.PodStats{
|
||||
{
|
||||
PodRef: kubeletv1alpha1.PodReference{
|
||||
Namespace: "default",
|
||||
Name: "my-pod",
|
||||
},
|
||||
VolumeStats: []kubeletv1alpha1.VolumeStats{
|
||||
{
|
||||
Name: "volume-1",
|
||||
PVCRef: &kubeletv1alpha1.PVCReference{
|
||||
Namespace: "default",
|
||||
Name: "my-pvc",
|
||||
},
|
||||
FsStats: kubeletv1alpha1.FsStats{
|
||||
AvailableBytes: utilptr.To(uint64(20)),
|
||||
UsedBytes: utilptr.To(uint64(80)),
|
||||
CapacityBytes: utilptr.To(uint64(100)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []pvcUsageStats{},
|
||||
},
|
||||
{
|
||||
name: "one summary with name regex filter",
|
||||
analyzer: troubleshootv1beta2.NodeMetricsAnalyze{
|
||||
Filters: troubleshootv1beta2.NodeMetricsAnalyzeFilters{
|
||||
PVC: &troubleshootv1beta2.PVCRef{
|
||||
NameRegex: ".*other.*",
|
||||
},
|
||||
},
|
||||
},
|
||||
summaries: []kubeletv1alpha1.Summary{
|
||||
{
|
||||
Pods: []kubeletv1alpha1.PodStats{
|
||||
{
|
||||
PodRef: kubeletv1alpha1.PodReference{
|
||||
Namespace: "default",
|
||||
Name: "my-pod",
|
||||
},
|
||||
VolumeStats: []kubeletv1alpha1.VolumeStats{
|
||||
{
|
||||
Name: "volume-1",
|
||||
PVCRef: &kubeletv1alpha1.PVCReference{
|
||||
Namespace: "default",
|
||||
Name: "my-pvc",
|
||||
},
|
||||
FsStats: kubeletv1alpha1.FsStats{
|
||||
AvailableBytes: utilptr.To(uint64(20)),
|
||||
UsedBytes: utilptr.To(uint64(80)),
|
||||
CapacityBytes: utilptr.To(uint64(100)),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "volume-1",
|
||||
PVCRef: &kubeletv1alpha1.PVCReference{
|
||||
Namespace: "default",
|
||||
Name: "my-other-pvc",
|
||||
},
|
||||
FsStats: kubeletv1alpha1.FsStats{
|
||||
AvailableBytes: utilptr.To(uint64(25)),
|
||||
UsedBytes: utilptr.To(uint64(75)),
|
||||
CapacityBytes: utilptr.To(uint64(100)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []pvcUsageStats{
|
||||
{
|
||||
Used: 75,
|
||||
PvcName: "default/my-other-pvc",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &AnalyzeNodeMetrics{
|
||||
analyzer: &tt.analyzer,
|
||||
}
|
||||
got, err := a.findPVCUsageStats(tt.summaries)
|
||||
assert.Equalf(t, tt.wantErr, err != nil, "AnalyzeNodeMetrics.findPVCUsageStats() error = %v, wantErr %v", err, tt.wantErr)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeNodeMetrics_Analyze(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
analyzer troubleshootv1beta2.NodeMetricsAnalyze
|
||||
nodeMetrics string
|
||||
want []*AnalyzeResult
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no node metrics",
|
||||
analyzer: troubleshootv1beta2.NodeMetricsAnalyze{
|
||||
Filters: troubleshootv1beta2.NodeMetricsAnalyzeFilters{},
|
||||
},
|
||||
nodeMetrics: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid node metrics",
|
||||
analyzer: troubleshootv1beta2.NodeMetricsAnalyze{
|
||||
Filters: troubleshootv1beta2.NodeMetricsAnalyzeFilters{},
|
||||
},
|
||||
nodeMetrics: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "no summaries",
|
||||
analyzer: troubleshootv1beta2.NodeMetricsAnalyze{
|
||||
Filters: troubleshootv1beta2.NodeMetricsAnalyzeFilters{},
|
||||
},
|
||||
nodeMetrics: "{}",
|
||||
want: []*AnalyzeResult{},
|
||||
},
|
||||
{
|
||||
name: "one summary with name regex filter",
|
||||
analyzer: troubleshootv1beta2.NodeMetricsAnalyze{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "pvcUsedPercentage >= 75",
|
||||
Message: "PVC space usage is too high for pvcs [{{ .PVC.ConcatenatedNames }}]",
|
||||
},
|
||||
},
|
||||
{
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
Message: "No PVCs are using more than 80% of storage",
|
||||
},
|
||||
},
|
||||
},
|
||||
Filters: troubleshootv1beta2.NodeMetricsAnalyzeFilters{
|
||||
PVC: &troubleshootv1beta2.PVCRef{
|
||||
NameRegex: ".*other.*",
|
||||
},
|
||||
},
|
||||
},
|
||||
nodeMetrics: `{
|
||||
"pods": [
|
||||
{
|
||||
"podRef": {
|
||||
"name": "my-pod",
|
||||
"namespace": "my-namespace"
|
||||
},
|
||||
"volume": [
|
||||
{
|
||||
"capacityBytes": 100,
|
||||
"usedBytes": 80,
|
||||
"pvcRef": {
|
||||
"name": "backup-pvc",
|
||||
"namespace": "my-namespace"
|
||||
}
|
||||
},
|
||||
{
|
||||
"capacityBytes": 100,
|
||||
"usedBytes": 75,
|
||||
"pvcRef": {
|
||||
"name": "another-pvc",
|
||||
"namespace": "my-namespace"
|
||||
}
|
||||
},
|
||||
{
|
||||
"capacityBytes": 100,
|
||||
"usedBytes": 80,
|
||||
"pvcRef": {
|
||||
"name": "the-other-pvc",
|
||||
"namespace": "my-namespace"
|
||||
}
|
||||
},
|
||||
{
|
||||
"capacityBytes": 100,
|
||||
"usedBytes": 65,
|
||||
"pvcRef": {
|
||||
"name": "to-other-pvc",
|
||||
"namespace": "my-namespace"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
want: []*AnalyzeResult{
|
||||
{
|
||||
Title: "Node Metrics",
|
||||
IsFail: true,
|
||||
Message: "PVC space usage is too high for pvcs [my-namespace/another-pvc, my-namespace/the-other-pvc]",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &AnalyzeNodeMetrics{
|
||||
analyzer: &tt.analyzer,
|
||||
}
|
||||
filesFn := func(string, []string) (map[string][]byte, error) {
|
||||
return map[string][]byte{
|
||||
"node-metrics.json": []byte(tt.nodeMetrics),
|
||||
}, nil
|
||||
}
|
||||
|
||||
got, err := a.Analyze(nil, filesFn)
|
||||
assert.Equalf(t, tt.wantErr, err != nil, "AnalyzeNodeMetrics.Analyze() error = %v, wantErr %v", err, tt.wantErr)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,10 @@ func analyzeOneReplicaSetStatus(analyzer *troubleshootv1beta2.ReplicaSetStatus,
|
||||
}
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
|
||||
|
||||
+15
-12
@@ -361,11 +361,9 @@ func analyzeBackups(backups []*velerov1.Backup) []*AnalyzeResult {
|
||||
results := []*AnalyzeResult{}
|
||||
|
||||
failedPhases := map[velerov1.BackupPhase]bool{
|
||||
velerov1.BackupPhaseFailed: true,
|
||||
velerov1.BackupPhasePartiallyFailed: true,
|
||||
velerov1.BackupPhaseFailedValidation: true,
|
||||
velerov1.BackupPhaseFinalizingPartiallyFailed: true,
|
||||
velerov1.BackupPhaseWaitingForPluginOperationsPartiallyFailed: true,
|
||||
velerov1.BackupPhaseFailed: true,
|
||||
velerov1.BackupPhasePartiallyFailed: true,
|
||||
velerov1.BackupPhaseFailedValidation: true,
|
||||
}
|
||||
|
||||
for _, backup := range backups {
|
||||
@@ -510,10 +508,9 @@ func analyzeRestores(restores []*velerov1.Restore) []*AnalyzeResult {
|
||||
if len(restores) > 0 {
|
||||
|
||||
failedPhases := map[velerov1.RestorePhase]bool{
|
||||
velerov1.RestorePhaseFailed: true,
|
||||
velerov1.RestorePhasePartiallyFailed: true,
|
||||
velerov1.RestorePhaseFailedValidation: true,
|
||||
velerov1.RestorePhaseWaitingForPluginOperationsPartiallyFailed: true,
|
||||
velerov1.RestorePhaseFailed: true,
|
||||
velerov1.RestorePhasePartiallyFailed: true,
|
||||
velerov1.RestorePhaseFailedValidation: true,
|
||||
}
|
||||
|
||||
for _, restore := range restores {
|
||||
@@ -658,17 +655,23 @@ func getVeleroVersion(excludedFiles []string, findFiles getChildCollectedFileCon
|
||||
veleroDeploymentGlob := filepath.Join(veleroDeploymentDir, "velero.json")
|
||||
veleroDeploymentJson, err := findFiles(veleroDeploymentGlob, excludedFiles)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to find velero deployment file under %s", veleroDeploymentDir)
|
||||
return "", errors.Wrapf(err, "failed to find Velero deployment")
|
||||
}
|
||||
if len(veleroDeploymentJson) == 0 {
|
||||
return "", errors.Errorf("could not find Velero deployment in %s", veleroDeploymentDir)
|
||||
}
|
||||
var deploymentList *appsV1.DeploymentList
|
||||
// should run only once
|
||||
for key, veleroDeploymentJsonBytes := range veleroDeploymentJson {
|
||||
err := json.Unmarshal(veleroDeploymentJsonBytes, &deploymentList)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to unmarshal velero deployment json from %s", key)
|
||||
return "", errors.Wrapf(err, "failed to unmarshal Velero deployment json from %s", key)
|
||||
}
|
||||
break
|
||||
}
|
||||
if deploymentList == nil {
|
||||
return "", errors.Errorf("could not find Velero deployment")
|
||||
}
|
||||
for _, deployment := range deploymentList.Items {
|
||||
for _, container := range deployment.Spec.Template.Spec.Containers {
|
||||
if container.Name == "velero" {
|
||||
@@ -679,7 +682,7 @@ func getVeleroVersion(excludedFiles []string, findFiles getChildCollectedFileCon
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.Errorf("Unable to get velero version. Could not find velero container in deployment!")
|
||||
return "", errors.Errorf("could not find Velero container in deployment")
|
||||
}
|
||||
|
||||
func GetVeleroBackupsDirectory() string {
|
||||
|
||||
@@ -626,32 +626,6 @@ func TestAnalyzeVelero_Restores(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "restores - failures",
|
||||
args: args{
|
||||
restores: []*velerov1.Restore{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "observability-backup-20210308150016",
|
||||
Namespace: "velero",
|
||||
},
|
||||
Spec: velerov1.RestoreSpec{
|
||||
BackupName: "observability-backup",
|
||||
},
|
||||
Status: velerov1.RestoreStatus{
|
||||
Phase: velerov1.RestorePhaseWaitingForPluginOperationsPartiallyFailed,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []*AnalyzeResult{
|
||||
{
|
||||
Title: "Restore observability-backup-20210308150016",
|
||||
Message: "Restore observability-backup-20210308150016 phase is WaitingForPluginOperationsPartiallyFailed",
|
||||
IsFail: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
util "github.com/replicatedhq/troubleshoot/internal/util"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
iutils "github.com/replicatedhq/troubleshoot/pkg/interfaceutils"
|
||||
"gopkg.in/yaml.v2"
|
||||
@@ -50,6 +51,8 @@ func (a *AnalyzeYamlCompare) analyzeYamlCompare(analyzer *troubleshootv1beta2.Ya
|
||||
return nil, errors.Wrap(err, "failed to parse collected data as yaml doc")
|
||||
}
|
||||
|
||||
originalActual := actual
|
||||
|
||||
if analyzer.Path != "" {
|
||||
actual, err = iutils.GetAtPath(actual, analyzer.Path)
|
||||
if err != nil {
|
||||
@@ -81,6 +84,11 @@ func (a *AnalyzeYamlCompare) analyzeYamlCompare(analyzer *troubleshootv1beta2.Ya
|
||||
}
|
||||
}
|
||||
|
||||
outcome.Fail.Message, err = util.RenderTemplate(outcome.Fail.Message, originalActual)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to render template on outcome message")
|
||||
}
|
||||
|
||||
if when == equal {
|
||||
result.IsFail = true
|
||||
result.Message = outcome.Fail.Message
|
||||
@@ -96,6 +104,11 @@ func (a *AnalyzeYamlCompare) analyzeYamlCompare(analyzer *troubleshootv1beta2.Ya
|
||||
}
|
||||
}
|
||||
|
||||
outcome.Warn.Message, err = util.RenderTemplate(outcome.Warn.Message, originalActual)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to render template on outcome message")
|
||||
}
|
||||
|
||||
if when == equal {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
@@ -111,6 +124,11 @@ func (a *AnalyzeYamlCompare) analyzeYamlCompare(analyzer *troubleshootv1beta2.Ya
|
||||
}
|
||||
}
|
||||
|
||||
outcome.Pass.Message, err = util.RenderTemplate(outcome.Pass.Message, originalActual)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to render template on outcome message")
|
||||
}
|
||||
|
||||
if when == equal {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
|
||||
@@ -430,6 +430,45 @@ otherstuff:
|
||||
},
|
||||
fileContents: []byte(``),
|
||||
},
|
||||
{
|
||||
name: "basic comparison with outcome message templated",
|
||||
analyzer: troubleshootv1beta2.YamlCompare{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
Message: "Status: {{ .stuff.status }}, Info: {{ .stuff.info }}",
|
||||
When: "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
Message: "Status: {{ .stuff.status }}, Info: {{ .stuff.info }}",
|
||||
When: "false",
|
||||
},
|
||||
},
|
||||
},
|
||||
CollectorName: "yaml-compare",
|
||||
FileName: "yaml-compare.yaml",
|
||||
Value: `ready`,
|
||||
Path: "stuff.status",
|
||||
},
|
||||
expectResult: AnalyzeResult{
|
||||
IsPass: true,
|
||||
IsWarn: false,
|
||||
IsFail: false,
|
||||
Title: "yaml-compare",
|
||||
Message: "Status: ready, Info: stuff is ready",
|
||||
IconKey: "kubernetes_text_analyze",
|
||||
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
|
||||
},
|
||||
fileContents: []byte(`
|
||||
stuff:
|
||||
status: ready
|
||||
info: stuff is ready
|
||||
morestuff:
|
||||
status: notready
|
||||
info: morestuff is not ready`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2019 Replicated, Inc..
|
||||
|
||||
@@ -225,6 +225,39 @@ type CertificatesAnalyze struct {
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type GoldpingerAnalyze struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
Outcomes []*Outcome `json:"outcomes,omitempty" yaml:"outcomes,omitempty"`
|
||||
CollectorName string `json:"collectorName" yaml:"collectorName"`
|
||||
FilePath string `json:"filePath,omitempty" yaml:"filePath,omitempty"`
|
||||
}
|
||||
|
||||
type EventAnalyze struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
CollectorName string `json:"collectorName" yaml:"collectorName"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||
Reason string `json:"reason" yaml:"reason"`
|
||||
RegexPattern string `json:"regex,omitempty" yaml:"regex,omitempty"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type NodeMetricsAnalyze struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
CollectorName string `json:"collectorName" yaml:"collectorName"`
|
||||
Filters NodeMetricsAnalyzeFilters `json:"filters,omitempty" yaml:"filters,omitempty"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type NodeMetricsAnalyzeFilters struct {
|
||||
PVC *PVCRef `json:"pvc,omitempty" yaml:"pvc,omitempty"`
|
||||
}
|
||||
|
||||
type PVCRef struct {
|
||||
NameRegex string `json:"nameRegex,omitempty" yaml:"nameRegex,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
type Analyze struct {
|
||||
ClusterVersion *ClusterVersion `json:"clusterVersion,omitempty" yaml:"clusterVersion,omitempty"`
|
||||
StorageClass *StorageClass `json:"storageClass,omitempty" yaml:"storageClass,omitempty"`
|
||||
@@ -256,4 +289,7 @@ type Analyze struct {
|
||||
Sysctl *SysctlAnalyze `json:"sysctl,omitempty" yaml:"sysctl,omitempty"`
|
||||
ClusterResource *ClusterResource `json:"clusterResource,omitempty" yaml:"clusterResource,omitempty"`
|
||||
Certificates *CertificatesAnalyze `json:"certificates,omitempty" yaml:"certificates,omitempty"`
|
||||
Goldpinger *GoldpingerAnalyze `json:"goldpinger,omitempty" yaml:"goldpinger,omitempty"`
|
||||
Event *EventAnalyze `json:"event,omitempty" yaml:"event,omitempty"`
|
||||
NodeMetrics *NodeMetricsAnalyze `json:"nodeMetrics,omitempty" yaml:"nodeMetrics,omitempty"`
|
||||
}
|
||||
|
||||
@@ -44,6 +44,12 @@ type CustomMetrics struct {
|
||||
MetricRequests []MetricRequest `json:"metricRequests,omitempty" yaml:"metricRequests,omitempty"`
|
||||
}
|
||||
|
||||
type NodeMetrics struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
NodeNames []string `json:"nodeNames,omitempty" yaml:"nodeNames,omitempty"`
|
||||
Selector []string `json:"selector,omitempty" yaml:"selector,omitempty"`
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
@@ -107,6 +113,15 @@ type RunPod struct {
|
||||
PodSpec corev1.PodSpec `json:"podSpec,omitempty" yaml:"podSpec,omitempty"`
|
||||
}
|
||||
|
||||
type RunDaemonSet struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Namespace string `json:"namespace" yaml:"namespace"`
|
||||
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
|
||||
ImagePullSecret *ImagePullSecrets `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
|
||||
PodSpec corev1.PodSpec `json:"podSpec,omitempty" yaml:"podSpec,omitempty"`
|
||||
}
|
||||
|
||||
type ImagePullSecrets struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Data map[string]string `json:"data,omitempty" yaml:"data,omitempty"`
|
||||
@@ -257,6 +272,30 @@ type Helm struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
ReleaseName string `json:"releaseName,omitempty" yaml:"releaseName,omitempty"`
|
||||
CollectValues bool `json:"collectValues,omitempty" yaml:"collectValues,omitempty"`
|
||||
}
|
||||
|
||||
type Goldpinger struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
PodLaunchOptions *PodLaunchOptions `json:"podLaunchOptions,omitempty" yaml:"podLaunchOptions,omitempty"`
|
||||
}
|
||||
|
||||
type PodLaunchOptions struct {
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
Image string `json:"image,omitempty" yaml:"image,omitempty"`
|
||||
ImagePullSecret *ImagePullSecrets `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
|
||||
ServiceAccountName string `json:"serviceAccountName,omitempty" yaml:"serviceAccountName,omitempty"`
|
||||
}
|
||||
|
||||
type Sonobuoy struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
type DNS struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
type Collect struct {
|
||||
@@ -268,6 +307,7 @@ type Collect struct {
|
||||
Logs *Logs `json:"logs,omitempty" yaml:"logs,omitempty"`
|
||||
Run *Run `json:"run,omitempty" yaml:"run,omitempty"`
|
||||
RunPod *RunPod `json:"runPod,omitempty" yaml:"runPod,omitempty"`
|
||||
RunDaemonSet *RunDaemonSet `json:"runDaemonSet,omitempty" yaml:"runDaemonSet,omitempty"`
|
||||
Exec *Exec `json:"exec,omitempty" yaml:"exec,omitempty"`
|
||||
Data *Data `json:"data,omitempty" yaml:"data,omitempty"`
|
||||
Copy *Copy `json:"copy,omitempty" yaml:"copy,omitempty"`
|
||||
@@ -284,6 +324,10 @@ type Collect struct {
|
||||
Sysctl *Sysctl `json:"sysctl,omitempty" yaml:"sysctl,omitempty"`
|
||||
Certificates *Certificates `json:"certificates,omitempty" yaml:"certificates,omitempty"`
|
||||
Helm *Helm `json:"helm,omitempty" yaml:"helm,omitempty"`
|
||||
Goldpinger *Goldpinger `json:"goldpinger,omitempty" yaml:"goldpinger,omitempty"`
|
||||
Sonobuoy *Sonobuoy `json:"sonobuoy,omitempty" yaml:"sonobuoy,omitempty"`
|
||||
NodeMetrics *NodeMetrics `json:"nodeMetrics,omitempty" yaml:"nodeMetrics,omitempty"`
|
||||
DNS *DNS `json:"dns,omitempty" yaml:"dns,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Collect) AccessReviewSpecs(overrideNS string) []authorizationv1.SelfSubjectAccessReviewSpec {
|
||||
@@ -417,6 +461,19 @@ func (c *Collect) AccessReviewSpecs(overrideNS string) []authorizationv1.SelfSub
|
||||
},
|
||||
NonResourceAttributes: nil,
|
||||
})
|
||||
} else if c.RunDaemonSet != nil {
|
||||
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationv1.ResourceAttributes{
|
||||
Namespace: pickNamespaceOrDefault(c.RunDaemonSet.Namespace, overrideNS),
|
||||
Verb: "create",
|
||||
Group: "",
|
||||
Version: "",
|
||||
Resource: "pods",
|
||||
Subresource: "",
|
||||
Name: "",
|
||||
},
|
||||
NonResourceAttributes: nil,
|
||||
})
|
||||
} else if c.Exec != nil {
|
||||
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationv1.ResourceAttributes{
|
||||
@@ -527,6 +584,10 @@ func (c *Collect) GetName() string {
|
||||
collector = "run-pod"
|
||||
name = c.RunPod.CollectorName
|
||||
}
|
||||
if c.RunDaemonSet != nil {
|
||||
collector = "run-daemonset"
|
||||
name = c.RunDaemonSet.CollectorName
|
||||
}
|
||||
if c.Exec != nil {
|
||||
collector = "exec"
|
||||
name = c.Exec.CollectorName
|
||||
|
||||
@@ -187,6 +187,7 @@ type HostRun struct {
|
||||
Env []string `json:"env,omitempty" yaml:"env,omitempty"`
|
||||
InheritEnvs []string `json:"inheritEnvs,omitempty" yaml:"inheritEnvs,omitempty"`
|
||||
IgnoreParentEnvs bool `json:"ignoreParentEnvs,omitempty" yaml:"ignoreParentEnvs,omitempty"`
|
||||
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
type HostCollect struct {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2019 Replicated, Inc..
|
||||
@@ -204,6 +203,21 @@ func (in *Analyze) DeepCopyInto(out *Analyze) {
|
||||
*out = new(CertificatesAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Goldpinger != nil {
|
||||
in, out := &in.Goldpinger, &out.Goldpinger
|
||||
*out = new(GoldpingerAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Event != nil {
|
||||
in, out := &in.Event, &out.Event
|
||||
*out = new(EventAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.NodeMetrics != nil {
|
||||
in, out := &in.NodeMetrics, &out.NodeMetrics
|
||||
*out = new(NodeMetricsAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Analyze.
|
||||
@@ -812,6 +826,11 @@ func (in *Collect) DeepCopyInto(out *Collect) {
|
||||
*out = new(RunPod)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.RunDaemonSet != nil {
|
||||
in, out := &in.RunDaemonSet, &out.RunDaemonSet
|
||||
*out = new(RunDaemonSet)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Exec != nil {
|
||||
in, out := &in.Exec, &out.Exec
|
||||
*out = new(Exec)
|
||||
@@ -892,6 +911,26 @@ func (in *Collect) DeepCopyInto(out *Collect) {
|
||||
*out = new(Helm)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Goldpinger != nil {
|
||||
in, out := &in.Goldpinger, &out.Goldpinger
|
||||
*out = new(Goldpinger)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Sonobuoy != nil {
|
||||
in, out := &in.Sonobuoy, &out.Sonobuoy
|
||||
*out = new(Sonobuoy)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.NodeMetrics != nil {
|
||||
in, out := &in.NodeMetrics, &out.NodeMetrics
|
||||
*out = new(NodeMetrics)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.DNS != nil {
|
||||
in, out := &in.DNS, &out.DNS
|
||||
*out = new(DNS)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Collect.
|
||||
@@ -1221,6 +1260,22 @@ func (in *CustomResourceDefinition) DeepCopy() *CustomResourceDefinition {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DNS) DeepCopyInto(out *DNS) {
|
||||
*out = *in
|
||||
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNS.
|
||||
func (in *DNS) DeepCopy() *DNS {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DNS)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Data) DeepCopyInto(out *Data) {
|
||||
*out = *in
|
||||
@@ -1392,6 +1447,33 @@ func (in *Distribution) DeepCopy() *Distribution {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *EventAnalyze) DeepCopyInto(out *EventAnalyze) {
|
||||
*out = *in
|
||||
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventAnalyze.
|
||||
func (in *EventAnalyze) DeepCopy() *EventAnalyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(EventAnalyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Exec) DeepCopyInto(out *Exec) {
|
||||
*out = *in
|
||||
@@ -1508,6 +1590,54 @@ func (in *Get) DeepCopy() *Get {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Goldpinger) DeepCopyInto(out *Goldpinger) {
|
||||
*out = *in
|
||||
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
|
||||
if in.PodLaunchOptions != nil {
|
||||
in, out := &in.PodLaunchOptions, &out.PodLaunchOptions
|
||||
*out = new(PodLaunchOptions)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Goldpinger.
|
||||
func (in *Goldpinger) DeepCopy() *Goldpinger {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Goldpinger)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GoldpingerAnalyze) DeepCopyInto(out *GoldpingerAnalyze) {
|
||||
*out = *in
|
||||
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GoldpingerAnalyze.
|
||||
func (in *GoldpingerAnalyze) DeepCopy() *GoldpingerAnalyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GoldpingerAnalyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTP) DeepCopyInto(out *HTTP) {
|
||||
*out = *in
|
||||
@@ -2885,6 +3015,80 @@ func (in *MetricRequest) DeepCopy() *MetricRequest {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeMetrics) DeepCopyInto(out *NodeMetrics) {
|
||||
*out = *in
|
||||
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
|
||||
if in.NodeNames != nil {
|
||||
in, out := &in.NodeNames, &out.NodeNames
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Selector != nil {
|
||||
in, out := &in.Selector, &out.Selector
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeMetrics.
|
||||
func (in *NodeMetrics) DeepCopy() *NodeMetrics {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NodeMetrics)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeMetricsAnalyze) DeepCopyInto(out *NodeMetricsAnalyze) {
|
||||
*out = *in
|
||||
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
|
||||
in.Filters.DeepCopyInto(&out.Filters)
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeMetricsAnalyze.
|
||||
func (in *NodeMetricsAnalyze) DeepCopy() *NodeMetricsAnalyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NodeMetricsAnalyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeMetricsAnalyzeFilters) DeepCopyInto(out *NodeMetricsAnalyzeFilters) {
|
||||
*out = *in
|
||||
if in.PVC != nil {
|
||||
in, out := &in.PVC, &out.PVC
|
||||
*out = new(PVCRef)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeMetricsAnalyzeFilters.
|
||||
func (in *NodeMetricsAnalyzeFilters) DeepCopy() *NodeMetricsAnalyzeFilters {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NodeMetricsAnalyzeFilters)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeResourceFilters) DeepCopyInto(out *NodeResourceFilters) {
|
||||
*out = *in
|
||||
@@ -2989,6 +3193,41 @@ func (in *Outcome) DeepCopy() *Outcome {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PVCRef) DeepCopyInto(out *PVCRef) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PVCRef.
|
||||
func (in *PVCRef) DeepCopy() *PVCRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PVCRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodLaunchOptions) DeepCopyInto(out *PodLaunchOptions) {
|
||||
*out = *in
|
||||
if in.ImagePullSecret != nil {
|
||||
in, out := &in.ImagePullSecret, &out.ImagePullSecret
|
||||
*out = new(ImagePullSecrets)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodLaunchOptions.
|
||||
func (in *PodLaunchOptions) DeepCopy() *PodLaunchOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PodLaunchOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Post) DeepCopyInto(out *Post) {
|
||||
*out = *in
|
||||
@@ -4000,6 +4239,28 @@ func (in *Run) DeepCopy() *Run {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RunDaemonSet) DeepCopyInto(out *RunDaemonSet) {
|
||||
*out = *in
|
||||
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
|
||||
if in.ImagePullSecret != nil {
|
||||
in, out := &in.ImagePullSecret, &out.ImagePullSecret
|
||||
*out = new(ImagePullSecrets)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.PodSpec.DeepCopyInto(&out.PodSpec)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunDaemonSet.
|
||||
func (in *RunDaemonSet) DeepCopy() *RunDaemonSet {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RunDaemonSet)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RunPod) DeepCopyInto(out *RunPod) {
|
||||
*out = *in
|
||||
@@ -4058,6 +4319,22 @@ func (in *SingleOutcome) DeepCopy() *SingleOutcome {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Sonobuoy) DeepCopyInto(out *Sonobuoy) {
|
||||
*out = *in
|
||||
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sonobuoy.
|
||||
func (in *Sonobuoy) DeepCopy() *Sonobuoy {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Sonobuoy)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StatefulsetStatus) DeepCopyInto(out *StatefulsetStatus) {
|
||||
*out = *in
|
||||
|
||||
@@ -19,6 +19,7 @@ package troubleshootclientset
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2"
|
||||
@@ -33,8 +34,7 @@ type Interface interface {
|
||||
TroubleshootV1beta2() troubleshootv1beta2.TroubleshootV1beta2Interface
|
||||
}
|
||||
|
||||
// Clientset contains the clients for groups. Each group has exactly one
|
||||
// version included in a Clientset.
|
||||
// Clientset contains the clients for groups.
|
||||
type Clientset struct {
|
||||
*discovery.DiscoveryClient
|
||||
troubleshootV1beta1 *troubleshootv1beta1.TroubleshootV1beta1Client
|
||||
@@ -62,26 +62,49 @@ func (c *Clientset) Discovery() discovery.DiscoveryInterface {
|
||||
// NewForConfig creates a new Clientset for the given config.
|
||||
// If config's RateLimiter is not set and QPS and Burst are acceptable,
|
||||
// NewForConfig will generate a rate-limiter in configShallowCopy.
|
||||
// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
|
||||
// where httpClient was generated with rest.HTTPClientFor(c).
|
||||
func NewForConfig(c *rest.Config) (*Clientset, error) {
|
||||
configShallowCopy := *c
|
||||
|
||||
if configShallowCopy.UserAgent == "" {
|
||||
configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent()
|
||||
}
|
||||
|
||||
// share the transport between all clients
|
||||
httpClient, err := rest.HTTPClientFor(&configShallowCopy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewForConfigAndClient(&configShallowCopy, httpClient)
|
||||
}
|
||||
|
||||
// NewForConfigAndClient creates a new Clientset for the given config and http client.
|
||||
// Note the http client provided takes precedence over the configured transport values.
|
||||
// If config's RateLimiter is not set and QPS and Burst are acceptable,
|
||||
// NewForConfigAndClient will generate a rate-limiter in configShallowCopy.
|
||||
func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) {
|
||||
configShallowCopy := *c
|
||||
if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
|
||||
if configShallowCopy.Burst <= 0 {
|
||||
return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
|
||||
}
|
||||
configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
|
||||
}
|
||||
|
||||
var cs Clientset
|
||||
var err error
|
||||
cs.troubleshootV1beta1, err = troubleshootv1beta1.NewForConfig(&configShallowCopy)
|
||||
cs.troubleshootV1beta1, err = troubleshootv1beta1.NewForConfigAndClient(&configShallowCopy, httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cs.troubleshootV1beta2, err = troubleshootv1beta2.NewForConfig(&configShallowCopy)
|
||||
cs.troubleshootV1beta2, err = troubleshootv1beta2.NewForConfigAndClient(&configShallowCopy, httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
|
||||
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -91,12 +114,11 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
|
||||
// NewForConfigOrDie creates a new Clientset for the given config and
|
||||
// panics if there is an error in the config.
|
||||
func NewForConfigOrDie(c *rest.Config) *Clientset {
|
||||
var cs Clientset
|
||||
cs.troubleshootV1beta1 = troubleshootv1beta1.NewForConfigOrDie(c)
|
||||
cs.troubleshootV1beta2 = troubleshootv1beta2.NewForConfigOrDie(c)
|
||||
|
||||
cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
|
||||
return &cs
|
||||
cs, err := NewForConfig(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cs
|
||||
}
|
||||
|
||||
// New creates a new Clientset for the given RESTClient.
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakeAnalyzers struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var analyzersResource = schema.GroupVersionResource{Group: "troubleshoot.replicated.com", Version: "v1beta1", Resource: "analyzers"}
|
||||
var analyzersResource = v1beta1.SchemeGroupVersion.WithResource("analyzers")
|
||||
|
||||
var analyzersKind = schema.GroupVersionKind{Group: "troubleshoot.replicated.com", Version: "v1beta1", Kind: "Analyzer"}
|
||||
var analyzersKind = v1beta1.SchemeGroupVersion.WithKind("Analyzer")
|
||||
|
||||
// Get takes name of the analyzer, and returns the corresponding analyzer object, and an error if there is any.
|
||||
func (c *FakeAnalyzers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Analyzer, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakeAnalyzers) UpdateStatus(ctx context.Context, analyzer *v1beta1.Anal
|
||||
// Delete takes name of the analyzer and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeAnalyzers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(analyzersResource, c.ns, name), &v1beta1.Analyzer{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(analyzersResource, c.ns, name, opts), &v1beta1.Analyzer{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakeCollectors struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var collectorsResource = schema.GroupVersionResource{Group: "troubleshoot.replicated.com", Version: "v1beta1", Resource: "collectors"}
|
||||
var collectorsResource = v1beta1.SchemeGroupVersion.WithResource("collectors")
|
||||
|
||||
var collectorsKind = schema.GroupVersionKind{Group: "troubleshoot.replicated.com", Version: "v1beta1", Kind: "Collector"}
|
||||
var collectorsKind = v1beta1.SchemeGroupVersion.WithKind("Collector")
|
||||
|
||||
// Get takes name of the collector, and returns the corresponding collector object, and an error if there is any.
|
||||
func (c *FakeCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Collector, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakeCollectors) UpdateStatus(ctx context.Context, collector *v1beta1.Co
|
||||
// Delete takes name of the collector and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(collectorsResource, c.ns, name), &v1beta1.Collector{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(collectorsResource, c.ns, name, opts), &v1beta1.Collector{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakePreflights struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var preflightsResource = schema.GroupVersionResource{Group: "troubleshoot.replicated.com", Version: "v1beta1", Resource: "preflights"}
|
||||
var preflightsResource = v1beta1.SchemeGroupVersion.WithResource("preflights")
|
||||
|
||||
var preflightsKind = schema.GroupVersionKind{Group: "troubleshoot.replicated.com", Version: "v1beta1", Kind: "Preflight"}
|
||||
var preflightsKind = v1beta1.SchemeGroupVersion.WithKind("Preflight")
|
||||
|
||||
// Get takes name of the preflight, and returns the corresponding preflight object, and an error if there is any.
|
||||
func (c *FakePreflights) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Preflight, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakePreflights) UpdateStatus(ctx context.Context, preflight *v1beta1.Pr
|
||||
// Delete takes name of the preflight and deletes it. Returns an error if one occurs.
|
||||
func (c *FakePreflights) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(preflightsResource, c.ns, name), &v1beta1.Preflight{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(preflightsResource, c.ns, name, opts), &v1beta1.Preflight{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakeRedactors struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var redactorsResource = schema.GroupVersionResource{Group: "troubleshoot.replicated.com", Version: "v1beta1", Resource: "redactors"}
|
||||
var redactorsResource = v1beta1.SchemeGroupVersion.WithResource("redactors")
|
||||
|
||||
var redactorsKind = schema.GroupVersionKind{Group: "troubleshoot.replicated.com", Version: "v1beta1", Kind: "Redactor"}
|
||||
var redactorsKind = v1beta1.SchemeGroupVersion.WithKind("Redactor")
|
||||
|
||||
// Get takes name of the redactor, and returns the corresponding redactor object, and an error if there is any.
|
||||
func (c *FakeRedactors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Redactor, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakeRedactors) UpdateStatus(ctx context.Context, redactor *v1beta1.Reda
|
||||
// Delete takes name of the redactor and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeRedactors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(redactorsResource, c.ns, name), &v1beta1.Redactor{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(redactorsResource, c.ns, name, opts), &v1beta1.Redactor{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
+3
-4
@@ -23,7 +23,6 @@ import (
|
||||
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakeSupportBundles struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var supportbundlesResource = schema.GroupVersionResource{Group: "troubleshoot.replicated.com", Version: "v1beta1", Resource: "supportbundles"}
|
||||
var supportbundlesResource = v1beta1.SchemeGroupVersion.WithResource("supportbundles")
|
||||
|
||||
var supportbundlesKind = schema.GroupVersionKind{Group: "troubleshoot.replicated.com", Version: "v1beta1", Kind: "SupportBundle"}
|
||||
var supportbundlesKind = v1beta1.SchemeGroupVersion.WithKind("SupportBundle")
|
||||
|
||||
// Get takes name of the supportBundle, and returns the corresponding supportBundle object, and an error if there is any.
|
||||
func (c *FakeSupportBundles) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.SupportBundle, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakeSupportBundles) UpdateStatus(ctx context.Context, supportBundle *v1
|
||||
// Delete takes name of the supportBundle and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeSupportBundles) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(supportbundlesResource, c.ns, name), &v1beta1.SupportBundle{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(supportbundlesResource, c.ns, name, opts), &v1beta1.SupportBundle{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ limitations under the License.
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme"
|
||||
rest "k8s.io/client-go/rest"
|
||||
@@ -58,12 +60,28 @@ func (c *TroubleshootV1beta1Client) SupportBundles(namespace string) SupportBund
|
||||
}
|
||||
|
||||
// NewForConfig creates a new TroubleshootV1beta1Client for the given config.
|
||||
// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
|
||||
// where httpClient was generated with rest.HTTPClientFor(c).
|
||||
func NewForConfig(c *rest.Config) (*TroubleshootV1beta1Client, error) {
|
||||
config := *c
|
||||
if err := setConfigDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := rest.RESTClientFor(&config)
|
||||
httpClient, err := rest.HTTPClientFor(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewForConfigAndClient(&config, httpClient)
|
||||
}
|
||||
|
||||
// NewForConfigAndClient creates a new TroubleshootV1beta1Client for the given config and http client.
|
||||
// Note the http client provided takes precedence over the configured transport values.
|
||||
func NewForConfigAndClient(c *rest.Config, h *http.Client) (*TroubleshootV1beta1Client, error) {
|
||||
config := *c
|
||||
if err := setConfigDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := rest.RESTClientForConfigAndClient(&config, h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakeAnalyzers struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var analyzersResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "analyzers"}
|
||||
var analyzersResource = v1beta2.SchemeGroupVersion.WithResource("analyzers")
|
||||
|
||||
var analyzersKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "Analyzer"}
|
||||
var analyzersKind = v1beta2.SchemeGroupVersion.WithKind("Analyzer")
|
||||
|
||||
// Get takes name of the analyzer, and returns the corresponding analyzer object, and an error if there is any.
|
||||
func (c *FakeAnalyzers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.Analyzer, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakeAnalyzers) UpdateStatus(ctx context.Context, analyzer *v1beta2.Anal
|
||||
// Delete takes name of the analyzer and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeAnalyzers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(analyzersResource, c.ns, name), &v1beta2.Analyzer{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(analyzersResource, c.ns, name, opts), &v1beta2.Analyzer{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakeCollectors struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var collectorsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "collectors"}
|
||||
var collectorsResource = v1beta2.SchemeGroupVersion.WithResource("collectors")
|
||||
|
||||
var collectorsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "Collector"}
|
||||
var collectorsKind = v1beta2.SchemeGroupVersion.WithKind("Collector")
|
||||
|
||||
// Get takes name of the collector, and returns the corresponding collector object, and an error if there is any.
|
||||
func (c *FakeCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.Collector, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakeCollectors) UpdateStatus(ctx context.Context, collector *v1beta2.Co
|
||||
// Delete takes name of the collector and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(collectorsResource, c.ns, name), &v1beta2.Collector{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(collectorsResource, c.ns, name, opts), &v1beta2.Collector{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
+3
-4
@@ -23,7 +23,6 @@ import (
|
||||
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakeHostCollectors struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var hostcollectorsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "hostcollectors"}
|
||||
var hostcollectorsResource = v1beta2.SchemeGroupVersion.WithResource("hostcollectors")
|
||||
|
||||
var hostcollectorsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "HostCollector"}
|
||||
var hostcollectorsKind = v1beta2.SchemeGroupVersion.WithKind("HostCollector")
|
||||
|
||||
// Get takes name of the hostCollector, and returns the corresponding hostCollector object, and an error if there is any.
|
||||
func (c *FakeHostCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.HostCollector, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakeHostCollectors) UpdateStatus(ctx context.Context, hostCollector *v1
|
||||
// Delete takes name of the hostCollector and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeHostCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(hostcollectorsResource, c.ns, name), &v1beta2.HostCollector{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(hostcollectorsResource, c.ns, name, opts), &v1beta2.HostCollector{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
+3
-4
@@ -23,7 +23,6 @@ import (
|
||||
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakeHostPreflights struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var hostpreflightsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "hostpreflights"}
|
||||
var hostpreflightsResource = v1beta2.SchemeGroupVersion.WithResource("hostpreflights")
|
||||
|
||||
var hostpreflightsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "HostPreflight"}
|
||||
var hostpreflightsKind = v1beta2.SchemeGroupVersion.WithKind("HostPreflight")
|
||||
|
||||
// Get takes name of the hostPreflight, and returns the corresponding hostPreflight object, and an error if there is any.
|
||||
func (c *FakeHostPreflights) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.HostPreflight, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakeHostPreflights) UpdateStatus(ctx context.Context, hostPreflight *v1
|
||||
// Delete takes name of the hostPreflight and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeHostPreflights) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(hostpreflightsResource, c.ns, name), &v1beta2.HostPreflight{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(hostpreflightsResource, c.ns, name, opts), &v1beta2.HostPreflight{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakePreflights struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var preflightsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "preflights"}
|
||||
var preflightsResource = v1beta2.SchemeGroupVersion.WithResource("preflights")
|
||||
|
||||
var preflightsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "Preflight"}
|
||||
var preflightsKind = v1beta2.SchemeGroupVersion.WithKind("Preflight")
|
||||
|
||||
// Get takes name of the preflight, and returns the corresponding preflight object, and an error if there is any.
|
||||
func (c *FakePreflights) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.Preflight, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakePreflights) UpdateStatus(ctx context.Context, preflight *v1beta2.Pr
|
||||
// Delete takes name of the preflight and deletes it. Returns an error if one occurs.
|
||||
func (c *FakePreflights) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(preflightsResource, c.ns, name), &v1beta2.Preflight{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(preflightsResource, c.ns, name, opts), &v1beta2.Preflight{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakeRedactors struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var redactorsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "redactors"}
|
||||
var redactorsResource = v1beta2.SchemeGroupVersion.WithResource("redactors")
|
||||
|
||||
var redactorsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "Redactor"}
|
||||
var redactorsKind = v1beta2.SchemeGroupVersion.WithKind("Redactor")
|
||||
|
||||
// Get takes name of the redactor, and returns the corresponding redactor object, and an error if there is any.
|
||||
func (c *FakeRedactors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.Redactor, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakeRedactors) UpdateStatus(ctx context.Context, redactor *v1beta2.Reda
|
||||
// Delete takes name of the redactor and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeRedactors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(redactorsResource, c.ns, name), &v1beta2.Redactor{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(redactorsResource, c.ns, name, opts), &v1beta2.Redactor{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
+3
-4
@@ -23,7 +23,6 @@ import (
|
||||
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakeRemoteCollectors struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var remotecollectorsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "remotecollectors"}
|
||||
var remotecollectorsResource = v1beta2.SchemeGroupVersion.WithResource("remotecollectors")
|
||||
|
||||
var remotecollectorsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "RemoteCollector"}
|
||||
var remotecollectorsKind = v1beta2.SchemeGroupVersion.WithKind("RemoteCollector")
|
||||
|
||||
// Get takes name of the remoteCollector, and returns the corresponding remoteCollector object, and an error if there is any.
|
||||
func (c *FakeRemoteCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.RemoteCollector, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakeRemoteCollectors) UpdateStatus(ctx context.Context, remoteCollector
|
||||
// Delete takes name of the remoteCollector and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeRemoteCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(remotecollectorsResource, c.ns, name), &v1beta2.RemoteCollector{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(remotecollectorsResource, c.ns, name, opts), &v1beta2.RemoteCollector{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
+3
-4
@@ -23,7 +23,6 @@ import (
|
||||
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
@@ -35,9 +34,9 @@ type FakeSupportBundles struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var supportbundlesResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "supportbundles"}
|
||||
var supportbundlesResource = v1beta2.SchemeGroupVersion.WithResource("supportbundles")
|
||||
|
||||
var supportbundlesKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "SupportBundle"}
|
||||
var supportbundlesKind = v1beta2.SchemeGroupVersion.WithKind("SupportBundle")
|
||||
|
||||
// Get takes name of the supportBundle, and returns the corresponding supportBundle object, and an error if there is any.
|
||||
func (c *FakeSupportBundles) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.SupportBundle, err error) {
|
||||
@@ -116,7 +115,7 @@ func (c *FakeSupportBundles) UpdateStatus(ctx context.Context, supportBundle *v1
|
||||
// Delete takes name of the supportBundle and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeSupportBundles) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(supportbundlesResource, c.ns, name), &v1beta2.SupportBundle{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(supportbundlesResource, c.ns, name, opts), &v1beta2.SupportBundle{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ limitations under the License.
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme"
|
||||
rest "k8s.io/client-go/rest"
|
||||
@@ -73,12 +75,28 @@ func (c *TroubleshootV1beta2Client) SupportBundles(namespace string) SupportBund
|
||||
}
|
||||
|
||||
// NewForConfig creates a new TroubleshootV1beta2Client for the given config.
|
||||
// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
|
||||
// where httpClient was generated with rest.HTTPClientFor(c).
|
||||
func NewForConfig(c *rest.Config) (*TroubleshootV1beta2Client, error) {
|
||||
config := *c
|
||||
if err := setConfigDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := rest.RESTClientFor(&config)
|
||||
httpClient, err := rest.HTTPClientFor(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewForConfigAndClient(&config, httpClient)
|
||||
}
|
||||
|
||||
// NewForConfigAndClient creates a new TroubleshootV1beta2Client for the given config and http client.
|
||||
// Note the http client provided takes precedence over the configured transport values.
|
||||
func NewForConfigAndClient(c *rest.Config, h *http.Client) (*TroubleshootV1beta2Client, error) {
|
||||
config := *c
|
||||
if err := setConfigDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := rest.RESTClientForConfigAndClient(&config, h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+33
-7
@@ -35,6 +35,12 @@ var CephCommands = []CephCommand{
|
||||
Format: "json",
|
||||
DefaultTimeout: "30s",
|
||||
},
|
||||
{
|
||||
ID: "status-txt",
|
||||
Command: []string{"ceph", "status"},
|
||||
Format: "txt",
|
||||
DefaultTimeout: "30s",
|
||||
},
|
||||
{
|
||||
ID: "fs",
|
||||
Command: []string{"ceph", "fs", "status"},
|
||||
@@ -42,6 +48,12 @@ var CephCommands = []CephCommand{
|
||||
Format: "json",
|
||||
DefaultTimeout: "30s",
|
||||
},
|
||||
{
|
||||
ID: "fs-txt",
|
||||
Command: []string{"ceph", "fs", "status"},
|
||||
Format: "txt",
|
||||
DefaultTimeout: "30s",
|
||||
},
|
||||
{
|
||||
ID: "fs-ls",
|
||||
Command: []string{"ceph", "fs", "ls"},
|
||||
@@ -53,7 +65,7 @@ var CephCommands = []CephCommand{
|
||||
ID: "osd-status",
|
||||
Command: []string{"ceph", "osd", "status"},
|
||||
Args: []string{"-f", "json-pretty"},
|
||||
Format: "txt",
|
||||
Format: "json",
|
||||
DefaultTimeout: "30s",
|
||||
},
|
||||
{
|
||||
@@ -88,15 +100,17 @@ var CephCommands = []CephCommand{
|
||||
ID: "rgw-stats", // the disk usage (and other stats) of each object store bucket
|
||||
Command: []string{"radosgw-admin", "bucket", "stats"},
|
||||
Args: []string{"--rgw-cache-enabled=false"},
|
||||
Format: "txt",
|
||||
Format: "json",
|
||||
DefaultTimeout: "30s", // include a default timeout because this command will hang if the RGW daemon isn't running/is unhealthy
|
||||
},
|
||||
{
|
||||
ID: "rbd-du", // the disk usage of each PVC
|
||||
Command: []string{"rbd", "du"},
|
||||
Args: []string{"--pool=replicapool"},
|
||||
Format: "txt",
|
||||
DefaultTimeout: "30s",
|
||||
ID: "rbd-du-txt", // the disk usage of each PVC
|
||||
Command: []string{"rbd", "du"},
|
||||
Args: []string{"--pool=replicapool"},
|
||||
Format: "txt",
|
||||
// On ceph clusters with a lot of data, this command can take a long time to run
|
||||
// especially if fast-diff rbd_default_features is disabled
|
||||
DefaultTimeout: "60s",
|
||||
},
|
||||
{
|
||||
ID: "df", // the disk usage of each pool
|
||||
@@ -105,6 +119,12 @@ var CephCommands = []CephCommand{
|
||||
Format: "json",
|
||||
DefaultTimeout: "30s",
|
||||
},
|
||||
{
|
||||
ID: "df-txt", // the disk usage of each pool
|
||||
Command: []string{"ceph", "df"},
|
||||
Format: "txt",
|
||||
DefaultTimeout: "30s",
|
||||
},
|
||||
{
|
||||
ID: "osd-df",
|
||||
Command: []string{"ceph", "osd", "df"},
|
||||
@@ -112,6 +132,12 @@ var CephCommands = []CephCommand{
|
||||
Format: "json",
|
||||
DefaultTimeout: "30s",
|
||||
},
|
||||
{
|
||||
ID: "osd-df-txt",
|
||||
Command: []string{"ceph", "osd", "df"},
|
||||
Format: "txt",
|
||||
DefaultTimeout: "30s",
|
||||
},
|
||||
}
|
||||
|
||||
type CollectCeph struct {
|
||||
|
||||
@@ -91,25 +91,23 @@ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
+OkuE6N36B9K
|
||||
-----END CERTIFICATE-----`,
|
||||
"validCert": `-----BEGIN CERTIFICATE-----
|
||||
MIIDejCCAmKgAwIBAgIEAZaq0DANBgkqhkiG9w0BAQsFADAuMRgwFgYDVQQDEw9Q
|
||||
cm9qZWN0IENvbnRvdXIxEjAQBgNVBAUTCTYxNTkyOTg5MTAeFw0yMzAyMjQwNDI3
|
||||
MThaFw0yNDAyMjUwNDI3MTZaMBAxDjAMBgNVBAMTBWVudm95MIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqsNmmxb1ICso6Ay25lapcRyvLxAX/5u422uV
|
||||
eiNn5jseCVXfg1jJr2Symrgou2dgMtIpZoVKT7w0el8sNpD+az5oMOWUNfTGEpYI
|
||||
5zNAhtiedxCWcX15gfezOZ/DECL7HP8U37JFVdazm0CjvlQWI+8rFGvFQJJFDLYJ
|
||||
h0fHGfbX6L/ST6cANtkXZyIU6CYFSgniuuDjHmQnQr6CC8lkisJxY5QVS7MZ02RR
|
||||
nU/dK14ABY+mo/0ZeBKR5si04hr4i18nJJnk4DnHN+jQ/WWWSO1yLqr9kAOj37dA
|
||||
nRAeKuzkx/VbU8DC/3sh0otcazoWO470D+irOy1is0whDArLNwIDAQABo4G9MIG6
|
||||
MA4GA1UdDwEB/wQEAwIE8DAdBgNVHQ4EFgQUbAavOY+vIXgc44k8GvLHH6mdzzYw
|
||||
HwYDVR0jBBgwFoAUb+S3Mu7cbwZiqZaEHTEMyUhLyH4waAYDVR0RBGEwX4IFZW52
|
||||
b3mCFGVudm95LnByb2plY3Rjb250b3VyghhlbnZveS5wcm9qZWN0Y29udG91ci5z
|
||||
dmOCJmVudm95LnByb2plY3Rjb250b3VyLnN2Yy5jbHVzdGVyLmxvY2FsMA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQBIKpBD1T9tugzJF7lajbdulXTb9qGibwQALqauskX9Sq57
|
||||
po/R2TjyxywLn4DgM7BAzzu9qfHWf+S4eQjRUHQshPbUEX9CEsSd5tCu8ZHVbBds
|
||||
6qFagl2+YQ9ng0Xwta9ezvctM3T6Dy9Kkf5OOe9ysMEsBX7s8NFxe68Qku+cExr3
|
||||
78oERlIoNOlT0cNbFLAlH2svNv1uB4qOThRDha52L+mlUdZfTMYZAwNDJWm52t/M
|
||||
NCIm5NJ5jAJpcJmoEb+JMP3j0x6wydHDXFtGm3WRggZRcrjasyodSKK6szbf96+9
|
||||
6syzAwvg9xxNtFxwbhRqqplMEz2sDWaggTrxCQzd
|
||||
MIIDEjCCAfqgAwIBAgIUWG+wobb7huLDKQ4bUn80QKsbVSQwDQYJKoZIhvcNAQEL
|
||||
BQAwGDEWMBQGA1UEAwwNRXhhbXBsZVNlcnZlcjAeFw0yNDAyMjYyMjA5MjdaFw0y
|
||||
OTAyMjQyMjA5MjdaMBgxFjAUBgNVBAMMDUV4YW1wbGVTZXJ2ZXIwggEiMA0GCSqG
|
||||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsb9MSNERE7l4b/vxzkRnL6qvkjodbY3yQ
|
||||
DDCbG29jJ71wSwYoz0v4n/Q/tDaJDBUcSjSPIa+L6Gn1BGLNUD2LFec5H5XlwcDq
|
||||
A0D8qzvPLMYaIw4p04M/2Wvkqme79yQta8jXaCnPxTkgysno/FuQR+nDMqXoyW01
|
||||
wMmj8JAYFs9MVPbmP97RqbVHnn7cydzvQvi4+dfBuII9L+TvTQY2o42bbCqCcmov
|
||||
16m0uXp98j4CFfv/8KhCmQcqS0uyvp+J/iJZzEow+1NP72pozPEPv9JhXsYDb+vH
|
||||
KxX5k44fYXVYVQHxYQllLjDDRcbz2DJh2DQXMZTiTLE63tC0m2RjAgMBAAGjVDBS
|
||||
MDEGA1UdEQQqMCiCCGV4YW1wbGUxgghleGFtcGxlMoIIZXhhbXBsZTOCCGV4YW1w
|
||||
bGU0MB0GA1UdDgQWBBTEkowqyNS1ez5XJ0shro/18UQlqTANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEANhnxV81NZmt8FcgrcRt73WdKDCE0hQbci65ZkAbcTVOCAs6Rw+aEC04L
|
||||
0gKJT4Zmx3kR3fUwcDQRhjoXIrhmvP59k/8SN0N+Ua50oEYikaetsxEDr8oikwP2
|
||||
eHDKU451jZZfcyII2Jx8XjgtiExcNZL3N9ZtSstp3I9n9BSmMdQHDIvPxNeGdJri
|
||||
l4YGTm+sJLs+c5efSNaEPw1Dr6bhAPamNQwvIspuASJlCTcMCLw2+F3dEolAFUuM
|
||||
E5wI9498c+P3pvl85UfiQISTu95jOl7YmZOYl8xBpu9yhQ7kjWhVRdVAnhl9Ts9F
|
||||
3TD68rCMXegJYyg5VbiwVdSPkcw3eQ==
|
||||
-----END CERTIFICATE-----`,
|
||||
"nonCert": `-----BEGIN CERTIFICATE-----
|
||||
Oy1is0whDArLNwIDAQABo4G9MIG6
|
||||
@@ -248,16 +246,16 @@ func TestCertParser(t *testing.T) {
|
||||
CertificateChain: []ParsedCertificate{
|
||||
{
|
||||
CertName: "tls.crt",
|
||||
Subject: "CN=envoy",
|
||||
Subject: "CN=ExampleServer",
|
||||
SubjectAlternativeNames: []string{
|
||||
"envoy",
|
||||
"envoy.projectcontour",
|
||||
"envoy.projectcontour.svc",
|
||||
"envoy.projectcontour.svc.cluster.local",
|
||||
"example1",
|
||||
"example2",
|
||||
"example3",
|
||||
"example4",
|
||||
},
|
||||
Issuer: "SERIALNUMBER=615929891,CN=Project Contour",
|
||||
NotAfter: time.Date(2024, time.February, 25, 4, 27, 16, 0, time.UTC),
|
||||
NotBefore: time.Date(2023, time.February, 24, 4, 27, 18, 0, time.UTC),
|
||||
Issuer: "CN=ExampleServer",
|
||||
NotAfter: time.Date(2029, time.February, 24, 22, 9, 27, 0, time.UTC),
|
||||
NotBefore: time.Date(2024, time.February, 26, 22, 9, 27, 0, time.UTC),
|
||||
IsValid: true,
|
||||
IsCA: false,
|
||||
},
|
||||
|
||||
@@ -506,7 +506,7 @@ func pods(ctx context.Context, client *kubernetes.Clientset, namespaces []string
|
||||
}
|
||||
|
||||
func getPodDisruptionBudgets(ctx context.Context, client *kubernetes.Clientset, namespaces []string) (map[string][]byte, map[string]string) {
|
||||
ok, err := discovery.HasResource(client, "policy.k8s.io/v1", "PodDisruptionBudgets")
|
||||
ok, err := discovery.HasResource(client, "policy/v1", "PodDisruptionBudgets")
|
||||
if err != nil {
|
||||
return nil, map[string]string{"": err.Error()}
|
||||
}
|
||||
@@ -801,7 +801,7 @@ func jobs(ctx context.Context, client *kubernetes.Clientset, namespaces []string
|
||||
}
|
||||
|
||||
func cronJobs(ctx context.Context, client *kubernetes.Clientset, namespaces []string) (map[string][]byte, map[string]string) {
|
||||
ok, err := discovery.HasResource(client, "batch.k8s.io/v1", "CronJobs")
|
||||
ok, err := discovery.HasResource(client, "batch/v1", "CronJobs")
|
||||
if err != nil {
|
||||
return nil, map[string]string{"": err.Error()}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,8 @@ func GetCollector(collector *troubleshootv1beta2.Collect, bundlePath string, nam
|
||||
return &CollectRun{collector.Run, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
|
||||
case collector.RunPod != nil:
|
||||
return &CollectRunPod{collector.RunPod, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
|
||||
case collector.RunDaemonSet != nil:
|
||||
return &CollectRunDaemonSet{collector.RunDaemonSet, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
|
||||
case collector.Exec != nil:
|
||||
return &CollectExec{collector.Exec, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
|
||||
case collector.Data != nil:
|
||||
@@ -116,6 +118,14 @@ func GetCollector(collector *troubleshootv1beta2.Collect, bundlePath string, nam
|
||||
return &CollectCertificates{collector.Certificates, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
|
||||
case collector.Helm != nil:
|
||||
return &CollectHelm{collector.Helm, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
|
||||
case collector.Goldpinger != nil:
|
||||
return &CollectGoldpinger{collector.Goldpinger, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
|
||||
case collector.Sonobuoy != nil:
|
||||
return &CollectSonobuoyResults{collector.Sonobuoy, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
|
||||
case collector.NodeMetrics != nil:
|
||||
return &CollectNodeMetrics{collector.NodeMetrics, bundlePath, clientConfig, client, ctx, RBACErrors}, true
|
||||
case collector.DNS != nil:
|
||||
return &CollectDNS{collector.DNS, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
@@ -150,6 +160,9 @@ func getCollectorName(c interface{}) string {
|
||||
case *CollectRunPod:
|
||||
collector = "run-pod"
|
||||
name = v.Collector.CollectorName
|
||||
case *CollectRunDaemonSet:
|
||||
collector = "run-daemonset"
|
||||
name = v.Collector.CollectorName
|
||||
case *CollectExec:
|
||||
collector = "exec"
|
||||
name = v.Collector.CollectorName
|
||||
@@ -198,6 +211,14 @@ func getCollectorName(c interface{}) string {
|
||||
collector = "certificates"
|
||||
case *CollectHelm:
|
||||
collector = "helm"
|
||||
case *CollectGoldpinger:
|
||||
collector = "goldpinger"
|
||||
case *CollectSonobuoyResults:
|
||||
collector = "sonobuoy"
|
||||
case *CollectNodeMetrics:
|
||||
collector = "node-metrics"
|
||||
case *CollectDNS:
|
||||
collector = "dns"
|
||||
default:
|
||||
collector = "<none>"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
dnsUtilsImage = "registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3"
|
||||
)
|
||||
|
||||
type CollectDNS struct {
|
||||
Collector *troubleshootv1beta2.DNS
|
||||
BundlePath string
|
||||
Namespace string
|
||||
ClientConfig *rest.Config
|
||||
Client kubernetes.Interface
|
||||
Context context.Context
|
||||
RBACErrors
|
||||
}
|
||||
|
||||
func (c *CollectDNS) Title() string {
|
||||
return getCollectorName(c)
|
||||
}
|
||||
|
||||
func (c *CollectDNS) IsExcluded() (bool, error) {
|
||||
return isExcluded(c.Collector.Exclude)
|
||||
}
|
||||
|
||||
func (c *CollectDNS) Collect(progressChan chan<- interface{}) (CollectorResult, error) {
|
||||
|
||||
ctx, cancel := context.WithTimeout(c.Context, time.Duration(60*time.Second))
|
||||
defer cancel()
|
||||
|
||||
sb := strings.Builder{}
|
||||
|
||||
// get kubernetes Cluster IP
|
||||
clusterIP, err := getKubernetesClusterIP(c.Client, ctx)
|
||||
if err == nil {
|
||||
sb.WriteString(fmt.Sprintf("=== Kubernetes Cluster IP from API Server: %s\n", clusterIP))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("=== Failed to detect Kubernetes Cluster IP: %v\n", err))
|
||||
}
|
||||
|
||||
// run a pod and perform DNS lookup
|
||||
podLog, err := troubleshootDNSFromPod(c.Client, ctx)
|
||||
if err == nil {
|
||||
sb.WriteString(fmt.Sprintf("=== Test DNS resolution in pod %s: \n", dnsUtilsImage))
|
||||
sb.WriteString(podLog)
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("=== Failed to run commands from pod: %v\n", err))
|
||||
}
|
||||
|
||||
// is DNS pods running?
|
||||
sb.WriteString(fmt.Sprintf("=== Running kube-dns pods: %s\n", getRunningKubeDNSPodNames(c.Client, ctx)))
|
||||
|
||||
// is DNS service up?
|
||||
sb.WriteString(fmt.Sprintf("=== Running kube-dns service: %s\n", getKubeDNSServiceClusterIP(c.Client, ctx)))
|
||||
|
||||
// are DNS endpoints exposed?
|
||||
sb.WriteString(fmt.Sprintf("=== kube-dns endpoints: %s\n", getKubeDNSEndpoints(c.Client, ctx)))
|
||||
|
||||
// get DNS server config
|
||||
coreDNSConfig, err := getCoreDNSConfig(c.Client, ctx)
|
||||
if err == nil {
|
||||
sb.WriteString("=== CoreDNS config: \n")
|
||||
sb.WriteString(coreDNSConfig)
|
||||
}
|
||||
kubeDNSConfig, err := getKubeDNSConfig(c.Client, ctx)
|
||||
if err == nil {
|
||||
sb.WriteString("=== KubeDNS config: \n")
|
||||
sb.WriteString(kubeDNSConfig)
|
||||
}
|
||||
|
||||
data := sb.String()
|
||||
output := NewResult()
|
||||
output.SaveResult(c.BundlePath, filepath.Join("dns", c.Collector.CollectorName), bytes.NewBuffer([]byte(data)))
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func getKubernetesClusterIP(client kubernetes.Interface, ctx context.Context) (string, error) {
|
||||
service, err := client.CoreV1().Services("default").Get(ctx, "kubernetes", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
klog.V(2).Infof("Failed to detect Kubernetes Cluster IP: %v", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return service.Spec.ClusterIP, nil
|
||||
}
|
||||
|
||||
func troubleshootDNSFromPod(client kubernetes.Interface, ctx context.Context) (string, error) {
|
||||
namespace := "default"
|
||||
command := []string{"/bin/sh", "-c", `
|
||||
set -x
|
||||
cat /etc/resolv.conf
|
||||
nslookup -debug kubernetes
|
||||
exit 0
|
||||
`}
|
||||
|
||||
// TODO: image pull secret?
|
||||
podLabels := map[string]string{
|
||||
"troubleshoot-role": "dns-collector",
|
||||
}
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "troubleshoot-dns-",
|
||||
Namespace: namespace,
|
||||
Labels: podLabels,
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "troubleshoot-dns",
|
||||
Image: dnsUtilsImage,
|
||||
Command: command,
|
||||
},
|
||||
},
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
},
|
||||
}
|
||||
|
||||
created, err := client.CoreV1().Pods(namespace).Create(ctx, pod, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to run troubleshoot DNS pod")
|
||||
}
|
||||
klog.V(2).Infof("Pod with prefix %s has been created", created.GenerateName)
|
||||
|
||||
defer func() {
|
||||
if created == nil {
|
||||
return
|
||||
}
|
||||
err := client.CoreV1().Pods(namespace).Delete(ctx, created.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to delete troubleshoot DNS pod %s: %v", created.Name, err)
|
||||
}
|
||||
klog.V(2).Infof("Deleted pod %s", created.Name)
|
||||
}()
|
||||
|
||||
// wait for pod to be completed
|
||||
watcher, err := client.CoreV1().Pods(namespace).Watch(ctx, metav1.ListOptions{
|
||||
LabelSelector: "troubleshoot-role=dns-collector",
|
||||
})
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to watch pod")
|
||||
}
|
||||
defer func() {
|
||||
if watcher != nil {
|
||||
watcher.Stop()
|
||||
}
|
||||
}()
|
||||
|
||||
for event := range watcher.ResultChan() {
|
||||
pod, ok := event.Object.(*corev1.Pod)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if pod.Status.Phase == corev1.PodSucceeded {
|
||||
break
|
||||
}
|
||||
if pod.Status.Phase == corev1.PodFailed {
|
||||
return "", errors.New("troubleshoot DNS pod failed")
|
||||
}
|
||||
}
|
||||
|
||||
// get pod logs
|
||||
podLogOpts := corev1.PodLogOptions{}
|
||||
req := client.CoreV1().Pods(namespace).GetLogs(created.Name, &podLogOpts)
|
||||
podLogs, err := req.Stream(ctx)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to get pod logs")
|
||||
}
|
||||
defer podLogs.Close()
|
||||
|
||||
bytes, err := io.ReadAll(podLogs)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to read troubleshoot DNS pod logs")
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
func getCoreDNSConfig(client kubernetes.Interface, ctx context.Context) (string, error) {
|
||||
configMap, err := client.CoreV1().ConfigMaps("kube-system").Get(ctx, "coredns", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
klog.V(2).Infof("Failed to detect CoreDNS config: %v", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return configMap.Data["Corefile"], nil
|
||||
}
|
||||
|
||||
func getKubeDNSConfig(client kubernetes.Interface, ctx context.Context) (string, error) {
|
||||
configMap, err := client.CoreV1().ConfigMaps("kube-system").Get(ctx, "kube-dns", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
klog.V(2).Infof("Failed to detect KubeDNS config: %v", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if configMap.Data == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
dataBytes, err := json.Marshal(configMap.Data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(dataBytes), nil
|
||||
}
|
||||
|
||||
func getRunningKubeDNSPodNames(client kubernetes.Interface, ctx context.Context) string {
|
||||
pods, err := client.CoreV1().Pods("kube-system").List(ctx, metav1.ListOptions{
|
||||
LabelSelector: "k8s-app=kube-dns",
|
||||
})
|
||||
if err != nil {
|
||||
klog.V(2).Infof("failed to list kube-dns pods: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
var podNames []string
|
||||
for _, pod := range pods.Items {
|
||||
if pod.Status.Phase == corev1.PodRunning {
|
||||
podNames = append(podNames, pod.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(podNames, ", ")
|
||||
}
|
||||
|
||||
func getKubeDNSServiceClusterIP(client kubernetes.Interface, ctx context.Context) string {
|
||||
service, err := client.CoreV1().Services("kube-system").Get(ctx, "kube-dns", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
klog.V(2).Infof("failed to get kube-dns service: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return service.Spec.ClusterIP
|
||||
}
|
||||
|
||||
func getKubeDNSEndpoints(client kubernetes.Interface, ctx context.Context) string {
|
||||
endpoints, err := client.CoreV1().Endpoints("kube-system").Get(ctx, "kube-dns", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
klog.V(2).Infof("failed to get kube-dns endpoints: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
var endpointStrings []string
|
||||
for _, subset := range endpoints.Subsets {
|
||||
for _, address := range subset.Addresses {
|
||||
if len(subset.Ports) > 0 {
|
||||
endpointStrings = append(endpointStrings, fmt.Sprintf("%s:%d", address.IP, subset.Ports[0].Port))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(endpointStrings, ", ")
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
func TestGetKubernetesClusterIP(t *testing.T) {
|
||||
k8sSvcIp := "10.0.0.1"
|
||||
client := fake.NewSimpleClientset()
|
||||
service := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kubernetes",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
ClusterIP: k8sSvcIp,
|
||||
},
|
||||
}
|
||||
|
||||
// Add the service to the fake clientset
|
||||
_, err := client.CoreV1().Services("default").Create(context.TODO(), service, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("error injecting service into fake clientset: %v", err)
|
||||
}
|
||||
|
||||
// Call the function
|
||||
clusterIP, err := getKubernetesClusterIP(client, context.TODO())
|
||||
if err != nil {
|
||||
t.Fatalf("error getting cluster IP: %v", err)
|
||||
}
|
||||
|
||||
// Check the result
|
||||
if clusterIP != k8sSvcIp {
|
||||
t.Errorf("expected %s, got %s", k8sSvcIp, clusterIP)
|
||||
}
|
||||
}
|
||||
+8
-5
@@ -47,6 +47,7 @@ func (c *CollectExec) Collect(progressChan chan<- interface{}) (CollectorResult,
|
||||
errCh := make(chan error, 1)
|
||||
resultCh := make(chan CollectorResult, 1)
|
||||
|
||||
// TODO: Use a context with timeout instead of a goroutine
|
||||
go func() {
|
||||
b, err := execWithoutTimeout(c.ClientConfig, c.BundlePath, c.Collector)
|
||||
if err != nil {
|
||||
@@ -78,12 +79,12 @@ func execWithoutTimeout(clientConfig *rest.Config, bundlePath string, execCollec
|
||||
|
||||
pods, podsErrors := listPodsInSelectors(ctx, client, execCollector.Namespace, execCollector.Selector)
|
||||
if len(podsErrors) > 0 {
|
||||
output.SaveResult(bundlePath, getExecErrosFileName(execCollector), marshalErrors(podsErrors))
|
||||
output.SaveResult(bundlePath, getExecErrorsFileName(execCollector), marshalErrors(podsErrors))
|
||||
}
|
||||
|
||||
if len(pods) > 0 {
|
||||
for _, pod := range pods {
|
||||
stdout, stderr, execErrors := getExecOutputs(clientConfig, client, pod, execCollector)
|
||||
stdout, stderr, execErrors := getExecOutputs(ctx, clientConfig, client, pod, execCollector)
|
||||
|
||||
path := filepath.Join(execCollector.Name, pod.Namespace, pod.Name)
|
||||
if len(stdout) > 0 {
|
||||
@@ -103,7 +104,9 @@ func execWithoutTimeout(clientConfig *rest.Config, bundlePath string, execCollec
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func getExecOutputs(clientConfig *rest.Config, client *kubernetes.Clientset, pod corev1.Pod, execCollector *troubleshootv1beta2.Exec) ([]byte, []byte, []string) {
|
||||
func getExecOutputs(
|
||||
ctx context.Context, clientConfig *rest.Config, client *kubernetes.Clientset, pod corev1.Pod, execCollector *troubleshootv1beta2.Exec,
|
||||
) ([]byte, []byte, []string) {
|
||||
container := pod.Spec.Containers[0].Name
|
||||
if execCollector.ContainerName != "" {
|
||||
container = execCollector.ContainerName
|
||||
@@ -133,7 +136,7 @@ func getExecOutputs(clientConfig *rest.Config, client *kubernetes.Clientset, pod
|
||||
stdout := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
|
||||
err = exec.Stream(remotecommand.StreamOptions{
|
||||
err = exec.StreamWithContext(ctx, remotecommand.StreamOptions{
|
||||
Stdin: nil,
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
@@ -147,7 +150,7 @@ func getExecOutputs(clientConfig *rest.Config, client *kubernetes.Clientset, pod
|
||||
return stdout.Bytes(), stderr.Bytes(), nil
|
||||
}
|
||||
|
||||
func getExecErrosFileName(execCollector *troubleshootv1beta2.Exec) string {
|
||||
func getExecErrorsFileName(execCollector *troubleshootv1beta2.Exec) string {
|
||||
if len(execCollector.Name) > 0 {
|
||||
return fmt.Sprintf("%s-errors.json", execCollector.Name)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/replicatedhq/troubleshoot/internal/util"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/constants"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// Collect goldpinger results from goldpinger service running in a cluster
|
||||
// The results are stored in goldpinger/check_all.json since we use
|
||||
// the /check_all endpoint
|
||||
type CollectGoldpinger struct {
|
||||
Collector *troubleshootv1beta2.Goldpinger
|
||||
BundlePath string
|
||||
Namespace string
|
||||
ClientConfig *rest.Config
|
||||
Client kubernetes.Interface
|
||||
Context context.Context
|
||||
RBACErrors
|
||||
}
|
||||
|
||||
func (c *CollectGoldpinger) Title() string {
|
||||
return getCollectorName(c)
|
||||
}
|
||||
|
||||
func (c *CollectGoldpinger) IsExcluded() (bool, error) {
|
||||
return isExcluded(c.Collector.Exclude)
|
||||
}
|
||||
|
||||
func (c *CollectGoldpinger) Collect(progressChan chan<- interface{}) (CollectorResult, error) {
|
||||
output := NewResult()
|
||||
var results []byte
|
||||
var err error
|
||||
|
||||
if util.IsInCluster() {
|
||||
klog.V(2).Infof("Collector running in cluster, querying goldpinger endpoint straight away")
|
||||
results, err = c.fetchCheckAllOutput()
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("Failed to query goldpinger endpoint in cluster: %v", err)
|
||||
klog.V(2).Infof(errMsg)
|
||||
err = output.SaveResult(c.BundlePath, "goldpinger/error.txt", bytes.NewBuffer([]byte(errMsg)))
|
||||
return output, err
|
||||
}
|
||||
} else {
|
||||
klog.V(2).Infof("Launch pod to query goldpinger endpoint then collect results from pod logs")
|
||||
results, err = c.runPodAndCollectGPResults(progressChan)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("Failed to run pod to collect goldpinger results: %v", err)
|
||||
klog.V(2).Infof(errMsg)
|
||||
err = output.SaveResult(c.BundlePath, "goldpinger/error.txt", bytes.NewBuffer([]byte(errMsg)))
|
||||
return output, err
|
||||
}
|
||||
}
|
||||
|
||||
err = output.SaveResult(c.BundlePath, constants.GP_CHECK_ALL_RESULTS_PATH, bytes.NewBuffer(results))
|
||||
return output, err
|
||||
}
|
||||
|
||||
func (c *CollectGoldpinger) fetchCheckAllOutput() ([]byte, error) {
|
||||
client := &http.Client{
|
||||
Timeout: time.Minute, // Long enough timeout
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(c.Context, "GET", c.endpoint(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func (c *CollectGoldpinger) runPodAndCollectGPResults(progressChan chan<- interface{}) ([]byte, error) {
|
||||
rest.InClusterConfig()
|
||||
|
||||
namespace := "default"
|
||||
serviceAccountName := ""
|
||||
image := constants.GP_DEFAULT_IMAGE
|
||||
var imagePullSecret *troubleshootv1beta2.ImagePullSecrets
|
||||
|
||||
if c.Collector.PodLaunchOptions != nil {
|
||||
if c.Collector.PodLaunchOptions.Namespace != "" {
|
||||
namespace = c.Collector.PodLaunchOptions.Namespace
|
||||
}
|
||||
|
||||
if c.Collector.PodLaunchOptions.ServiceAccountName != "" {
|
||||
serviceAccountName = c.Collector.PodLaunchOptions.ServiceAccountName
|
||||
if err := checkForExistingServiceAccount(c.Context, c.Client, namespace, serviceAccountName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if c.Collector.PodLaunchOptions.Image != "" {
|
||||
image = c.Collector.PodLaunchOptions.Image
|
||||
}
|
||||
imagePullSecret = c.Collector.PodLaunchOptions.ImagePullSecret
|
||||
}
|
||||
|
||||
runPodCollectorName := "ts-goldpinger-collector"
|
||||
collectorContainerName := "collector"
|
||||
runPodSpec := &troubleshootv1beta2.RunPod{
|
||||
CollectorMeta: troubleshootv1beta2.CollectorMeta{
|
||||
CollectorName: runPodCollectorName,
|
||||
},
|
||||
Name: runPodCollectorName,
|
||||
Namespace: namespace,
|
||||
Timeout: time.Minute.String(),
|
||||
ImagePullSecret: imagePullSecret,
|
||||
PodSpec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
ServiceAccountName: serviceAccountName,
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Image: image,
|
||||
ImagePullPolicy: corev1.PullIfNotPresent,
|
||||
Name: collectorContainerName,
|
||||
Command: []string{"wget"},
|
||||
Args: []string{"-q", "-O-", c.endpoint()},
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("50m"),
|
||||
corev1.ResourceMemory: resource.MustParse("64Mi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("200m"),
|
||||
corev1.ResourceMemory: resource.MustParse("256Mi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rbacErrors := c.GetRBACErrors()
|
||||
// Pass an empty bundle path since we don't need to save the results
|
||||
runPodCollector := &CollectRunPod{runPodSpec, "", c.Namespace, c.ClientConfig, c.Client, c.Context, rbacErrors}
|
||||
|
||||
output, err := runPodCollector.Collect(progressChan)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if the collector container exited with an error
|
||||
var pod corev1.Pod
|
||||
err = json.Unmarshal(output[fmt.Sprintf("%s/%s.json", runPodCollectorName, runPodCollectorName)], &pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var terminationError *corev1.ContainerStateTerminated
|
||||
for _, status := range pod.Status.ContainerStatuses {
|
||||
if status.Name == collectorContainerName && status.State.Terminated != nil {
|
||||
if status.State.Terminated.ExitCode != 0 {
|
||||
terminationError = status.State.Terminated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
podLogs := output[fmt.Sprintf("%s/%s.log", runPodCollectorName, runPodCollectorName)]
|
||||
if terminationError != nil {
|
||||
m := map[string]string{
|
||||
"podName": pod.Name,
|
||||
"exitCode": strconv.Itoa(int(terminationError.ExitCode)),
|
||||
"reason": terminationError.Reason,
|
||||
"message": terminationError.Message,
|
||||
"logs": string(podLogs),
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(m, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, errors.New(string(b))
|
||||
}
|
||||
return podLogs, nil
|
||||
}
|
||||
|
||||
func (c *CollectGoldpinger) endpoint() string {
|
||||
namespace := c.Collector.Namespace
|
||||
if namespace == "" {
|
||||
namespace = constants.GP_DEFAULT_NAMESPACE
|
||||
}
|
||||
|
||||
return fmt.Sprintf("http://goldpinger.%s.svc.cluster.local:80/check_all", namespace)
|
||||
}
|
||||
+70
-22
@@ -5,12 +5,11 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
@@ -39,10 +38,11 @@ type ReleaseInfo struct {
|
||||
|
||||
// Helm release version information struct
|
||||
type VersionInfo struct {
|
||||
Revision string `json:"revision"`
|
||||
Date string `json:"date"`
|
||||
Status string `json:"status"`
|
||||
IsPending bool `json:"isPending,omitempty"`
|
||||
Revision string `json:"revision"`
|
||||
Date string `json:"date"`
|
||||
Status string `json:"status"`
|
||||
IsPending bool `json:"isPending,omitempty"`
|
||||
Values map[string]interface{} `json:"values,omitempty"`
|
||||
}
|
||||
|
||||
func (c *CollectHelm) Title() string {
|
||||
@@ -57,9 +57,15 @@ func (c *CollectHelm) Collect(progressChan chan<- interface{}) (CollectorResult,
|
||||
|
||||
output := NewResult()
|
||||
|
||||
releaseInfos, err := helmReleaseHistoryCollector(c.Collector.ReleaseName, c.Collector.Namespace)
|
||||
releaseInfos, err := helmReleaseHistoryCollector(c.Collector.ReleaseName, c.Collector.Namespace, c.Collector.CollectValues)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get Helm release history")
|
||||
errsToMarhsal := []string{}
|
||||
for _, e := range err {
|
||||
errsToMarhsal = append(errsToMarhsal, e.Error())
|
||||
}
|
||||
output.SaveResult(c.BundlePath, "helm/errors.json", marshalErrors(errsToMarhsal))
|
||||
klog.Errorf("error collecting helm release info: %v", err)
|
||||
return output, nil
|
||||
}
|
||||
|
||||
releaseInfoByNamespace := helmReleaseInfoByNamespaces(releaseInfos)
|
||||
@@ -72,22 +78,23 @@ func (c *CollectHelm) Collect(progressChan chan<- interface{}) (CollectorResult,
|
||||
}
|
||||
|
||||
filePath := fmt.Sprintf("helm/%s.json", namespace)
|
||||
|
||||
err := output.SaveResult(c.BundlePath, filePath, bytes.NewBuffer(helmHistoryJson))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if c.Collector.ReleaseName != "" {
|
||||
filePath = fmt.Sprintf("helm/%s/%s.json", namespace, c.Collector.ReleaseName)
|
||||
}
|
||||
|
||||
output.SaveResult(c.BundlePath, filePath, bytes.NewBuffer(helmHistoryJson))
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func helmReleaseHistoryCollector(releaseName string, namespace string) ([]ReleaseInfo, error) {
|
||||
func helmReleaseHistoryCollector(releaseName string, namespace string, collectValues bool) ([]ReleaseInfo, []error) {
|
||||
var results []ReleaseInfo
|
||||
error_list := []error{}
|
||||
|
||||
actionConfig := new(action.Configuration)
|
||||
if err := actionConfig.Init(nil, namespace, "", klog.V(2).Infof); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to initialize Helm action config")
|
||||
return nil, []error{err}
|
||||
}
|
||||
|
||||
// If releaseName is specified, get the history of that release
|
||||
@@ -95,7 +102,11 @@ func helmReleaseHistoryCollector(releaseName string, namespace string) ([]Releas
|
||||
getAction := action.NewGet(actionConfig)
|
||||
r, err := getAction.Run(releaseName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get Helm release %s", releaseName)
|
||||
return nil, []error{err}
|
||||
}
|
||||
versionInfo, err := getVersionInfo(actionConfig, r.Name, r.Namespace, collectValues)
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
results = append(results, ReleaseInfo{
|
||||
ReleaseName: r.Name,
|
||||
@@ -103,7 +114,7 @@ func helmReleaseHistoryCollector(releaseName string, namespace string) ([]Releas
|
||||
ChartVersion: r.Chart.Metadata.Version,
|
||||
AppVersion: r.Chart.Metadata.AppVersion,
|
||||
Namespace: r.Namespace,
|
||||
VersionInfo: getVersionInfo(actionConfig, releaseName),
|
||||
VersionInfo: versionInfo,
|
||||
})
|
||||
return results, nil
|
||||
}
|
||||
@@ -114,39 +125,64 @@ func helmReleaseHistoryCollector(releaseName string, namespace string) ([]Releas
|
||||
listAction := action.NewList(actionConfig)
|
||||
releases, err := listAction.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to list Helm releases: %v", err)
|
||||
return nil, []error{err}
|
||||
}
|
||||
|
||||
for _, r := range releases {
|
||||
versionInfo, err := getVersionInfo(actionConfig, r.Name, r.Namespace, collectValues)
|
||||
if err != nil {
|
||||
error_list = append(error_list, err)
|
||||
}
|
||||
results = append(results, ReleaseInfo{
|
||||
ReleaseName: r.Name,
|
||||
Chart: r.Chart.Metadata.Name,
|
||||
ChartVersion: r.Chart.Metadata.Version,
|
||||
AppVersion: r.Chart.Metadata.AppVersion,
|
||||
Namespace: r.Namespace,
|
||||
VersionInfo: getVersionInfo(actionConfig, r.Name),
|
||||
VersionInfo: versionInfo,
|
||||
})
|
||||
}
|
||||
|
||||
if len(error_list) > 0 {
|
||||
return nil, error_list
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func getVersionInfo(actionConfig *action.Configuration, releaseName string) []VersionInfo {
|
||||
func getVersionInfo(actionConfig *action.Configuration, releaseName, namespace string, collectValues bool) ([]VersionInfo, error) {
|
||||
|
||||
versionCollect := []VersionInfo{}
|
||||
error_list := []error{}
|
||||
|
||||
history, _ := action.NewHistory(actionConfig).Run(releaseName)
|
||||
history, err := action.NewHistory(actionConfig).Run(releaseName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, release := range history {
|
||||
values := map[string]interface{}{}
|
||||
if collectValues {
|
||||
values, err = getHelmValues(releaseName, namespace, release.Version)
|
||||
if err != nil {
|
||||
error_list = append(error_list, err)
|
||||
}
|
||||
}
|
||||
|
||||
versionCollect = append(versionCollect, VersionInfo{
|
||||
Revision: strconv.Itoa(release.Version),
|
||||
Date: release.Info.LastDeployed.String(),
|
||||
Status: release.Info.Status.String(),
|
||||
IsPending: release.Info.Status.IsPending(),
|
||||
Values: values,
|
||||
})
|
||||
}
|
||||
return versionCollect
|
||||
if len(error_list) > 0 {
|
||||
errs := []string{}
|
||||
for _, e := range error_list {
|
||||
errs = append(errs, e.Error())
|
||||
}
|
||||
return nil, errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return versionCollect, nil
|
||||
}
|
||||
|
||||
func helmReleaseInfoByNamespaces(releaseInfo []ReleaseInfo) map[string][]ReleaseInfo {
|
||||
@@ -158,3 +194,15 @@ func helmReleaseInfoByNamespaces(releaseInfo []ReleaseInfo) map[string][]Release
|
||||
|
||||
return releaseInfoByNamespace
|
||||
}
|
||||
|
||||
func getHelmValues(releaseName, namespace string, revision int) (map[string]interface{}, error) {
|
||||
actionConfig := new(action.Configuration)
|
||||
if err := actionConfig.Init(nil, namespace, "", klog.V(2).Infof); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
getAction := action.NewGetValues(actionConfig)
|
||||
getAction.AllValues = true
|
||||
getAction.Version = revision
|
||||
helmValues, err := getAction.Run(releaseName)
|
||||
return helmValues, err
|
||||
}
|
||||
|
||||
@@ -24,16 +24,16 @@ func Test_HostCertParser(t *testing.T) {
|
||||
CertificatePath: path,
|
||||
CertificateChain: []ParsedCertificate{
|
||||
{
|
||||
Subject: "CN=envoy",
|
||||
Subject: "CN=ExampleServer",
|
||||
SubjectAlternativeNames: []string{
|
||||
"envoy",
|
||||
"envoy.projectcontour",
|
||||
"envoy.projectcontour.svc",
|
||||
"envoy.projectcontour.svc.cluster.local",
|
||||
"example1",
|
||||
"example2",
|
||||
"example3",
|
||||
"example4",
|
||||
},
|
||||
Issuer: "SERIALNUMBER=615929891,CN=Project Contour",
|
||||
NotAfter: time.Date(2024, time.February, 25, 4, 27, 16, 0, time.UTC),
|
||||
NotBefore: time.Date(2023, time.February, 24, 4, 27, 18, 0, time.UTC),
|
||||
Issuer: "CN=ExampleServer",
|
||||
NotAfter: time.Date(2029, time.February, 24, 22, 9, 27, 0, time.UTC),
|
||||
NotBefore: time.Date(2024, time.February, 26, 22, 9, 27, 0, time.UTC),
|
||||
IsValid: true,
|
||||
IsCA: false,
|
||||
},
|
||||
|
||||
@@ -60,7 +60,7 @@ func isValidLoadBalancerAddress(address string) bool {
|
||||
|
||||
// Check for isValidIP
|
||||
|
||||
test := validation.IsValidIP(hostAddress)
|
||||
test := validation.IsValidIP(nil, hostAddress)
|
||||
return len(test) == 0
|
||||
|
||||
}
|
||||
|
||||
+28
-1
@@ -2,12 +2,14 @@ package collect
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
@@ -49,7 +51,29 @@ func (c *CollectHostRun) Collect(progressChan chan<- interface{}) (map[string][]
|
||||
collectorName = "run-host"
|
||||
}
|
||||
|
||||
cmd := exec.Command(c.attemptToConvertCmdToAbsPath(), runHostCollector.Args...)
|
||||
var (
|
||||
timeout time.Duration
|
||||
cmd *exec.Cmd
|
||||
errInvalidDuration error
|
||||
)
|
||||
|
||||
ctx := context.Background()
|
||||
cmdPath := c.attemptToConvertCmdToAbsPath()
|
||||
|
||||
if runHostCollector.Timeout != "" {
|
||||
timeout, errInvalidDuration = time.ParseDuration(runHostCollector.Timeout)
|
||||
if errInvalidDuration != nil {
|
||||
return nil, errors.Wrapf(errInvalidDuration, "failed to parse timeout %q", runHostCollector.Timeout)
|
||||
}
|
||||
}
|
||||
|
||||
if timeout <= time.Duration(0) {
|
||||
cmd = exec.Command(cmdPath, runHostCollector.Args...)
|
||||
} else {
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
cmd = exec.CommandContext(timeoutCtx, cmdPath, runHostCollector.Args...)
|
||||
}
|
||||
|
||||
klog.V(2).Infof("Run host collector command: %q", cmd.String())
|
||||
runInfo := &HostRunInfo{
|
||||
@@ -118,6 +142,9 @@ func (c *CollectHostRun) Collect(progressChan chan<- interface{}) (map[string][]
|
||||
if werr, ok := err.(*exec.ExitError); ok {
|
||||
runInfo.ExitCode = strings.TrimPrefix(werr.Error(), "exit status ")
|
||||
runInfo.Error = stderr.String()
|
||||
} else if err == context.DeadlineExceeded {
|
||||
runInfo.ExitCode = "-1"
|
||||
runInfo.Error = fmt.Sprintf("command timed out after %s", timeout.String())
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "failed to run")
|
||||
}
|
||||
|
||||
@@ -158,3 +158,77 @@ func TestCollectHostRun_Collect(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectHostRunCollectWithTimeout(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
collector *CollectHostRun
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "no timeout",
|
||||
collector: &CollectHostRun{
|
||||
hostCollector: &troubleshootv1beta2.HostRun{
|
||||
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
|
||||
CollectorName: "no timeout",
|
||||
},
|
||||
Command: "echo",
|
||||
Args: []string{"1"},
|
||||
},
|
||||
},
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "invalid timeout",
|
||||
collector: &CollectHostRun{
|
||||
hostCollector: &troubleshootv1beta2.HostRun{
|
||||
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
|
||||
CollectorName: "negative timeout",
|
||||
},
|
||||
Command: "echo",
|
||||
Args: []string{"1"},
|
||||
Timeout: "thisistheway",
|
||||
},
|
||||
},
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "negative timeout",
|
||||
collector: &CollectHostRun{
|
||||
hostCollector: &troubleshootv1beta2.HostRun{
|
||||
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
|
||||
CollectorName: "negative timeout",
|
||||
},
|
||||
Command: "echo",
|
||||
Args: []string{"1"},
|
||||
Timeout: "-10s",
|
||||
},
|
||||
},
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "endless command with timeout",
|
||||
collector: &CollectHostRun{
|
||||
hostCollector: &troubleshootv1beta2.HostRun{
|
||||
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
|
||||
CollectorName: "endless command",
|
||||
},
|
||||
Command: "ping",
|
||||
Args: []string{"google.com"},
|
||||
Timeout: "200ms",
|
||||
},
|
||||
},
|
||||
wantError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got, err := test.collector.Collect(nil)
|
||||
if test.wantError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,14 @@ import (
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type HTTPResponse struct {
|
||||
Status int `json:"status"`
|
||||
Body string `json:"body"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
RawJSON json.RawMessage `json:"raw_json,omitempty"`
|
||||
}
|
||||
|
||||
type HTTPError struct {
|
||||
@@ -127,10 +129,17 @@ func responseToOutput(response *http.Response, err error) ([]byte, error) {
|
||||
headers[k] = strings.Join(v, ",")
|
||||
}
|
||||
|
||||
var rawJSON json.RawMessage
|
||||
if err := json.Unmarshal(body, &rawJSON); err != nil {
|
||||
klog.Infof("failed to unmarshal response body as JSON: %v", err)
|
||||
rawJSON = json.RawMessage{}
|
||||
}
|
||||
|
||||
output["response"] = HTTPResponse{
|
||||
Status: response.StatusCode,
|
||||
Body: string(body),
|
||||
Headers: headers,
|
||||
RawJSON: rawJSON,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user