mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-08-27 00:37:20 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5778d4656a | ||
|
|
905c9771bc | ||
|
|
ce155270c8 | ||
|
|
91da8f13bb | ||
|
|
1a8aff1d79 | ||
|
|
ece20d3c2c | ||
|
|
80e5fac07c | ||
|
|
51c07b42c3 | ||
|
|
bd7bae6302 | ||
|
|
9d41460d33 | ||
|
|
fb0f81d076 | ||
|
|
78bbea18ac | ||
|
|
224d55b076 | ||
|
|
db967d2896 | ||
|
|
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 | ||
|
|
d4623d9404 | ||
|
|
bc4856869e | ||
|
|
f6373f3e36 | ||
|
|
7038da85b1 | ||
|
|
a7bb9ea31e | ||
|
|
08c3fcf3df | ||
|
|
1d2c1191e2 | ||
|
|
66e070a07d | ||
|
|
7528c57e83 | ||
|
|
42fc4f06de | ||
|
|
312e467160 | ||
|
|
5e280d0652 | ||
|
|
15cfdb9eee | ||
|
|
15a4802cd2 | ||
|
|
0d4d305ae6 | ||
|
|
de7112fed2 | ||
|
|
73a2d882d7 | ||
|
|
461cc994ef | ||
|
|
1bdf87e8cd | ||
|
|
32b0e1a890 | ||
|
|
cc56522571 | ||
|
|
e3adc1cb35 | ||
|
|
359475ba87 | ||
|
|
f3d49c657f | ||
|
|
7f3808b8d7 | ||
|
|
f44d077756 | ||
|
|
e34742f3cc | ||
|
|
fd29012624 | ||
|
|
be5e66cbca | ||
|
|
c079d96efe | ||
|
|
7b797b3725 | ||
|
|
6cbe188abe | ||
|
|
86279b4ec4 | ||
|
|
514c86d891 | ||
|
|
b9f4fc4390 | ||
|
|
53813502de | ||
|
|
597fb2996b | ||
|
|
e44aff4100 | ||
|
|
d0d6f00c0f | ||
|
|
4460e8c639 | ||
|
|
c166d46af4 | ||
|
|
ded1f42b5a | ||
|
|
32176178a8 | ||
|
|
949b645336 | ||
|
|
1f50fe3163 | ||
|
|
24c6278e5d | ||
|
|
8d527ead8c | ||
|
|
1dedbcd67a | ||
|
|
ff03bfa9cd | ||
|
|
b7d5a9876c | ||
|
|
96c482a1f5 | ||
|
|
543931631b | ||
|
|
d730be0fae | ||
|
|
38ff340883 | ||
|
|
04f69b3f8c | ||
|
|
57b988b100 | ||
|
|
6972789bea | ||
|
|
17cbc16e05 | ||
|
|
87404982fe | ||
|
|
5bb4358f7a | ||
|
|
39314ef200 | ||
|
|
73c7dba0e1 | ||
|
|
a8f8bf9257 | ||
|
|
08c37c7333 | ||
|
|
8f20b665f9 | ||
|
|
638d99eaad | ||
|
|
25ddcf4388 | ||
|
|
31ccbf696c | ||
|
|
a068561af4 | ||
|
|
f3777bef69 | ||
|
|
addc2ce925 | ||
|
|
247c60afd2 | ||
|
|
8237ac991c | ||
|
|
73201c0304 | ||
|
|
531edf42e2 | ||
|
|
f69ae77bc3 | ||
|
|
b02d12ff1e | ||
|
|
24822b7f95 | ||
|
|
784918e7ee | ||
|
|
819dd5fc26 | ||
|
|
27e06517bf | ||
|
|
d14e0dd18c | ||
|
|
d7b3dd8abb | ||
|
|
f64d98b5c3 | ||
|
|
f0efbf658a | ||
|
|
ec2d14a7ad | ||
|
|
6385a1247f | ||
|
|
80a946d0c2 | ||
|
|
58c428347e | ||
|
|
2c6b1869e2 | ||
|
|
944da4543a | ||
|
|
e8340d54d4 | ||
|
|
0fc92122d7 | ||
|
|
f7f5604734 | ||
|
|
de44c21efa | ||
|
|
557a1381a9 | ||
|
|
fefe118943 | ||
|
|
5b1e48258f | ||
|
|
03c53cabd6 | ||
|
|
a5628874b0 | ||
|
|
620fa75eb5 | ||
|
|
60d5b686cc | ||
|
|
f9dbccd354 | ||
|
|
08a1075d82 | ||
|
|
0a855f26c2 | ||
|
|
0d1e23651a | ||
|
|
70f0e071dd | ||
|
|
5add5d6f9d | ||
|
|
191e002a9b | ||
|
|
fbacd055cd |
@@ -1,4 +0,0 @@
|
||||
version: 3
|
||||
|
||||
project:
|
||||
id: github.com/replicatedhq/troubleshoot
|
||||
+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.19"
|
||||
go-version: "1.22"
|
||||
|
||||
- name: setup env
|
||||
run: |
|
||||
@@ -38,16 +40,16 @@ jobs:
|
||||
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: make test
|
||||
|
||||
test-integration:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.19"
|
||||
go-version: "1.22"
|
||||
|
||||
- name: setup env
|
||||
run: |
|
||||
@@ -55,26 +57,26 @@ jobs:
|
||||
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- 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.19"
|
||||
go-version: "1.22"
|
||||
- name: setup env
|
||||
run: |
|
||||
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
||||
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: make check-schemas
|
||||
@@ -82,17 +84,17 @@ jobs:
|
||||
compile-preflight:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.19"
|
||||
go-version: "1.22"
|
||||
- name: setup env
|
||||
run: |
|
||||
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
||||
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- 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/
|
||||
@@ -149,33 +151,39 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: compile-preflight
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: replicatedhq/action-k3s@main
|
||||
id: k3s
|
||||
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/
|
||||
- run: chmod +x bin/preflight
|
||||
- run: make preflight-e2e-test
|
||||
|
||||
run-examples:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: make run-examples
|
||||
|
||||
compile-supportbundle:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.19"
|
||||
go-version: "1.22"
|
||||
- name: setup env
|
||||
run: |
|
||||
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
||||
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- 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
|
||||
@@ -184,13 +192,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: compile-supportbundle
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: replicatedhq/action-k3s@main
|
||||
id: k3s
|
||||
with:
|
||||
version: v1.23.6-k3s1
|
||||
- name: Download support-bundle binary
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: support-bundle
|
||||
path: bin/
|
||||
@@ -203,33 +211,54 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: compile-supportbundle
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: replicatedhq/action-k3s@main
|
||||
id: k3s
|
||||
with:
|
||||
version: v1.23.6-k3s1
|
||||
- name: Download support bundle binary
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: support-bundle
|
||||
path: bin/
|
||||
- run: chmod +x bin/support-bundle
|
||||
- run: make support-bundle-e2e-test
|
||||
|
||||
compile-collect:
|
||||
validate-supportbundle-e2e-go-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: compile-supportbundle
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.19"
|
||||
go-version: "1.22"
|
||||
- name: setup env
|
||||
run: |
|
||||
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
||||
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download support bundle binary
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: support-bundle
|
||||
path: bin/
|
||||
- run: chmod +x bin/support-bundle
|
||||
- run: make support-bundle-e2e-go-test
|
||||
|
||||
compile-collect:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.22"
|
||||
- name: setup env
|
||||
run: |
|
||||
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
|
||||
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
- uses: actions/checkout@v4
|
||||
- run: make generate collect
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: collect
|
||||
path: bin/collect
|
||||
@@ -244,18 +273,20 @@ jobs:
|
||||
include:
|
||||
- goarch: arm
|
||||
goos: linux
|
||||
- goarch: riscv64
|
||||
goos: linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.19"
|
||||
go-version: "1.22"
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
version: "v0.183.0"
|
||||
args: build --rm-dist --snapshot --config deploy/.goreleaser.yaml --single-target
|
||||
@@ -272,22 +303,23 @@ jobs:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: azure/docker-login@v1
|
||||
- uses: azure/docker-login@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USER }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.19"
|
||||
go-version: "1.22"
|
||||
|
||||
- uses: sigstore/cosign-installer@main
|
||||
- uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: "v1.2.1"
|
||||
# DO NOT USE v2 until we decide on whether to use Rekor or not
|
||||
cosign-release: "v1.13.1" # Binary version to install
|
||||
|
||||
- name: Get Cosign Key
|
||||
run: |
|
||||
@@ -303,9 +335,9 @@ jobs:
|
||||
COSIGN_KEY: ${{ secrets.COSIGN_KEY }}
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
version: "v0.183.0"
|
||||
version: "v1.x" # Binary version to install
|
||||
args: release --rm-dist --config deploy/.goreleaser.yaml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -318,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"
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run Trivy vulnerability scanner in repo mode
|
||||
uses: aquasecurity/trivy-action@master
|
||||
@@ -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'
|
||||
@@ -1,49 +0,0 @@
|
||||
name: FOSSA license scan
|
||||
|
||||
on:
|
||||
pull_request_target: # this is safe as these scans do not execute provided code
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- go.sum
|
||||
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
fossa-scan-pr:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: refs/pull/${{ github.event.number }}/merge
|
||||
- name: "Install FOSSA"
|
||||
uses: replicatedhq/action-fossa/install@main
|
||||
- name: "Run FOSSA Scan"
|
||||
uses: replicatedhq/action-fossa/scan@main
|
||||
with:
|
||||
api-key: ${{ secrets.FOSSA_API_KEY }}
|
||||
diff: true
|
||||
diff-ref: ${{ github.event.pull_request.base.sha }}
|
||||
debug: true
|
||||
|
||||
fossa-scan-merge:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- id: previous
|
||||
run: echo "sha=$(git rev-parse HEAD~1)" >> "${GITHUB_OUTPUT}"
|
||||
- name: "Install FOSSA"
|
||||
uses: replicatedhq/action-fossa/install@main
|
||||
- name: "Run FOSSA Scan"
|
||||
uses: replicatedhq/action-fossa/scan@main
|
||||
with:
|
||||
api-key: ${{ secrets.FOSSA_API_KEY }}
|
||||
diff: true
|
||||
diff-ref: ${{ steps.previous.outputs.sha }}
|
||||
debug: true
|
||||
@@ -0,0 +1,44 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TRIVY_VERSION: 0.44.1
|
||||
|
||||
name: License scan
|
||||
|
||||
jobs:
|
||||
license:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21
|
||||
|
||||
- name: Install Go deps
|
||||
run: go mod download
|
||||
|
||||
- name: Install trivy
|
||||
run: |
|
||||
wget https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.deb
|
||||
sudo dpkg -i trivy_${TRIVY_VERSION}_Linux-64bit.deb
|
||||
|
||||
- name: Create license report artifact
|
||||
run: trivy fs --scanners license --skip-dirs ".github" . | tee license-report.txt
|
||||
|
||||
- name: Upload license report artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: license-report
|
||||
path: license-report.txt
|
||||
|
||||
- name: Check for unknown licenses
|
||||
run: trivy fs --scanners license --skip-dirs ".github" --exit-code 1 --severity UNKNOWN . || echo "::warning::Unknown licenses found, please verify"
|
||||
|
||||
- name: Check for forbidden licenses and fail
|
||||
run: trivy fs --scanners license --skip-dirs ".github" --exit-code 1 --severity CRITICAL,HIGH .
|
||||
@@ -7,7 +7,10 @@
|
||||
*.dylib
|
||||
bin
|
||||
.DS_Store
|
||||
|
||||
# npm generated files
|
||||
node_modules/
|
||||
package*.json
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
@@ -10,3 +10,4 @@ linters:
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- gosec
|
||||
- govet
|
||||
|
||||
+16
-5
@@ -21,17 +21,22 @@ When implementing a new feature please review the [design principles](./docs/des
|
||||
|
||||
To get started we recommend:
|
||||
|
||||
1. Go (v1.19 or later)
|
||||
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.
|
||||
|
||||
### Build automatically on save with `watch`
|
||||
|
||||
1. Install `npm`
|
||||
2. Run `make watch` to build binaries automatically on saving. Note: you may still have to run `make schemas` if you've added API changes, like a new collector or analyzer type.
|
||||
|
||||
### Syncing to a test cluster with `watchrsync`
|
||||
|
||||
1. Install `npm`
|
||||
@@ -89,6 +94,12 @@ go tool pprof -http=":8000" cpu.prof
|
||||
go tool pprof -http=":8001" mem.prof
|
||||
```
|
||||
|
||||
**Additional flags for memory profiling**
|
||||
- `inuse_space`: Amount of memory allocated and not released yet (default).
|
||||
- `inuse_objects`: Amount of objects allocated and not released yet.
|
||||
- `alloc_space`: Total amount of memory allocated (regardless of released).
|
||||
- `alloc_objects`: Total amount of objects allocated (regardless of released).
|
||||
|
||||
More on profiling please visit https://go.dev/doc/diagnostics#profiling
|
||||
|
||||
## Contribution workflow
|
||||
|
||||
@@ -36,9 +36,11 @@ endef
|
||||
|
||||
BUILDFLAGS = -tags "netgo containers_image_ostree_stub exclude_graphdriver_devicemapper exclude_graphdriver_btrfs containers_image_openpgp" -installsuffix netgo
|
||||
BUILDPATHS = ./pkg/... ./cmd/... ./internal/...
|
||||
E2EPATHS = ./test/e2e/...
|
||||
TESTFLAGS ?= -v -coverprofile cover.out
|
||||
|
||||
all: test support-bundle preflight collect analyze
|
||||
.DEFAULT_GOAL := all
|
||||
all: clean build test
|
||||
|
||||
.PHONY: ffi
|
||||
ffi: fmt vet
|
||||
@@ -62,29 +64,54 @@ test-integration:
|
||||
preflight-e2e-test:
|
||||
./test/validate-preflight-e2e.sh
|
||||
|
||||
.PHONY: run-examples
|
||||
run-examples:
|
||||
./test/run-examples.sh
|
||||
|
||||
.PHONY: support-bundle-e2e-test
|
||||
support-bundle-e2e-test:
|
||||
./test/validate-support-bundle-e2e.sh
|
||||
|
||||
# Build all binaries in parallel ( -j )
|
||||
build:
|
||||
@echo "Build cli binaries"
|
||||
$(MAKE) -j support-bundle preflight analyze collect
|
||||
.PHONY: support-bundle-e2e-go-test
|
||||
support-bundle-e2e-go-test:
|
||||
if [ -n $(RUN) ]; then \
|
||||
go test ${BUILDFLAGS} ${E2EPATHS} -v -run $(RUN); \
|
||||
else \
|
||||
go test ${BUILDFLAGS} ${E2EPATHS} -v; \
|
||||
fi
|
||||
|
||||
.PHONY: support-bundle
|
||||
support-bundle:
|
||||
rebuild: clean build
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
@@ -121,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.26.1
|
||||
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})
|
||||
@@ -163,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
|
||||
@@ -195,6 +222,55 @@ 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
|
||||
|
||||
.PHONY: scan
|
||||
scan:
|
||||
trivy fs \
|
||||
--scanners vuln \
|
||||
--exit-code=1 \
|
||||
--severity="HIGH,CRITICAL" \
|
||||
--ignore-unfixed \
|
||||
./
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
golangci-lint run --new -c .golangci.yaml ${BUILDPATHS}
|
||||
|
||||
.PHONY: lint-and-fix
|
||||
lint-and-fix:
|
||||
golangci-lint run --new --fix -c .golangci.yaml ${BUILDPATHS}
|
||||
|
||||
.PHONY: watch
|
||||
watch: npm-install
|
||||
bin/watch.js
|
||||
|
||||
## Syncronize the code with a remote server. More info: CONTRIBUTING.md
|
||||
.PHONY: watchrsync
|
||||
watchrsync: npm-install
|
||||
bin/watchrsync.js
|
||||
|
||||
.PHONY: 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 ..
|
||||
@@ -208,30 +284,3 @@ longhorn:
|
||||
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 \
|
||||
--scanners vuln \
|
||||
--exit-code=1 \
|
||||
--severity="HIGH,CRITICAL" \
|
||||
--ignore-unfixed \
|
||||
./
|
||||
|
||||
.PHONY: lint
|
||||
lint: fmt vet
|
||||
golangci-lint run --new -c .golangci.yaml ${BUILDPATHS}
|
||||
|
||||
.PHONY: lint-and-fix
|
||||
lint-and-fix: fmt vet
|
||||
golangci-lint run --new --fix -c .golangci.yaml ${BUILDPATHS}
|
||||
|
||||
## Syncronize the code with a remote server. More info: CONTRIBUTING.md
|
||||
.PHONY: watchrsync
|
||||
watchrsync: npm-install
|
||||
bin/watchrsync.js
|
||||
|
||||
.PHONY: 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 gaze-run-interrupt@~2.0.0
|
||||
|
||||
@@ -61,7 +61,14 @@ A signed SBOM that includes Troubleshoot dependencies is included in each relea
|
||||
|
||||
The following example illustrates using [cosign](https://github.com/sigstore/cosign) to verify that **troubleshoot-sbom.tgz** has
|
||||
not been tampered with.
|
||||
```
|
||||
```sh
|
||||
$ cosign verify-blob --key key.pub --signature troubleshoot-sbom.tgz.sig troubleshoot-sbom.tgz
|
||||
Verified OK
|
||||
```
|
||||
|
||||
If you were to get an error similar to the one below, it means you are verifying an SBOM signed using cosign `v1` using a newer `v2` of the binary. This version introduced [breaking changes](https://github.com/sigstore/cosign/blob/main/CHANGELOG.md#breaking-changes) which require an additional flag `--insecure-ignore-tlog=true` to successfully verify SBOMs like so.
|
||||
```sh
|
||||
$ cosign verify-blob --key key.pub --signature troubleshoot-sbom.tgz.sig troubleshoot-sbom.tgz --insecure-ignore-tlog=true
|
||||
WARNING: Skipping tlog verification is an insecure practice that lacks of transparency and auditability verification for the blob.
|
||||
Verified OK
|
||||
```
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const gri = require('gaze-run-interrupt');
|
||||
|
||||
const commands = [
|
||||
// {
|
||||
// command: 'rm',
|
||||
// args: binList,
|
||||
// },
|
||||
{
|
||||
command: 'make',
|
||||
args: ['build'],
|
||||
},
|
||||
];
|
||||
|
||||
commands.push({
|
||||
command: "date",
|
||||
args: [],
|
||||
});
|
||||
|
||||
commands.push({
|
||||
command: "echo",
|
||||
args: ["synced"],
|
||||
});
|
||||
|
||||
gri([
|
||||
'cmd/**/*.go',
|
||||
'pkg/**/*.go',
|
||||
], commands);
|
||||
+1
-7
@@ -16,12 +16,6 @@ const binList = [
|
||||
'bin/support-bundle',
|
||||
// 'bin/collect'
|
||||
]
|
||||
const makeList = [
|
||||
// 'analyze',
|
||||
// 'preflight',
|
||||
'support-bundle',
|
||||
// 'collect'
|
||||
]
|
||||
|
||||
const commands = [
|
||||
// {
|
||||
@@ -30,7 +24,7 @@ const commands = [
|
||||
// },
|
||||
{
|
||||
command: 'make',
|
||||
args: makeList,
|
||||
args: ['build'],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/cmd/util"
|
||||
"github.com/replicatedhq/troubleshoot/cmd/internal/util"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -43,6 +43,8 @@ func RootCmd() *cobra.Command {
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
cmd.AddCommand(util.VersionCmd())
|
||||
|
||||
cmd.Flags().String("analyzers", "", "filename or url of the analyzers to use")
|
||||
cmd.Flags().Bool("debug", false, "enable debug logging")
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ func runAnalyzers(v *viper.Viper, bundlePath string) error {
|
||||
specContent := ""
|
||||
var err error
|
||||
if _, err = os.Stat(specPath); err == nil {
|
||||
b, err := ioutil.ReadFile(specPath)
|
||||
b, err := os.ReadFile(specPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -26,7 +26,8 @@ func runAnalyzers(v *viper.Viper, bundlePath 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)
|
||||
// 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", specPath)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", specPath, nil)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/cmd/util"
|
||||
"github.com/replicatedhq/troubleshoot/cmd/internal/util"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -43,7 +43,7 @@ func RootCmd() *cobra.Command {
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
cmd.AddCommand(VersionCmd())
|
||||
cmd.AddCommand(util.VersionCmd())
|
||||
|
||||
cmd.Flags().StringSlice("redactors", []string{}, "names of the additional redactors to use")
|
||||
cmd.Flags().Bool("redact", true, "enable/disable default redactions")
|
||||
|
||||
@@ -50,7 +50,7 @@ func runCollect(v *viper.Viper, arg string) error {
|
||||
|
||||
collectorContent = spec
|
||||
} else if _, err = os.Stat(arg); err == nil {
|
||||
b, err := ioutil.ReadFile(arg)
|
||||
b, err := os.ReadFile(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -58,7 +58,7 @@ func runCollect(v *viper.Viper, arg string) error {
|
||||
collectorContent = b
|
||||
} else {
|
||||
if !util.IsURL(arg) {
|
||||
return fmt.Errorf("%s is not a URL and was not found (err %s)", arg, err)
|
||||
return fmt.Errorf("%s is not a URL and was not found", arg)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", arg, nil)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/pkg/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func VersionCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the current version and exit",
|
||||
Long: `Print the current version and exit`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Printf("Replicated Collect %s\n", version.Version())
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cli
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -2,9 +2,12 @@ package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/oci"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func OciFetchCmd() *cobra.Command {
|
||||
@@ -12,6 +15,13 @@ func OciFetchCmd() *cobra.Command {
|
||||
Use: "oci-fetch [URI]",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Short: "Fetch a preflight from an OCI registry and print it to standard out",
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
v := viper.GetViper()
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
v.BindPFlags(cmd.Flags())
|
||||
|
||||
logger.SetupLogger(v)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
uri := args[0]
|
||||
data, err := oci.PullPreflightFromOCI(uri)
|
||||
@@ -22,5 +32,9 @@ func OciFetchCmd() *cobra.Command {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// Initialize klog flags
|
||||
logger.InitKlogFlags(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/cmd/util"
|
||||
"github.com/replicatedhq/troubleshoot/cmd/internal/util"
|
||||
"github.com/replicatedhq/troubleshoot/internal/traces"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/constants"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
@@ -49,7 +49,7 @@ that a cluster meets the requirements to run an application.`,
|
||||
}
|
||||
|
||||
err = preflight.RunPreflights(v.GetBool("interactive"), v.GetString("output"), v.GetString("format"), args)
|
||||
if v.GetBool("debug") || v.IsSet("v") {
|
||||
if !v.GetBool("dry-run") && (v.GetBool("debug") || v.IsSet("v")) {
|
||||
fmt.Printf("\n%s", traces.GetExporterInstance().GetSummary())
|
||||
}
|
||||
|
||||
@@ -64,10 +64,14 @@ that a cluster meets the requirements to run an application.`,
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
cmd.AddCommand(VersionCmd())
|
||||
cmd.AddCommand(util.VersionCmd())
|
||||
cmd.AddCommand(OciFetchCmd())
|
||||
preflight.AddFlags(cmd.PersistentFlags())
|
||||
|
||||
// Dry run flag should be in cmd.PersistentFlags() flags made available to all subcommands
|
||||
// Adding here to avoid that
|
||||
cmd.Flags().Bool("dry-run", false, "print the preflight spec without running preflight checks")
|
||||
|
||||
k8sutil.AddFlags(cmd.Flags())
|
||||
|
||||
// Initialize klog flags
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/pkg/version"
|
||||
)
|
||||
|
||||
func VersionCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the current version and exit",
|
||||
Long: `Print the current version and exit`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Printf("Replicated Preflight %s\n", version.Version())
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -7,11 +7,13 @@ import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/go-wordwrap"
|
||||
"github.com/pkg/errors"
|
||||
ui "github.com/replicatedhq/termui/v3"
|
||||
"github.com/replicatedhq/termui/v3/widgets"
|
||||
"github.com/replicatedhq/troubleshoot/internal/util"
|
||||
analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/constants"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -178,7 +180,10 @@ func drawDetails(analysisResult *analyzerunner.AnalyzeResult) {
|
||||
|
||||
currentTop := 4
|
||||
title := widgets.NewParagraph()
|
||||
title.Text = analysisResult.Title
|
||||
// WrapText is set to false to prevent internal wordwrap which is not accounting for the padding
|
||||
title.WrapText = false
|
||||
// For long title that lead to wrapping text, the terminal width is divided by 2 and deducted by MESSAGE_TEXT_PADDING to account for the padding
|
||||
title.Text = wordwrap.WrapString(analysisResult.Title, uint(termWidth/2-constants.MESSAGE_TEXT_PADDING))
|
||||
title.Border = false
|
||||
if analysisResult.IsPass {
|
||||
title.TextStyle = ui.NewStyle(ui.ColorGreen, ui.ColorClear, ui.ModifierBold)
|
||||
@@ -187,34 +192,26 @@ func drawDetails(analysisResult *analyzerunner.AnalyzeResult) {
|
||||
} else if analysisResult.IsFail {
|
||||
title.TextStyle = ui.NewStyle(ui.ColorRed, ui.ColorClear, ui.ModifierBold)
|
||||
}
|
||||
height := estimateNumberOfLines(title.Text, termWidth/2)
|
||||
height := util.EstimateNumberOfLines(title.Text)
|
||||
title.SetRect(termWidth/2, currentTop, termWidth, currentTop+height)
|
||||
ui.Render(title)
|
||||
currentTop = currentTop + height + 1
|
||||
|
||||
message := widgets.NewParagraph()
|
||||
message.Text = analysisResult.Message
|
||||
// WrapText is set to false to prevent internal wordwrap which is not accounting for the padding
|
||||
message.WrapText = false
|
||||
// For long text that lead to wrapping text, the terminal width is divided by 2 and deducted by MESSAGE_TEXT_PADDING to account for the padding
|
||||
message.Text = wordwrap.WrapString(analysisResult.Message, uint(termWidth/2-constants.MESSAGE_TEXT_PADDING))
|
||||
if analysisResult.URI != "" {
|
||||
// Add URL to the message with wordwrap
|
||||
// Add emply lines as separator
|
||||
urlText := wordwrap.WrapString(fmt.Sprintf("For more information: %s", analysisResult.URI), uint(termWidth/2-constants.MESSAGE_TEXT_PADDING))
|
||||
message.Text = message.Text + "\n\n" + urlText
|
||||
}
|
||||
height = util.EstimateNumberOfLines(message.Text) + constants.MESSAGE_TEXT_LINES_MARGIN_TO_BOTTOM
|
||||
message.Border = false
|
||||
height = estimateNumberOfLines(message.Text, termWidth/2) + 2
|
||||
message.SetRect(termWidth/2, currentTop, termWidth, currentTop+height)
|
||||
ui.Render(message)
|
||||
currentTop = currentTop + height + 1
|
||||
|
||||
if analysisResult.URI != "" {
|
||||
uri := widgets.NewParagraph()
|
||||
uri.Text = fmt.Sprintf("For more information: %s", analysisResult.URI)
|
||||
uri.Border = false
|
||||
// For long urls that lead to wrapping text, make the rectangle bigger by
|
||||
// increasing the calculated height by 2
|
||||
height = estimateNumberOfLines(uri.Text, termWidth/2) + 2
|
||||
uri.SetRect(termWidth/2, currentTop, termWidth, currentTop+height)
|
||||
ui.Render(uri)
|
||||
}
|
||||
}
|
||||
|
||||
func estimateNumberOfLines(text string, width int) int {
|
||||
lines := len(text)/width + 1
|
||||
return lines
|
||||
}
|
||||
|
||||
func showSaved(filename string) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/cmd/util"
|
||||
"github.com/replicatedhq/troubleshoot/cmd/internal/util"
|
||||
"github.com/replicatedhq/troubleshoot/internal/traces"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
@@ -45,7 +45,7 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust
|
||||
}
|
||||
|
||||
err = runTroubleshoot(v, args)
|
||||
if v.GetBool("debug") || v.IsSet("v") {
|
||||
if !v.IsSet("dry-run") && (v.GetBool("debug") || v.IsSet("v")) {
|
||||
fmt.Printf("\n%s", traces.GetExporterInstance().GetSummary())
|
||||
}
|
||||
|
||||
@@ -62,18 +62,19 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust
|
||||
|
||||
cmd.AddCommand(Analyze())
|
||||
cmd.AddCommand(Redact())
|
||||
cmd.AddCommand(VersionCmd())
|
||||
cmd.AddCommand(util.VersionCmd())
|
||||
|
||||
cmd.Flags().StringSlice("redactors", []string{}, "names of the additional redactors to use")
|
||||
cmd.Flags().Bool("redact", true, "enable/disable default redactions")
|
||||
cmd.Flags().Bool("interactive", true, "enable/disable interactive mode")
|
||||
cmd.Flags().Bool("collect-without-permissions", true, "always generate a support bundle, even if it some require additional permissions")
|
||||
cmd.Flags().StringSliceP("selector", "l", []string{"troubleshoot.io/kind=support-bundle"}, "selector to filter on for loading additional support bundle specs found in secrets within the cluster")
|
||||
cmd.Flags().StringSliceP("selector", "l", []string{"troubleshoot.sh/kind=support-bundle"}, "selector to filter on for loading additional support bundle specs found in secrets within the cluster")
|
||||
cmd.Flags().Bool("load-cluster-specs", false, "enable/disable loading additional troubleshoot specs found within the cluster. required when no specs are provided on the command line")
|
||||
cmd.Flags().String("since-time", "", "force pod logs collectors to return logs after a specific date (RFC3339)")
|
||||
cmd.Flags().String("since", "", "force pod logs collectors to return logs newer than a relative duration like 5s, 2m, or 3h.")
|
||||
cmd.Flags().StringP("output", "o", "", "specify the output file path for the support bundle")
|
||||
cmd.Flags().Bool("debug", false, "enable debug logging. This is equivalent to --v=0")
|
||||
cmd.Flags().Bool("dry-run", false, "print support bundle spec without collecting anything")
|
||||
|
||||
// hidden in favor of the `insecure-skip-tls-verify` flag
|
||||
cmd.Flags().Bool("allow-insecure-connections", false, "when set, do not verify TLS certs when retrieving spec and reporting results")
|
||||
@@ -81,7 +82,7 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust
|
||||
|
||||
// `no-uri` references the `followURI` functionality where we can use an upstream spec when creating a support bundle
|
||||
// This flag makes sure we can also disable this and fall back to the default spec.
|
||||
cmd.Flags().Bool("no-uri", false, "When this flag is used, Troubleshoot does not attempt to retrieve the bundle referenced by the uri: field in the spec.`")
|
||||
cmd.Flags().Bool("no-uri", false, "When this flag is used, Troubleshoot does not attempt to retrieve the spec referenced by the uri: field`")
|
||||
|
||||
k8sutil.AddFlags(cmd.Flags())
|
||||
|
||||
|
||||
+161
-205
@@ -9,7 +9,6 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -17,28 +16,65 @@ import (
|
||||
"github.com/fatih/color"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/replicatedhq/troubleshoot/internal/specs"
|
||||
"github.com/replicatedhq/troubleshoot/internal/util"
|
||||
analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/constants"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/convert"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/httputil"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/specs"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/loader"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/supportbundle"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/types"
|
||||
"github.com/spf13/viper"
|
||||
spin "github.com/tj/go-spin"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func runTroubleshoot(v *viper.Viper, arg []string) error {
|
||||
if !v.GetBool("load-cluster-specs") && len(arg) < 1 {
|
||||
func runTroubleshoot(v *viper.Viper, args []string) error {
|
||||
ctx := context.Background()
|
||||
if !v.GetBool("load-cluster-specs") && len(args) < 1 {
|
||||
return errors.New("flag load-cluster-specs must be set if no specs are provided on the command line")
|
||||
}
|
||||
|
||||
restConfig, err := k8sutil.GetRESTConfig()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to convert kube flags to rest config")
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create kubernetes client")
|
||||
}
|
||||
|
||||
mainBundle, additionalRedactors, err := loadSpecs(ctx, args, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// For --dry-run, we want to print the yaml and exit
|
||||
if v.GetBool("dry-run") {
|
||||
k := loader.TroubleshootKinds{
|
||||
SupportBundlesV1Beta2: []troubleshootv1beta2.SupportBundle{*mainBundle},
|
||||
}
|
||||
// If we have redactors, add them to the temp kinds object
|
||||
if len(additionalRedactors.Spec.Redactors) > 0 {
|
||||
k.RedactorsV1Beta2 = []troubleshootv1beta2.Redactor{*additionalRedactors}
|
||||
}
|
||||
|
||||
out, err := k.ToYaml()
|
||||
if err != nil {
|
||||
return types.NewExitCodeError(constants.EXIT_CODE_CATCH_ALL, errors.Wrap(err, "failed to convert specs to yaml"))
|
||||
}
|
||||
fmt.Printf("%s", out)
|
||||
return nil
|
||||
}
|
||||
|
||||
interactive := v.GetBool("interactive") && isatty.IsTerminal(os.Stdout.Fd())
|
||||
|
||||
if interactive {
|
||||
@@ -56,11 +92,6 @@ func runTroubleshoot(v *viper.Viper, arg []string) error {
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
restConfig, err := k8sutil.GetRESTConfig()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to convert kube flags to rest config")
|
||||
}
|
||||
|
||||
var sinceTime *time.Time
|
||||
if v.GetString("since-time") != "" || v.GetString("since") != "" {
|
||||
sinceTime, err = parseTimeFlags(v)
|
||||
@@ -75,61 +106,22 @@ func runTroubleshoot(v *viper.Viper, arg []string) error {
|
||||
})
|
||||
}
|
||||
|
||||
var mainBundle *troubleshootv1beta2.SupportBundle
|
||||
|
||||
additionalRedactors := &troubleshootv1beta2.Redactor{}
|
||||
|
||||
// Defining `v` below will render using `v` in reference to Viper unusable.
|
||||
// Therefore refactoring `v` to `val` will make sure we can still use it.
|
||||
for _, val := range arg {
|
||||
|
||||
collectorContent, err := supportbundle.LoadSupportBundleSpec(val)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to load support bundle spec")
|
||||
}
|
||||
multidocs := strings.Split(string(collectorContent), "\n---\n")
|
||||
// Referencing `ParseSupportBundle with a secondary arg of `no-uri`
|
||||
// Will make sure we can enable or disable the use of the `Spec.uri` field for an upstream spec.
|
||||
// This change will not have an impact on KOTS' usage of `ParseSupportBundle`
|
||||
// As Kots uses `load.go` directly.
|
||||
supportBundle, err := supportbundle.ParseSupportBundle([]byte(multidocs[0]), !v.GetBool("no-uri"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse support bundle spec")
|
||||
}
|
||||
|
||||
mainBundle = supportbundle.ConcatSpec(mainBundle, supportBundle)
|
||||
|
||||
parsedRedactors, err := supportbundle.ParseRedactorsFromDocs(multidocs)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse redactors from doc")
|
||||
}
|
||||
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, parsedRedactors...)
|
||||
if interactive {
|
||||
c := color.New()
|
||||
c.Println(fmt.Sprintf("\r%s\r", cursor.ClearEntireLine()))
|
||||
}
|
||||
|
||||
if v.GetBool("load-cluster-specs") {
|
||||
sbFromCluster, redactorsFromCluster, err := loadClusterSpecs()
|
||||
if err != nil {
|
||||
return err
|
||||
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())
|
||||
}
|
||||
if sbFromCluster == nil {
|
||||
return errors.New("no specs found in cluster")
|
||||
}
|
||||
mainBundle = supportbundle.ConcatSpec(mainBundle, sbFromCluster)
|
||||
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, redactorsFromCluster.Spec.Redactors...)
|
||||
}
|
||||
|
||||
if mainBundle == nil {
|
||||
return errors.New("no support bundle specs provided to run")
|
||||
} else if mainBundle.Spec.Collectors == nil && mainBundle.Spec.HostCollectors == nil {
|
||||
return errors.New("no collectors specified in support bundle")
|
||||
}
|
||||
|
||||
redactors, err := supportbundle.GetRedactorsFromURIs(v.GetStringSlice("redactors"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get redactors")
|
||||
}
|
||||
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, redactors...)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
collectorCB := func(c chan interface{}, msg string) { c <- msg }
|
||||
progressChan := make(chan interface{})
|
||||
@@ -198,11 +190,6 @@ func runTroubleshoot(v *viper.Viper, arg []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")
|
||||
@@ -256,163 +243,132 @@ the %s Admin Console to begin analysis.`
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadClusterSpecs loads the support bundle and redactor specs from the cluster
|
||||
// based on troubleshoot.io/kind=support-bundle label selector. We search for secrets
|
||||
// and configmaps with the label selector and parse the data as a support bundle. If the
|
||||
// user does not have sufficient permissions to list & read secrets and configmaps from
|
||||
// all namespaces, we will fallback to trying each namespace individually, and eventually
|
||||
// default to the configured kubeconfig namespace.
|
||||
func loadClusterSpecs() (*troubleshootv1beta2.SupportBundle, *troubleshootv1beta2.Redactor, error) {
|
||||
redactors := &troubleshootv1beta2.Redactor{}
|
||||
// loadSupportBundleSpecsFromURIs loads support bundle specs from URIs
|
||||
func loadSupportBundleSpecsFromURIs(ctx context.Context, kinds *loader.TroubleshootKinds) error {
|
||||
moreKinds := loader.NewTroubleshootKinds()
|
||||
|
||||
v := viper.GetViper() // It's singleton, so we can use it anywhere
|
||||
|
||||
klog.Info("Discover troubleshoot specs from cluster")
|
||||
|
||||
labelSelector := strings.Join(v.GetStringSlice("selector"), ",")
|
||||
|
||||
parsedSelector, err := labels.Parse(labelSelector)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unable to parse selector")
|
||||
}
|
||||
|
||||
config, err := k8sutil.GetRESTConfig()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to convert kube flags to rest config")
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to convert create k8s client")
|
||||
}
|
||||
|
||||
// List of namespaces we want to search for secrets and configmaps with support bundle specs
|
||||
namespaces := []string{}
|
||||
ctx := context.Background()
|
||||
|
||||
if v.GetString("namespace") != "" {
|
||||
// Just progress with the namespace provided
|
||||
namespaces = []string{v.GetString("namespace")}
|
||||
} else {
|
||||
// Check if I can read secrets and configmaps in all namespaces
|
||||
ican, err := k8sutil.CanIListAndGetAllSecretsAndConfigMaps(ctx, client)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to check if I can read secrets and configmaps")
|
||||
}
|
||||
klog.V(1).Infof("Can I read any secrets and configmaps: %v", ican)
|
||||
|
||||
if ican {
|
||||
// I can read secrets and configmaps in all namespaces
|
||||
// No need to iterate over all namespaces
|
||||
namespaces = []string{""}
|
||||
} else {
|
||||
// Get list of all namespaces and try to find specs from each namespace
|
||||
nsList, err := client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
if k8serrors.IsForbidden(err) {
|
||||
kubeconfig := k8sutil.GetKubeconfig()
|
||||
ns, _, err := kubeconfig.Namespace()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to get namespace from kubeconfig")
|
||||
}
|
||||
// If we are not allowed to list namespaces, just use the default namespace
|
||||
// configured in the kubeconfig
|
||||
namespaces = []string{ns}
|
||||
} else {
|
||||
return nil, nil, errors.Wrap(err, "failed to list namespaces")
|
||||
}
|
||||
}
|
||||
|
||||
for _, ns := range nsList.Items {
|
||||
namespaces = append(namespaces, ns.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var bundlesFromCluster []string
|
||||
|
||||
// Search cluster for support bundle specs
|
||||
klog.V(1).Infof("Search support bundle specs from [%q] namespaces using %q selector", strings.Join(namespaces, ", "), parsedSelector.String())
|
||||
for _, ns := range namespaces {
|
||||
bundlesFromSecrets, err := specs.LoadFromSecretMatchingLabel(client, parsedSelector.String(), ns, specs.SupportBundleKey)
|
||||
if err != nil {
|
||||
if !k8serrors.IsForbidden(err) {
|
||||
klog.Errorf("failed to load support bundle spec from secrets: %s", err)
|
||||
} else {
|
||||
klog.Warningf("Reading secrets from %q namespace forbidden", ns)
|
||||
}
|
||||
}
|
||||
bundlesFromCluster = append(bundlesFromCluster, bundlesFromSecrets...)
|
||||
|
||||
bundlesFromConfigMaps, err := specs.LoadFromConfigMapMatchingLabel(client, parsedSelector.String(), ns, specs.SupportBundleKey)
|
||||
if err != nil {
|
||||
if !k8serrors.IsForbidden(err) {
|
||||
klog.Errorf("failed to load support bundle spec from configmap: %s", err)
|
||||
} else {
|
||||
klog.Warningf("Reading configmaps from %q namespace forbidden", ns)
|
||||
}
|
||||
}
|
||||
bundlesFromCluster = append(bundlesFromCluster, bundlesFromConfigMaps...)
|
||||
}
|
||||
|
||||
parsedBundle := &troubleshootv1beta2.SupportBundle{}
|
||||
|
||||
for _, bundle := range bundlesFromCluster {
|
||||
multidocs := strings.Split(string(bundle), "\n---\n")
|
||||
bundleFromDoc, err := supportbundle.ParseSupportBundleFromDoc([]byte(multidocs[0]))
|
||||
if err != nil {
|
||||
klog.Errorf("failed to parse support bundle spec: %s", err)
|
||||
// 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) {
|
||||
moreKinds.SupportBundlesV1Beta2 = append(moreKinds.SupportBundlesV1Beta2, s)
|
||||
continue
|
||||
}
|
||||
|
||||
parsedBundle = supportbundle.ConcatSpec(parsedBundle, bundleFromDoc)
|
||||
|
||||
parsedRedactors, err := supportbundle.ParseRedactorsFromDocs(multidocs)
|
||||
// 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 {
|
||||
klog.Errorf("failed to parse redactors from doc: %s", err)
|
||||
// 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
|
||||
}
|
||||
|
||||
redactors.Spec.Redactors = append(redactors.Spec.Redactors, parsedRedactors...)
|
||||
// finally append the uri spec
|
||||
moreKinds.SupportBundlesV1Beta2 = append(moreKinds.SupportBundlesV1Beta2, k.SupportBundlesV1Beta2...)
|
||||
|
||||
}
|
||||
|
||||
var redactorsFromCluster []string
|
||||
kinds.SupportBundlesV1Beta2 = moreKinds.SupportBundlesV1Beta2
|
||||
|
||||
// Search cluster for redactor specs
|
||||
klog.V(1).Infof("Search redactor specs from [%q] namespaces using %q selector", strings.Join(namespaces, ", "), parsedSelector.String())
|
||||
for _, ns := range namespaces {
|
||||
redactorsFromSecrets, err := specs.LoadFromSecretMatchingLabel(client, parsedSelector.String(), ns, specs.RedactorKey)
|
||||
if err != nil {
|
||||
if !k8serrors.IsForbidden(err) {
|
||||
klog.Errorf("failed to load support bundle spec from secrets: %s", err)
|
||||
} else {
|
||||
klog.Warningf("Reading secrets from %q namespace forbidden", ns)
|
||||
}
|
||||
}
|
||||
redactorsFromCluster = append(redactorsFromCluster, redactorsFromSecrets...)
|
||||
return nil
|
||||
}
|
||||
|
||||
redactorsFromConfigMaps, err := specs.LoadFromConfigMapMatchingLabel(client, parsedSelector.String(), ns, specs.RedactorKey)
|
||||
if err != nil {
|
||||
if !k8serrors.IsForbidden(err) {
|
||||
klog.Errorf("failed to load support bundle spec from configmap: %s", err)
|
||||
} else {
|
||||
klog.Warningf("Reading configmaps from %q namespace forbidden", ns)
|
||||
}
|
||||
}
|
||||
redactorsFromCluster = append(redactorsFromCluster, redactorsFromConfigMaps...)
|
||||
func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface) (*troubleshootv1beta2.SupportBundle, *troubleshootv1beta2.Redactor, error) {
|
||||
// Append redactor uris to the args
|
||||
allArgs := append(args, viper.GetStringSlice("redactors")...)
|
||||
kinds, err := specs.LoadFromCLIArgs(ctx, client, allArgs, viper.GetViper())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, redactor := range redactorsFromCluster {
|
||||
multidocs := strings.Split(string(redactor), "\n---\n")
|
||||
parsedRedactors, err := supportbundle.ParseRedactorsFromDocs(multidocs)
|
||||
// Load additional specs from support bundle URIs
|
||||
if !viper.GetBool("no-uri") {
|
||||
err := loadSupportBundleSpecsFromURIs(ctx, kinds)
|
||||
if err != nil {
|
||||
klog.Errorf("failed to parse redactors from doc: %s", err)
|
||||
klog.Warningf("unable to load support bundles from URIs: %v", err)
|
||||
}
|
||||
|
||||
redactors.Spec.Redactors = append(redactors.Spec.Redactors, parsedRedactors...)
|
||||
}
|
||||
|
||||
return parsedBundle, redactors, nil
|
||||
// Check if we have any collectors to run in the troubleshoot specs
|
||||
// TODO: Do we use the RemoteCollectors anymore?
|
||||
if len(kinds.CollectorsV1Beta2) == 0 &&
|
||||
len(kinds.HostCollectorsV1Beta2) == 0 &&
|
||||
len(kinds.SupportBundlesV1Beta2) == 0 {
|
||||
return nil, nil, errors.New("no collectors specified to run")
|
||||
}
|
||||
|
||||
// Merge specs
|
||||
// We need to add the default type information to the support bundle spec
|
||||
// since by default these fields would be empty
|
||||
mainBundle := &troubleshootv1beta2.SupportBundle{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "troubleshoot.sh/v1beta2",
|
||||
Kind: "SupportBundle",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "merged-support-bundle-spec",
|
||||
},
|
||||
}
|
||||
for _, sb := range kinds.SupportBundlesV1Beta2 {
|
||||
sb := sb
|
||||
mainBundle = supportbundle.ConcatSpec(mainBundle, &sb)
|
||||
}
|
||||
|
||||
for _, c := range kinds.CollectorsV1Beta2 {
|
||||
mainBundle.Spec.Collectors = util.Append(mainBundle.Spec.Collectors, c.Spec.Collectors)
|
||||
}
|
||||
|
||||
for _, hc := range kinds.HostCollectorsV1Beta2 {
|
||||
mainBundle.Spec.HostCollectors = util.Append(mainBundle.Spec.HostCollectors, hc.Spec.Collectors)
|
||||
}
|
||||
|
||||
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{
|
||||
APIVersion: "troubleshoot.sh/v1beta2",
|
||||
Kind: "Redactor",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "merged-redactors-spec",
|
||||
},
|
||||
}
|
||||
for _, r := range kinds.RedactorsV1Beta2 {
|
||||
additionalRedactors.Spec.Redactors = util.Append(additionalRedactors.Spec.Redactors, r.Spec.Redactors)
|
||||
}
|
||||
|
||||
// dedupe specs
|
||||
mainBundle.Spec.Collectors = util.Dedup(mainBundle.Spec.Collectors)
|
||||
mainBundle.Spec.Analyzers = util.Dedup(mainBundle.Spec.Analyzers)
|
||||
mainBundle.Spec.HostCollectors = util.Dedup(mainBundle.Spec.HostCollectors)
|
||||
mainBundle.Spec.HostAnalyzers = util.Dedup(mainBundle.Spec.HostAnalyzers)
|
||||
|
||||
return mainBundle, additionalRedactors, nil
|
||||
}
|
||||
|
||||
func parseTimeFlags(v *viper.Viper) (*time.Time, error) {
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
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"
|
||||
)
|
||||
|
||||
func templSpec() string {
|
||||
return `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
metadata:
|
||||
name: sb-1
|
||||
spec:
|
||||
uri: $MY_URI
|
||||
collectors:
|
||||
- configMap:
|
||||
name: kube-root-ca.crt
|
||||
namespace: default
|
||||
`
|
||||
}
|
||||
|
||||
func Test_loadSupportBundleSpecsFromURIs(t *testing.T) {
|
||||
// Run a webserver to serve the spec
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
metadata:
|
||||
name: sb-2
|
||||
spec:
|
||||
collectors:
|
||||
- clusterInfo: {}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
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)
|
||||
|
||||
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, 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) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
time.Sleep(2 * time.Second) // this will cause a timeout
|
||||
}))
|
||||
defer srv.Close()
|
||||
ctx := context.Background()
|
||||
|
||||
kinds, err := loader.LoadSpecs(ctx, loader.LoadOptions{
|
||||
RawSpec: strings.ReplaceAll(templSpec(), "$MY_URI", srv.URL),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set the timeout on the http client to 10ms
|
||||
// supportbundle.LoadSupportBundleSpec does not yet use the context
|
||||
before := httputil.GetHttpClient().Timeout
|
||||
httputil.GetHttpClient().Timeout = 10 * time.Millisecond
|
||||
defer func() {
|
||||
// Reinstate the original timeout. Its a global var so we need to reset it
|
||||
httputil.GetHttpClient().Timeout = before
|
||||
}()
|
||||
|
||||
beforeJSON, err := json.Marshal(kinds)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = loadSupportBundleSpecsFromURIs(ctx, kinds)
|
||||
require.NoError(t, err)
|
||||
|
||||
afterJSON, err := json.Marshal(kinds)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.JSONEq(t, string(beforeJSON), string(afterJSON))
|
||||
}
|
||||
|
||||
func Test_loadSupportBundleSpecs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
expected troubleshootv1beta2.SupportBundleSpec
|
||||
}{
|
||||
{
|
||||
name: "empty collectors array in spec, default collectors added",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
spec:
|
||||
collectors: []
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
Collectors: []*troubleshootv1beta2.Collect{
|
||||
{
|
||||
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
|
||||
},
|
||||
{
|
||||
ClusterResources: &troubleshootv1beta2.ClusterResources{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no collectors defined in spec, default collectors added",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
spec:
|
||||
analyzers: []
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
Collectors: []*troubleshootv1beta2.Collect{
|
||||
{
|
||||
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
|
||||
},
|
||||
{
|
||||
ClusterResources: &troubleshootv1beta2.ClusterResources{},
|
||||
},
|
||||
},
|
||||
Analyzers: []*troubleshootv1beta2.Analyze{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "collectors present but defaults missing, default collectors added",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
spec:
|
||||
collectors:
|
||||
- logs: {}
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
Collectors: []*troubleshootv1beta2.Collect{
|
||||
{
|
||||
Logs: &troubleshootv1beta2.Logs{},
|
||||
},
|
||||
{
|
||||
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
|
||||
},
|
||||
{
|
||||
ClusterResources: &troubleshootv1beta2.ClusterResources{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty support bundle spec adds default collectors",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
Collectors: []*troubleshootv1beta2.Collect{
|
||||
{
|
||||
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
|
||||
},
|
||||
{
|
||||
ClusterResources: &troubleshootv1beta2.ClusterResources{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sb spec with host collectors does not add default collectors",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
spec:
|
||||
hostCollectors:
|
||||
- cpu: {}
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
HostCollectors: []*troubleshootv1beta2.HostCollect{
|
||||
{
|
||||
CPU: &troubleshootv1beta2.CPU{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "host collector spec with collectors does not add default collectors",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostCollector
|
||||
spec:
|
||||
collectors:
|
||||
- cpu: {}
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
HostCollectors: []*troubleshootv1beta2.HostCollect{
|
||||
{
|
||||
CPU: &troubleshootv1beta2.CPU{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sb spec with host and in-cluster collectors adds default collectors",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
spec:
|
||||
hostCollectors:
|
||||
- cpu: {}
|
||||
collectors:
|
||||
- logs: {}
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
HostCollectors: []*troubleshootv1beta2.HostCollect{
|
||||
{
|
||||
CPU: &troubleshootv1beta2.CPU{},
|
||||
},
|
||||
},
|
||||
Collectors: []*troubleshootv1beta2.Collect{
|
||||
{
|
||||
Logs: &troubleshootv1beta2.Logs{},
|
||||
},
|
||||
{
|
||||
ClusterInfo: &troubleshootv1beta2.ClusterInfo{},
|
||||
},
|
||||
{
|
||||
ClusterResources: &troubleshootv1beta2.ClusterResources{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sb spec with host collectors and empty in-cluster collectors does not default collectors",
|
||||
args: []string{testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
spec:
|
||||
hostCollectors:
|
||||
- cpu: {}
|
||||
collectors: []
|
||||
`)},
|
||||
expected: troubleshootv1beta2.SupportBundleSpec{
|
||||
HostCollectors: []*troubleshootv1beta2.HostCollect{
|
||||
{
|
||||
CPU: &troubleshootv1beta2.CPU{},
|
||||
},
|
||||
},
|
||||
Collectors: []*troubleshootv1beta2.Collect{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := testclient.NewSimpleClientset()
|
||||
|
||||
sb, _, err := loadSpecs(ctx, test.args, client)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected, sb.Spec)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_loadDuplicatedBundleSpecs(t *testing.T) {
|
||||
spec := testutils.ServeFromFilePath(t, `
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
metadata:
|
||||
name: sb
|
||||
spec:
|
||||
collectors:
|
||||
- helm: {}
|
||||
analyzers:
|
||||
- clusterVersion: {}
|
||||
hostCollectors:
|
||||
- cpu: {}
|
||||
hostAnalyzers:
|
||||
- cpu: {}
|
||||
`)
|
||||
args := []string{spec, spec}
|
||||
|
||||
ctx := context.Background()
|
||||
client := testclient.NewSimpleClientset()
|
||||
sb, _, err := loadSpecs(ctx, args, client)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, sb.Spec.Collectors, 1+2) // default clusterInfo + clusterResources
|
||||
assert.Len(t, sb.Spec.Analyzers, 1)
|
||||
assert.Len(t, sb.Spec.HostCollectors, 1)
|
||||
assert.Len(t, sb.Spec.HostAnalyzers, 1)
|
||||
}
|
||||
@@ -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
|
||||
@@ -163,6 +167,13 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -180,6 +191,13 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -195,6 +213,13 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
|
||||
@@ -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
|
||||
@@ -835,6 +839,13 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -852,6 +863,13 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -867,6 +885,13 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
|
||||
@@ -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
|
||||
@@ -866,6 +870,13 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -883,6 +894,13 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -898,6 +916,13 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
|
||||
@@ -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:
|
||||
@@ -732,6 +846,8 @@ spec:
|
||||
type: BoolString
|
||||
fileName:
|
||||
type: string
|
||||
jsonPath:
|
||||
type: string
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
@@ -929,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:
|
||||
@@ -1487,6 +1663,19 @@ spec:
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
velero:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
strict:
|
||||
type: BoolString
|
||||
type: object
|
||||
weaveReport:
|
||||
properties:
|
||||
annotations:
|
||||
@@ -1669,6 +1858,55 @@ spec:
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
certificatesCollection:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
strict:
|
||||
type: BoolString
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
cpu:
|
||||
properties:
|
||||
annotations:
|
||||
@@ -2061,6 +2299,60 @@ spec:
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
kernelConfigs:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
selectedConfigs:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
strict:
|
||||
type: BoolString
|
||||
required:
|
||||
- outcomes
|
||||
- selectedConfigs
|
||||
type: object
|
||||
kernelModules:
|
||||
properties:
|
||||
annotations:
|
||||
@@ -2404,6 +2696,67 @@ spec:
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
textAnalyze:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
excludeFiles:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
fileName:
|
||||
type: string
|
||||
ignoreIfNoFiles:
|
||||
type: boolean
|
||||
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
|
||||
regex:
|
||||
type: string
|
||||
regexGroups:
|
||||
type: string
|
||||
strict:
|
||||
type: BoolString
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
time:
|
||||
properties:
|
||||
annotations:
|
||||
|
||||
+12054
-3913
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
|
||||
@@ -143,6 +147,55 @@ spec:
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
certificatesCollection:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
strict:
|
||||
type: BoolString
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
cpu:
|
||||
properties:
|
||||
annotations:
|
||||
@@ -535,6 +588,60 @@ spec:
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
kernelConfigs:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
selectedConfigs:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
strict:
|
||||
type: BoolString
|
||||
required:
|
||||
- outcomes
|
||||
- selectedConfigs
|
||||
type: object
|
||||
kernelModules:
|
||||
properties:
|
||||
annotations:
|
||||
@@ -878,6 +985,67 @@ spec:
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
textAnalyze:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
excludeFiles:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
fileName:
|
||||
type: string
|
||||
ignoreIfNoFiles:
|
||||
type: boolean
|
||||
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
|
||||
regex:
|
||||
type: string
|
||||
regexGroups:
|
||||
type: string
|
||||
strict:
|
||||
type: BoolString
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
time:
|
||||
properties:
|
||||
annotations:
|
||||
@@ -1002,6 +1170,19 @@ spec:
|
||||
- certificatePath
|
||||
- keyPath
|
||||
type: object
|
||||
certificatesCollection:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
paths:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- paths
|
||||
type: object
|
||||
copy:
|
||||
properties:
|
||||
collectorName:
|
||||
@@ -1032,9 +1213,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:
|
||||
@@ -1042,34 +1223,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
|
||||
@@ -1081,16 +1261,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:
|
||||
@@ -1137,6 +1315,11 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -1152,6 +1335,11 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -1167,6 +1355,11 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -1199,6 +1392,13 @@ spec:
|
||||
exclude:
|
||||
type: BoolString
|
||||
type: object
|
||||
kernelConfigs:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
type: object
|
||||
kernelModules:
|
||||
properties:
|
||||
collectorName:
|
||||
@@ -1230,8 +1430,26 @@ spec:
|
||||
type: string
|
||||
command:
|
||||
type: string
|
||||
env:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
exclude:
|
||||
type: BoolString
|
||||
ignoreParentEnvs:
|
||||
type: boolean
|
||||
inheritEnvs:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
input:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
outputDir:
|
||||
type: string
|
||||
timeout:
|
||||
type: string
|
||||
required:
|
||||
- args
|
||||
- command
|
||||
|
||||
@@ -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
|
||||
@@ -143,6 +147,55 @@ spec:
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
certificatesCollection:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
strict:
|
||||
type: BoolString
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
cpu:
|
||||
properties:
|
||||
annotations:
|
||||
@@ -535,6 +588,60 @@ spec:
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
kernelConfigs:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
selectedConfigs:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
strict:
|
||||
type: BoolString
|
||||
required:
|
||||
- outcomes
|
||||
- selectedConfigs
|
||||
type: object
|
||||
kernelModules:
|
||||
properties:
|
||||
annotations:
|
||||
@@ -878,6 +985,67 @@ spec:
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
textAnalyze:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
checkName:
|
||||
type: string
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
excludeFiles:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
fileName:
|
||||
type: string
|
||||
ignoreIfNoFiles:
|
||||
type: boolean
|
||||
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
|
||||
regex:
|
||||
type: string
|
||||
regexGroups:
|
||||
type: string
|
||||
strict:
|
||||
type: BoolString
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
time:
|
||||
properties:
|
||||
annotations:
|
||||
@@ -1002,6 +1170,19 @@ spec:
|
||||
- certificatePath
|
||||
- keyPath
|
||||
type: object
|
||||
certificatesCollection:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
paths:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- paths
|
||||
type: object
|
||||
copy:
|
||||
properties:
|
||||
collectorName:
|
||||
@@ -1032,9 +1213,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:
|
||||
@@ -1042,34 +1223,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
|
||||
@@ -1081,16 +1261,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:
|
||||
@@ -1137,6 +1315,11 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -1152,6 +1335,11 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -1167,6 +1355,11 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -1199,6 +1392,13 @@ spec:
|
||||
exclude:
|
||||
type: BoolString
|
||||
type: object
|
||||
kernelConfigs:
|
||||
properties:
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
type: BoolString
|
||||
type: object
|
||||
kernelModules:
|
||||
properties:
|
||||
collectorName:
|
||||
@@ -1230,8 +1430,26 @@ spec:
|
||||
type: string
|
||||
command:
|
||||
type: string
|
||||
env:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
exclude:
|
||||
type: BoolString
|
||||
ignoreParentEnvs:
|
||||
type: boolean
|
||||
inheritEnvs:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
input:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
outputDir:
|
||||
type: string
|
||||
timeout:
|
||||
type: string
|
||||
required:
|
||||
- args
|
||||
- command
|
||||
@@ -1448,44 +1666,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
|
||||
@@ -1497,16 +1714,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:
|
||||
@@ -1546,6 +1761,11 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -1561,6 +1781,11 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -1576,6 +1801,11 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
|
||||
+12281
-3943
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:
|
||||
@@ -207,6 +208,11 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -222,6 +228,11 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
@@ -237,6 +248,11 @@ spec:
|
||||
type: object
|
||||
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.
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
|
||||
+12482
-3942
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,4 @@
|
||||
project_name: troubleshoot
|
||||
release:
|
||||
github:
|
||||
owner: replicatedhq
|
||||
name: troubleshoot
|
||||
builds:
|
||||
- id: preflight
|
||||
# NOTE: if you add any additional goos/goarch values, ensure you update ../.github/workflows/build-test-deploy.yaml
|
||||
@@ -15,6 +11,7 @@ builds:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
- riscv64
|
||||
ignore:
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
@@ -38,6 +35,7 @@ builds:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
- riscv64
|
||||
ignore:
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
@@ -61,6 +59,7 @@ builds:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
- riscv64
|
||||
ignore:
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
|
||||
@@ -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?
|
||||
|
||||
|
||||
+5
-1
@@ -28,6 +28,8 @@ preflight [url] [flags]
|
||||
--context string The name of the kubeconfig context to use
|
||||
--cpuprofile string File path to write cpu profiling data
|
||||
--debug enable debug logging
|
||||
--disable-compression If true, opt-out of response compression for all requests to the server
|
||||
--dry-run print the preflight spec without running preflight checks
|
||||
--format string output format, one of human, json, yaml. only used when interactive is set to false (default "human")
|
||||
-h, --help help for preflight
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
@@ -44,10 +46,12 @@ preflight [url] [flags]
|
||||
--tls-server-name string Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used
|
||||
--token string Bearer token for authentication to the API server
|
||||
--user string The name of the kubeconfig user to use
|
||||
-v, --v Level number for the log level verbosity
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [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 3-Jan-2023
|
||||
###### Auto generated by spf13/cobra on 25-Jan-2024
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
## preflight oci-fetch
|
||||
|
||||
Fetch a preflight from an OCI registry and print it to standard out
|
||||
|
||||
```
|
||||
preflight oci-fetch [URI] [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for oci-fetch
|
||||
-v, --v Level number for the log level verbosity
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--collect-without-permissions always run preflight checks even if some require permissions that preflight does not have (default true)
|
||||
--collector-image string the full name of the collector image to use
|
||||
--collector-pullpolicy string the pull policy of the collector image
|
||||
--cpuprofile string File path to write cpu profiling data
|
||||
--debug enable debug logging
|
||||
--format string output format, one of human, json, yaml. only used when interactive is set to false (default "human")
|
||||
--interactive interactive preflights (default true)
|
||||
--memprofile string File path to write memory profiling data
|
||||
-o, --output string specify the output file path for the preflight checks
|
||||
--selector string selector (label query) to filter remote collection nodes on.
|
||||
--since string force pod logs collectors to return logs newer than a relative duration like 5s, 2m, or 3h.
|
||||
--since-time string force pod logs collectors to return logs after a specific date (RFC3339)
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [preflight](preflight.md) - Run and retrieve preflight checks in a cluster
|
||||
|
||||
###### 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 3-Jan-2023
|
||||
###### Auto generated by spf13/cobra on 25-Jan-2024
|
||||
|
||||
@@ -25,7 +25,9 @@ support-bundle [urls...] [flags]
|
||||
--collect-without-permissions always generate a support bundle, even if it some require additional permissions (default true)
|
||||
--context string The name of the kubeconfig context to use
|
||||
--cpuprofile string File path to write cpu profiling data
|
||||
--debug enable debug logging
|
||||
--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)
|
||||
@@ -33,18 +35,19 @@ 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
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
|
||||
-l, --selector strings selector to filter on for loading additional support bundle specs found in secrets within the cluster (default [troubleshoot.io/kind=support-bundle])
|
||||
-l, --selector strings selector to filter on for loading additional support bundle specs found in secrets within the cluster (default [troubleshoot.sh/kind=support-bundle])
|
||||
-s, --server string The address and port of the Kubernetes API server
|
||||
--since string force pod logs collectors to return logs newer than a relative duration like 5s, 2m, or 3h.
|
||||
--since-time string force pod logs collectors to return logs after a specific date (RFC3339)
|
||||
--tls-server-name string Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used
|
||||
--token string Bearer token for authentication to the API server
|
||||
--user string The name of the kubeconfig user to use
|
||||
-v, --v Level number for the log level verbosity
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
@@ -53,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 3-Jan-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 3-Jan-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 3-Jan-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 3-Jan-2023
|
||||
###### Auto generated by spf13/cobra on 25-Jan-2024
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostCollector
|
||||
metadata:
|
||||
name: run-host-cmd-and-save-output
|
||||
spec:
|
||||
collectors:
|
||||
- run:
|
||||
collectorName: "my-custom-run"
|
||||
command: "sh"
|
||||
# this is for demonstration purpose only -- you probably don't want to drop your input to the bundle!
|
||||
args:
|
||||
- "-c"
|
||||
- "cat $TS_INPUT_DIR/dummy.yaml > $TS_OUTPUT_DIR/dummy_content.yaml"
|
||||
outputDir: "myCommandOutputs"
|
||||
env:
|
||||
- AWS_REGION=us-west-1
|
||||
# if ignoreParentEnvs is true, it will not inherit envs from parent process.
|
||||
# values specified in inheritEnv will not be used either
|
||||
# ignoreParentEnvs: true
|
||||
inheritEnvs:
|
||||
- USER
|
||||
input:
|
||||
dummy.conf: |-
|
||||
[hello]
|
||||
hello = 1
|
||||
|
||||
[bye]
|
||||
bye = 2
|
||||
dummy.yaml: |-
|
||||
username: postgres
|
||||
password: <my-pass>
|
||||
dbHost: <hostname>
|
||||
map:
|
||||
key: value
|
||||
list:
|
||||
- val1
|
||||
- val2
|
||||
@@ -8,7 +8,15 @@ spec:
|
||||
name: config/replicas.txt
|
||||
data: "5"
|
||||
- runPod:
|
||||
collectorName: "static-hi"
|
||||
collectorName: "static-hi-1"
|
||||
podSpec:
|
||||
containers:
|
||||
- name: static-hi
|
||||
image: alpine:3
|
||||
command: ["echo", "hi static!"]
|
||||
## This collector is intentionally duplicated to test same pod should be terminated after success
|
||||
- runPod:
|
||||
collectorName: "static-hi-2"
|
||||
podSpec:
|
||||
containers:
|
||||
- name: static-hi
|
||||
@@ -47,7 +55,16 @@ spec:
|
||||
message: You have at least 5 replicas
|
||||
- textAnalyze:
|
||||
checkName: Said hi!
|
||||
fileName: /static-hi.log
|
||||
fileName: /static-hi-1.log
|
||||
regex: 'hi static'
|
||||
outcomes:
|
||||
- fail:
|
||||
message: Didn't say hi.
|
||||
- pass:
|
||||
message: Said hi!
|
||||
- textAnalyze:
|
||||
checkName: Said hi!
|
||||
fileName: /static-hi-2.log
|
||||
regex: 'hi static'
|
||||
outcomes:
|
||||
- fail:
|
||||
|
||||
@@ -19,4 +19,3 @@ spec:
|
||||
message: At least 16 CPU cores preferred
|
||||
- pass:
|
||||
message: This server has sufficient CPU cores
|
||||
|
||||
|
||||
@@ -72,6 +72,12 @@ spec:
|
||||
- pass:
|
||||
when: "== k3s"
|
||||
message: K3S is a supported distribution
|
||||
- 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:
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
## Parse troubleshoot specs from a helm chart
|
||||
|
||||
This is an example of using troubleshoot's `loader.LoadSpecs` API to load specs from rendered manifests in a helm chart. The chart in this example contains a kubernetes `Secret` & `ConfigMap` with troubleshoot specs, as well as a troubleshoot custom resource. The custom resource is behind a values flag and does not get rendered by default. The code adds this flag to ensure that the manifest is rendered so as to load it.
|
||||
|
||||
The manifests are rendered with the equivalent of `helm template --values values.yaml` where the output is a YAML multidoc. `loader.LoadSpecs` will take the YAML multidoc as an input and extract troubleshoot specs inside kuberenetes `Secrets` and `ConfigMap`s, and any troubleshoot custom resources found.
|
||||
|
||||
This application always uses the local version of troubleshoot so as to build using the latest version of the library. This ensures that the example is kept in sync with new features.
|
||||
|
||||
### Running
|
||||
|
||||
```go
|
||||
go mod tidy # sync go modules
|
||||
go run main.go # run the application. This should print out a YAML multidoc of the loaded troubleshoot specs
|
||||
```
|
||||
@@ -0,0 +1,72 @@
|
||||
module helm-template
|
||||
|
||||
go 1.22.3
|
||||
|
||||
// Always use the local version of troubleshoot so as to build using
|
||||
// the latest version of the library. This will ensure the example
|
||||
// is kept in sync with new features
|
||||
replace github.com/replicatedhq/troubleshoot v0.0.0 => ../../../
|
||||
|
||||
require (
|
||||
github.com/replicatedhq/troubleshoot v0.0.0
|
||||
helm.sh/helm/v3 v3.15.1
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
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.11.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.10 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/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.23.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/oauth2 v0.18.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/term v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // 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.30.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.30.1 // indirect
|
||||
k8s.io/apimachinery v0.30.1 // indirect
|
||||
k8s.io/client-go v0.30.1 // 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.18.2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
)
|
||||
@@ -0,0 +1,232 @@
|
||||
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=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI=
|
||||
github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.10 h1:4y86NVn7Z2yYd6pfS4Z+Nyh3aAUL3Nul+LMbhFKy0gA=
|
||||
github.com/go-openapi/swag v0.22.10/go.mod h1:Cnn8BYtRlx6BNE3DPN86f/xkapGIcLWzh3CLEb4C1jI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
|
||||
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
|
||||
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
|
||||
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
|
||||
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
|
||||
github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/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=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
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=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.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.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.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.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.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
helm.sh/helm/v3 v3.15.1 h1:22ztacHz4gMqhXNqCQ9NAg6BFWoRUryNLvnkz6OVyw0=
|
||||
helm.sh/helm/v3 v3.15.1/go.mod h1:fvfoRcB8UKRUV5jrIfOTaN/pG1TPhuqSb56fjYdTKXg=
|
||||
k8s.io/api v0.30.1 h1:kCm/6mADMdbAxmIh0LBjS54nQBE+U4KmbCfIkF5CpJY=
|
||||
k8s.io/api v0.30.1/go.mod h1:ddbN2C0+0DIiPntan/bye3SW3PdwLa11/0yqwvuRrJM=
|
||||
k8s.io/apiextensions-apiserver v0.30.1 h1:4fAJZ9985BmpJG6PkoxVRpXv9vmPUOVzl614xarePws=
|
||||
k8s.io/apiextensions-apiserver v0.30.1/go.mod h1:R4GuSrlhgq43oRY9sF2IToFh7PVlF1JjfWdoG3pixk4=
|
||||
k8s.io/apimachinery v0.30.1 h1:ZQStsEfo4n65yAdlGTfP/uSHMQSoYzU/oeEbkmF7P2U=
|
||||
k8s.io/apimachinery v0.30.1/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
|
||||
k8s.io/client-go v0.30.1 h1:uC/Ir6A3R46wdkgCV3vbLyNOYyCJ8oZnjtJGKfytl/Q=
|
||||
k8s.io/client-go v0.30.1/go.mod h1:wrAqLNs2trwiCH/wxxmT/x3hKVH9PuV0GGW0oDoHVqc=
|
||||
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-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
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.18.2 h1:RqVW6Kpeaji67CY5nPEfRz6ZfFMk0lWQlNrLqlNpx+Q=
|
||||
sigs.k8s.io/controller-runtime v0.18.2/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
@@ -0,0 +1,115 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
tsloader "github.com/replicatedhq/troubleshoot/pkg/loader"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/engine"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Load chart from tarball
|
||||
// The helm loader package has a few other ways to load a chart, such as from a directory `loader.LoadDir()`
|
||||
// or from an archive in memory `loader.LoadArchive()` etc.
|
||||
chart, err := loader.LoadFile("mychart-0.1.0.tgz")
|
||||
if err != nil {
|
||||
log.Fatalf("error loading at chart: %v", err)
|
||||
}
|
||||
|
||||
// Render chart to manifests. Equivalent to `helm template --values values.yaml`
|
||||
renderedManifests, err := renderChartToManifests(chart, chartutil.Values{
|
||||
"preflight": true,
|
||||
"includeTroubleshootCRs": true,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("error rendering chart: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("############# Raw helm manifests #############")
|
||||
fmt.Println(renderedManifests)
|
||||
|
||||
// Load rendered manifests into a slice of troubleshoot specs
|
||||
// 'tsKinds' will contain all the specs. This object will the the
|
||||
// input of other troubleshoot APIs such as ones used to collect
|
||||
// bundles, analyze bundles, redact etc.
|
||||
ctx := context.Background()
|
||||
tsKinds, err := tsloader.LoadSpecs(ctx, tsloader.LoadOptions{
|
||||
RawSpec: renderedManifests,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("error loading troubleshoot specs: %v", err)
|
||||
}
|
||||
|
||||
// Ensure we have the correct number of specs
|
||||
if len(tsKinds.PreflightsV1Beta2) != 1 {
|
||||
log.Fatalf("expected 1 preflight, got %d", len(tsKinds.PreflightsV1Beta2))
|
||||
}
|
||||
if len(tsKinds.SupportBundlesV1Beta2) != 1 {
|
||||
log.Fatalf("expected 1 supportbundle, got %d", len(tsKinds.SupportBundlesV1Beta2))
|
||||
}
|
||||
if len(tsKinds.RedactorsV1Beta2) != 1 {
|
||||
log.Fatalf("expected 1 redactor, got %d", len(tsKinds.RedactorsV1Beta2))
|
||||
}
|
||||
|
||||
// Print the specs as yaml
|
||||
fmt.Println("---\n############# Support bundle #############")
|
||||
printSpecAsYaml(tsKinds.SupportBundlesV1Beta2[0])
|
||||
fmt.Println("---\n############# Preflight #############")
|
||||
printSpecAsYaml(tsKinds.PreflightsV1Beta2[0])
|
||||
fmt.Println("---\n############# Redactor #############")
|
||||
printSpecAsYaml(tsKinds.RedactorsV1Beta2[0])
|
||||
}
|
||||
|
||||
func renderChartToManifests(chart *chart.Chart, inputValues chartutil.Values) (string, error) {
|
||||
options := chartutil.ReleaseOptions{
|
||||
Name: "my-release", // Not mandatory, but this is how helm sets the release name
|
||||
}
|
||||
|
||||
// Pull in imported values from dependencies to the parent chart
|
||||
// https://helm.sh/docs/topics/charts/#importing-child-values-via-dependencies
|
||||
if err := chartutil.ProcessDependencies(chart, chartutil.Values{}); err != nil {
|
||||
return "", fmt.Errorf("failed to process chart %q dependencies: %w", chart.Name(), err)
|
||||
}
|
||||
|
||||
// Gather all values necessary to render the templates
|
||||
// imported values, values from the chart and user provided values
|
||||
rValues, err := chartutil.ToRenderValues(chart, inputValues, options, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to render template values: %w", err)
|
||||
}
|
||||
|
||||
// Render the chart templates. This will include non YAML files as well
|
||||
renderedTemplates, err := engine.Render(chart, rValues)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to render template: %w", err)
|
||||
}
|
||||
|
||||
// Combine all rendered templates into a single yaml multidoc string
|
||||
// Only pick up YAML files, ignore any others. Troubleshoot loader
|
||||
// expects only valid YAML input
|
||||
var out strings.Builder
|
||||
for k, v := range renderedTemplates {
|
||||
if strings.HasSuffix(k, ".yaml") || strings.HasSuffix(k, ".yml") {
|
||||
// Add the multidoc split marker
|
||||
out.WriteString(fmt.Sprintf("---\n# Source: %s\n%s\n", k, v))
|
||||
}
|
||||
}
|
||||
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
func printSpecAsYaml(v any) {
|
||||
b, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
log.Fatalf("error marshalling troubleshoot specs: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
Binary file not shown.
@@ -1,222 +1,260 @@
|
||||
module github.com/replicatedhq/troubleshoot
|
||||
|
||||
go 1.19
|
||||
go 1.22.3
|
||||
|
||||
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.31.0
|
||||
github.com/distribution/distribution/v3 v3.0.0-alpha.1
|
||||
github.com/fatih/color v1.17.0
|
||||
github.com/go-logr/logr v1.4.2
|
||||
github.com/go-redis/redis/v7 v7.4.1
|
||||
github.com/go-sql-driver/mysql v1.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.0
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
github.com/hashicorp/go-getter v1.7.1
|
||||
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.3.1
|
||||
github.com/jackc/pgx/v5 v5.6.0
|
||||
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.0.0
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b
|
||||
github.com/microsoft/go-mssqldb v1.7.2
|
||||
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.4
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/shirou/gopsutil/v3 v3.24.5
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.15.0
|
||||
github.com/stretchr/testify v1.8.3
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tj/go-spin v1.1.0
|
||||
go.opentelemetry.io/otel v1.16.0
|
||||
go.opentelemetry.io/otel/sdk v1.16.0
|
||||
golang.org/x/sync v0.2.0
|
||||
github.com/vmware-tanzu/velero v1.13.2
|
||||
go.opentelemetry.io/otel v1.27.0
|
||||
go.opentelemetry.io/otel/sdk v1.27.0
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||
golang.org/x/mod v0.18.0
|
||||
golang.org/x/sync v0.7.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/api v0.27.2
|
||||
k8s.io/apiextensions-apiserver v0.27.2
|
||||
k8s.io/apimachinery v0.27.2
|
||||
k8s.io/apiserver v0.27.2
|
||||
k8s.io/cli-runtime v0.27.2
|
||||
k8s.io/client-go v0.27.2
|
||||
k8s.io/klog/v2 v2.100.1
|
||||
oras.land/oras-go v1.2.3
|
||||
sigs.k8s.io/controller-runtime v0.15.0
|
||||
k8s.io/api v0.30.1
|
||||
k8s.io/apiextensions-apiserver v0.30.1
|
||||
k8s.io/apimachinery v0.30.1
|
||||
k8s.io/apiserver v0.30.1
|
||||
k8s.io/cli-runtime v0.30.1
|
||||
k8s.io/client-go v0.30.1
|
||||
k8s.io/klog/v2 v2.120.1
|
||||
oras.land/oras-go v1.2.5
|
||||
sigs.k8s.io/controller-runtime v0.18.4
|
||||
sigs.k8s.io/e2e-framework v0.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // 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
|
||||
cloud.google.com/go/compute/metadata v0.3.0 // 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
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
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/containerd/cgroups/v3 v3.0.3 // 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.6.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.1.0 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-containerregistry v0.19.1 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // 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
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mistifyio/go-zfs/v3 v3.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/docker-image-spec v1.3.1 // 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.5.2 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.5 // indirect
|
||||
github.com/sylabs/sif/v2 v2.11.1 // 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.16.0 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.16.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.16.0 // indirect
|
||||
go.uber.org/atomic v1.10.0 // 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/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.27.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.27.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/tools v0.9.1 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
k8s.io/component-base v0.30.1 // indirect
|
||||
k8s.io/kubectl v0.30.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.110.0 // indirect
|
||||
cloud.google.com/go/compute v1.19.0 // indirect
|
||||
cloud.google.com/go/iam v0.13.0 // indirect
|
||||
cloud.google.com/go/storage v1.28.1 // indirect
|
||||
cloud.google.com/go v0.112.1 // indirect
|
||||
cloud.google.com/go/iam v1.1.6 // indirect
|
||||
cloud.google.com/go/storage v1.38.0 // 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.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.12.3 // indirect
|
||||
github.com/andybalholm/brotli v1.0.1 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.122 // 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/cyphar/filepath-securejoin v0.2.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // 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/containers/ocicrypt v1.1.10 // indirect
|
||||
github.com/containers/storage v1.54.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/docker/cli v26.1.3+incompatible // indirect
|
||||
github.com/docker/docker v26.1.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.1 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.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/gnostic v0.6.9 // 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.7.1 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.3 // 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
|
||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.16.3 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8 // indirect
|
||||
github.com/klauspost/compress v1.17.8 // 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-shellwords v1.0.12 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // 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.0 // 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.0.6 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.15.1 // indirect
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.9.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/spf13/afero v1.9.3 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.19.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.0 // indirect
|
||||
github.com/prometheus/common v0.51.1 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/vbatts/tar-split v0.11.3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // 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.1.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
github.com/xlab/treeprint v1.2.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||
golang.org/x/crypto v0.8.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/oauth2 v0.7.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/term v0.8.0 // indirect
|
||||
golang.org/x/text v0.9.0
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.114.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
google.golang.org/grpc v1.54.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/net v0.26.0
|
||||
golang.org/x/oauth2 v0.20.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/term v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/api v0.171.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
|
||||
google.golang.org/grpc v1.62.1 // 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
|
||||
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2
|
||||
helm.sh/helm/v3 v3.15.1
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/kubelet v0.30.1
|
||||
k8s.io/metrics v0.30.1
|
||||
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.2 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.1 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
replace (
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package specs
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func LoadFromConfigMap(ctx context.Context, client kubernetes.Interface, ns string, name string) (map[string]string, error) {
|
||||
foundConfigMap, err := client.CoreV1().ConfigMaps(ns).Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get configmap")
|
||||
}
|
||||
|
||||
klog.V(1).InfoS("Loaded data from config map", "name",
|
||||
foundConfigMap.Name, "namespace", foundConfigMap.Namespace,
|
||||
)
|
||||
|
||||
return foundConfigMap.Data, nil
|
||||
}
|
||||
|
||||
func LoadFromConfigMapMatchingLabel(ctx context.Context, client kubernetes.Interface, label string, ns string, key string) ([]string, error) {
|
||||
var configMapMatchingKey []string
|
||||
|
||||
configMaps, err := client.CoreV1().ConfigMaps(ns).List(ctx, metav1.ListOptions{LabelSelector: label})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to search for configmaps in the cluster")
|
||||
}
|
||||
|
||||
for _, configMap := range configMaps.Items {
|
||||
spec, ok := configMap.Data[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
klog.V(1).InfoS("Loaded spec from config map", "name", configMap.Name,
|
||||
"namespace", configMap.Namespace, "data key", key, "label selector", label,
|
||||
)
|
||||
configMapMatchingKey = append(configMapMatchingKey, string(spec))
|
||||
}
|
||||
|
||||
return configMapMatchingKey, nil
|
||||
}
|
||||
@@ -359,7 +359,7 @@ spec:
|
||||
_, err := client.CoreV1().ConfigMaps(configmap.Namespace).Create(ctx, &configmap, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
got, err := LoadFromConfigMapMatchingLabel(client, "troubleshoot.io/kind=support-bundle", "", "support-bundle-spec")
|
||||
got, err := LoadFromConfigMapMatchingLabel(ctx, client, "troubleshoot.io/kind=support-bundle", "", "support-bundle-spec")
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
@@ -451,7 +451,7 @@ spec:
|
||||
_, err := client.CoreV1().ConfigMaps(configmap.Namespace).Create(ctx, &configmap, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
got, err := LoadFromConfigMapMatchingLabel(client, "troubleshoot.io/kind=support-bundle", "some-namespace", "support-bundle-spec")
|
||||
got, err := LoadFromConfigMapMatchingLabel(ctx, client, "troubleshoot.io/kind=support-bundle", "some-namespace", "support-bundle-spec")
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
@@ -549,7 +549,7 @@ spec:
|
||||
_, err := client.CoreV1().ConfigMaps(configmap.Namespace).Create(ctx, &configmap, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
got, err := LoadFromConfigMapMatchingLabel(client, "troubleshoot.io/kind=support-bundle", "", "redactor-spec")
|
||||
got, err := LoadFromConfigMapMatchingLabel(ctx, client, "troubleshoot.io/kind=support-bundle", "", "redactor-spec")
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
@@ -0,0 +1,42 @@
|
||||
package specs
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func LoadFromSecret(ctx context.Context, client kubernetes.Interface, ns string, name string) (map[string][]byte, error) {
|
||||
foundSecret, err := client.CoreV1().Secrets(ns).Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get secret")
|
||||
}
|
||||
|
||||
return foundSecret.Data, nil
|
||||
}
|
||||
|
||||
func LoadFromSecretMatchingLabel(ctx context.Context, client kubernetes.Interface, label string, ns string, key string) ([]string, error) {
|
||||
var secretsMatchingKey []string
|
||||
|
||||
secrets, err := client.CoreV1().Secrets(ns).List(ctx, metav1.ListOptions{LabelSelector: label})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to search for secrets in the cluster")
|
||||
}
|
||||
|
||||
for _, secret := range secrets.Items {
|
||||
spec, ok := secret.Data[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
klog.V(1).InfoS("Loaded spec from secret", "name", secret.Name,
|
||||
"namespace", secret.Namespace, "data key", key, "label selector", label,
|
||||
)
|
||||
secretsMatchingKey = append(secretsMatchingKey, string(spec))
|
||||
}
|
||||
|
||||
return secretsMatchingKey, nil
|
||||
}
|
||||
@@ -359,7 +359,7 @@ spec:
|
||||
_, err := client.CoreV1().Secrets(secret.Namespace).Create(ctx, &secret, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
got, err := LoadFromSecretMatchingLabel(client, "troubleshoot.io/kind=support-bundle", "", "support-bundle-spec")
|
||||
got, err := LoadFromSecretMatchingLabel(ctx, client, "troubleshoot.io/kind=support-bundle", "", "support-bundle-spec")
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
@@ -451,7 +451,7 @@ spec:
|
||||
_, err := client.CoreV1().Secrets(secret.Namespace).Create(ctx, &secret, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
got, err := LoadFromSecretMatchingLabel(client, "troubleshoot.io/kind=support-bundle", "some-namespace", "support-bundle-spec")
|
||||
got, err := LoadFromSecretMatchingLabel(ctx, client, "troubleshoot.io/kind=support-bundle", "some-namespace", "support-bundle-spec")
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
@@ -549,7 +549,7 @@ spec:
|
||||
_, err := client.CoreV1().Secrets(secret.Namespace).Create(ctx, &secret, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
got, err := LoadFromSecretMatchingLabel(client, "troubleshoot.io/kind=support-bundle", "", "redactor-spec")
|
||||
got, err := LoadFromSecretMatchingLabel(ctx, client, "troubleshoot.io/kind=support-bundle", "", "redactor-spec")
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
@@ -0,0 +1,346 @@
|
||||
package specs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/replicatedhq/troubleshoot/internal/util"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/constants"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/loader"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/oci"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/types"
|
||||
"github.com/spf13/viper"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// SplitTroubleshootSecretLabelSelector splits a label selector into two selectors, if applicable:
|
||||
// 1. troubleshoot.io/kind=support-bundle and non-troubleshoot (if contains) labels selector.
|
||||
// 2. troubleshoot.sh/kind=support-bundle and non-troubleshoot (if contains) labels selector.
|
||||
func SplitTroubleshootSecretLabelSelector(ctx context.Context, labelSelector labels.Selector) ([]string, error) {
|
||||
|
||||
klog.V(1).Infof("Split %q selector into troubleshoot and non-troubleshoot labels selector separately, if applicable", labelSelector.String())
|
||||
|
||||
selectorRequirements, selectorSelectable := labelSelector.Requirements()
|
||||
if !selectorSelectable {
|
||||
return nil, errors.Errorf("Selector %q is not selectable", labelSelector.String())
|
||||
}
|
||||
|
||||
var troubleshootReqs, otherReqs []labels.Requirement
|
||||
|
||||
for _, req := range selectorRequirements {
|
||||
if req.Key() == constants.TroubleshootIOLabelKey || req.Key() == constants.TroubleshootSHLabelKey {
|
||||
troubleshootReqs = append(troubleshootReqs, req)
|
||||
} else {
|
||||
otherReqs = append(otherReqs, req)
|
||||
}
|
||||
}
|
||||
|
||||
parsedSelectorStrings := make([]string, 0)
|
||||
// Combine each troubleshoot requirement with other requirements to form new selectors
|
||||
s := labelSelector.String()
|
||||
if len(troubleshootReqs) == 0 && s != "" {
|
||||
return []string{s}, nil
|
||||
}
|
||||
|
||||
for _, tReq := range troubleshootReqs {
|
||||
reqs := append(otherReqs, tReq)
|
||||
newSelector := labels.NewSelector().Add(reqs...)
|
||||
parsedSelectorStrings = append(parsedSelectorStrings, newSelector.String())
|
||||
}
|
||||
|
||||
return parsedSelectorStrings, nil
|
||||
}
|
||||
|
||||
var httpClient = &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
// LoadFromCLIArgs loads troubleshoot specs from args passed to a CLI command.
|
||||
// This loader function is meant for troubleshoot CLI commands only, hence not making it public.
|
||||
// It will contain opinionated logic for CLI commands such as interpreting viper flags,
|
||||
// supporting secret/ uri format, downloading from OCI registry and other URLs, etc.
|
||||
func LoadFromCLIArgs(ctx context.Context, client kubernetes.Interface, args []string, vp *viper.Viper) (*loader.TroubleshootKinds, error) {
|
||||
// Let's always ensure we have a context
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
rawSpecs := []string{}
|
||||
|
||||
for _, v := range args {
|
||||
if strings.HasPrefix(v, "secret/") {
|
||||
// format secret/namespace-name/secret-name[/data-key]
|
||||
pathParts := strings.Split(v, "/")
|
||||
if len(pathParts) > 4 {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Errorf("secret path %s must have at most 4 components", v))
|
||||
}
|
||||
if len(pathParts) < 3 {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Errorf("secret path %s must have at least 3 components", v))
|
||||
}
|
||||
|
||||
data, err := LoadFromSecret(ctx, client, pathParts[1], pathParts[2])
|
||||
if err != nil {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Wrap(err, "failed to get spec from secret"))
|
||||
}
|
||||
|
||||
// If we have a key defined, then load specs from that key only.
|
||||
if len(pathParts) == 4 {
|
||||
spec, ok := data[pathParts[3]]
|
||||
if ok {
|
||||
rawSpecs = append(rawSpecs, string(spec))
|
||||
}
|
||||
} else {
|
||||
// Append all data in the secret. Some may not be specs, but that's ok. They will be ignored.
|
||||
for _, spec := range data {
|
||||
rawSpecs = append(rawSpecs, string(spec))
|
||||
}
|
||||
}
|
||||
} else if strings.HasPrefix(v, "configmap/") {
|
||||
// format configmap/namespace-name/configmap-name[/data-key]
|
||||
pathParts := strings.Split(v, "/")
|
||||
if len(pathParts) > 4 {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Errorf("configmap path %s must have at most 4 components", v))
|
||||
}
|
||||
if len(pathParts) < 3 {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Errorf("configmap path %s must have at least 3 components", v))
|
||||
}
|
||||
|
||||
data, err := LoadFromConfigMap(ctx, client, pathParts[1], pathParts[2])
|
||||
if err != nil {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Wrap(err, "failed to get spec from configmap"))
|
||||
}
|
||||
|
||||
// If we have a key defined, then load specs from that key only.
|
||||
if len(pathParts) == 4 {
|
||||
spec, ok := data[pathParts[3]]
|
||||
if ok {
|
||||
rawSpecs = append(rawSpecs, spec)
|
||||
}
|
||||
} else {
|
||||
// Append all data in the configmap. Some may not be specs, but that's ok. They will be ignored.
|
||||
for _, spec := range data {
|
||||
rawSpecs = append(rawSpecs, spec)
|
||||
}
|
||||
}
|
||||
} else if _, err := os.Stat(v); err == nil {
|
||||
b, err := os.ReadFile(v)
|
||||
if err != nil {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
|
||||
}
|
||||
|
||||
rawSpecs = append(rawSpecs, string(b))
|
||||
} else if v == "-" {
|
||||
b, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_CATCH_ALL, err)
|
||||
}
|
||||
rawSpecs = append(rawSpecs, string(b))
|
||||
} else {
|
||||
u, err := url.Parse(v)
|
||||
if err != nil {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
|
||||
}
|
||||
|
||||
if u.Scheme == "oci" {
|
||||
content, err := oci.PullSpecsFromOCI(ctx, v)
|
||||
if err != nil {
|
||||
if err == oci.ErrNoRelease {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, errors.Errorf("no release found for %s.\nCheck the oci:// uri for errors or contact the application vendor for support.", v))
|
||||
}
|
||||
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
|
||||
}
|
||||
|
||||
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", v))
|
||||
}
|
||||
|
||||
parsedURL, err := url.ParseRequestURI(v)
|
||||
if err != nil {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kinds, err := loader.LoadSpecs(ctx, loader.LoadOptions{
|
||||
RawSpecs: rawSpecs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vp.GetBool("load-cluster-specs") {
|
||||
clusterKinds, err := LoadFromCluster(ctx, client, vp.GetStringSlice("selector"), vp.GetString("namespace"))
|
||||
if err != nil {
|
||||
return nil, types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
|
||||
}
|
||||
|
||||
kinds.Add(clusterKinds)
|
||||
}
|
||||
|
||||
return kinds, nil
|
||||
}
|
||||
|
||||
func downloadFromHttpURL(ctx context.Context, url string, headers map[string]string) (string, error) {
|
||||
hs := []string{}
|
||||
for k, v := range headers {
|
||||
hs = append(hs, fmt.Sprintf("%s: %s", k, v))
|
||||
}
|
||||
|
||||
klog.V(1).Infof("Downloading troubleshoot specs: url=%s, headers=[%v]", url, strings.Join(hs, ", "))
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
// exit code: should this be catch all or spec issues...?
|
||||
return "", types.NewExitCodeError(constants.EXIT_CODE_CATCH_ALL, err)
|
||||
}
|
||||
for k, v := range headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
// exit code: should this be catch all or spec issues...?
|
||||
return "", types.NewExitCodeError(constants.EXIT_CODE_CATCH_ALL, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
klog.V(1).Infof("Response status: %s", resp.Status)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", types.NewExitCodeError(constants.EXIT_CODE_SPEC_ISSUES, err)
|
||||
}
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
// LoadFromCluster loads troubleshoot specs from the cluster based on the provided labels.
|
||||
// By default this will be troubleshoot.io/kind=support-bundle and troubleshoot.sh/kind=support-bundle
|
||||
// labels. We search for secrets and configmaps with the label selector and extract the raw data.
|
||||
// We then load the specs from the raw data. If the user does not have sufficient permissions
|
||||
// to list & read secrets and configmaps from all namespaces, we will fallback to trying each
|
||||
// namespace individually, and eventually default to the configured kubeconfig namespace.
|
||||
func LoadFromCluster(ctx context.Context, client kubernetes.Interface, selectors []string, ns string) (*loader.TroubleshootKinds, error) {
|
||||
if reflect.DeepEqual(selectors, []string{"troubleshoot.sh/kind=support-bundle"}) {
|
||||
// Its the default selector so we append troubleshoot.io/kind=support-bundle to it due to backwards compatibility
|
||||
selectors = append(selectors, "troubleshoot.io/kind=support-bundle")
|
||||
}
|
||||
|
||||
labelSelector := strings.Join(selectors, ",")
|
||||
|
||||
parsedSelector, err := labels.Parse(labelSelector)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to parse selector")
|
||||
}
|
||||
|
||||
// List of namespaces we want to search for secrets and configmaps with support bundle specs
|
||||
namespaces := []string{}
|
||||
if ns != "" {
|
||||
// Just progress with the namespace provided
|
||||
namespaces = []string{ns}
|
||||
} else {
|
||||
// Check if I can read secrets and configmaps in all namespaces
|
||||
ican, err := k8sutil.CanIListAndGetAllSecretsAndConfigMaps(ctx, client)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to check if I can read secrets and configmaps")
|
||||
}
|
||||
klog.V(1).Infof("Can I read any secrets and configmaps: %v", ican)
|
||||
|
||||
if ican {
|
||||
// I can read secrets and configmaps in all namespaces
|
||||
// No need to iterate over all namespaces
|
||||
namespaces = []string{""}
|
||||
} else {
|
||||
// Get list of all namespaces and try to find specs from each namespace
|
||||
nsList, err := client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
if k8serrors.IsForbidden(err) {
|
||||
kubeconfig := k8sutil.GetKubeconfig()
|
||||
ns, _, err := kubeconfig.Namespace()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get namespace from kubeconfig")
|
||||
}
|
||||
// If we are not allowed to list namespaces, just use the default namespace
|
||||
// configured in the kubeconfig
|
||||
namespaces = []string{ns}
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "failed to list namespaces")
|
||||
}
|
||||
}
|
||||
|
||||
for _, ns := range nsList.Items {
|
||||
namespaces = append(namespaces, ns.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var rawSpecs []string
|
||||
|
||||
parsedSelectorStrings, err := SplitTroubleshootSecretLabelSelector(ctx, parsedSelector)
|
||||
if err != nil {
|
||||
klog.Errorf("failed to parse troubleshoot labels selector %s", err)
|
||||
}
|
||||
|
||||
// Iteratively search for troubleshoot specs in all namespaces using the given selectors
|
||||
for _, parsedSelectorString := range parsedSelectorStrings {
|
||||
klog.V(1).Infof("Search specs from [%q] namespace using %q selector", strings.Join(namespaces, ", "), parsedSelectorString)
|
||||
for _, ns := range namespaces {
|
||||
for _, key := range []string{constants.SupportBundleKey, constants.PreflightKey, constants.RedactorKey} {
|
||||
specs, err := LoadFromSecretMatchingLabel(ctx, client, parsedSelectorString, ns, key)
|
||||
if err != nil {
|
||||
if !k8serrors.IsForbidden(err) {
|
||||
klog.Errorf("failed to load support bundle spec from secrets: %s", err)
|
||||
} else {
|
||||
klog.Warningf("Reading secrets from %q namespace forbidden", ns)
|
||||
}
|
||||
}
|
||||
rawSpecs = append(rawSpecs, specs...)
|
||||
|
||||
specs, err = LoadFromConfigMapMatchingLabel(ctx, client, parsedSelectorString, ns, key)
|
||||
if err != nil {
|
||||
if !k8serrors.IsForbidden(err) {
|
||||
klog.Errorf("failed to load support bundle spec from configmap: %s", err)
|
||||
} else {
|
||||
klog.Warningf("Reading configmaps from %q namespace forbidden", ns)
|
||||
}
|
||||
}
|
||||
rawSpecs = append(rawSpecs, specs...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load troubleshoot specs from the raw specs
|
||||
return loader.LoadSpecs(ctx, loader.LoadOptions{
|
||||
RawSpecs: rawSpecs,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package specs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"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"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
testclient "k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
func Test_SplitTroubleshootSecretLabelSelector(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
selectorString string
|
||||
expectedSelectors []string
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "Split both troubleshoot and non-troubleshoot labels",
|
||||
selectorString: "troubleshoot.io/kind=support-bundle,troubleshoot.sh/kind=support-bundle,a=b",
|
||||
expectedSelectors: []string{
|
||||
"a=b,troubleshoot.io/kind=support-bundle",
|
||||
"a=b,troubleshoot.sh/kind=support-bundle",
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "Split only troubleshoot.io label",
|
||||
selectorString: "troubleshoot.io/kind=support-bundle",
|
||||
expectedSelectors: []string{"troubleshoot.io/kind=support-bundle"},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "Split only troubleshoot.sh label",
|
||||
selectorString: "troubleshoot.sh/kind=support-bundle",
|
||||
expectedSelectors: []string{"troubleshoot.sh/kind=support-bundle"},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "Split only non-troubleshoot label",
|
||||
selectorString: "a=b",
|
||||
expectedSelectors: []string{"a=b"},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "No selector labels to split",
|
||||
selectorString: "",
|
||||
expectedSelectors: []string{},
|
||||
expectedError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
selector, err := labels.Parse(tt.selectorString)
|
||||
if err != nil {
|
||||
t.Errorf("Error parsing selector string: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
gotSelectors, err := SplitTroubleshootSecretLabelSelector(context.TODO(), selector)
|
||||
if (err != nil) != tt.expectedError {
|
||||
t.Errorf("Expected error: %v, got: %v", tt.expectedError, err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.ElementsMatch(t, tt.expectedSelectors, gotSelectors)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFromCluster(t *testing.T) {
|
||||
theRedactor := troubleshootv1beta2.Redactor{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "troubleshoot.sh/v1beta2",
|
||||
Kind: "Redactor",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "redact-some-content",
|
||||
},
|
||||
Spec: troubleshootv1beta2.RedactorSpec{
|
||||
Redactors: []*troubleshootv1beta2.Redact{
|
||||
{
|
||||
Name: "redact-text-1",
|
||||
Removals: troubleshootv1beta2.Removals{
|
||||
Values: []string{"TEXT"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
selectors []string
|
||||
namespace string
|
||||
objects []runtime.Object
|
||||
want *loader.TroubleshootKinds
|
||||
}{
|
||||
{
|
||||
name: "no selectors",
|
||||
want: loader.NewTroubleshootKinds(),
|
||||
},
|
||||
{
|
||||
name: "spec in secret and default label selector",
|
||||
namespace: "bigbank",
|
||||
selectors: []string{
|
||||
"troubleshoot.sh/kind=support-bundle",
|
||||
},
|
||||
objects: []runtime.Object{
|
||||
secretObject("bigbank", map[string]string{
|
||||
"troubleshoot.io/kind": "support-bundle",
|
||||
}),
|
||||
},
|
||||
want: &loader.TroubleshootKinds{
|
||||
RedactorsV1Beta2: []troubleshootv1beta2.Redactor{theRedactor},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "spec in secret and no selector argument passed",
|
||||
namespace: "bigbank",
|
||||
objects: []runtime.Object{
|
||||
secretObject("bigbank", map[string]string{
|
||||
"troubleshoot.io/kind": "support-bundle",
|
||||
}),
|
||||
},
|
||||
want: loader.NewTroubleshootKinds(),
|
||||
},
|
||||
{
|
||||
name: "multiple specs default selector",
|
||||
namespace: "bigbank",
|
||||
selectors: []string{
|
||||
"troubleshoot.sh/kind=support-bundle",
|
||||
},
|
||||
objects: []runtime.Object{
|
||||
secretObject("bigbank", map[string]string{
|
||||
"troubleshoot.io/kind": "support-bundle",
|
||||
}),
|
||||
secretObject("bigbank", map[string]string{
|
||||
"troubleshoot.io/kind": "support-bundle",
|
||||
}),
|
||||
},
|
||||
want: &loader.TroubleshootKinds{
|
||||
RedactorsV1Beta2: []troubleshootv1beta2.Redactor{theRedactor, theRedactor},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "spec in secret but different namespace",
|
||||
namespace: "bigbank",
|
||||
objects: []runtime.Object{
|
||||
secretObject("anotherbank", map[string]string{
|
||||
"troubleshoot.io/kind": "support-bundle",
|
||||
}),
|
||||
},
|
||||
want: loader.NewTroubleshootKinds(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := testclient.NewSimpleClientset(tt.objects...)
|
||||
got, err := LoadFromCluster(ctx, client, tt.selectors, tt.namespace)
|
||||
require.NoError(t, err)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("got = %v, want %v", testutils.AsJSON(t, got), testutils.AsJSON(t, tt.want))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func secretObject(ns string, selectors map[string]string) runtime.Object {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("secret-name-%s", uuid.New().String()),
|
||||
Namespace: ns,
|
||||
Labels: selectors,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"redactor-spec": []byte(`apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: Redactor
|
||||
metadata:
|
||||
name: redact-some-content
|
||||
spec:
|
||||
redactors:
|
||||
- name: redact-text-1
|
||||
removals:
|
||||
values:
|
||||
- TEXT`),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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,13 +15,25 @@ import (
|
||||
|
||||
func GetTestFixture(t *testing.T, path string) string {
|
||||
t.Helper()
|
||||
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)
|
||||
@@ -29,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) {
|
||||
@@ -53,10 +69,24 @@ func CreateTestFileWithData(t *testing.T, path, data string) {
|
||||
func LogJSON(t *testing.T, v interface{}) {
|
||||
t.Helper()
|
||||
|
||||
t.Log(AsJSON(t, v))
|
||||
}
|
||||
|
||||
func AsJSON(t *testing.T, v interface{}) string {
|
||||
t.Helper()
|
||||
|
||||
b, err := json.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
t.Log(v)
|
||||
return fmt.Sprintf("%#v", v)
|
||||
} else {
|
||||
t.Log(string(b))
|
||||
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
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ var (
|
||||
printer = message.NewPrinter(language.English)
|
||||
)
|
||||
|
||||
const legend = "Suceeded (S), eXcluded (X), Failed (F)\n"
|
||||
const legend = "Succeeded (S), eXcluded (X), Failed (F)\n"
|
||||
|
||||
// FUTURE WORK: This exporter should only be used by troubleshoot CLIs
|
||||
// until the following issue is addressed:
|
||||
|
||||
@@ -76,7 +76,7 @@ func TestExporter_GetSummary(t *testing.T) {
|
||||
},
|
||||
want: `
|
||||
============ Collectors summary =============
|
||||
Suceeded (S), eXcluded (X), Failed (F)
|
||||
Succeeded (S), eXcluded (X), Failed (F)
|
||||
=============================================
|
||||
all-logs (S) : 60,000ms
|
||||
host-os (S) : 1,000ms
|
||||
@@ -118,7 +118,7 @@ failed-collector (F) : 1ms`,
|
||||
},
|
||||
want: `
|
||||
============= Analyzers summary =============
|
||||
Suceeded (S), eXcluded (X), Failed (F)
|
||||
Succeeded (S), eXcluded (X), Failed (F)
|
||||
=============================================
|
||||
host-cpu (S) : 60,000ms
|
||||
cluster-version (S) : 1,000ms
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"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
|
||||
@@ -44,3 +52,120 @@ func AppName(name string) string {
|
||||
func SplitYAML(doc string) []string {
|
||||
return strings.Split(doc, "\n---\n")
|
||||
}
|
||||
|
||||
func EstimateNumberOfLines(text string) int {
|
||||
n := strings.Count(text, "\n")
|
||||
if len(text) > 0 && !strings.HasSuffix(text, "\n") {
|
||||
n++
|
||||
}
|
||||
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'.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Dedup[T any](objs []T) []T {
|
||||
seen := make(map[string]bool)
|
||||
out := []T{}
|
||||
|
||||
if len(objs) == 0 {
|
||||
return objs
|
||||
}
|
||||
|
||||
for _, o := range objs {
|
||||
data, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
out = append(out, o)
|
||||
continue
|
||||
}
|
||||
key := string(data)
|
||||
if _, ok := seen[key]; !ok {
|
||||
out = append(out, o)
|
||||
seen[key] = true
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_HomeDir(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
env string
|
||||
dir string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "test linux/unix home directory",
|
||||
env: "HOME",
|
||||
dir: "/home/test",
|
||||
want: "/home/test",
|
||||
},
|
||||
{
|
||||
name: "test windows home directory",
|
||||
env: "USERPROFILE",
|
||||
dir: `C:\Users\test`,
|
||||
want: `C:\Users\test`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
os.Setenv(tt.env, tt.dir)
|
||||
if HomeDir() != tt.want {
|
||||
assert.Equal(t, tt.want, HomeDir())
|
||||
}
|
||||
os.Unsetenv(tt.env) // cleanup
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_IsURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "valid url with http",
|
||||
url: "http://example.com",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid url with https",
|
||||
url: "https://example.com",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "invalid url without scheme",
|
||||
url: "example.com",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid url with spaces",
|
||||
url: "http://example .com",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "empty string",
|
||||
url: "",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := IsURL(tt.url); got != tt.want {
|
||||
assert.Equal(t, tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_AppName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "simple name",
|
||||
in: "myapp",
|
||||
want: "Myapp",
|
||||
},
|
||||
{
|
||||
name: "hyphenated name",
|
||||
in: "my-app",
|
||||
want: "My App",
|
||||
},
|
||||
{
|
||||
name: "name with ai",
|
||||
in: "my-ai-app",
|
||||
want: "My AI App",
|
||||
},
|
||||
{
|
||||
name: "name with io",
|
||||
in: "my-app-io",
|
||||
want: "My App.io",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := AppName(tt.in); got != tt.want {
|
||||
assert.Equal(t, tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SplitYAML(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
doc string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "single document",
|
||||
doc: `apiVersion: v1`,
|
||||
want: []string{`apiVersion: v1`},
|
||||
},
|
||||
{
|
||||
name: "multiple documents",
|
||||
doc: `apiVersion: v1
|
||||
---
|
||||
apiVersion: v1`,
|
||||
want: []string{`apiVersion: v1`, `apiVersion: v1`},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := SplitYAML(tt.doc); !reflect.DeepEqual(got, tt.want) {
|
||||
assert.Equal(t, tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_EstimateNumberOfLines(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
text string
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "no line",
|
||||
text: "",
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "single line without newline",
|
||||
text: "Hello, World!",
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "single line with newline",
|
||||
text: "Hello, World!\n",
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "multiple lines",
|
||||
text: "Hello,\nWorld!",
|
||||
want: 2,
|
||||
},
|
||||
{
|
||||
name: "multiple lines ending with newline",
|
||||
text: "Hello,\nWorld!\n",
|
||||
want: 2,
|
||||
},
|
||||
{
|
||||
name: "multiple lines with extra newlines",
|
||||
text: "\nHello,\nWorld!\n\n",
|
||||
want: 4,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := EstimateNumberOfLines(tt.text); got != tt.want {
|
||||
assert.Equal(t, tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
+32
-1
@@ -1,16 +1,20 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/constants"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/multitype"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/redact"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
@@ -18,6 +22,8 @@ import (
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
const FILE_NOT_COLLECTED = "fileNotCollected"
|
||||
|
||||
type AnalyzeResult struct {
|
||||
IsPass bool
|
||||
IsFail bool
|
||||
@@ -79,7 +85,7 @@ func HostAnalyze(
|
||||
return nil
|
||||
}
|
||||
|
||||
result, err := analyzer.Analyze(getFile)
|
||||
result, err := analyzer.Analyze(getFile, findFiles)
|
||||
if err != nil {
|
||||
return NewAnalyzeResultError(analyzer, errors.Wrap(err, "analyze"))
|
||||
}
|
||||
@@ -218,10 +224,14 @@ func getAnalyzer(analyzer *troubleshootv1beta2.Analyze) Analyzer {
|
||||
return &AnalyzePostgres{analyzer: analyzer.Postgres}
|
||||
case analyzer.Mysql != nil:
|
||||
return &AnalyzeMysql{analyzer: analyzer.Mysql}
|
||||
case analyzer.Mssql != nil:
|
||||
return &AnalyzeMssql{analyzer: analyzer.Mssql}
|
||||
case analyzer.Redis != nil:
|
||||
return &AnalyzeRedis{analyzer: analyzer.Redis}
|
||||
case analyzer.CephStatus != nil:
|
||||
return &AnalyzeCephStatus{analyzer: analyzer.CephStatus}
|
||||
case analyzer.Velero != nil:
|
||||
return &AnalyzeVelero{analyzer: analyzer.Velero}
|
||||
case analyzer.Longhorn != nil:
|
||||
return &AnalyzeLonghorn{analyzer: analyzer.Longhorn}
|
||||
case analyzer.RegistryImages != nil:
|
||||
@@ -234,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
|
||||
}
|
||||
@@ -261,3 +277,18 @@ func DedupAnalyzers(allAnalyzers []*troubleshootv1beta2.Analyze) []*troubleshoot
|
||||
}
|
||||
return finalAnalyzers
|
||||
}
|
||||
|
||||
func stripRedactedLines(yaml []byte) []byte {
|
||||
buf := bytes.NewBuffer(yaml)
|
||||
scanner := bufio.NewScanner(buf)
|
||||
|
||||
out := []byte{}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := strings.ReplaceAll(scanner.Text(), redact.MASK_TEXT, "HIDDEN")
|
||||
out = append(out, []byte(line)...)
|
||||
out = append(out, '\n')
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package analyzer
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
@@ -32,7 +33,8 @@ func (a *AnalyzeClusterPodStatuses) IsExcluded() (bool, error) {
|
||||
}
|
||||
|
||||
func (a *AnalyzeClusterPodStatuses) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
||||
results, err := clusterPodStatuses(a.analyzer, findFiles)
|
||||
// findFiles is used to get the pod status and events files
|
||||
results, err := clusterPodStatuses(a.analyzer, findFiles, findFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -42,7 +44,7 @@ func (a *AnalyzeClusterPodStatuses) Analyze(getFile getCollectedFileContents, fi
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func clusterPodStatuses(analyzer *troubleshootv1beta2.ClusterPodStatuses, getChildCollectedFileContents getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
||||
func clusterPodStatuses(analyzer *troubleshootv1beta2.ClusterPodStatuses, getChildCollectedFileContents getChildCollectedFileContents, getChildCollectedFileContentsEvents getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
||||
excludeFiles := []string{}
|
||||
collected, err := getChildCollectedFileContents(filepath.Join(constants.CLUSTER_RESOURCES_DIR, constants.CLUSTER_RESOURCES_PODS, "*.json"), excludeFiles)
|
||||
if err != nil {
|
||||
@@ -77,7 +79,38 @@ func clusterPodStatuses(analyzer *troubleshootv1beta2.ClusterPodStatuses, getChi
|
||||
|
||||
for _, pod := range pods {
|
||||
if pod.Status.Reason == "" {
|
||||
pod.Status.Reason = k8sutil.GetPodStatusReason(&pod)
|
||||
// get pod status reason and message from the pod
|
||||
pod.Status.Reason, pod.Status.Message = k8sutil.GetPodStatusReason(&pod)
|
||||
}
|
||||
|
||||
// if the pod has no last termination message like pending or container creating, then check the pod events and get the warning messages. Errors will be logged and return empty message.
|
||||
if pod.Status.Message == "" {
|
||||
messages := []string{}
|
||||
collectedEvents, err := getChildCollectedFileContentsEvents(filepath.Join(constants.CLUSTER_RESOURCES_DIR, "events", fmt.Sprintf("%s.json", pod.Namespace)), excludeFiles)
|
||||
if err != nil {
|
||||
klog.V(2).Infof("failed to read collected events for namespace %s: %v", pod.Namespace, err)
|
||||
}
|
||||
|
||||
for _, fileContent := range collectedEvents {
|
||||
var nsEvents []corev1.Event
|
||||
if err := json.Unmarshal(fileContent, &nsEvents); err != nil {
|
||||
// try new format
|
||||
var nsEventsList corev1.EventList
|
||||
if err := json.Unmarshal(fileContent, &nsEventsList); err != nil {
|
||||
klog.V(2).Infof("failed to unmarshal events for namespace %s: %v", pod.Namespace, err)
|
||||
}
|
||||
nsEvents = nsEventsList.Items
|
||||
}
|
||||
|
||||
for _, event := range nsEvents {
|
||||
if event.InvolvedObject.Kind == "Pod" && event.InvolvedObject.Name == pod.Name && event.InvolvedObject.Namespace == pod.Namespace {
|
||||
if event.Type == "Warning" && event.Message != "" {
|
||||
messages = append(messages, event.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pod.Status.Message = strings.Join(messages, ". ")
|
||||
}
|
||||
|
||||
for _, outcome := range analyzer.Outcomes {
|
||||
@@ -149,7 +182,12 @@ func clusterPodStatuses(analyzer *troubleshootv1beta2.ClusterPodStatuses, getChi
|
||||
}
|
||||
|
||||
if r.Message == "" {
|
||||
r.Message = "Pod {{ .Namespace }}/{{ .Name }} status is {{ .Status.Reason }}"
|
||||
r.Message = "Pod {{ .Namespace }}/{{ .Name }} status is {{ .Status.Reason }}. Message is: {{ .Status.Message }}"
|
||||
}
|
||||
|
||||
// if the pod has no status message, set it to None
|
||||
if pod.Status.Message == "" {
|
||||
pod.Status.Message = "None"
|
||||
}
|
||||
|
||||
tmpl := template.New("pod")
|
||||
@@ -176,7 +214,7 @@ func clusterPodStatuses(analyzer *troubleshootv1beta2.ClusterPodStatuses, getChi
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to execute template")
|
||||
}
|
||||
r.Message = m.String()
|
||||
r.Message = strings.TrimSpace(m.String())
|
||||
|
||||
// add to results, break and check the next pod
|
||||
allResults = append(allResults, &r)
|
||||
|
||||
@@ -15,6 +15,7 @@ func Test_ClusterPodStatuses(t *testing.T) {
|
||||
analyzer troubleshootv1beta2.ClusterPodStatuses
|
||||
expectResult []*AnalyzeResult
|
||||
files map[string][]byte
|
||||
eventFiles map[string][]byte
|
||||
}{
|
||||
{
|
||||
name: "pass_when_all_pods_are_healthy_in_specific_namespace",
|
||||
@@ -374,6 +375,295 @@ func Test_ClusterPodStatuses(t *testing.T) {
|
||||
"cluster-resources/pods/other-unhealthy.json": []byte(otherPodsUnhealthy),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "show_message_of_pending_pods_with_wrong_node_affinity",
|
||||
analyzer: troubleshootv1beta2.ClusterPodStatuses{
|
||||
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
|
||||
CheckName: "show_message_of_pending_pods_with_wrong_node_affinity",
|
||||
},
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "!= Healthy",
|
||||
Message: "A Pod, {{ .Name }}, is unhealthy with a status of: {{ .Status.Reason }}. Message is: {{ .Status.Message }}",
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespaces: []string{"message-pending-node-affinity"},
|
||||
},
|
||||
expectResult: []*AnalyzeResult{
|
||||
{
|
||||
IsPass: false,
|
||||
IsWarn: true,
|
||||
IsFail: false,
|
||||
Title: "show_message_of_pending_pods_with_wrong_node_affinity",
|
||||
Message: "A Pod, kotsadm-b6cb54c8f-zgzrn, is unhealthy with a status of: Pending. Message is: 0/1 nodes are available: 1 node(s) didn't match Pod's node affinity/selector. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling.",
|
||||
InvolvedObject: &corev1.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
Namespace: "message-pending-node-affinity",
|
||||
Name: "kotsadm-b6cb54c8f-zgzrn",
|
||||
},
|
||||
},
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/pods/message-pending-node-affinity.json": []byte(messagePendingNodeAffinity),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "show_message_of_container_creating_pod_with_failed_mount",
|
||||
analyzer: troubleshootv1beta2.ClusterPodStatuses{
|
||||
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
|
||||
CheckName: "show_message_of_container_creating_pod_with_failed_mount",
|
||||
},
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "!= Healthy",
|
||||
Message: "A Pod, {{ .Name }}, is unhealthy with a status of: {{ .Status.Reason }}. Message is: {{ .Status.Message }}",
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespaces: []string{"message-container-creating-failed-mount"},
|
||||
},
|
||||
expectResult: []*AnalyzeResult{
|
||||
{
|
||||
IsPass: false,
|
||||
IsWarn: true,
|
||||
IsFail: false,
|
||||
Title: "show_message_of_container_creating_pod_with_failed_mount",
|
||||
Message: "A Pod, troubleshoot-copyfromhost-4m79m-psdjm, is unhealthy with a status of: ContainerCreating. Message is: MountVolume.SetUp failed for volume \"host\" : hostPath type check failed: /var/lib/collectd is not a directory. Unable to attach or mount volumes: unmounted volumes=[host], unattached volumes=[host kube-api-access-xddvj]: timed out waiting for the condition",
|
||||
InvolvedObject: &corev1.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
Namespace: "message-container-creating-failed-mount",
|
||||
Name: "troubleshoot-copyfromhost-4m79m-psdjm",
|
||||
},
|
||||
},
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/pods/message-container-creating-failed-mount.json": []byte(messageContainerCreatingFailedMount),
|
||||
},
|
||||
eventFiles: map[string][]byte{
|
||||
"cluster-resources/events/message-container-creating-failed-mount.json": []byte(messageContainerCreatingFailedMountEvents),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "show_message_of_pod_crashloop_backoff",
|
||||
analyzer: troubleshootv1beta2.ClusterPodStatuses{
|
||||
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
|
||||
CheckName: "show_message_of_pod_crashloop_backoff",
|
||||
},
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "!= Healthy",
|
||||
Message: "A Pod, {{ .Name }}, is unhealthy with a status of: {{ .Status.Reason }}. Message is: {{ .Status.Message }}",
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespaces: []string{"message-pod-crashloop-backoff"},
|
||||
},
|
||||
expectResult: []*AnalyzeResult{
|
||||
{
|
||||
IsPass: false,
|
||||
IsWarn: true,
|
||||
IsFail: false,
|
||||
Title: "show_message_of_pod_crashloop_backoff",
|
||||
Message: "A Pod, init-demo, is unhealthy with a status of: CrashLoopBackOff. Message is: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: \"wge\": executable file not found in $PATH: unknown",
|
||||
InvolvedObject: &corev1.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
Namespace: "message-pod-crashloop-backoff",
|
||||
Name: "init-demo",
|
||||
},
|
||||
},
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/pods/message-pod-crashloop-backoff.json": []byte(messagePodCrashLoopBackOff),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "show_message_of_init_pod_crashloop_backoff",
|
||||
analyzer: troubleshootv1beta2.ClusterPodStatuses{
|
||||
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
|
||||
CheckName: "show_message_of_init_pod_crashloop_backoff",
|
||||
},
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "!= Healthy",
|
||||
Message: "A Pod, {{ .Name }}, is unhealthy with a status of: {{ .Status.Reason }}. Message is: {{ .Status.Message }}",
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespaces: []string{"message-pod-init-crashloop-backoff"},
|
||||
},
|
||||
expectResult: []*AnalyzeResult{
|
||||
{
|
||||
IsPass: false,
|
||||
IsWarn: true,
|
||||
IsFail: false,
|
||||
Title: "show_message_of_init_pod_crashloop_backoff",
|
||||
Message: "A Pod, init-demo2, is unhealthy with a status of: Init:RunContainerError. Message is: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: \"wge\": executable file not found in $PATH: unknown",
|
||||
InvolvedObject: &corev1.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
Namespace: "message-pod-init-crashloop-backoff",
|
||||
Name: "init-demo2",
|
||||
},
|
||||
},
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/pods/message-pod-init-crashloop-backoff.json": []byte(messagePodInitCrashLoopBackOff),
|
||||
},
|
||||
eventFiles: map[string][]byte{
|
||||
"cluster-resources/events/message-pod-init-crashloop-backoff.json": []byte(messagePodInitCrashLoopBackOffEvents),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "show_message_of_pending_pod_resource",
|
||||
analyzer: troubleshootv1beta2.ClusterPodStatuses{
|
||||
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
|
||||
CheckName: "show_message_of_pending_pod_resource",
|
||||
},
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "!= Healthy",
|
||||
Message: "A Pod, {{ .Name }}, is unhealthy with a status of: {{ .Status.Reason }}. Message is: {{ .Status.Message }}",
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespaces: []string{"message-pending-pod-resources"},
|
||||
},
|
||||
expectResult: []*AnalyzeResult{
|
||||
{
|
||||
IsPass: false,
|
||||
IsWarn: true,
|
||||
IsFail: false,
|
||||
Title: "show_message_of_pending_pod_resource",
|
||||
Message: "A Pod, pending-pod-resources-5fddcf7688-djjfc, is unhealthy with a status of: Pending. Message is: 0/1 nodes are available: 1 Insufficient nvidia.com/gpu. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.",
|
||||
InvolvedObject: &corev1.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
Namespace: "message-pending-pod-resources",
|
||||
Name: "pending-pod-resources-5fddcf7688-djjfc",
|
||||
},
|
||||
},
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/pods/message-pending-pod-resources.json": []byte(messagePendingPodResources),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "show_message_of_oom_killed_pod",
|
||||
analyzer: troubleshootv1beta2.ClusterPodStatuses{
|
||||
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
|
||||
CheckName: "show_message_of_oom_killed_pod",
|
||||
},
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "!= Healthy",
|
||||
Message: "A Pod, {{ .Name }}, is unhealthy with a status of: {{ .Status.Reason }}. Message is: {{ .Status.Message }}",
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespaces: []string{"message-oomkill-pod"},
|
||||
},
|
||||
expectResult: []*AnalyzeResult{
|
||||
{
|
||||
IsPass: false,
|
||||
IsWarn: true,
|
||||
IsFail: false,
|
||||
Title: "show_message_of_oom_killed_pod",
|
||||
Message: "A Pod, oom-kill-job3-gbb89, is unhealthy with a status of: OOMKilled. Message is: ExitCode:137",
|
||||
InvolvedObject: &corev1.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
Namespace: "message-oomkill-pod",
|
||||
Name: "oom-kill-job3-gbb89",
|
||||
},
|
||||
},
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/pods/message-oomkill-pod.json": []byte(messageOOMKillPod),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "show_message_of_image_pull_fail",
|
||||
analyzer: troubleshootv1beta2.ClusterPodStatuses{
|
||||
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
|
||||
CheckName: "show_message_of_image_pull_fail",
|
||||
},
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "!= Healthy",
|
||||
Message: "A Pod, {{ .Name }}, is unhealthy with a status of: {{ .Status.Reason }}. Message is: {{ .Status.Message }}",
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespaces: []string{"message-image-pull-fail"},
|
||||
},
|
||||
expectResult: []*AnalyzeResult{
|
||||
{
|
||||
IsPass: false,
|
||||
IsWarn: true,
|
||||
IsFail: false,
|
||||
Title: "show_message_of_image_pull_fail",
|
||||
Message: "A Pod, no-image-deployment-849c4c4958-rxqmt, is unhealthy with a status of: ErrImagePull. Message is: Failed to pull image \"noimage.com/no-such-image\": rpc error: code = Unknown desc = Error response from daemon: Get \"https://noimage.com/v2/\": x509: certificate is not valid for any names, but wanted to match noimage.com. Error: ErrImagePull. Error: ImagePullBackOff",
|
||||
InvolvedObject: &corev1.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
Namespace: "message-image-pull-fail",
|
||||
Name: "no-image-deployment-849c4c4958-rxqmt",
|
||||
},
|
||||
},
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/pods/message-image-pull-fail.json": []byte(messageImagePullFail),
|
||||
},
|
||||
eventFiles: map[string][]byte{
|
||||
"cluster-resources/events/message-image-pull-fail.json": []byte(messageImagePullFailEvents),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "show_none_message_of_no_events_pod",
|
||||
analyzer: troubleshootv1beta2.ClusterPodStatuses{
|
||||
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
|
||||
CheckName: "show_none_message_of_no_events_pod",
|
||||
},
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "!= Healthy",
|
||||
Message: "A Pod, {{ .Name }}, is unhealthy with a status of: {{ .Status.Reason }}. Message is: {{ .Status.Message }}",
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespaces: []string{"message-image-pull-fail"},
|
||||
},
|
||||
expectResult: []*AnalyzeResult{
|
||||
{
|
||||
IsPass: false,
|
||||
IsWarn: true,
|
||||
IsFail: false,
|
||||
Title: "show_none_message_of_no_events_pod",
|
||||
Message: "A Pod, no-image-deployment-849c4c4958-rxqmt, is unhealthy with a status of: ErrImagePull. Message is: None",
|
||||
InvolvedObject: &corev1.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
Namespace: "message-image-pull-fail",
|
||||
Name: "no-image-deployment-849c4c4958-rxqmt",
|
||||
},
|
||||
},
|
||||
},
|
||||
files: map[string][]byte{
|
||||
"cluster-resources/pods/message-image-pull-fail.json": []byte(messageImagePullFail),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -387,7 +677,14 @@ func Test_ClusterPodStatuses(t *testing.T) {
|
||||
return test.files, nil
|
||||
}
|
||||
|
||||
actual, err := clusterPodStatuses(&test.analyzer, getFiles)
|
||||
getEventFiles := func(n string, _ []string) (map[string][]byte, error) {
|
||||
if file, ok := test.eventFiles[n]; ok {
|
||||
return map[string][]byte{n: file}, nil
|
||||
}
|
||||
return test.files, nil
|
||||
}
|
||||
|
||||
actual, err := clusterPodStatuses(&test.analyzer, getFiles, getEventFiles)
|
||||
req.NoError(err)
|
||||
req.Equal(len(test.expectResult), len(actual))
|
||||
for _, a := range actual {
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@ package analyzer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
)
|
||||
|
||||
func commonStatus(outcomes []*troubleshootv1beta2.Outcome, name string, iconKey string, iconURI string, readyReplicas int, exists bool, resourceType string) (*AnalyzeResult, error) {
|
||||
@@ -47,7 +48,7 @@ func commonStatus(outcomes []*troubleshootv1beta2.Outcome, name string, iconKey
|
||||
}
|
||||
}
|
||||
|
||||
match, err := compareActualToWhen(outcome.Fail.When, readyReplicas, exists)
|
||||
match, err := compareActualToWhen(outcome.Fail.When, readyReplicas)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse fail range")
|
||||
}
|
||||
@@ -64,7 +65,7 @@ func commonStatus(outcomes []*troubleshootv1beta2.Outcome, name string, iconKey
|
||||
if exists == false && outcome.Warn.When != "absent" {
|
||||
result.IsFail = true
|
||||
result.Message = fmt.Sprintf("The %s %q was not found", resourceType, name)
|
||||
result.URI = outcome.Fail.URI
|
||||
result.URI = outcome.Warn.URI
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -87,7 +88,7 @@ func commonStatus(outcomes []*troubleshootv1beta2.Outcome, name string, iconKey
|
||||
}
|
||||
}
|
||||
|
||||
match, err := compareActualToWhen(outcome.Warn.When, readyReplicas, exists)
|
||||
match, err := compareActualToWhen(outcome.Warn.When, readyReplicas)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse warn range")
|
||||
}
|
||||
@@ -104,7 +105,7 @@ func commonStatus(outcomes []*troubleshootv1beta2.Outcome, name string, iconKey
|
||||
if exists == false && outcome.Pass.When != "absent" {
|
||||
result.IsFail = true
|
||||
result.Message = fmt.Sprintf("The %s %q was not found", resourceType, name)
|
||||
result.URI = outcome.Fail.URI
|
||||
result.URI = outcome.Pass.URI
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -127,7 +128,7 @@ func commonStatus(outcomes []*troubleshootv1beta2.Outcome, name string, iconKey
|
||||
}
|
||||
}
|
||||
|
||||
match, err := compareActualToWhen(outcome.Pass.When, readyReplicas, exists)
|
||||
match, err := compareActualToWhen(outcome.Pass.When, readyReplicas)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse pass range")
|
||||
}
|
||||
@@ -145,9 +146,8 @@ func commonStatus(outcomes []*troubleshootv1beta2.Outcome, name string, iconKey
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func compareActualToWhen(when string, actual int, exists bool) (bool, error) {
|
||||
func compareActualToWhen(when string, actual int) (bool, error) {
|
||||
parts := strings.Split(strings.TrimSpace(when), " ")
|
||||
|
||||
// we can make this a lot more flexible
|
||||
if len(parts) != 2 {
|
||||
return false, errors.New("unable to parse when range")
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -45,3 +45,33 @@ var defaultPodsUnhealthy string
|
||||
|
||||
//go:embed files/pods/other-unhealthy.json
|
||||
var otherPodsUnhealthy string
|
||||
|
||||
//go:embed files/pods/message-pending-node-affinity.json
|
||||
var messagePendingNodeAffinity string
|
||||
|
||||
//go:embed files/pods/message-container-creating-failed-mount.json
|
||||
var messageContainerCreatingFailedMount string
|
||||
|
||||
//go:embed files/events/message-container-creating-failed-mount.json
|
||||
var messageContainerCreatingFailedMountEvents string
|
||||
|
||||
//go:embed files/pods/message-pod-crashloop-backoff.json
|
||||
var messagePodCrashLoopBackOff string
|
||||
|
||||
//go:embed files/pods/message-pod-init-crashloop-backoff.json
|
||||
var messagePodInitCrashLoopBackOff string
|
||||
|
||||
//go:embed files/events/message-pod-init-crashloop-backoff.json
|
||||
var messagePodInitCrashLoopBackOffEvents string
|
||||
|
||||
//go:embed files/pods/message-pending-pod-resources.json
|
||||
var messagePendingPodResources string
|
||||
|
||||
//go:embed files/pods/message-oomkill-pod.json
|
||||
var messageOOMKillPod string
|
||||
|
||||
//go:embed files/pods/message-image-pull-fail.json
|
||||
var messageImagePullFail string
|
||||
|
||||
//go:embed files/events/message-image-pull-fail.json
|
||||
var messageImagePullFailEvents string
|
||||
|
||||
+74
-27
@@ -13,38 +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
|
||||
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
|
||||
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 {
|
||||
@@ -125,6 +133,20 @@ func ParseNodesForProviders(nodes []corev1.Node) (providers, string) {
|
||||
foundProviders.k3s = true
|
||||
stringProvider = "k3s"
|
||||
}
|
||||
if k == "oci.oraclecloud.com/fault-domain" {
|
||||
// Based on: https://docs.oracle.com/en-us/iaas/Content/ContEng/Reference/contengsupportedlabelsusecases.htm
|
||||
foundProviders.oke = true
|
||||
stringProvider = "oke"
|
||||
}
|
||||
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 {
|
||||
@@ -155,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 {
|
||||
@@ -165,6 +191,11 @@ func ParseNodesForProviders(nodes []corev1.Node) (providers, string) {
|
||||
}
|
||||
}
|
||||
|
||||
// If k0s and embedded-cluster are both found, prefer embedded-cluster
|
||||
if foundProviders.k0s && foundProviders.embeddedCluster {
|
||||
stringProvider = "embedded-cluster"
|
||||
}
|
||||
|
||||
return foundProviders, stringProvider
|
||||
}
|
||||
|
||||
@@ -326,6 +357,14 @@ func compareDistributionConditionalToActual(conditional string, actual providers
|
||||
isMatch = actual.rke2
|
||||
case k3s:
|
||||
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] {
|
||||
@@ -366,6 +405,14 @@ func mustNormalizeDistributionName(raw string) Provider {
|
||||
return rke2
|
||||
case "k3s":
|
||||
return k3s
|
||||
case "oke":
|
||||
return oke
|
||||
case "kind":
|
||||
return kind
|
||||
case "k0s":
|
||||
return k0s
|
||||
case "embeddedcluster":
|
||||
return embeddedCluster
|
||||
}
|
||||
|
||||
return unknown
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func Test_compareDistributionConditionalToActual(t *testing.T) {
|
||||
@@ -39,6 +41,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 +99,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 {
|
||||
@@ -83,3 +169,48 @@ func Test_mustNormalizeDistributionName(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNodesForProviders(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
nodes []corev1.Node
|
||||
wantProviders providers
|
||||
wantProviderString string
|
||||
}{
|
||||
{
|
||||
name: "embedded-cluster",
|
||||
nodes: []corev1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "embedded-cluster",
|
||||
Labels: map[string]string{
|
||||
"beta.kubernetes.io/arch": "amd64",
|
||||
"beta.kubernetes.io/os": "linux",
|
||||
"kots.io/embedded-cluster-role": "total-1",
|
||||
"kots.io/embedded-cluster-role-0": "management",
|
||||
"kubernetes.io/arch": "amd64",
|
||||
"kubernetes.io/hostname": "evans-vm1",
|
||||
"kubernetes.io/os": "linux",
|
||||
"management": "true",
|
||||
"node-role.kubernetes.io/control-plane": "true",
|
||||
"node.k0sproject.io/role": "control-plane",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantProviders: providers{embeddedCluster: true, k0s: true},
|
||||
wantProviderString: "embedded-cluster",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
providers, stringProvider := ParseNodesForProviders(tt.nodes)
|
||||
assert.Equalf(t, tt.wantProviders, providers,
|
||||
"ParseNodesForProviders() gotProviders = %v, providers %v", providers, tt.wantProviders,
|
||||
)
|
||||
assert.Equalf(t, tt.wantProviderString, stringProvider,
|
||||
"ParseNodesForProviders() gotStringProvider = %v, stringProvider %v", stringProvider, tt.wantProviderString,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,913 @@
|
||||
{
|
||||
"kind": "EventList",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"resourceVersion": "986840"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt.1770b96096f009dd",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "3b134296-43f6-4228-9c98-aeb622fb0a9a",
|
||||
"resourceVersion": "986732",
|
||||
"creationTimestamp": "2023-07-11T05:34:44Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-scheduler",
|
||||
"operation": "Update",
|
||||
"apiVersion": "events.k8s.io/v1",
|
||||
"time": "2023-07-11T05:34:44Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:action": {},
|
||||
"f:eventTime": {},
|
||||
"f:note": {},
|
||||
"f:reason": {},
|
||||
"f:regarding": {},
|
||||
"f:reportingController": {},
|
||||
"f:reportingInstance": {},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt",
|
||||
"uid": "209076ce-26d6-4836-981b-8c05cb0081eb",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "986727"
|
||||
},
|
||||
"reason": "Scheduled",
|
||||
"message": "Successfully assigned default/no-image-deployment-849c4c4958-rxqmt to minikube",
|
||||
"source": {},
|
||||
"firstTimestamp": null,
|
||||
"lastTimestamp": null,
|
||||
"type": "Normal",
|
||||
"eventTime": "2023-07-11T05:34:44.764227Z",
|
||||
"action": "Binding",
|
||||
"reportingComponent": "default-scheduler",
|
||||
"reportingInstance": "default-scheduler-minikube"
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt.1770b960b6f1e0ea",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "e5ea1f55-0d14-4443-8f79-d5b6ef692f61",
|
||||
"resourceVersion": "986820",
|
||||
"creationTimestamp": "2023-07-11T05:34:45Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T05:36:11Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt",
|
||||
"uid": "209076ce-26d6-4836-981b-8c05cb0081eb",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "986730",
|
||||
"fieldPath": "spec.containers{demo-deployment-container}"
|
||||
},
|
||||
"reason": "Pulling",
|
||||
"message": "Pulling image \"noimage.com/no-such-image\"",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T05:34:45Z",
|
||||
"lastTimestamp": "2023-07-11T05:36:11Z",
|
||||
"count": 4,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt.1770b960f12b4918",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "4ed0acd0-b3a2-477c-bb33-0e14512f5dd8",
|
||||
"resourceVersion": "986821",
|
||||
"creationTimestamp": "2023-07-11T05:34:46Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T05:36:12Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt",
|
||||
"uid": "209076ce-26d6-4836-981b-8c05cb0081eb",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "986730",
|
||||
"fieldPath": "spec.containers{demo-deployment-container}"
|
||||
},
|
||||
"reason": "Failed",
|
||||
"message": "Failed to pull image \"noimage.com/no-such-image\": rpc error: code = Unknown desc = Error response from daemon: Get \"https://noimage.com/v2/\": x509: certificate is not valid for any names, but wanted to match noimage.com",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T05:34:46Z",
|
||||
"lastTimestamp": "2023-07-11T05:36:12Z",
|
||||
"count": 4,
|
||||
"type": "Warning",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt.1770b960f12bd46b",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "4a66c16a-b2ee-4345-8de6-028936861f03",
|
||||
"resourceVersion": "986822",
|
||||
"creationTimestamp": "2023-07-11T05:34:46Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T05:36:12Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt",
|
||||
"uid": "209076ce-26d6-4836-981b-8c05cb0081eb",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "986730",
|
||||
"fieldPath": "spec.containers{demo-deployment-container}"
|
||||
},
|
||||
"reason": "Failed",
|
||||
"message": "Error: ErrImagePull",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T05:34:46Z",
|
||||
"lastTimestamp": "2023-07-11T05:36:12Z",
|
||||
"count": 4,
|
||||
"type": "Warning",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt.1770b960fa64918a",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "0b4b988c-faf5-447a-bb2e-367b627152dc",
|
||||
"resourceVersion": "986833",
|
||||
"creationTimestamp": "2023-07-11T05:34:46Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T05:36:25Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt",
|
||||
"uid": "209076ce-26d6-4836-981b-8c05cb0081eb",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "986730",
|
||||
"fieldPath": "spec.containers{demo-deployment-container}"
|
||||
},
|
||||
"reason": "BackOff",
|
||||
"message": "Back-off pulling image \"noimage.com/no-such-image\"",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T05:34:46Z",
|
||||
"lastTimestamp": "2023-07-11T05:36:25Z",
|
||||
"count": 5,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt.1770b960fa64bb88",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "eae7cb57-316e-4dc7-8e3a-45c01ca369a5",
|
||||
"resourceVersion": "986835",
|
||||
"creationTimestamp": "2023-07-11T05:34:46Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T05:36:25Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt",
|
||||
"uid": "209076ce-26d6-4836-981b-8c05cb0081eb",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "986730",
|
||||
"fieldPath": "spec.containers{demo-deployment-container}"
|
||||
},
|
||||
"reason": "Failed",
|
||||
"message": "Error: ImagePullBackOff",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T05:34:46Z",
|
||||
"lastTimestamp": "2023-07-11T05:36:25Z",
|
||||
"count": 5,
|
||||
"type": "Warning",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "no-image-deployment-849c4c4958.1770b96096c622fa",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "55bc2a37-2ff2-4a17-a29d-c3eab878e353",
|
||||
"resourceVersion": "986728",
|
||||
"creationTimestamp": "2023-07-11T05:34:44Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-controller-manager",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T05:34:44Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "ReplicaSet",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "no-image-deployment-849c4c4958",
|
||||
"uid": "d992c1a5-d2c4-4065-ac5b-3bb893f9000a",
|
||||
"apiVersion": "apps/v1",
|
||||
"resourceVersion": "986723"
|
||||
},
|
||||
"reason": "SuccessfulCreate",
|
||||
"message": "Created pod: no-image-deployment-849c4c4958-rxqmt",
|
||||
"source": {
|
||||
"component": "replicaset-controller"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T05:34:44Z",
|
||||
"lastTimestamp": "2023-07-11T05:34:44Z",
|
||||
"count": 1,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "no-image-deployment.1770b96096492cbe",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "3feddf40-caa5-45f9-9d8b-bc24796d508f",
|
||||
"resourceVersion": "986725",
|
||||
"creationTimestamp": "2023-07-11T05:34:44Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-controller-manager",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T05:34:44Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Deployment",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "no-image-deployment",
|
||||
"uid": "abe37ef6-38c5-4f29-a956-f79def48c342",
|
||||
"apiVersion": "apps/v1",
|
||||
"resourceVersion": "986722"
|
||||
},
|
||||
"reason": "ScalingReplicaSet",
|
||||
"message": "Scaled up replica set no-image-deployment-849c4c4958 to 1",
|
||||
"source": {
|
||||
"component": "deployment-controller"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T05:34:44Z",
|
||||
"lastTimestamp": "2023-07-11T05:34:44Z",
|
||||
"count": 1,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "oom-kill-job3-gbb89.1770b6922c75433d",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "3cfa33ed-cedc-4fa3-a9b7-aa657048784a",
|
||||
"resourceVersion": "984536",
|
||||
"creationTimestamp": "2023-07-11T04:43:19Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-scheduler",
|
||||
"operation": "Update",
|
||||
"apiVersion": "events.k8s.io/v1",
|
||||
"time": "2023-07-11T04:43:19Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:action": {},
|
||||
"f:eventTime": {},
|
||||
"f:note": {},
|
||||
"f:reason": {},
|
||||
"f:regarding": {},
|
||||
"f:reportingController": {},
|
||||
"f:reportingInstance": {},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "oom-kill-job3-gbb89",
|
||||
"uid": "c8d3f094-b80c-4ff8-aff7-f0e624f1bf3a",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "984532"
|
||||
},
|
||||
"reason": "Scheduled",
|
||||
"message": "Successfully assigned default/oom-kill-job3-gbb89 to minikube",
|
||||
"source": {},
|
||||
"firstTimestamp": null,
|
||||
"lastTimestamp": null,
|
||||
"type": "Normal",
|
||||
"eventTime": "2023-07-11T04:43:19.191283Z",
|
||||
"action": "Binding",
|
||||
"reportingComponent": "default-scheduler",
|
||||
"reportingInstance": "default-scheduler-minikube"
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "oom-kill-job3-gbb89.1770b6924d361854",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "695da2fb-7e2f-4c20-b60a-9a4cd3b1b60e",
|
||||
"resourceVersion": "984539",
|
||||
"creationTimestamp": "2023-07-11T04:43:19Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:43:19Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "oom-kill-job3-gbb89",
|
||||
"uid": "c8d3f094-b80c-4ff8-aff7-f0e624f1bf3a",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "984533",
|
||||
"fieldPath": "spec.containers{memory-eater}"
|
||||
},
|
||||
"reason": "Pulling",
|
||||
"message": "Pulling image \"us-central1-docker.pkg.dev/genuine-flight-317411/devel/memory-eater:1.0\"",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T04:43:19Z",
|
||||
"lastTimestamp": "2023-07-11T04:43:19Z",
|
||||
"count": 1,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "oom-kill-job3-gbb89.1770b6939d446433",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "b7ddf622-f531-4822-bdb2-46f6e27218ae",
|
||||
"resourceVersion": "984544",
|
||||
"creationTimestamp": "2023-07-11T04:43:25Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:43:25Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "oom-kill-job3-gbb89",
|
||||
"uid": "c8d3f094-b80c-4ff8-aff7-f0e624f1bf3a",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "984533",
|
||||
"fieldPath": "spec.containers{memory-eater}"
|
||||
},
|
||||
"reason": "Pulled",
|
||||
"message": "Successfully pulled image \"us-central1-docker.pkg.dev/genuine-flight-317411/devel/memory-eater:1.0\" in 5.638046961s",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T04:43:25Z",
|
||||
"lastTimestamp": "2023-07-11T04:43:25Z",
|
||||
"count": 1,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "oom-kill-job3-gbb89.1770b6939f884130",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "84e0a635-a3b8-4cdc-b439-a71454e7d7d5",
|
||||
"resourceVersion": "984545",
|
||||
"creationTimestamp": "2023-07-11T04:43:25Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:43:25Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "oom-kill-job3-gbb89",
|
||||
"uid": "c8d3f094-b80c-4ff8-aff7-f0e624f1bf3a",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "984533",
|
||||
"fieldPath": "spec.containers{memory-eater}"
|
||||
},
|
||||
"reason": "Created",
|
||||
"message": "Created container memory-eater",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T04:43:25Z",
|
||||
"lastTimestamp": "2023-07-11T04:43:25Z",
|
||||
"count": 1,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "oom-kill-job3-gbb89.1770b693a39538e4",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "a15333ad-0390-4325-8880-34ef36508831",
|
||||
"resourceVersion": "984546",
|
||||
"creationTimestamp": "2023-07-11T04:43:25Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:43:25Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "oom-kill-job3-gbb89",
|
||||
"uid": "c8d3f094-b80c-4ff8-aff7-f0e624f1bf3a",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "984533",
|
||||
"fieldPath": "spec.containers{memory-eater}"
|
||||
},
|
||||
"reason": "Started",
|
||||
"message": "Started container memory-eater",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T04:43:25Z",
|
||||
"lastTimestamp": "2023-07-11T04:43:25Z",
|
||||
"count": 1,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "oom-kill-job3.1770b6922c53f55c",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "db095cdc-df52-490d-a308-9b283f99eaa6",
|
||||
"resourceVersion": "984534",
|
||||
"creationTimestamp": "2023-07-11T04:43:19Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-controller-manager",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:43:19Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Job",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "oom-kill-job3",
|
||||
"uid": "a85295e8-54e4-40a4-aeb0-b47c19f1a69f",
|
||||
"apiVersion": "batch/v1",
|
||||
"resourceVersion": "984531"
|
||||
},
|
||||
"reason": "SuccessfulCreate",
|
||||
"message": "Created pod: oom-kill-job3-gbb89",
|
||||
"source": {
|
||||
"component": "job-controller"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T04:43:19Z",
|
||||
"lastTimestamp": "2023-07-11T04:43:19Z",
|
||||
"count": 1,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "oom-kill-job3.1770b6944e3d254b",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "fb3ea640-2484-44d5-b65d-698d8e5bd3db",
|
||||
"resourceVersion": "984553",
|
||||
"creationTimestamp": "2023-07-11T04:43:28Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-controller-manager",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:43:28Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Job",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "oom-kill-job3",
|
||||
"uid": "a85295e8-54e4-40a4-aeb0-b47c19f1a69f",
|
||||
"apiVersion": "batch/v1",
|
||||
"resourceVersion": "984551"
|
||||
},
|
||||
"reason": "BackoffLimitExceeded",
|
||||
"message": "Job has reached the specified backoff limit",
|
||||
"source": {
|
||||
"component": "job-controller"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T04:43:28Z",
|
||||
"lastTimestamp": "2023-07-11T04:43:28Z",
|
||||
"count": 1,
|
||||
"type": "Warning",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "pending-pod-resources-5fddcf7688-djjfc.1770b59c8b866b56",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "64330c31-0f5e-46f3-8fb6-ffdac523bb77",
|
||||
"resourceVersion": "984662",
|
||||
"creationTimestamp": "2023-07-11T04:25:44Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-scheduler",
|
||||
"operation": "Update",
|
||||
"apiVersion": "events.k8s.io/v1",
|
||||
"time": "2023-07-11T04:46:01Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:action": {},
|
||||
"f:eventTime": {},
|
||||
"f:note": {},
|
||||
"f:reason": {},
|
||||
"f:regarding": {},
|
||||
"f:reportingController": {},
|
||||
"f:reportingInstance": {},
|
||||
"f:series": {
|
||||
".": {},
|
||||
"f:count": {},
|
||||
"f:lastObservedTime": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "pending-pod-resources-5fddcf7688-djjfc",
|
||||
"uid": "e4d4c4cc-2c41-4ab7-b41c-9c8b28ded451",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "983556"
|
||||
},
|
||||
"reason": "FailedScheduling",
|
||||
"message": "0/1 nodes are available: 1 Insufficient nvidia.com/gpu. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.",
|
||||
"source": {},
|
||||
"firstTimestamp": null,
|
||||
"lastTimestamp": null,
|
||||
"type": "Warning",
|
||||
"eventTime": "2023-07-11T04:25:44.224298Z",
|
||||
"series": {
|
||||
"count": 3,
|
||||
"lastObservedTime": "2023-07-11T04:40:44.264058Z"
|
||||
},
|
||||
"action": "Scheduling",
|
||||
"reportingComponent": "default-scheduler",
|
||||
"reportingInstance": "default-scheduler-minikube"
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "pending-pod-resources-5fddcf7688-djjfc.1770b68fb368b5a6",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "369454df-c891-4e76-96b9-ebdaabe2a6a6",
|
||||
"resourceVersion": "984523",
|
||||
"creationTimestamp": "2023-07-11T04:43:08Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-scheduler",
|
||||
"operation": "Update",
|
||||
"apiVersion": "events.k8s.io/v1",
|
||||
"time": "2023-07-11T04:43:08Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:action": {},
|
||||
"f:eventTime": {},
|
||||
"f:note": {},
|
||||
"f:reason": {},
|
||||
"f:regarding": {},
|
||||
"f:reportingController": {},
|
||||
"f:reportingInstance": {},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"name": "pending-pod-resources-5fddcf7688-djjfc",
|
||||
"uid": "e4d4c4cc-2c41-4ab7-b41c-9c8b28ded451",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "984521"
|
||||
},
|
||||
"reason": "FailedScheduling",
|
||||
"message": "skip schedule deleting pod: default/pending-pod-resources-5fddcf7688-djjfc",
|
||||
"source": {},
|
||||
"firstTimestamp": null,
|
||||
"lastTimestamp": null,
|
||||
"type": "Warning",
|
||||
"eventTime": "2023-07-11T04:43:08.570490Z",
|
||||
"action": "Scheduling",
|
||||
"reportingComponent": "default-scheduler",
|
||||
"reportingInstance": "default-scheduler-minikube"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,928 @@
|
||||
{
|
||||
"kind": "EventList",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"resourceVersion": "982712"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo.1770b2a98cf7b53c",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "cfdfe0af-6dc5-4c6c-a3be-f1532d424ab4",
|
||||
"resourceVersion": "981383",
|
||||
"creationTimestamp": "2023-07-11T03:31:41Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-scheduler",
|
||||
"operation": "Update",
|
||||
"apiVersion": "events.k8s.io/v1",
|
||||
"time": "2023-07-11T03:31:41Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:action": {},
|
||||
"f:eventTime": {},
|
||||
"f:note": {},
|
||||
"f:reason": {},
|
||||
"f:regarding": {},
|
||||
"f:reportingController": {},
|
||||
"f:reportingInstance": {},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo",
|
||||
"uid": "8d27f35a-4b45-4752-a5db-7985ea8b38bf",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "981381"
|
||||
},
|
||||
"reason": "Scheduled",
|
||||
"message": "Successfully assigned default/init-demo to minikube",
|
||||
"source": {},
|
||||
"firstTimestamp": null,
|
||||
"lastTimestamp": null,
|
||||
"type": "Normal",
|
||||
"eventTime": "2023-07-11T03:31:41.548190Z",
|
||||
"action": "Binding",
|
||||
"reportingComponent": "default-scheduler",
|
||||
"reportingInstance": "default-scheduler-minikube"
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo.1770b2a9adf25515",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "091c0f14-75df-47e6-af31-309d8a0b0b86",
|
||||
"resourceVersion": "981479",
|
||||
"creationTimestamp": "2023-07-11T03:31:42Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:33:12Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo",
|
||||
"uid": "8d27f35a-4b45-4752-a5db-7985ea8b38bf",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "981382",
|
||||
"fieldPath": "spec.containers{nginx}"
|
||||
},
|
||||
"reason": "Pulling",
|
||||
"message": "Pulling image \"nginx\"",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T03:31:42Z",
|
||||
"lastTimestamp": "2023-07-11T03:33:12Z",
|
||||
"count": 5,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo.1770b2aa4adc35a4",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "740be04d-5aa8-4bce-ace3-ec61e4d229e0",
|
||||
"resourceVersion": "981389",
|
||||
"creationTimestamp": "2023-07-11T03:31:44Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:31:44Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo",
|
||||
"uid": "8d27f35a-4b45-4752-a5db-7985ea8b38bf",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "981382",
|
||||
"fieldPath": "spec.containers{nginx}"
|
||||
},
|
||||
"reason": "Pulled",
|
||||
"message": "Successfully pulled image \"nginx\" in 2.633289584s",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T03:31:44Z",
|
||||
"lastTimestamp": "2023-07-11T03:31:44Z",
|
||||
"count": 1,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo.1770b2aa4cbb718f",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "a158f7ff-6b95-45db-ac28-2d02ba1d8ca0",
|
||||
"resourceVersion": "981483",
|
||||
"creationTimestamp": "2023-07-11T03:31:44Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:33:15Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo",
|
||||
"uid": "8d27f35a-4b45-4752-a5db-7985ea8b38bf",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "981382",
|
||||
"fieldPath": "spec.containers{nginx}"
|
||||
},
|
||||
"reason": "Created",
|
||||
"message": "Created container nginx",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T03:31:44Z",
|
||||
"lastTimestamp": "2023-07-11T03:33:15Z",
|
||||
"count": 5,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo.1770b2aa50edfd38",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "0fc24955-49dd-4c16-8c7b-c4a3316b1f9d",
|
||||
"resourceVersion": "981484",
|
||||
"creationTimestamp": "2023-07-11T03:31:44Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:33:15Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo",
|
||||
"uid": "8d27f35a-4b45-4752-a5db-7985ea8b38bf",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "981382",
|
||||
"fieldPath": "spec.containers{nginx}"
|
||||
},
|
||||
"reason": "Failed",
|
||||
"message": "Error: failed to start container \"nginx\": Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: \"wge\": executable file not found in $PATH: unknown",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T03:31:44Z",
|
||||
"lastTimestamp": "2023-07-11T03:33:15Z",
|
||||
"count": 5,
|
||||
"type": "Warning",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo.1770b2aae7fb3f76",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "3c489943-7de7-4a1b-a108-1e9d55a2d6a7",
|
||||
"resourceVersion": "981396",
|
||||
"creationTimestamp": "2023-07-11T03:31:47Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:31:47Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo",
|
||||
"uid": "8d27f35a-4b45-4752-a5db-7985ea8b38bf",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "981382",
|
||||
"fieldPath": "spec.containers{nginx}"
|
||||
},
|
||||
"reason": "Pulled",
|
||||
"message": "Successfully pulled image \"nginx\" in 1.952280959s",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T03:31:47Z",
|
||||
"lastTimestamp": "2023-07-11T03:31:47Z",
|
||||
"count": 1,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo.1770b2ae5cec9549",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "793ce261-4cb0-4b2a-9c97-fe1924e534e3",
|
||||
"resourceVersion": "981413",
|
||||
"creationTimestamp": "2023-07-11T03:32:02Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:32:02Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo",
|
||||
"uid": "8d27f35a-4b45-4752-a5db-7985ea8b38bf",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "981382",
|
||||
"fieldPath": "spec.containers{nginx}"
|
||||
},
|
||||
"reason": "Pulled",
|
||||
"message": "Successfully pulled image \"nginx\" in 2.228363585s",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T03:32:02Z",
|
||||
"lastTimestamp": "2023-07-11T03:32:02Z",
|
||||
"count": 1,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo.1770b2b1917b81fa",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "2dbed976-4a5d-42f8-8cd7-8778f2382b7a",
|
||||
"resourceVersion": "982495",
|
||||
"creationTimestamp": "2023-07-11T03:32:15Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:56:47Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo",
|
||||
"uid": "8d27f35a-4b45-4752-a5db-7985ea8b38bf",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "981382",
|
||||
"fieldPath": "spec.containers{nginx}"
|
||||
},
|
||||
"reason": "BackOff",
|
||||
"message": "Back-off restarting failed container",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T03:32:15Z",
|
||||
"lastTimestamp": "2023-07-11T03:56:47Z",
|
||||
"count": 111,
|
||||
"type": "Warning",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo.1770b2b3b6fab35b",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "f3d43cf2-f8b7-428a-b834-4826603b414c",
|
||||
"resourceVersion": "981439",
|
||||
"creationTimestamp": "2023-07-11T03:32:25Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:32:25Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo",
|
||||
"uid": "8d27f35a-4b45-4752-a5db-7985ea8b38bf",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "981382",
|
||||
"fieldPath": "spec.containers{nginx}"
|
||||
},
|
||||
"reason": "Pulled",
|
||||
"message": "Successfully pulled image \"nginx\" in 2.117272501s",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T03:32:25Z",
|
||||
"lastTimestamp": "2023-07-11T03:32:25Z",
|
||||
"count": 1,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo.1770b2bf5bb3f082",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "f9f3da46-c519-43b0-9721-07113e30e2e3",
|
||||
"resourceVersion": "981482",
|
||||
"creationTimestamp": "2023-07-11T03:33:15Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:33:15Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo",
|
||||
"uid": "8d27f35a-4b45-4752-a5db-7985ea8b38bf",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "981382",
|
||||
"fieldPath": "spec.containers{nginx}"
|
||||
},
|
||||
"reason": "Pulled",
|
||||
"message": "Successfully pulled image \"nginx\" in 2.219218792s",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T03:33:15Z",
|
||||
"lastTimestamp": "2023-07-11T03:33:15Z",
|
||||
"count": 1,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo2.1770b446b3f016ce",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "12009dc6-123f-488f-9b1f-412930c8d28d",
|
||||
"resourceVersion": "982692",
|
||||
"creationTimestamp": "2023-07-11T04:01:16Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-scheduler",
|
||||
"operation": "Update",
|
||||
"apiVersion": "events.k8s.io/v1",
|
||||
"time": "2023-07-11T04:01:16Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:action": {},
|
||||
"f:eventTime": {},
|
||||
"f:note": {},
|
||||
"f:reason": {},
|
||||
"f:regarding": {},
|
||||
"f:reportingController": {},
|
||||
"f:reportingInstance": {},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo2",
|
||||
"uid": "5a560e83-c634-4c91-b308-510ee692ed6a",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "982690"
|
||||
},
|
||||
"reason": "Scheduled",
|
||||
"message": "Successfully assigned default/init-demo2 to minikube",
|
||||
"source": {},
|
||||
"firstTimestamp": null,
|
||||
"lastTimestamp": null,
|
||||
"type": "Normal",
|
||||
"eventTime": "2023-07-11T04:01:16.023486Z",
|
||||
"action": "Binding",
|
||||
"reportingComponent": "default-scheduler",
|
||||
"reportingInstance": "default-scheduler-minikube"
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo2.1770b446d40953ed",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "a5ffd925-1612-4178-b5ce-02e981500ee5",
|
||||
"resourceVersion": "982698",
|
||||
"creationTimestamp": "2023-07-11T04:01:16Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:01:16Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo2",
|
||||
"uid": "5a560e83-c634-4c91-b308-510ee692ed6a",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "982691",
|
||||
"fieldPath": "spec.initContainers{install}"
|
||||
},
|
||||
"reason": "Pulled",
|
||||
"message": "Container image \"busybox:1.28\" already present on machine",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T04:01:16Z",
|
||||
"lastTimestamp": "2023-07-11T04:01:16Z",
|
||||
"count": 2,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo2.1770b446d5539074",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "f431bae6-64fb-47ba-81ee-ab4f32122024",
|
||||
"resourceVersion": "982700",
|
||||
"creationTimestamp": "2023-07-11T04:01:16Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:01:16Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo2",
|
||||
"uid": "5a560e83-c634-4c91-b308-510ee692ed6a",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "982691",
|
||||
"fieldPath": "spec.initContainers{install}"
|
||||
},
|
||||
"reason": "Created",
|
||||
"message": "Created container install",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T04:01:16Z",
|
||||
"lastTimestamp": "2023-07-11T04:01:16Z",
|
||||
"count": 2,
|
||||
"type": "Normal",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo2.1770b446d9b392f2",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "9e237187-eb36-4f2d-a139-3c5c450a75e9",
|
||||
"resourceVersion": "982701",
|
||||
"creationTimestamp": "2023-07-11T04:01:16Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:01:17Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo2",
|
||||
"uid": "5a560e83-c634-4c91-b308-510ee692ed6a",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "982691",
|
||||
"fieldPath": "spec.initContainers{install}"
|
||||
},
|
||||
"reason": "Failed",
|
||||
"message": "Error: failed to start container \"install\": Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: \"wge\": executable file not found in $PATH: unknown",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T04:01:16Z",
|
||||
"lastTimestamp": "2023-07-11T04:01:17Z",
|
||||
"count": 2,
|
||||
"type": "Warning",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo2.1770b448549c898a",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "44c503f2-0bd9-46ad-81be-9c2626d3bdf3",
|
||||
"resourceVersion": "982706",
|
||||
"creationTimestamp": "2023-07-11T04:01:23Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:01:23Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "init-demo2",
|
||||
"uid": "5a560e83-c634-4c91-b308-510ee692ed6a",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "982691",
|
||||
"fieldPath": "spec.initContainers{install}"
|
||||
},
|
||||
"reason": "BackOff",
|
||||
"message": "Back-off restarting failed container",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-11T04:01:23Z",
|
||||
"lastTimestamp": "2023-07-11T04:01:23Z",
|
||||
"count": 1,
|
||||
"type": "Warning",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "troubleshoot-copyfromhost-4m79m-psdjm.177058280d4c0493",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "380bec72-7cfd-4a00-85ff-8725c9fc28f9",
|
||||
"resourceVersion": "981372",
|
||||
"creationTimestamp": "2023-07-09T23:53:09Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:31:29Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "troubleshoot-copyfromhost-4m79m-psdjm",
|
||||
"uid": "55ca5d14-2541-42a9-9eb1-5eb53133e178",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "938779"
|
||||
},
|
||||
"reason": "FailedMount",
|
||||
"message": "MountVolume.SetUp failed for volume \"host\" : hostPath type check failed: /var/lib/collectd is not a directory",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-09T23:53:09Z",
|
||||
"lastTimestamp": "2023-07-11T03:31:29Z",
|
||||
"count": 490,
|
||||
"type": "Warning",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
},
|
||||
{
|
||||
"kind": "Event",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "troubleshoot-copyfromhost-4m79m-psdjm.17705863d9fdab48",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "bab0a7d9-61a5-43a1-813c-ed2d050aee34",
|
||||
"resourceVersion": "981185",
|
||||
"creationTimestamp": "2023-07-09T23:57:26Z",
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:27:03Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:count": {},
|
||||
"f:firstTimestamp": {},
|
||||
"f:involvedObject": {},
|
||||
"f:lastTimestamp": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:source": {
|
||||
"f:component": {},
|
||||
"f:host": {}
|
||||
},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"involvedObject": {
|
||||
"kind": "Pod",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"name": "troubleshoot-copyfromhost-4m79m-psdjm",
|
||||
"uid": "55ca5d14-2541-42a9-9eb1-5eb53133e178",
|
||||
"apiVersion": "v1",
|
||||
"resourceVersion": "938779"
|
||||
},
|
||||
"reason": "FailedMount",
|
||||
"message": "Unable to attach or mount volumes: unmounted volumes=[host], unattached volumes=[host kube-api-access-xddvj]: timed out waiting for the condition",
|
||||
"source": {
|
||||
"component": "kubelet",
|
||||
"host": "minikube"
|
||||
},
|
||||
"firstTimestamp": "2023-07-09T23:57:26Z",
|
||||
"lastTimestamp": "2023-07-11T03:27:03Z",
|
||||
"count": 324,
|
||||
"type": "Warning",
|
||||
"eventTime": null,
|
||||
"reportingComponent": "",
|
||||
"reportingInstance": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
{
|
||||
"kind": "PodList",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"resourceVersion": "954528"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Pod",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "troubleshoot-copyfromhost-4m79m-psdjm",
|
||||
"generateName": "troubleshoot-copyfromhost-4m79m-",
|
||||
"namespace": "message-container-creating-failed-mount",
|
||||
"uid": "55ca5d14-2541-42a9-9eb1-5eb53133e178",
|
||||
"resourceVersion": "938782",
|
||||
"creationTimestamp": "2023-07-09T23:53:09Z",
|
||||
"labels": {
|
||||
"app.kubernetes.io/managed-by": "troubleshoot.sh",
|
||||
"controller-revision-hash": "54b4f4f449",
|
||||
"pod-template-generation": "1",
|
||||
"troubleshoot.sh/collector": "copyfromhost",
|
||||
"troubleshoot.sh/copyfromhost-id": "2SAoFNhXU68STgniojM3fJcNQig"
|
||||
},
|
||||
"ownerReferences": [
|
||||
{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "DaemonSet",
|
||||
"name": "troubleshoot-copyfromhost-4m79m",
|
||||
"uid": "ed30f463-4614-41b3-b2b6-52f65e50b1d3",
|
||||
"controller": true,
|
||||
"blockOwnerDeletion": true
|
||||
}
|
||||
],
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-controller-manager",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-09T23:53:09Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:metadata": {
|
||||
"f:generateName": {},
|
||||
"f:labels": {
|
||||
".": {},
|
||||
"f:app.kubernetes.io/managed-by": {},
|
||||
"f:controller-revision-hash": {},
|
||||
"f:pod-template-generation": {},
|
||||
"f:troubleshoot.sh/collector": {},
|
||||
"f:troubleshoot.sh/copyfromhost-id": {}
|
||||
},
|
||||
"f:ownerReferences": {
|
||||
".": {},
|
||||
"k:{\"uid\":\"ed30f463-4614-41b3-b2b6-52f65e50b1d3\"}": {}
|
||||
}
|
||||
},
|
||||
"f:spec": {
|
||||
"f:affinity": {
|
||||
".": {},
|
||||
"f:nodeAffinity": {
|
||||
".": {},
|
||||
"f:requiredDuringSchedulingIgnoredDuringExecution": {}
|
||||
}
|
||||
},
|
||||
"f:containers": {
|
||||
"k:{\"name\":\"collector\"}": {
|
||||
".": {},
|
||||
"f:args": {},
|
||||
"f:command": {},
|
||||
"f:image": {},
|
||||
"f:imagePullPolicy": {},
|
||||
"f:name": {},
|
||||
"f:resources": {},
|
||||
"f:terminationMessagePath": {},
|
||||
"f:terminationMessagePolicy": {},
|
||||
"f:volumeMounts": {
|
||||
".": {},
|
||||
"k:{\"mountPath\":\"/host\"}": {
|
||||
".": {},
|
||||
"f:mountPath": {},
|
||||
"f:name": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"f:dnsPolicy": {},
|
||||
"f:enableServiceLinks": {},
|
||||
"f:restartPolicy": {},
|
||||
"f:schedulerName": {},
|
||||
"f:securityContext": {},
|
||||
"f:terminationGracePeriodSeconds": {},
|
||||
"f:tolerations": {},
|
||||
"f:volumes": {
|
||||
".": {},
|
||||
"k:{\"name\":\"host\"}": {
|
||||
".": {},
|
||||
"f:hostPath": {
|
||||
".": {},
|
||||
"f:path": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"f:name": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-09T23:53:09Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:status": {
|
||||
"f:conditions": {
|
||||
"k:{\"type\":\"ContainersReady\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Initialized\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Ready\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
}
|
||||
},
|
||||
"f:containerStatuses": {},
|
||||
"f:hostIP": {},
|
||||
"f:startTime": {}
|
||||
}
|
||||
},
|
||||
"subresource": "status"
|
||||
}
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"volumes": [
|
||||
{
|
||||
"name": "host",
|
||||
"hostPath": {
|
||||
"path": "/var/lib/collectd",
|
||||
"type": "Directory"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "kube-api-access-xddvj",
|
||||
"projected": {
|
||||
"sources": [
|
||||
{
|
||||
"serviceAccountToken": {
|
||||
"expirationSeconds": 3607,
|
||||
"path": "token"
|
||||
}
|
||||
},
|
||||
{
|
||||
"configMap": {
|
||||
"name": "kube-root-ca.crt",
|
||||
"items": [
|
||||
{
|
||||
"key": "ca.crt",
|
||||
"path": "ca.crt"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"downwardAPI": {
|
||||
"items": [
|
||||
{
|
||||
"path": "namespace",
|
||||
"fieldRef": {
|
||||
"apiVersion": "v1",
|
||||
"fieldPath": "metadata.namespace"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultMode": 420
|
||||
}
|
||||
}
|
||||
],
|
||||
"containers": [
|
||||
{
|
||||
"name": "collector",
|
||||
"image": "alpine",
|
||||
"command": [
|
||||
"sleep"
|
||||
],
|
||||
"args": [
|
||||
"1000000"
|
||||
],
|
||||
"resources": {},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"name": "host",
|
||||
"mountPath": "/host"
|
||||
},
|
||||
{
|
||||
"name": "kube-api-access-xddvj",
|
||||
"readOnly": true,
|
||||
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
|
||||
}
|
||||
],
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "IfNotPresent"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"serviceAccountName": "default",
|
||||
"serviceAccount": "default",
|
||||
"nodeName": "minikube",
|
||||
"securityContext": {},
|
||||
"affinity": {
|
||||
"nodeAffinity": {
|
||||
"requiredDuringSchedulingIgnoredDuringExecution": {
|
||||
"nodeSelectorTerms": [
|
||||
{
|
||||
"matchFields": [
|
||||
{
|
||||
"key": "metadata.name",
|
||||
"operator": "In",
|
||||
"values": [
|
||||
"minikube"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"schedulerName": "default-scheduler",
|
||||
"tolerations": [
|
||||
{
|
||||
"key": "node-role.kubernetes.io/master",
|
||||
"operator": "Exists",
|
||||
"effect": "NoSchedule"
|
||||
},
|
||||
{
|
||||
"key": "node-role.kubernetes.io/control-plane",
|
||||
"operator": "Exists",
|
||||
"effect": "NoSchedule"
|
||||
},
|
||||
{
|
||||
"key": "node.kubernetes.io/not-ready",
|
||||
"operator": "Exists",
|
||||
"effect": "NoExecute"
|
||||
},
|
||||
{
|
||||
"key": "node.kubernetes.io/unreachable",
|
||||
"operator": "Exists",
|
||||
"effect": "NoExecute"
|
||||
},
|
||||
{
|
||||
"key": "node.kubernetes.io/disk-pressure",
|
||||
"operator": "Exists",
|
||||
"effect": "NoSchedule"
|
||||
},
|
||||
{
|
||||
"key": "node.kubernetes.io/memory-pressure",
|
||||
"operator": "Exists",
|
||||
"effect": "NoSchedule"
|
||||
},
|
||||
{
|
||||
"key": "node.kubernetes.io/pid-pressure",
|
||||
"operator": "Exists",
|
||||
"effect": "NoSchedule"
|
||||
},
|
||||
{
|
||||
"key": "node.kubernetes.io/unschedulable",
|
||||
"operator": "Exists",
|
||||
"effect": "NoSchedule"
|
||||
}
|
||||
],
|
||||
"priority": 0,
|
||||
"enableServiceLinks": true,
|
||||
"preemptionPolicy": "PreemptLowerPriority"
|
||||
},
|
||||
"status": {
|
||||
"phase": "Pending",
|
||||
"conditions": [
|
||||
{
|
||||
"type": "Initialized",
|
||||
"status": "True",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-09T23:53:09Z"
|
||||
},
|
||||
{
|
||||
"type": "Ready",
|
||||
"status": "False",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-09T23:53:09Z",
|
||||
"reason": "ContainersNotReady",
|
||||
"message": "containers with unready status: [collector]"
|
||||
},
|
||||
{
|
||||
"type": "ContainersReady",
|
||||
"status": "False",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-09T23:53:09Z",
|
||||
"reason": "ContainersNotReady",
|
||||
"message": "containers with unready status: [collector]"
|
||||
},
|
||||
{
|
||||
"type": "PodScheduled",
|
||||
"status": "True",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-09T23:53:09Z"
|
||||
}
|
||||
],
|
||||
"hostIP": "192.168.49.2",
|
||||
"startTime": "2023-07-09T23:53:09Z",
|
||||
"containerStatuses": [
|
||||
{
|
||||
"name": "collector",
|
||||
"state": {
|
||||
"waiting": {
|
||||
"reason": "ContainerCreating"
|
||||
}
|
||||
},
|
||||
"lastState": {},
|
||||
"ready": false,
|
||||
"restartCount": 0,
|
||||
"image": "alpine",
|
||||
"imageID": "",
|
||||
"started": false
|
||||
}
|
||||
],
|
||||
"qosClass": "BestEffort"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
{
|
||||
"kind": "PodList",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"resourceVersion": "986839"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Pod",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "no-image-deployment-849c4c4958-rxqmt",
|
||||
"generateName": "no-image-deployment-849c4c4958-",
|
||||
"namespace": "message-image-pull-fail",
|
||||
"uid": "209076ce-26d6-4836-981b-8c05cb0081eb",
|
||||
"resourceVersion": "986834",
|
||||
"creationTimestamp": "2023-07-11T05:34:44Z",
|
||||
"labels": {
|
||||
"app": "no-image-deployment",
|
||||
"pod-template-hash": "849c4c4958",
|
||||
"visualize": "true"
|
||||
},
|
||||
"ownerReferences": [
|
||||
{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "ReplicaSet",
|
||||
"name": "no-image-deployment-849c4c4958",
|
||||
"uid": "d992c1a5-d2c4-4065-ac5b-3bb893f9000a",
|
||||
"controller": true,
|
||||
"blockOwnerDeletion": true
|
||||
}
|
||||
],
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-controller-manager",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T05:34:44Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:metadata": {
|
||||
"f:generateName": {},
|
||||
"f:labels": {
|
||||
".": {},
|
||||
"f:app": {},
|
||||
"f:pod-template-hash": {},
|
||||
"f:visualize": {}
|
||||
},
|
||||
"f:ownerReferences": {
|
||||
".": {},
|
||||
"k:{\"uid\":\"d992c1a5-d2c4-4065-ac5b-3bb893f9000a\"}": {}
|
||||
}
|
||||
},
|
||||
"f:spec": {
|
||||
"f:containers": {
|
||||
"k:{\"name\":\"demo-deployment-container\"}": {
|
||||
".": {},
|
||||
"f:image": {},
|
||||
"f:imagePullPolicy": {},
|
||||
"f:name": {},
|
||||
"f:resources": {},
|
||||
"f:terminationMessagePath": {},
|
||||
"f:terminationMessagePolicy": {}
|
||||
}
|
||||
},
|
||||
"f:dnsPolicy": {},
|
||||
"f:enableServiceLinks": {},
|
||||
"f:restartPolicy": {},
|
||||
"f:schedulerName": {},
|
||||
"f:securityContext": {},
|
||||
"f:terminationGracePeriodSeconds": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T05:36:25Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:status": {
|
||||
"f:conditions": {
|
||||
"k:{\"type\":\"ContainersReady\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Initialized\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Ready\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
}
|
||||
},
|
||||
"f:containerStatuses": {},
|
||||
"f:hostIP": {},
|
||||
"f:podIP": {},
|
||||
"f:podIPs": {
|
||||
".": {},
|
||||
"k:{\"ip\":\"172.17.0.3\"}": {
|
||||
".": {},
|
||||
"f:ip": {}
|
||||
}
|
||||
},
|
||||
"f:startTime": {}
|
||||
}
|
||||
},
|
||||
"subresource": "status"
|
||||
}
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"volumes": [
|
||||
{
|
||||
"name": "kube-api-access-5p7hr",
|
||||
"projected": {
|
||||
"sources": [
|
||||
{
|
||||
"serviceAccountToken": {
|
||||
"expirationSeconds": 3607,
|
||||
"path": "token"
|
||||
}
|
||||
},
|
||||
{
|
||||
"configMap": {
|
||||
"name": "kube-root-ca.crt",
|
||||
"items": [
|
||||
{
|
||||
"key": "ca.crt",
|
||||
"path": "ca.crt"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"downwardAPI": {
|
||||
"items": [
|
||||
{
|
||||
"path": "namespace",
|
||||
"fieldRef": {
|
||||
"apiVersion": "v1",
|
||||
"fieldPath": "metadata.namespace"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultMode": 420
|
||||
}
|
||||
}
|
||||
],
|
||||
"containers": [
|
||||
{
|
||||
"name": "demo-deployment-container",
|
||||
"image": "noimage.com/no-such-image",
|
||||
"resources": {},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"name": "kube-api-access-5p7hr",
|
||||
"readOnly": true,
|
||||
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
|
||||
}
|
||||
],
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "IfNotPresent"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"serviceAccountName": "default",
|
||||
"serviceAccount": "default",
|
||||
"nodeName": "minikube",
|
||||
"securityContext": {},
|
||||
"schedulerName": "default-scheduler",
|
||||
"tolerations": [
|
||||
{
|
||||
"key": "node.kubernetes.io/not-ready",
|
||||
"operator": "Exists",
|
||||
"effect": "NoExecute",
|
||||
"tolerationSeconds": 300
|
||||
},
|
||||
{
|
||||
"key": "node.kubernetes.io/unreachable",
|
||||
"operator": "Exists",
|
||||
"effect": "NoExecute",
|
||||
"tolerationSeconds": 300
|
||||
}
|
||||
],
|
||||
"priority": 0,
|
||||
"enableServiceLinks": true,
|
||||
"preemptionPolicy": "PreemptLowerPriority"
|
||||
},
|
||||
"status": {
|
||||
"phase": "Pending",
|
||||
"conditions": [
|
||||
{
|
||||
"type": "Initialized",
|
||||
"status": "True",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T05:34:44Z"
|
||||
},
|
||||
{
|
||||
"type": "Ready",
|
||||
"status": "False",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T05:34:44Z",
|
||||
"reason": "ContainersNotReady",
|
||||
"message": "containers with unready status: [demo-deployment-container]"
|
||||
},
|
||||
{
|
||||
"type": "ContainersReady",
|
||||
"status": "False",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T05:34:44Z",
|
||||
"reason": "ContainersNotReady",
|
||||
"message": "containers with unready status: [demo-deployment-container]"
|
||||
},
|
||||
{
|
||||
"type": "PodScheduled",
|
||||
"status": "True",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T05:34:44Z"
|
||||
}
|
||||
],
|
||||
"hostIP": "192.168.49.2",
|
||||
"podIP": "172.17.0.3",
|
||||
"podIPs": [
|
||||
{
|
||||
"ip": "172.17.0.3"
|
||||
}
|
||||
],
|
||||
"startTime": "2023-07-11T05:34:44Z",
|
||||
"containerStatuses": [
|
||||
{
|
||||
"name": "demo-deployment-container",
|
||||
"state": {
|
||||
"waiting": {
|
||||
"reason": "ErrImagePull",
|
||||
"message": "rpc error: code = Unknown desc = Error response from daemon: Get \"https://noimage.com/v2/\": x509: certificate is not valid for any names, but wanted to match noimage.com"
|
||||
}
|
||||
},
|
||||
"lastState": {},
|
||||
"ready": false,
|
||||
"restartCount": 0,
|
||||
"image": "noimage.com/no-such-image",
|
||||
"imageID": "",
|
||||
"started": false
|
||||
}
|
||||
],
|
||||
"qosClass": "BestEffort"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
{
|
||||
"kind": "PodList",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"resourceVersion": "985854"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Pod",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "oom-kill-job3-gbb89",
|
||||
"generateName": "oom-kill-job3-",
|
||||
"namespace": "message-oomkill-pod",
|
||||
"uid": "c8d3f094-b80c-4ff8-aff7-f0e624f1bf3a",
|
||||
"resourceVersion": "984552",
|
||||
"creationTimestamp": "2023-07-11T04:43:19Z",
|
||||
"labels": {
|
||||
"controller-uid": "a85295e8-54e4-40a4-aeb0-b47c19f1a69f",
|
||||
"job-name": "oom-kill-job3"
|
||||
},
|
||||
"ownerReferences": [
|
||||
{
|
||||
"apiVersion": "batch/v1",
|
||||
"kind": "Job",
|
||||
"name": "oom-kill-job3",
|
||||
"uid": "a85295e8-54e4-40a4-aeb0-b47c19f1a69f",
|
||||
"controller": true,
|
||||
"blockOwnerDeletion": true
|
||||
}
|
||||
],
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-controller-manager",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:43:19Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:metadata": {
|
||||
"f:generateName": {},
|
||||
"f:labels": {
|
||||
".": {},
|
||||
"f:controller-uid": {},
|
||||
"f:job-name": {}
|
||||
},
|
||||
"f:ownerReferences": {
|
||||
".": {},
|
||||
"k:{\"uid\":\"a85295e8-54e4-40a4-aeb0-b47c19f1a69f\"}": {}
|
||||
}
|
||||
},
|
||||
"f:spec": {
|
||||
"f:containers": {
|
||||
"k:{\"name\":\"memory-eater\"}": {
|
||||
".": {},
|
||||
"f:args": {},
|
||||
"f:image": {},
|
||||
"f:imagePullPolicy": {},
|
||||
"f:name": {},
|
||||
"f:resources": {
|
||||
".": {},
|
||||
"f:limits": {
|
||||
".": {},
|
||||
"f:memory": {}
|
||||
},
|
||||
"f:requests": {
|
||||
".": {},
|
||||
"f:memory": {}
|
||||
}
|
||||
},
|
||||
"f:terminationMessagePath": {},
|
||||
"f:terminationMessagePolicy": {}
|
||||
}
|
||||
},
|
||||
"f:dnsPolicy": {},
|
||||
"f:enableServiceLinks": {},
|
||||
"f:restartPolicy": {},
|
||||
"f:schedulerName": {},
|
||||
"f:securityContext": {},
|
||||
"f:terminationGracePeriodSeconds": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:43:28Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:status": {
|
||||
"f:conditions": {
|
||||
"k:{\"type\":\"ContainersReady\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Initialized\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Ready\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
}
|
||||
},
|
||||
"f:containerStatuses": {},
|
||||
"f:hostIP": {},
|
||||
"f:phase": {},
|
||||
"f:podIP": {},
|
||||
"f:podIPs": {
|
||||
".": {},
|
||||
"k:{\"ip\":\"172.17.0.3\"}": {
|
||||
".": {},
|
||||
"f:ip": {}
|
||||
}
|
||||
},
|
||||
"f:startTime": {}
|
||||
}
|
||||
},
|
||||
"subresource": "status"
|
||||
}
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"volumes": [
|
||||
{
|
||||
"name": "kube-api-access-rbt47",
|
||||
"projected": {
|
||||
"sources": [
|
||||
{
|
||||
"serviceAccountToken": {
|
||||
"expirationSeconds": 3607,
|
||||
"path": "token"
|
||||
}
|
||||
},
|
||||
{
|
||||
"configMap": {
|
||||
"name": "kube-root-ca.crt",
|
||||
"items": [
|
||||
{
|
||||
"key": "ca.crt",
|
||||
"path": "ca.crt"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"downwardAPI": {
|
||||
"items": [
|
||||
{
|
||||
"path": "namespace",
|
||||
"fieldRef": {
|
||||
"apiVersion": "v1",
|
||||
"fieldPath": "metadata.namespace"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultMode": 420
|
||||
}
|
||||
}
|
||||
],
|
||||
"containers": [
|
||||
{
|
||||
"name": "memory-eater",
|
||||
"image": "us-central1-docker.pkg.dev/genuine-flight-317411/devel/memory-eater:1.0",
|
||||
"args": [
|
||||
"75Mi",
|
||||
"0",
|
||||
"30Mi",
|
||||
"80",
|
||||
"1"
|
||||
],
|
||||
"resources": {
|
||||
"limits": {
|
||||
"memory": "100Mi"
|
||||
},
|
||||
"requests": {
|
||||
"memory": "100Mi"
|
||||
}
|
||||
},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"name": "kube-api-access-rbt47",
|
||||
"readOnly": true,
|
||||
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
|
||||
}
|
||||
],
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Never",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"serviceAccountName": "default",
|
||||
"serviceAccount": "default",
|
||||
"nodeName": "minikube",
|
||||
"securityContext": {},
|
||||
"schedulerName": "default-scheduler",
|
||||
"tolerations": [
|
||||
{
|
||||
"key": "node.kubernetes.io/not-ready",
|
||||
"operator": "Exists",
|
||||
"effect": "NoExecute",
|
||||
"tolerationSeconds": 300
|
||||
},
|
||||
{
|
||||
"key": "node.kubernetes.io/unreachable",
|
||||
"operator": "Exists",
|
||||
"effect": "NoExecute",
|
||||
"tolerationSeconds": 300
|
||||
}
|
||||
],
|
||||
"priority": 0,
|
||||
"enableServiceLinks": true,
|
||||
"preemptionPolicy": "PreemptLowerPriority"
|
||||
},
|
||||
"status": {
|
||||
"phase": "Failed",
|
||||
"conditions": [
|
||||
{
|
||||
"type": "Initialized",
|
||||
"status": "True",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T04:43:19Z"
|
||||
},
|
||||
{
|
||||
"type": "Ready",
|
||||
"status": "False",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T04:43:19Z",
|
||||
"reason": "PodFailed"
|
||||
},
|
||||
{
|
||||
"type": "ContainersReady",
|
||||
"status": "False",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T04:43:19Z",
|
||||
"reason": "PodFailed"
|
||||
},
|
||||
{
|
||||
"type": "PodScheduled",
|
||||
"status": "True",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T04:43:19Z"
|
||||
}
|
||||
],
|
||||
"hostIP": "192.168.49.2",
|
||||
"podIP": "172.17.0.3",
|
||||
"podIPs": [
|
||||
{
|
||||
"ip": "172.17.0.3"
|
||||
}
|
||||
],
|
||||
"startTime": "2023-07-11T04:43:19Z",
|
||||
"containerStatuses": [
|
||||
{
|
||||
"name": "memory-eater",
|
||||
"state": {
|
||||
"terminated": {
|
||||
"exitCode": 137,
|
||||
"reason": "OOMKilled",
|
||||
"startedAt": "2023-07-11T04:43:25Z",
|
||||
"finishedAt": "2023-07-11T04:43:25Z",
|
||||
"containerID": "docker://84d3d308bb9a5765e637884194e718d297046f156a377cf6145a9397d5e469b7"
|
||||
}
|
||||
},
|
||||
"lastState": {},
|
||||
"ready": false,
|
||||
"restartCount": 0,
|
||||
"image": "us-central1-docker.pkg.dev/genuine-flight-317411/devel/memory-eater:1.0",
|
||||
"imageID": "docker-pullable://us-central1-docker.pkg.dev/genuine-flight-317411/devel/memory-eater@sha256:b99901aacc6c87f86a767b3a0b02cdc926aae62bb4a5e7361d895f26f4773562",
|
||||
"containerID": "docker://84d3d308bb9a5765e637884194e718d297046f156a377cf6145a9397d5e469b7",
|
||||
"started": false
|
||||
}
|
||||
],
|
||||
"qosClass": "Burstable"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"kind": "PodList",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"resourceVersion": "984246"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Pod",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "pending-pod-resources-5fddcf7688-djjfc",
|
||||
"generateName": "pending-pod-resources-5fddcf7688-",
|
||||
"namespace": "message-pending-pod-resources",
|
||||
"uid": "e4d4c4cc-2c41-4ab7-b41c-9c8b28ded451",
|
||||
"resourceVersion": "983556",
|
||||
"creationTimestamp": "2023-07-11T04:20:29Z",
|
||||
"labels": {
|
||||
"app": "pending-pod-resources",
|
||||
"pod-template-hash": "5fddcf7688"
|
||||
},
|
||||
"ownerReferences": [
|
||||
{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "ReplicaSet",
|
||||
"name": "pending-pod-resources-5fddcf7688",
|
||||
"uid": "f57de73f-8492-4118-b4da-b0f9d7408c9b",
|
||||
"controller": true,
|
||||
"blockOwnerDeletion": true
|
||||
}
|
||||
],
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-controller-manager",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:20:29Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:metadata": {
|
||||
"f:generateName": {},
|
||||
"f:labels": {
|
||||
".": {},
|
||||
"f:app": {},
|
||||
"f:pod-template-hash": {}
|
||||
},
|
||||
"f:ownerReferences": {
|
||||
".": {},
|
||||
"k:{\"uid\":\"f57de73f-8492-4118-b4da-b0f9d7408c9b\"}": {}
|
||||
}
|
||||
},
|
||||
"f:spec": {
|
||||
"f:containers": {
|
||||
"k:{\"name\":\"nginx\"}": {
|
||||
".": {},
|
||||
"f:image": {},
|
||||
"f:imagePullPolicy": {},
|
||||
"f:name": {},
|
||||
"f:resources": {
|
||||
".": {},
|
||||
"f:limits": {
|
||||
".": {},
|
||||
"f:cpu": {},
|
||||
"f:memory": {},
|
||||
"f:nvidia.com/gpu": {}
|
||||
},
|
||||
"f:requests": {
|
||||
".": {},
|
||||
"f:cpu": {},
|
||||
"f:memory": {},
|
||||
"f:nvidia.com/gpu": {}
|
||||
}
|
||||
},
|
||||
"f:terminationMessagePath": {},
|
||||
"f:terminationMessagePolicy": {}
|
||||
}
|
||||
},
|
||||
"f:dnsPolicy": {},
|
||||
"f:enableServiceLinks": {},
|
||||
"f:restartPolicy": {},
|
||||
"f:schedulerName": {},
|
||||
"f:securityContext": {},
|
||||
"f:terminationGracePeriodSeconds": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"manager": "kube-scheduler",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:20:29Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:status": {
|
||||
"f:conditions": {
|
||||
".": {},
|
||||
"k:{\"type\":\"PodScheduled\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subresource": "status"
|
||||
}
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"volumes": [
|
||||
{
|
||||
"name": "kube-api-access-sjnlv",
|
||||
"projected": {
|
||||
"sources": [
|
||||
{
|
||||
"serviceAccountToken": {
|
||||
"expirationSeconds": 3607,
|
||||
"path": "token"
|
||||
}
|
||||
},
|
||||
{
|
||||
"configMap": {
|
||||
"name": "kube-root-ca.crt",
|
||||
"items": [
|
||||
{
|
||||
"key": "ca.crt",
|
||||
"path": "ca.crt"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"downwardAPI": {
|
||||
"items": [
|
||||
{
|
||||
"path": "namespace",
|
||||
"fieldRef": {
|
||||
"apiVersion": "v1",
|
||||
"fieldPath": "metadata.namespace"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultMode": 420
|
||||
}
|
||||
}
|
||||
],
|
||||
"containers": [
|
||||
{
|
||||
"name": "nginx",
|
||||
"image": "nginx",
|
||||
"resources": {
|
||||
"limits": {
|
||||
"cpu": "3",
|
||||
"memory": "5Gi",
|
||||
"nvidia.com/gpu": "5"
|
||||
},
|
||||
"requests": {
|
||||
"cpu": "3",
|
||||
"memory": "5Gi",
|
||||
"nvidia.com/gpu": "5"
|
||||
}
|
||||
},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"name": "kube-api-access-sjnlv",
|
||||
"readOnly": true,
|
||||
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
|
||||
}
|
||||
],
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "IfNotPresent"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"serviceAccountName": "default",
|
||||
"serviceAccount": "default",
|
||||
"securityContext": {},
|
||||
"schedulerName": "default-scheduler",
|
||||
"tolerations": [
|
||||
{
|
||||
"key": "node.kubernetes.io/not-ready",
|
||||
"operator": "Exists",
|
||||
"effect": "NoExecute",
|
||||
"tolerationSeconds": 300
|
||||
},
|
||||
{
|
||||
"key": "node.kubernetes.io/unreachable",
|
||||
"operator": "Exists",
|
||||
"effect": "NoExecute",
|
||||
"tolerationSeconds": 300
|
||||
}
|
||||
],
|
||||
"priority": 0,
|
||||
"enableServiceLinks": true,
|
||||
"preemptionPolicy": "PreemptLowerPriority"
|
||||
},
|
||||
"status": {
|
||||
"phase": "Pending",
|
||||
"conditions": [
|
||||
{
|
||||
"type": "PodScheduled",
|
||||
"status": "False",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T04:20:29Z",
|
||||
"reason": "Unschedulable",
|
||||
"message": "0/1 nodes are available: 1 Insufficient nvidia.com/gpu. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod."
|
||||
}
|
||||
],
|
||||
"qosClass": "Guaranteed"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
{
|
||||
"kind": "PodList",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"resourceVersion": "981519"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Pod",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo",
|
||||
"namespace": "message-pod-crashloop-backoff",
|
||||
"uid": "8d27f35a-4b45-4752-a5db-7985ea8b38bf",
|
||||
"resourceVersion": "981491",
|
||||
"creationTimestamp": "2023-07-11T03:31:41Z",
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"init-demo\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"command\":[\"wge\",\"-O\",\"/work-dir/index.html\",\"https://home.robusta.dev\"],\"image\":\"nginx\",\"name\":\"nginx\",\"ports\":[{\"containerPort\":80}]}]}}\n"
|
||||
},
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubectl-client-side-apply",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:31:41Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:metadata": {
|
||||
"f:annotations": {
|
||||
".": {},
|
||||
"f:kubectl.kubernetes.io/last-applied-configuration": {}
|
||||
}
|
||||
},
|
||||
"f:spec": {
|
||||
"f:containers": {
|
||||
"k:{\"name\":\"nginx\"}": {
|
||||
".": {},
|
||||
"f:command": {},
|
||||
"f:image": {},
|
||||
"f:imagePullPolicy": {},
|
||||
"f:name": {},
|
||||
"f:ports": {
|
||||
".": {},
|
||||
"k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {
|
||||
".": {},
|
||||
"f:containerPort": {},
|
||||
"f:protocol": {}
|
||||
}
|
||||
},
|
||||
"f:resources": {},
|
||||
"f:terminationMessagePath": {},
|
||||
"f:terminationMessagePolicy": {}
|
||||
}
|
||||
},
|
||||
"f:dnsPolicy": {},
|
||||
"f:enableServiceLinks": {},
|
||||
"f:restartPolicy": {},
|
||||
"f:schedulerName": {},
|
||||
"f:securityContext": {},
|
||||
"f:terminationGracePeriodSeconds": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T03:33:23Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:status": {
|
||||
"f:conditions": {
|
||||
"k:{\"type\":\"ContainersReady\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Initialized\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Ready\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
}
|
||||
},
|
||||
"f:containerStatuses": {},
|
||||
"f:hostIP": {},
|
||||
"f:phase": {},
|
||||
"f:podIP": {},
|
||||
"f:podIPs": {
|
||||
".": {},
|
||||
"k:{\"ip\":\"172.17.0.3\"}": {
|
||||
".": {},
|
||||
"f:ip": {}
|
||||
}
|
||||
},
|
||||
"f:startTime": {}
|
||||
}
|
||||
},
|
||||
"subresource": "status"
|
||||
}
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"volumes": [
|
||||
{
|
||||
"name": "kube-api-access-htb9q",
|
||||
"projected": {
|
||||
"sources": [
|
||||
{
|
||||
"serviceAccountToken": {
|
||||
"expirationSeconds": 3607,
|
||||
"path": "token"
|
||||
}
|
||||
},
|
||||
{
|
||||
"configMap": {
|
||||
"name": "kube-root-ca.crt",
|
||||
"items": [
|
||||
{
|
||||
"key": "ca.crt",
|
||||
"path": "ca.crt"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"downwardAPI": {
|
||||
"items": [
|
||||
{
|
||||
"path": "namespace",
|
||||
"fieldRef": {
|
||||
"apiVersion": "v1",
|
||||
"fieldPath": "metadata.namespace"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultMode": 420
|
||||
}
|
||||
}
|
||||
],
|
||||
"containers": [
|
||||
{
|
||||
"name": "nginx",
|
||||
"image": "nginx",
|
||||
"command": [
|
||||
"wge",
|
||||
"-O",
|
||||
"/work-dir/index.html",
|
||||
"https://home.robusta.dev"
|
||||
],
|
||||
"ports": [
|
||||
{
|
||||
"containerPort": 80,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"name": "kube-api-access-htb9q",
|
||||
"readOnly": true,
|
||||
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
|
||||
}
|
||||
],
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"serviceAccountName": "default",
|
||||
"serviceAccount": "default",
|
||||
"nodeName": "minikube",
|
||||
"securityContext": {},
|
||||
"schedulerName": "default-scheduler",
|
||||
"tolerations": [
|
||||
{
|
||||
"key": "node.kubernetes.io/not-ready",
|
||||
"operator": "Exists",
|
||||
"effect": "NoExecute",
|
||||
"tolerationSeconds": 300
|
||||
},
|
||||
{
|
||||
"key": "node.kubernetes.io/unreachable",
|
||||
"operator": "Exists",
|
||||
"effect": "NoExecute",
|
||||
"tolerationSeconds": 300
|
||||
}
|
||||
],
|
||||
"priority": 0,
|
||||
"enableServiceLinks": true,
|
||||
"preemptionPolicy": "PreemptLowerPriority"
|
||||
},
|
||||
"status": {
|
||||
"phase": "Running",
|
||||
"conditions": [
|
||||
{
|
||||
"type": "Initialized",
|
||||
"status": "True",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T03:31:41Z"
|
||||
},
|
||||
{
|
||||
"type": "Ready",
|
||||
"status": "False",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T03:31:41Z",
|
||||
"reason": "ContainersNotReady",
|
||||
"message": "containers with unready status: [nginx]"
|
||||
},
|
||||
{
|
||||
"type": "ContainersReady",
|
||||
"status": "False",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T03:31:41Z",
|
||||
"reason": "ContainersNotReady",
|
||||
"message": "containers with unready status: [nginx]"
|
||||
},
|
||||
{
|
||||
"type": "PodScheduled",
|
||||
"status": "True",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T03:31:41Z"
|
||||
}
|
||||
],
|
||||
"hostIP": "192.168.49.2",
|
||||
"podIP": "172.17.0.3",
|
||||
"podIPs": [
|
||||
{
|
||||
"ip": "172.17.0.3"
|
||||
}
|
||||
],
|
||||
"startTime": "2023-07-11T03:31:41Z",
|
||||
"containerStatuses": [
|
||||
{
|
||||
"name": "nginx",
|
||||
"state": {
|
||||
"waiting": {
|
||||
"reason": "CrashLoopBackOff",
|
||||
"message": "back-off 1m20s restarting failed container=nginx pod=init-demo_default(8d27f35a-4b45-4752-a5db-7985ea8b38bf)"
|
||||
}
|
||||
},
|
||||
"lastState": {
|
||||
"terminated": {
|
||||
"exitCode": 127,
|
||||
"reason": "ContainerCannotRun",
|
||||
"message": "failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: \"wge\": executable file not found in $PATH: unknown",
|
||||
"startedAt": "2023-07-11T03:33:15Z",
|
||||
"finishedAt": "2023-07-11T03:33:15Z",
|
||||
"containerID": "docker://c04bcccbdc5eee8f0fc1b88c85c9b31e79f963dbee75fda97c022938222c8d90"
|
||||
}
|
||||
},
|
||||
"ready": false,
|
||||
"restartCount": 4,
|
||||
"image": "nginx:latest",
|
||||
"imageID": "docker-pullable://nginx@sha256:08bc36ad52474e528cc1ea3426b5e3f4bad8a130318e3140d6cfe29c8892c7ef",
|
||||
"containerID": "docker://c04bcccbdc5eee8f0fc1b88c85c9b31e79f963dbee75fda97c022938222c8d90",
|
||||
"started": false
|
||||
}
|
||||
],
|
||||
"qosClass": "BestEffort"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
{
|
||||
"kind": "PodList",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"resourceVersion": "982711"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Pod",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "init-demo2",
|
||||
"namespace": "message-pod-init-crashloop-backoff",
|
||||
"uid": "5a560e83-c634-4c91-b308-510ee692ed6a",
|
||||
"resourceVersion": "982707",
|
||||
"creationTimestamp": "2023-07-11T04:01:16Z",
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"init-demo2\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx\",\"name\":\"nginx\",\"ports\":[{\"containerPort\":80}],\"volumeMounts\":[{\"mountPath\":\"/usr/share/nginx/html\",\"name\":\"workdir\"}]}],\"dnsPolicy\":\"Default\",\"initContainers\":[{\"command\":[\"wge\",\"-O\",\"/work-dir/index.html\",\"https://home.robusta.dev\"],\"image\":\"busybox:1.28\",\"name\":\"install\",\"volumeMounts\":[{\"mountPath\":\"/work-dir\",\"name\":\"workdir\"}]}],\"volumes\":[{\"emptyDir\":{},\"name\":\"workdir\"}]}}\n"
|
||||
},
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kubectl-client-side-apply",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:01:16Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:metadata": {
|
||||
"f:annotations": {
|
||||
".": {},
|
||||
"f:kubectl.kubernetes.io/last-applied-configuration": {}
|
||||
}
|
||||
},
|
||||
"f:spec": {
|
||||
"f:containers": {
|
||||
"k:{\"name\":\"nginx\"}": {
|
||||
".": {},
|
||||
"f:image": {},
|
||||
"f:imagePullPolicy": {},
|
||||
"f:name": {},
|
||||
"f:ports": {
|
||||
".": {},
|
||||
"k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {
|
||||
".": {},
|
||||
"f:containerPort": {},
|
||||
"f:protocol": {}
|
||||
}
|
||||
},
|
||||
"f:resources": {},
|
||||
"f:terminationMessagePath": {},
|
||||
"f:terminationMessagePolicy": {},
|
||||
"f:volumeMounts": {
|
||||
".": {},
|
||||
"k:{\"mountPath\":\"/usr/share/nginx/html\"}": {
|
||||
".": {},
|
||||
"f:mountPath": {},
|
||||
"f:name": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"f:dnsPolicy": {},
|
||||
"f:enableServiceLinks": {},
|
||||
"f:initContainers": {
|
||||
".": {},
|
||||
"k:{\"name\":\"install\"}": {
|
||||
".": {},
|
||||
"f:command": {},
|
||||
"f:image": {},
|
||||
"f:imagePullPolicy": {},
|
||||
"f:name": {},
|
||||
"f:resources": {},
|
||||
"f:terminationMessagePath": {},
|
||||
"f:terminationMessagePolicy": {},
|
||||
"f:volumeMounts": {
|
||||
".": {},
|
||||
"k:{\"mountPath\":\"/work-dir\"}": {
|
||||
".": {},
|
||||
"f:mountPath": {},
|
||||
"f:name": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"f:restartPolicy": {},
|
||||
"f:schedulerName": {},
|
||||
"f:securityContext": {},
|
||||
"f:terminationGracePeriodSeconds": {},
|
||||
"f:volumes": {
|
||||
".": {},
|
||||
"k:{\"name\":\"workdir\"}": {
|
||||
".": {},
|
||||
"f:emptyDir": {},
|
||||
"f:name": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-07-11T04:01:23Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:status": {
|
||||
"f:conditions": {
|
||||
"k:{\"type\":\"ContainersReady\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Initialized\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Ready\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:message": {},
|
||||
"f:reason": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
}
|
||||
},
|
||||
"f:containerStatuses": {},
|
||||
"f:hostIP": {},
|
||||
"f:initContainerStatuses": {},
|
||||
"f:podIP": {},
|
||||
"f:podIPs": {
|
||||
".": {},
|
||||
"k:{\"ip\":\"172.17.0.3\"}": {
|
||||
".": {},
|
||||
"f:ip": {}
|
||||
}
|
||||
},
|
||||
"f:startTime": {}
|
||||
}
|
||||
},
|
||||
"subresource": "status"
|
||||
}
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"volumes": [
|
||||
{
|
||||
"name": "workdir",
|
||||
"emptyDir": {}
|
||||
},
|
||||
{
|
||||
"name": "kube-api-access-w4mrq",
|
||||
"projected": {
|
||||
"sources": [
|
||||
{
|
||||
"serviceAccountToken": {
|
||||
"expirationSeconds": 3607,
|
||||
"path": "token"
|
||||
}
|
||||
},
|
||||
{
|
||||
"configMap": {
|
||||
"name": "kube-root-ca.crt",
|
||||
"items": [
|
||||
{
|
||||
"key": "ca.crt",
|
||||
"path": "ca.crt"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"downwardAPI": {
|
||||
"items": [
|
||||
{
|
||||
"path": "namespace",
|
||||
"fieldRef": {
|
||||
"apiVersion": "v1",
|
||||
"fieldPath": "metadata.namespace"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultMode": 420
|
||||
}
|
||||
}
|
||||
],
|
||||
"initContainers": [
|
||||
{
|
||||
"name": "install",
|
||||
"image": "busybox:1.28",
|
||||
"command": [
|
||||
"wge",
|
||||
"-O",
|
||||
"/work-dir/index.html",
|
||||
"https://home.robusta.dev"
|
||||
],
|
||||
"resources": {},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"name": "workdir",
|
||||
"mountPath": "/work-dir"
|
||||
},
|
||||
{
|
||||
"name": "kube-api-access-w4mrq",
|
||||
"readOnly": true,
|
||||
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
|
||||
}
|
||||
],
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "IfNotPresent"
|
||||
}
|
||||
],
|
||||
"containers": [
|
||||
{
|
||||
"name": "nginx",
|
||||
"image": "nginx",
|
||||
"ports": [
|
||||
{
|
||||
"containerPort": 80,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"name": "workdir",
|
||||
"mountPath": "/usr/share/nginx/html"
|
||||
},
|
||||
{
|
||||
"name": "kube-api-access-w4mrq",
|
||||
"readOnly": true,
|
||||
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
|
||||
}
|
||||
],
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "Default",
|
||||
"serviceAccountName": "default",
|
||||
"serviceAccount": "default",
|
||||
"nodeName": "minikube",
|
||||
"securityContext": {},
|
||||
"schedulerName": "default-scheduler",
|
||||
"tolerations": [
|
||||
{
|
||||
"key": "node.kubernetes.io/not-ready",
|
||||
"operator": "Exists",
|
||||
"effect": "NoExecute",
|
||||
"tolerationSeconds": 300
|
||||
},
|
||||
{
|
||||
"key": "node.kubernetes.io/unreachable",
|
||||
"operator": "Exists",
|
||||
"effect": "NoExecute",
|
||||
"tolerationSeconds": 300
|
||||
}
|
||||
],
|
||||
"priority": 0,
|
||||
"enableServiceLinks": true,
|
||||
"preemptionPolicy": "PreemptLowerPriority"
|
||||
},
|
||||
"status": {
|
||||
"phase": "Pending",
|
||||
"conditions": [
|
||||
{
|
||||
"type": "Initialized",
|
||||
"status": "False",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T04:01:16Z",
|
||||
"reason": "ContainersNotInitialized",
|
||||
"message": "containers with incomplete status: [install]"
|
||||
},
|
||||
{
|
||||
"type": "Ready",
|
||||
"status": "False",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T04:01:16Z",
|
||||
"reason": "ContainersNotReady",
|
||||
"message": "containers with unready status: [nginx]"
|
||||
},
|
||||
{
|
||||
"type": "ContainersReady",
|
||||
"status": "False",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T04:01:16Z",
|
||||
"reason": "ContainersNotReady",
|
||||
"message": "containers with unready status: [nginx]"
|
||||
},
|
||||
{
|
||||
"type": "PodScheduled",
|
||||
"status": "True",
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-07-11T04:01:16Z"
|
||||
}
|
||||
],
|
||||
"hostIP": "192.168.49.2",
|
||||
"podIP": "172.17.0.3",
|
||||
"podIPs": [
|
||||
{
|
||||
"ip": "172.17.0.3"
|
||||
}
|
||||
],
|
||||
"startTime": "2023-07-11T04:01:16Z",
|
||||
"initContainerStatuses": [
|
||||
{
|
||||
"name": "install",
|
||||
"state": {
|
||||
"waiting": {
|
||||
"reason": "RunContainerError",
|
||||
"message": "failed to start container \"b201927f10154b9ab9913f65aa13b46e5eeed74b8efff500d11e3f08af09621b\": Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: \"wge\": executable file not found in $PATH: unknown"
|
||||
}
|
||||
},
|
||||
"lastState": {
|
||||
"terminated": {
|
||||
"exitCode": 127,
|
||||
"reason": "ContainerCannotRun",
|
||||
"message": "failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: \"wge\": executable file not found in $PATH: unknown",
|
||||
"startedAt": "2023-07-11T04:01:16Z",
|
||||
"finishedAt": "2023-07-11T04:01:16Z",
|
||||
"containerID": "docker://b201927f10154b9ab9913f65aa13b46e5eeed74b8efff500d11e3f08af09621b"
|
||||
}
|
||||
},
|
||||
"ready": false,
|
||||
"restartCount": 1,
|
||||
"image": "busybox:1.28",
|
||||
"imageID": "docker-pullable://busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47",
|
||||
"containerID": "docker://b201927f10154b9ab9913f65aa13b46e5eeed74b8efff500d11e3f08af09621b"
|
||||
}
|
||||
],
|
||||
"containerStatuses": [
|
||||
{
|
||||
"name": "nginx",
|
||||
"state": {
|
||||
"waiting": {
|
||||
"reason": "PodInitializing"
|
||||
}
|
||||
},
|
||||
"lastState": {},
|
||||
"ready": false,
|
||||
"restartCount": 0,
|
||||
"image": "nginx",
|
||||
"imageID": "",
|
||||
"started": false
|
||||
}
|
||||
],
|
||||
"qosClass": "BestEffort"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"kind": "ConfigMapList",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"resourceVersion": "4825753"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "ConfigMap",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "kube-root-ca.crt",
|
||||
"namespace": "kube-public",
|
||||
"uid": "37a912b2-a666-480f-a1a8-05ab012432e4",
|
||||
"resourceVersion": "332",
|
||||
"creationTimestamp": "2023-05-29T23:33:07Z",
|
||||
"annotations": {
|
||||
"kubernetes.io/description": "Contains a CA bundle that can be used to verify the kube-apiserver when using internal endpoints such as the internal service IP or kubernetes.default.svc. No other usage is guaranteed across distributions of Kubernetes clusters."
|
||||
},
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "kube-controller-manager",
|
||||
"operation": "Update",
|
||||
"apiVersion": "v1",
|
||||
"time": "2023-05-29T23:33:07Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:data": {
|
||||
".": {},
|
||||
"f:ca.crt": {}
|
||||
},
|
||||
"f:metadata": {
|
||||
"f:annotations": {
|
||||
".": {},
|
||||
"f:kubernetes.io/description": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"ca.crt": "-----BEGIN CERTIFICATE-----\nMIIDBjCCAe6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwptaW5p\na3ViZUNBMB4XDTIzMDUyODIzMzIzOVoXDTMzMDUyNjIzMzIzOVowFTETMBEGA1UE\nAxMKbWluaWt1YmVDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOpU\nswd15tD+QCelgYlu5MRB260+Ke63gEDTkXIyAr+mR+TjW/v9TPPIP9iidvTUg+jq\nryrf1PwURVx3EUSZWtd9NpAEDT1ov/ggx16xxj2El7KLen0SXQutSF28zjCFXxYG\nMkPxXu+qYsn2mLJX5i1kaCZNffyToCJ0n2bxxx83rOS+fgz11JntAwcgC8V3Mtq6\nIv+2Xb9PSxPs38ef7r15j5KSTOrmWCR5texPFz/WU/YbZ3W42pj9T/EiuGwamfmI\ngevuwv7AxlfjKutp5UQEth5GhY6V4kJyVIUExN3ddEsTPLQD9zvdsP4DlkGpZmiR\n/Ip3rXY/ldxeeGZ0HUUCAwEAAaNhMF8wDgYDVR0PAQH/BAQDAgKkMB0GA1UdJQQW\nMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW\nBBS0XUXhCu/DOH8/0W5L0n4BOw9b6zANBgkqhkiG9w0BAQsFAAOCAQEAHrXOxKdy\n+DvPtvgfzzV3qqmIvTuuFyW0BdZdXV8yo1tZajkEO0B0HvLUyn8ljKgoK5YtCQcr\nzSj6QEewgP+JCBTsWCKzbOhMcDKw1pa6bSeLcQWwMxox+1Zcj7edMPlPcQ3SVLxZ\n6y7fD7BArTKRBKCr8Uwudwox5Vm0URWLRAvb+8jPv9BDuC/uMPJ4UrexL/Q2QQs8\nQkyWYeSk4mBCM3qAahQhYc0WSHbk+a/5iua/y+VUaa208CUbm5glBoAroHDk5eTN\nYstOSLUQAlzTdr4kCEVi+a3+NgmuvzYXWubAdy/PT860aFBJlmVNuDhy8V/bGpnS\nKxmG7B3yJmbmzg==\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user