Compare commits

...
154 Commits
Author SHA1 Message Date
divolgin 4e74fd209e Merge pull request #293 from replicatedhq/divolgin/fix
fix build
2020-11-10 22:00:21 -08:00
divolgin a00a1892d9 include darwin builds in archives 2020-11-11 02:10:30 +00:00
divolgin 474a874f82 Merge pull request #292 from replicatedhq/divolgin/fix
fixing build
2020-11-10 16:38:49 -08:00
divolgin 463ba62ffa fixing build 2020-11-11 00:26:53 +00:00
divolgin 46194fccb7 Merge pull request #291 from replicatedhq/divolgin/fix
fixing build
2020-11-10 14:59:59 -08:00
divolgin badedf34ee fixing build 2020-11-10 22:55:21 +00:00
divolgin af2f49cb07 Merge pull request #271 from manavellamnimble/fixImagePullSecretAndTextAnalyzer
ImagePullSecret and TextAnalyzer with regex fixed
2020-11-10 09:40:10 -08:00
divolgin 88a6d3f715 Merge pull request #290 from replicatedhq/divolgin/rrd
Collector for RRD data
2020-11-10 09:19:46 -08:00
divolgin 5a1321da02 Collector and analyzer for RRD data 2020-11-10 17:19:17 +00:00
divolgin 014a716949 Merge pull request #287 from manavellamnimble/addLogFlags
added flag --since-time for Logs Collectors
2020-10-29 15:20:10 -07:00
Matias Manavella ad4893bec5 default message 2020-10-29 18:19:32 -03:00
Matias Manavella 989093394c test fixed 2020-10-29 17:47:37 -03:00
Matias Manavella f1868d0ba8 return messages modified 2020-10-22 14:42:05 -03:00
Matias Manavella 3cbdb41c8f return error when both flags are used 2020-10-22 14:35:08 -03:00
Matias Manavella d2f6594c0c return error when both flags are used 2020-10-22 14:31:34 -03:00
Matias Manavella 56408fab01 return error when both flags are used 2020-10-22 14:28:35 -03:00
Matias Manavella f0d9418e21 parseTimeFlag function and error handling added 2020-10-22 12:26:29 -03:00
Matias Manavella 1ba3840ad9 parseTimeFlag function and error handling added 2020-10-22 11:37:52 -03:00
Matias ManavellaandMark Pundsack a3d667298e Update cmd/preflight/cli/root.go
Co-authored-by: Mark Pundsack <markpundsack@users.noreply.github.com>
2020-10-22 10:13:45 -03:00
Matias ManavellaandMark Pundsack 5cf4ae2157 Update cmd/preflight/cli/root.go
Co-authored-by: Mark Pundsack <markpundsack@users.noreply.github.com>
2020-10-22 10:13:32 -03:00
Matias ManavellaandMark Pundsack dede187841 Update cmd/troubleshoot/cli/root.go
Co-authored-by: Mark Pundsack <markpundsack@users.noreply.github.com>
2020-10-22 09:26:29 -03:00
Matias ManavellaandMark Pundsack 7dc6e22d8d Update cmd/troubleshoot/cli/root.go
Co-authored-by: Mark Pundsack <markpundsack@users.noreply.github.com>
2020-10-22 09:26:11 -03:00
Matias Manavella f3af73b6e3 nil and error handling 2020-10-21 14:49:46 -03:00
Matias Manavella 4e047b2372 nil and error handling 2020-10-21 14:47:25 -03:00
Matias ManavellaandSalah Aldeen Al Saleh 7f190e3cce Update cmd/troubleshoot/cli/run.go
Co-authored-by: Salah Aldeen Al Saleh <salahalsaleh1993@gmail.com>
2020-10-21 13:59:51 -03:00
Matias ManavellaandSalah Aldeen Al Saleh 2436a0c163 Update cmd/preflight/cli/root.go
Co-authored-by: Salah Aldeen Al Saleh <salahalsaleh1993@gmail.com>
2020-10-21 13:59:24 -03:00
Matias Manavella 883abacaf8 --since flag added 2020-10-21 09:59:10 -03:00
Matias Manavella 7186b75f7e --since flag added 2020-10-21 09:51:52 -03:00
Matias Manavella e16eabd531 added flag --since-time 2020-10-19 16:53:13 -03:00
Salah Aldeen Al Saleh 3ff91a3b92 support bundle subdirectory (#285) 2020-10-19 11:11:56 -07:00
Marc Campbell 2020049cb7 Merge pull request #283 from manavellamnimble/fixFilePath
Filepath fixed
2020-10-14 19:02:41 -07:00
Salah Aldeen Al Saleh d94954e6dd use '_' instead of ':' in support bundle name format (#284) 2020-10-14 15:57:26 -07:00
Matias Manavella 06817c1682 Filepath fixed 2020-10-14 15:01:00 -03:00
divolgin 0554c2ac0f Merge pull request #278 from replicatedhq/divolgin/insecure
Retry http requests on x509 errors if allowed
2020-10-06 14:44:36 -07:00
divolgin 1087eb65d2 Retry http requests on x509 errors if allowed 2020-10-06 21:42:04 +00:00
divolgin 09dc6b5862 Merge pull request #276 from replicatedhq/divolgin/panic
Don't panic when pod is in a waiting state
2020-10-05 17:27:04 -07:00
divolgin 3d301f4307 Don't panic when pod is in a waiting state 2020-10-06 00:24:46 +00:00
divolgin 446b1d5552 Merge pull request #274 from manavellamnimble/removeImagePullSecretPathSupport
Remove image pull secret path support
2020-10-05 16:55:46 -07:00
divolgin 63c8422e63 Merge pull request #270 from manavellamnimble/secretAnalyzerFixed
Secret analyzer fixed
2020-10-05 16:51:28 -07:00
divolgin 616f6e73a5 Merge pull request #273 from replicatedhq/divolgin/preflight
Allow preflight spec to be loaded from a secret
2020-10-01 12:12:29 -07:00
Matias Manavella 213b52477f Run collectors docs modified 2020-10-01 13:53:54 -03:00
Matias Manavella e9ba73d87f Support for path to config.json file removed 2020-10-01 13:42:29 -03:00
divolgin 6e86cdc803 Allow preflight spec to be loaded from a secret 2020-10-01 01:37:37 +00:00
divolgin c023a81966 Merge pull request #272 from replicatedhq/divolgin/secrets
Support loading spec from a secret
2020-09-30 16:21:10 -07:00
divolgin 7d0bb1a5d7 Support loading spec from a secret 2020-09-30 23:06:42 +00:00
Matias Manavella 3cf679260e ImagePullSecret and TextAnalyzer with regex fixed 2020-09-30 18:06:52 -03:00
Matias Manavella 53bd30755d secret analyzer fixed 2020-09-29 11:42:21 -03:00
divolgin 7fe7216b29 Merge pull request #268 from manavellamnimble/postgressVersion
Error Parsing Postgres Version
2020-09-24 15:43:28 -07:00
Matias Manavella 2da1f518d3 parse tolerant 2020-09-23 15:10:54 -03:00
divolgin 8a30f2ddce Merge pull request #269 from manavellamnimble/distributionEnhacement
Distribution enhacement
2020-09-23 08:53:19 -07:00
Matias Manavella 0ea4e1d52f distribution_test fixed 2020-09-22 18:10:28 -03:00
Matias Manavella 74c9ff2432 add ibm and minikube to distribution analyzer 2020-09-22 17:36:27 -03:00
Matias Manavella bfa50c9643 add ibm and minikube to distribution analyzer 2020-09-22 17:32:41 -03:00
Matias Manavella 74a7e5f19c add ibm and minikube to distribution analyzer 2020-09-22 16:31:52 -03:00
Matias Manavella e3c954f460 add ibm and minikube to distribution analyzer 2020-09-22 16:17:47 -03:00
Matias Manavella 16d3ff372f add ibm and minikube to distribution analyzer 2020-09-22 16:08:03 -03:00
Matias Manavella 437357f099 Filename fixed 2020-09-21 11:06:15 -03:00
Matias Manavella 955e08c35a fixed varsion formats 2020-09-17 17:38:42 -03:00
divolgin 58261cdaf2 Merge pull request #267 from manavellamnimble/fixIngress
ingres analyzer fixed
2020-09-17 08:55:14 -07:00
divolgin 545313aa06 Merge pull request #260 from manavellamnimble/abortImagePullBackoff
Abort run collector on ImagePullBackoff
2020-09-17 08:54:39 -07:00
divolgin 1ceab64b6f Merge pull request #259 from manavellamnimble/emptyKeySecret
fixEmptyKeySecret
2020-09-17 08:50:39 -07:00
Matias Manavella eb0f692aa0 using filepath instead of path 2020-09-16 18:25:15 -03:00
Matias Manavella 2ae1b9b678 abort run collector on ImagePullBackOff 2020-09-16 18:16:32 -03:00
Matias Manavella 2f2501ac3b fix empty key in secret 2020-09-16 18:12:19 -03:00
Matias Manavella a57894962d ingres analyzer fixed 2020-09-16 18:04:46 -03:00
Bryant Hagadorn 3f48574071 Add all auth providers (#266)
* Update main.go

* Adding generic auth for OIDC
2020-09-15 15:34:10 -07:00
Matias Manavella 00bbfc0414 Abort run collector on ImagePullBackoff 2020-09-11 10:20:39 -03:00
Matias Manavella c4d4af6890 fixEmptyKeySecret 2020-09-10 13:01:31 -03:00
Salah Aldeen Al Saleh 4149e4d6ed this is an empty commit (#256) 2020-09-09 08:53:29 -07:00
Salah Aldeen Al Saleh b2cb3bf43b handle nil ImagePullSecret in collector (#255) 2020-09-09 07:41:34 -07:00
divolgin e232cc0f34 Merge pull request #253 from manavellamnimble/runImagePullSecret
Add or create ImagePullSecret  in a run collector
2020-09-08 13:59:25 -07:00
Andrew Lavery f68e0dbae0 Merge pull request #174 from replicatedhq/laverya/allow-analyzing-all-files-in-dir
allow textAnalyze to run on all files in a dir or matching a prefix
2020-09-08 14:43:58 -04:00
Andrew Lavery 0126fb7d7a Merge remote-tracking branch 'origin/master' into laverya/allow-analyzing-all-files-in-dir 2020-09-08 14:27:26 -04:00
Andrew Lavery d2bc571a18 Merge pull request #250 from manavellamnimble/defaultStorageClass
Check for Default Storage Class when storageClassName is empty
2020-09-08 14:25:36 -04:00
Matias Manavella 1f0842484c error-handling modified 2020-09-07 11:33:06 -03:00
Matias Manavella 8c31054331 error-handling modified 2020-09-07 11:21:23 -03:00
Matias Manavella 4b2e7e153e add or create ImagePullSecret 2020-09-04 12:41:47 -03:00
Marc Campbell 2289e00558 Merge pull request #252 from replicatedhq/fix-statefulset-status
Fix statefulset status
2020-09-03 14:00:16 -07:00
Marc Campbell 949f0f2213 Fix statefulset status 2020-09-03 13:48:25 -07:00
divolgin 703219119e Merge pull request #251 from replicatedhq/divolgin/v1beta2
Adding troubleshoot.sh/v1beta2
2020-09-01 15:19:19 -07:00
Matias Manavella 5522b0623c check Default SC 2020-09-01 19:07:13 -03:00
Matias Manavella 8b1ae0194e check Default SC 2020-09-01 18:36:55 -03:00
divolgin a0ce85ae1e Adding troubleshoot.sh/v1beta2 2020-09-01 19:57:11 +00:00
Matias Manavella b1e251ed03 selector:MatchLabel (#249)
* selector:MatchLabel

* selector:MatchLabel
2020-08-31 09:46:04 -07:00
Matias Manavella f7e4103d9b check Default SC 2020-08-31 11:54:49 -03:00
Matias Manavella 442c97f138 check Default SC 2020-08-31 11:52:45 -03:00
Matias Manavella 6862f0f6c1 K8s events (#248)
* K8s events collected

* collect events

* collect events
2020-08-24 14:41:38 -07:00
Matias Manavella 6ad9103f86 Copy collector enhanced (#237)
Copy collector enhanced
2020-08-24 10:57:40 -07:00
divolgin 016d6e4f18 Merge pull request #246 from manavellamnimble/add_checkNames
checkName in Distribution and Runtime
2020-08-18 15:55:31 -07:00
Matias Manavella 97db83b270 checkName in Distribution and Runtime 2020-08-18 13:57:53 -03:00
GraysonNull 20e82be101 Merge pull request #244 from GraysonNull/grayson-termui-scrolling
Add scrolling to table, use table widget for support bundle
2020-08-13 12:49:29 -05:00
GraysonNull 4e08ce4d93 remove commented code blocks, move appName func to shared package 2020-08-13 16:38:51 +00:00
GraysonNull e8dc5ae1b8 use table widget for bundle analysis so scrolling works 2020-08-13 16:02:29 +00:00
GraysonNull 0e667e9685 scrolling on analyzers table 2020-08-11 15:16:23 +00:00
Andrew Lavery 99f5e2d896 Merge pull request #238 from replicatedhq/laverya/latest-controller-gen
update controller-gen to v0.3.0
2020-08-10 17:55:45 -04:00
Andrew Lavery 407b80daf5 update controller-gen to v0.3.0
the first version to be k8s 1.18 compatible
2020-08-10 17:51:53 -04:00
Marc Campbell c84dc6de45 Merge pull request #233 from replicatedhq/support-bundle-command
Update help text to match the command
2020-08-08 06:47:45 -07:00
Matias Manavella a686e47fa2 Issue#228 (#230)
* Added timestamp to support-bundle file

* Added timestamp to support-bundle file

* Added timestamp to support-bundle file

* Added timestamp with second-level precision
2020-08-03 15:09:21 -07:00
Marc Campbell 56e58c0849 Update help text to match the command 2020-08-03 11:55:19 -07:00
GraysonNull 3b8e53bf90 Merge pull request #225 from GraysonNull/grayson/update-samples
fix invalid analyzer in sample support-bundle
2020-07-13 17:36:00 -05:00
GraysonNull cc9d3aedec move logs to collectors array 2020-07-13 22:26:17 +00:00
GraysonNull a70843cd69 add space 2020-07-13 22:07:37 +00:00
GraysonNull 08c52770a9 fix invalid analyzer in sample support-bundle 2020-07-13 22:06:37 +00:00
Mark Pundsack 52e385ffce Merge pull request #224 from replicatedhq/markpundsack-patch-1
Update URL
2020-07-13 16:24:51 -05:00
Mark Pundsack d63321bc43 Update URL 2020-07-13 16:19:01 -05:00
GraysonNull 37ae457aa1 Merge pull request #223 from GraysonNull/grayson/update-samples
update sample preflight and support bundles, update directory name
2020-07-13 14:14:30 -05:00
GraysonNull 4b1852c3e1 updates from feedback 2020-07-13 16:45:46 +00:00
GraysonNull dc80733397 update sample preflight and support bundles, update diretory name 2020-07-13 15:43:10 +00:00
GraysonNull 78e328be86 Merge pull request #219 from replicatedhq/msp-update-readme
Update readme
2020-07-07 10:50:57 -05:00
Mark Pundsack ea43d4d9b6 Simplify language 2020-07-06 12:23:16 -05:00
Mark Pundsack 8ce751f989 Update readme 2020-07-06 12:14:08 -05:00
Salah Aldeen Al Saleh f55b9b6ae4 update schemas + add redactor and supportbundle schemas (#218) 2020-06-23 10:08:59 -07:00
Andrew Lavery 086f85845a Merge pull request #217 from replicatedhq/laverya/krew-yaml-refactor
refactor krew yaml to match upstream format
2020-06-17 14:45:54 -04:00
Andrew Lavery eb90b6d935 remove extra trailing whitespace 2020-06-17 14:44:20 -04:00
Andrew Lavery 13df9a0f87 refactor krew yaml to match upstream format 2020-06-17 14:37:38 -04:00
Andrew Lavery 89b4979cbc Merge pull request #216 from replicatedhq/laverya/redactor-reformat
redactor reformat
2020-06-15 14:02:40 -04:00
Andrew Lavery 6e874483b6 update yaml key name, fixup example troubleshoot 2020-06-15 13:55:07 -04:00
Andrew Lavery afd2ee95ca Merge remote-tracking branch 'origin/master' into laverya/redactor-reformat 2020-06-15 11:21:37 -04:00
Andrew Lavery e66d12dab2 combine multiline and single line regex 2020-06-15 11:15:50 -04:00
Marc Campbell d98172eb9f Merge pull request #215 from replicatedhq/analyzer-cli
Interactive results for support bundle kinds
2020-06-12 14:48:08 -07:00
Marc Campbell b070b4009c Interactive results for support bundle kinds 2020-06-12 14:26:48 -07:00
Marc Campbell 890cbb1378 Merge pull request #214 from replicatedhq/analyzer-cli
Refactor to support K8s 1.18
2020-06-12 13:41:00 -07:00
Marc Campbell 0f1f8b511e checkout before download 2020-06-12 13:32:30 -07:00
Marc Campbell 19fb95e99b fix workflow 2020-06-12 13:24:30 -07:00
Marc Campbell a22e2e25df Analyze from the CLI 2020-06-12 12:47:45 -07:00
Marc Campbell 8d991e1f9e fix workflow 2020-06-12 10:25:34 -07:00
Marc Campbell 91e856d6d5 Add validation for collectors and support bundle in e2e 2020-06-12 10:23:28 -07:00
Marc Campbell 65f957db81 Refactor to support K8s 1.18 2020-06-12 09:28:49 -07:00
Andrew Lavery a561254756 break apart redactor type 2020-06-09 18:43:44 -04:00
Salah Aldeen Al Saleh 53626bb025 Default BoolString to string and bool to 0 (#211)
* default boolstring to string and bool to 0
2020-06-01 14:55:33 -07:00
Andrew Lavery 54d8d7585d Merge pull request #208 from replicatedhq/laverya/don-t-use-goreleaser-binary-template
use hardcoded binary name, not {{ .Binary }}
2020-05-28 21:29:11 -04:00
Andrew Lavery 4021a6c176 use hardcoded binary name, not {{ .Binary }} 2020-05-28 21:18:03 -04:00
Andrew Lavery 8c9734c19c Merge pull request #207 from replicatedhq/laverya/krew-autodeploy
goreleaser no longer includes .exe in download links
2020-05-27 18:07:29 -04:00
Andrew Lavery b6ac6e4702 goreleaser no longer includes .exe in download links 2020-05-27 18:03:58 -04:00
Andrew Lavery f256bdb653 Merge pull request #206 from replicatedhq/laverya/tidy
go mod tidy
2020-05-27 17:34:08 -04:00
Andrew Lavery 1c3496e2f7 go mod tidy 2020-05-27 17:24:17 -04:00
Andrew Lavery f5d737f2c5 Merge pull request #204 from replicatedhq/divolgin/panic
Don't panic when parsing resources, return error
2020-05-27 17:08:02 -04:00
Andrew Lavery 619c4550bc Merge pull request #203 from replicatedhq/laverya/better-default-redactor-names
better default redactor names
2020-05-27 16:31:22 -04:00
Marc Campbell 6f42f85705 Merge pull request #205 from replicatedhq/naming
Update names
2020-05-27 13:30:30 -07:00
Marc Campbell 661d7fcfc4 Update names 2020-05-27 13:10:46 -07:00
divolgin 47e715a350 Don't panic when parsing resources, return error 2020-05-27 19:56:23 +00:00
Andrew Lavery b9c3ad2635 better default redactor names 2020-05-27 14:36:40 -04:00
Andrew Lavery 2787cf385c Merge pull request #202 from replicatedhq/laverya/is-default-redaction
include whether a redaction is from a default redactor in reports
2020-05-27 14:08:23 -04:00
Andrew Lavery f03916cfd2 include whether a redaction is from a default redactor in reports 2020-05-27 13:46:39 -04:00
Andrew Lavery 252b29fb0a Merge pull request #201 from replicatedhq/laverya/better-redactor-names
default redactor name improvements
2020-05-26 19:49:32 -04:00
Andrew Lavery 553718eada fully spell out default redactor names 2020-05-26 19:33:40 -04:00
Andrew Lavery 171c440f27 improve generated names for custom redactors 2020-05-26 18:13:48 -04:00
Andrew Lavery 869a9eafd3 actually increment withinRedactNum 2020-05-26 18:07:23 -04:00
Andrew Lavery 6c61040f74 default redactor name improvements
for instance, defaultRegex.SECRET_ACCESS_KEY instead of the raw regex
2020-05-26 17:15:30 -04:00
Andrew Lavery 079765b558 add 'no matching files' analyze result 2020-04-15 19:17:01 -04:00
Andrew Lavery 45ccd7be5b don't conglomerate results
doing so sort of breaks the 'invalid analyzer' message if no files match
2020-04-15 19:13:37 -04:00
Andrew Lavery 2bc9938a2a use filepath.Glob and filepath.Match 2020-04-15 18:59:17 -04:00
Andrew Lavery 5050f46b1e use gobwas/glob to match globs 2020-04-15 18:47:16 -04:00
Andrew Lavery 222a93162d allow textAnalyze to run on all files in a dir or matching a prefix 2020-04-15 18:08:08 -04:00
176 changed files with 23097 additions and 1887 deletions
+62 -8
View File
@@ -11,6 +11,11 @@ on:
jobs:
build:
runs-on: ubuntu-18.04
container:
image: replicated/troubleshoot-builder:18.04
credentials:
username: repldeploy2
password: ${{ secrets.DOCKERHUB_PASSWORD }}
steps:
- uses: actions/setup-go@v1
with:
@@ -23,11 +28,15 @@ jobs:
shell: bash
- uses: actions/checkout@v2
- run: make
- run: make
compile-preflight:
runs-on: ubuntu-latest
runs-on: ubuntu-18.04
container:
image: replicated/troubleshoot-builder:18.04
credentials:
username: repldeploy2
password: ${{ secrets.DOCKERHUB_PASSWORD }}
needs: build
steps:
- uses: actions/setup-go@v1
@@ -46,7 +55,7 @@ jobs:
path: bin/preflight
validate-preflight:
runs-on: ubuntu-latest
runs-on: ubuntu-18.04
needs: compile-preflight
steps:
- name: Download preflight binary
@@ -54,12 +63,57 @@ jobs:
with:
name: preflight
path: bin/
- uses: engineerd/setup-kind@v0.2.0
- uses: engineerd/setup-kind@v0.4.0
- run: chmod +x bin/preflight
- run: bin/preflight --interactive=false --format=json https://preflight.replicated.com
- run: ./bin/preflight --interactive=false --format=json https://preflight.replicated.com
compile-supportbundle:
runs-on: ubuntu-18.04
container:
image: replicated/troubleshoot-builder:18.04
credentials:
username: repldeploy2
password: ${{ secrets.DOCKERHUB_PASSWORD }}
needs: build
steps:
- uses: actions/setup-go@v1
with:
go-version: '1.14'
- name: setup env
run: |
echo "::set-env name=GOPATH::$(go env GOPATH)"
echo "::add-path::$(go env GOPATH)/bin"
shell: bash
- uses: actions/checkout@master
- run: make support-bundle
- uses: actions/upload-artifact@v1
with:
name: support-bundle
path: bin/support-bundle
validate-supportbundle:
runs-on: ubuntu-18.04
needs: compile-supportbundle
steps:
- uses: actions/checkout@master
- name: Download support-bundle binary
uses: actions/download-artifact@v1
with:
name: support-bundle
path: bin/
- uses: engineerd/setup-kind@v0.4.0
- run: chmod +x bin/support-bundle
- run: ./bin/support-bundle ./examples/support-bundle/sample-collectors.yaml
- run: ./bin/support-bundle ./examples/support-bundle/sample-supportbundle.yaml
- run: ./bin/support-bundle https://kots.io
goreleaser:
runs-on: ubuntu-latest
runs-on: ubuntu-18.04
container:
image: replicated/troubleshoot-builder:18.04
credentials:
username: repldeploy2
password: ${{ secrets.DOCKERHUB_PASSWORD }}
needs:
- validate-preflight
if: startsWith(github.ref, 'refs/tags/v')
@@ -74,7 +128,7 @@ jobs:
- name: Unshallow
run: git fetch --prune --unshallow
- uses: actions/setup-go@v1
with:
go-version: "1.14"
+17 -13
View File
@@ -29,6 +29,8 @@ endif
define LDFLAGS
-ldflags "\
-s -w \
-extldflags \"-static\" \
-X ${VERSION_PACKAGE}.version=${VERSION} \
-X ${VERSION_PACKAGE}.gitSHA=${GIT_SHA} \
-X ${VERSION_PACKAGE}.buildTime=${DATE} \
@@ -47,15 +49,15 @@ test: generate fmt vet
.PHONY: support-bundle
support-bundle: generate fmt vet
go build ${LDFLAGS} -o bin/support-bundle github.com/replicatedhq/troubleshoot/cmd/troubleshoot
go build -tags netgo ${LDFLAGS} -o bin/support-bundle github.com/replicatedhq/troubleshoot/cmd/troubleshoot
.PHONY: preflight
preflight: generate fmt vet
go build ${LDFLAGS} -o bin/preflight github.com/replicatedhq/troubleshoot/cmd/preflight
go build -tags netgo ${LDFLAGS} -o bin/preflight github.com/replicatedhq/troubleshoot/cmd/preflight
.PHONY: analyze
analyze: generate fmt vet
go build ${LDFLAGS} -o bin/analyze github.com/replicatedhq/troubleshoot/cmd/analyze
go build -tags netgo ${LDFLAGS} -o bin/analyze github.com/replicatedhq/troubleshoot/cmd/analyze
.PHONY: fmt
fmt:
@@ -67,37 +69,39 @@ vet:
.PHONY: generate
generate: controller-gen client-gen
$(shell go env GOPATH)/bin/controller-gen object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/...
$(shell go env GOPATH)/bin/client-gen \
$(CONTROLLER_GEN) \
object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/...
$(CLIENT_GEN) \
--output-package=github.com/replicatedhq/troubleshoot/pkg/client \
--clientset-name troubleshootclientset \
--input-base github.com/replicatedhq/troubleshoot/pkg/apis \
--input troubleshoot/v1beta1 \
--input troubleshoot/v1beta2 \
-h ./hack/boilerplate.go.txt
.PHONY: openapischema
openapischema: controller-gen
controller-gen crd +output:dir=./config/crds paths=./pkg/apis/troubleshoot/v1beta1
controller-gen crd +output:dir=./config/crds paths=./pkg/apis/troubleshoot/v1beta2
.PHONY: schemas
schemas: fmt vet openapischema
go build ${LDFLAGS} -o bin/schemagen github.com/replicatedhq/troubleshoot/cmd/schemagen
./bin/schemagen --output-dir ./schemas
# find or download controller-gen
# download controller-gen if necessary
.PHONY: contoller-gen
controller-gen:
ifeq (, $(shell which controller-gen))
go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.2.8
go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.3.0
CONTROLLER_GEN=$(shell go env GOPATH)/bin/controller-gen
else
CONTROLLER_GEN=$(shell which controller-gen)
endif
# find or download client-gen
.PHONY: client-gen
client-gen:
ifeq (, $(shell which client-gen))
go get k8s.io/code-generator/cmd/client-gen@kubernetes-1.16.4
go get k8s.io/code-generator/cmd/client-gen@kubernetes-1.18.0
CLIENT_GEN=$(shell go env GOPATH)/bin/client-gen
else
CLIENT_GEN=$(shell which client-gen)
@@ -116,7 +120,7 @@ snapshot-release:
.PHONY: local-release
local-release:
curl -sL https://git.io/goreleaser | bash -s -- --rm-dist --snapshot --config deploy/.goreleaser.local.yml
curl -sL https://git.io/goreleaser | bash -s -- --rm-dist --snapshot --config deploy/.goreleaser.yaml
docker tag replicated/troubleshoot:alpha localhost:32000/troubleshoot:alpha
docker tag replicated/preflight:alpha localhost:32000/preflight:alpha
docker push localhost:32000/troubleshoot:alpha
@@ -128,8 +132,8 @@ run-preflight: preflight
.PHONY: run-troubleshoot
run-troubleshoot: support-bundle
./bin/support-bundle ./examples/troubleshoot/sample-troubleshoot.yaml
./bin/support-bundle ./examples/support-bundle/sample-supportbundle.yaml
.PHONY: run-analyze
run-analyze: analyze
./bin/analyze --analyzers ./examples/troubleshoot/sample-analyzers.yaml ./support-bundle.tar.gz
./bin/analyze --analyzers ./examples/support-bundle/sample-analyzers.yaml ./support-bundle.tar.gz
+22 -6
View File
@@ -1,8 +1,8 @@
# Replicated Troubleshoot
Replicated Troubleshoot is a framework for collecting, redacting and analyzing highly customizable diagnostic information about a Kubernetes cluster. Troubleshoot specs are created by 3rd-party application developers/maintainers and run by cluster operators in the initial and ongoing operation of those applications.
Replicated Troubleshoot is a framework for collecting, redacting, and analyzing highly customizable diagnostic information about a Kubernetes cluster. Troubleshoot specs are created by 3rd-party application developers/maintainers and run by cluster operators in the initial and ongoing operation of those applications.
Troubleshoot provides two CLI tools as kubectl plugins (using [Krew](https://krew.dev)) `kubectl preflight` and `kubectl support-bundle`. Preflight provides pre-installation cluster conformance testing and validation (preflight checks) and support-bundle provides post-installation troubleshooting and diagnostics (support bundles).
Troubleshoot provides two CLI tools as kubectl plugins (using [Krew](https://krew.dev)): `kubectl preflight` and `kubectl support-bundle`. Preflight provides pre-installation cluster conformance testing and validation (preflight checks) and support-bundle provides post-installation troubleshooting and diagnostics (support bundles).
## Preflight Checks
Preflight checks are an easy-to-run set of conformance tests that can be written to verify that specific requirements in a cluster are met.
@@ -18,11 +18,11 @@ curl https://krew.sh/preflight | bash
kubectl preflight https://preflight.replicated.com
```
For a details on creating the custom resource files that drive preflight checks, visit [creating preflight checks](https://troubleshoot.sh/docs/preflight/creating/).
For a details on creating the custom resource files that drive preflight checks, visit [creating preflight checks](https://troubleshoot.sh/docs/preflight/introduction/).
## Support Bundle
A support bundle is an archive that's created in-cluster, by collecting logs, cluster information and executing specified commands (including redaction of sensitive information). After creating a support bundle, the cluster operator will normally deliver it to the 3rd-party application vendor for analysis and disconnected debugging. Another Replicated project, [Kotsadm](https://github.com/replicatedhq/kotsadm), provides cluster operators with in-cluster UI for processing support bundles and viewing analyzers (as well as support bundle collection).
A support bundle is an archive that's created in-cluster, by collecting logs and cluster information, and executing specified commands (including redaction of sensitive information). After creating a support bundle, the cluster operator will normally deliver it to the 3rd-party application vendor for analysis and disconnected debugging. Another Replicated project, [Kotsadm](https://github.com/replicatedhq/kotsadm), provides cluster operators with an in-cluster UI for processing support bundles and viewing analyzers (as well as support bundle collection).
To collect a sample support bundle, install the troubleshoot kubectl plugin:
@@ -32,11 +32,27 @@ curl https://krew.sh/support-bundle | bash
and run:
```shell
kubectl support-bundle https://troubleshoot.replicated.com
kubectl support-bundle https://support-bundle.replicated.com
```
For details on creating the custom resource files that drive support-bundle collection [creating collectors](https://troubleshoot.sh/reference/collectors/overview/) and for analyzers [creating analyzers](https://troubleshoot.sh/reference/analyzers/overview/).
For details on creating the custom resource files that drive support-bundle collection, visit [creating collectors](https://troubleshoot.sh/docs/collect/) and [creating analyzers](https://troubleshoot.sh/docs/analyze/).
# Community
For questions about using Troubleshoot, there's a [Replicated Community](https://help.replicated.com/community) forum, and a [#app-troubleshoot channel in Kubernetes Slack](https://kubernetes.slack.com/channels/app-troubleshoot).
# Building
The following packages are required for building the project from source code:
pkg-config
librrd-dev
libglib2.0-dev
libcairo2-dev
libpango1.0-dev
libpixman-1-dev
libpng-dev
libsdl-pango-dev
libthai-dev
libpcre3-dev
There are known issues with libc6 2.27-3ubuntu1.2 on Ubuntu 18. Upgrading to 2.27-3ubuntu1.3 (apt-get install libc6) resolves these.
+2 -7
View File
@@ -4,14 +4,10 @@ import (
"os"
"strings"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/logger"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
var (
KubernetesConfigFlags *genericclioptions.ConfigFlags
)
func RootCmd() *cobra.Command {
@@ -41,8 +37,7 @@ func RootCmd() *cobra.Command {
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
KubernetesConfigFlags = genericclioptions.NewConfigFlags(false)
KubernetesConfigFlags.AddFlags(cmd.Flags())
k8sutil.AddFlags(cmd.Flags())
return cmd
}
+1 -1
View File
@@ -2,7 +2,7 @@ package main
import (
"github.com/replicatedhq/troubleshoot/cmd/analyze/cli"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
_ "k8s.io/client-go/plugin/pkg/client/auth"
)
func main() {
+9 -22
View File
@@ -5,18 +5,18 @@ import (
"io/ioutil"
"os"
"path"
"strings"
"time"
ui "github.com/gizak/termui/v3"
"github.com/gizak/termui/v3/widgets"
"github.com/pkg/errors"
ui "github.com/replicatedhq/termui/v3"
"github.com/replicatedhq/termui/v3/widgets"
"github.com/replicatedhq/troubleshoot/cmd/util"
analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze"
)
var (
selectedResult = 0
table = widgets.NewTable()
isShowingSaved = false
)
@@ -64,7 +64,9 @@ func showInteractiveResults(preflightName string, analyzeResults []*analyzerunne
selectedResult++
} else {
selectedResult = 0
table.SelectedRow = 0
}
table.ScrollDown()
ui.Clear()
drawUI(preflightName, analyzeResults)
case "<Up>":
@@ -72,7 +74,9 @@ func showInteractiveResults(preflightName string, analyzeResults []*analyzerunne
selectedResult--
} else {
selectedResult = len(analyzeResults) - 1
table.SelectedRow = len(analyzeResults)
}
table.ScrollUp()
ui.Clear()
drawUI(preflightName, analyzeResults)
}
@@ -95,7 +99,7 @@ func drawHeader(preflightName string) {
termWidth, _ := ui.TerminalDimensions()
title := widgets.NewParagraph()
title.Text = fmt.Sprintf("%s Preflight Checks", appName(preflightName))
title.Text = fmt.Sprintf("%s Preflight Checks", util.AppName(preflightName))
title.TextStyle.Fg = ui.ColorWhite
title.TextStyle.Bg = ui.ColorClear
title.TextStyle.Modifier = ui.ModifierBold
@@ -127,7 +131,6 @@ func drawFooter() {
func drawPreflightTable(analyzeResults []*analyzerunner.AnalyzeResult) {
termWidth, termHeight := ui.TerminalDimensions()
table := widgets.NewTable()
table.SetRect(0, 3, termWidth/2, termHeight-6)
table.FillRow = true
table.Border = true
@@ -221,7 +224,7 @@ func save(preflightName string, analyzeResults []*analyzerunner.AnalyzeResult) (
os.Remove(filename)
}
results := fmt.Sprintf("%s Preflight Checks\n\n", appName(preflightName))
results := fmt.Sprintf("%s Preflight Checks\n\n", util.AppName(preflightName))
for _, analyzeResult := range analyzeResults {
result := ""
@@ -270,19 +273,3 @@ func showSaved(filename string) {
isShowingSaved = true
}
func appName(preflightName string) string {
words := strings.Split(strings.Title(strings.Replace(preflightName, "-", " ", -1)), " ")
casedWords := []string{}
for i, word := range words {
if strings.ToLower(word) == "ai" {
casedWords = append(casedWords, "AI")
} else if strings.ToLower(word) == "io" && i > 0 {
casedWords[i-1] += ".io"
} else {
casedWords = append(casedWords, word)
}
}
return strings.Join(casedWords, " ")
}
+4 -12
View File
@@ -4,13 +4,9 @@ import (
"os"
"strings"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
var (
KubernetesConfigFlags *genericclioptions.ConfigFlags
)
func RootCmd() *cobra.Command {
@@ -36,19 +32,15 @@ that a cluster meets the requirements to run an application.`,
cmd.Flags().Bool("interactive", true, "interactive preflights")
cmd.Flags().String("format", "human", "output format, one of human, json, yaml. only used when interactive is set to false")
cmd.Flags().String("preflight", "", "name of the preflight to use")
cmd.Flags().String("image", "", "the full name of the preflight image to use")
cmd.Flags().String("pullpolicy", "", "the pull policy of the preflight image")
cmd.Flags().String("collector-image", "", "the full name of the collector image to use")
cmd.Flags().String("collector-pullpolicy", "", "the pull policy of the collector image")
cmd.Flags().Bool("collect-without-permissions", false, "always run preflight checks even if some require permissions that preflight does not have")
cmd.Flags().String("serviceaccount", "", "name of the service account to use. if not provided, one will be created")
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.")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
KubernetesConfigFlags = genericclioptions.NewConfigFlags(false)
KubernetesConfigFlags.AddFlags(cmd.Flags())
k8sutil.AddFlags(cmd.Flags())
return cmd
}
+70 -8
View File
@@ -5,17 +5,22 @@ import (
"io/ioutil"
"net/http"
"os"
"strings"
"time"
cursor "github.com/ahmetalpbalkan/go-cursor"
"github.com/fatih/color"
"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/cmd/util"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
troubleshootclientsetscheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme"
"github.com/replicatedhq/troubleshoot/pkg/docrewrite"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/preflight"
"github.com/replicatedhq/troubleshoot/pkg/specs"
"github.com/spf13/viper"
spin "github.com/tj/go-spin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
)
@@ -23,15 +28,28 @@ func runPreflights(v *viper.Viper, arg string) error {
fmt.Print(cursor.Hide())
defer fmt.Print(cursor.Show())
preflightContent := ""
var preflightContent []byte
var err error
if _, err = os.Stat(arg); err == nil {
if strings.HasPrefix(arg, "secret/") {
// format secret/namespace-name/secret-name
pathParts := strings.Split(arg, "/")
if len(pathParts) != 3 {
return errors.Errorf("path %s must have 3 components", arg)
}
spec, err := specs.LoadFromSecret(pathParts[1], pathParts[2], "preflight-spec")
if err != nil {
return errors.Wrap(err, "failed to get spec from secret")
}
preflightContent = spec
} else if _, err = os.Stat(arg); err == nil {
b, err := ioutil.ReadFile(arg)
if err != nil {
return err
}
preflightContent = string(b)
preflightContent = b
} else {
if !util.IsURL(arg) {
return fmt.Errorf("%s is not a URL and was not found (err %s)", arg, err)
@@ -41,7 +59,7 @@ func runPreflights(v *viper.Viper, arg string) error {
if err != nil {
return err
}
req.Header.Set("User-Agent", "Replicated_Preflight/v1beta1")
req.Header.Set("User-Agent", "Replicated_Preflight/v1beta2")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
@@ -53,7 +71,12 @@ func runPreflights(v *viper.Viper, arg string) error {
return err
}
preflightContent = string(body)
preflightContent = body
}
preflightContent, err = docrewrite.ConvertToV1Beta2(preflightContent)
if err != nil {
return errors.Wrap(err, "failed to convert to v1beta2")
}
troubleshootclientsetscheme.AddToScheme(scheme.Scheme)
@@ -63,7 +86,7 @@ func runPreflights(v *viper.Viper, arg string) error {
return errors.Wrapf(err, "failed to parse %s", arg)
}
preflightSpec := obj.(*troubleshootv1beta1.Preflight)
preflightSpec := obj.(*troubleshootv1beta2.Preflight)
s := spin.New()
finishedCh := make(chan bool, 1)
@@ -95,7 +118,7 @@ func runPreflights(v *viper.Viper, arg string) error {
close(finishedCh)
}()
restConfig, err := KubernetesConfigFlags.ToRESTConfig()
restConfig, err := k8sutil.GetRESTConfig()
if err != nil {
return errors.Wrap(err, "failed to convert kube flags to rest config")
}
@@ -107,6 +130,13 @@ func runPreflights(v *viper.Viper, arg string) error {
KubernetesRestConfig: restConfig,
}
if v.GetString("since") != "" || v.GetString("since-time") != "" {
err := parseTimeFlags(v, progressChan, preflightSpec.Spec.Collectors)
if err != nil {
return err
}
}
collectResults, err := preflight.Collect(collectOpts, preflightSpec)
if err != nil {
if !collectResults.IsRBACAllowed {
@@ -139,3 +169,35 @@ func runPreflights(v *viper.Viper, arg string) error {
return showStdoutResults(v.GetString("format"), preflightSpec.Name, analyzeResults)
}
func parseTimeFlags(v *viper.Viper, progressChan chan interface{}, collectors []*troubleshootv1beta2.Collect) error {
var (
sinceTime time.Time
err error
)
if v.GetString("since-time") != "" {
if v.GetString("since") != "" {
return errors.Errorf("at most one of `sinceTime` or `since` may be specified")
}
sinceTime, err = time.Parse(time.RFC3339, v.GetString("since-time"))
if err != nil {
return errors.Wrap(err, "unable to parse --since-time flag")
}
} else {
parsedDuration, err := time.ParseDuration(v.GetString("since"))
if err != nil {
return errors.Wrap(err, "unable to parse --since flag")
}
now := time.Now()
sinceTime = now.Add(0 - parsedDuration)
}
for _, collector := range collectors {
if collector.Logs != nil {
if collector.Logs.Limits == nil {
collector.Logs.Limits = new(troubleshootv1beta2.LogLimits)
}
collector.Logs.Limits.SinceTime = metav1.NewTime(sinceTime)
}
}
return nil
}
-151
View File
@@ -1,151 +0,0 @@
package cli
import (
"fmt"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
func createServiceAccount(preflight troubleshootv1beta1.Preflight, namespace string, clientset *kubernetes.Clientset) (string, error) {
name := fmt.Sprintf("preflight-%s", preflight.Name)
serviceAccount := corev1.ServiceAccount{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ServiceAccount",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Secrets: []corev1.ObjectReference{
{
APIVersion: "v1",
Kind: "Secret",
Name: name,
Namespace: namespace,
},
},
}
_, err := clientset.CoreV1().ServiceAccounts(namespace).Get(serviceAccount.Name, metav1.GetOptions{})
if err != nil && !kuberneteserrors.IsNotFound(err) {
return "", errors.Wrap(err, "failed to get existing service account")
}
if kuberneteserrors.IsNotFound(err) {
_, err := clientset.CoreV1().ServiceAccounts(namespace).Create(&serviceAccount)
if err != nil {
return "", errors.Wrap(err, "failed to create service account")
}
}
role := rbacv1.ClusterRole{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ClusterRole",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{
"namespaces",
"pods",
"services",
"secrets",
},
Verbs: metav1.Verbs{"list"},
},
{
APIGroups: []string{"apps"},
Resources: []string{"deployments"},
Verbs: metav1.Verbs{"list"},
},
{
APIGroups: []string{"extensions"},
Resources: []string{"ingresses"},
Verbs: metav1.Verbs{"list"},
},
{
APIGroups: []string{"storage.k8s.io"},
Resources: []string{"storageclasses"},
Verbs: metav1.Verbs{"list"},
},
{
APIGroups: []string{"apiextensions.k8s.io"},
Resources: []string{"customresourcedefinitions"},
Verbs: metav1.Verbs{"list"},
},
},
}
_, err = clientset.RbacV1().ClusterRoles().Get(role.Name, metav1.GetOptions{})
if err != nil && !kuberneteserrors.IsNotFound(err) {
return "", errors.Wrap(err, "failed to get eisting cluster role")
}
if kuberneteserrors.IsNotFound(err) {
_, err = clientset.RbacV1().ClusterRoles().Create(&role)
if err != nil {
return "", errors.Wrap(err, "failed to create cluster role")
}
}
roleBinding := rbacv1.ClusterRoleBinding{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ClusterRoleBinding",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: name,
Namespace: namespace,
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
Name: name,
},
}
_, err = clientset.RbacV1().ClusterRoleBindings().Get(roleBinding.Name, metav1.GetOptions{})
if err != nil && !kuberneteserrors.IsNotFound(err) {
return "", errors.Wrap(err, "failed to get existing cluster role binding")
}
if kuberneteserrors.IsNotFound(err) {
_, err = clientset.RbacV1().ClusterRoleBindings().Create(&roleBinding)
if err != nil {
return "", errors.Wrap(err, "failed to create cluster role binding")
}
}
return name, nil
}
func removeServiceAccount(name string, namespace string, clientset *kubernetes.Clientset) error {
if err := clientset.RbacV1().ClusterRoleBindings().Delete(name, &metav1.DeleteOptions{}); err != nil {
return errors.Wrap(err, "failed to delete cluster role binding")
}
if err := clientset.RbacV1().ClusterRoles().Delete(name, &metav1.DeleteOptions{}); err != nil {
return errors.Wrap(err, "failed to delete cluster role")
}
if err := clientset.CoreV1().ServiceAccounts(namespace).Delete(name, &metav1.DeleteOptions{}); err != nil {
return errors.Wrap(err, "failed to delete service account")
}
return nil
}
-21
View File
@@ -1,21 +0,0 @@
package cli
import (
"github.com/pkg/errors"
troubleshootclientv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
func createTroubleshootK8sClient(configFlags *genericclioptions.ConfigFlags) (*troubleshootclientv1beta1.TroubleshootV1beta1Client, error) {
config, err := configFlags.ToRESTConfig()
if err != nil {
return nil, errors.Wrap(err, "failed to convert kube flags to rest config")
}
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(config)
if err != nil {
return nil, errors.Wrap(err, "failed to create troubleshoot client")
}
return troubleshootClient, nil
}
+1 -1
View File
@@ -2,7 +2,7 @@ package main
import (
"github.com/replicatedhq/troubleshoot/cmd/preflight/cli"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
_ "k8s.io/client-go/plugin/pkg/client/auth"
)
func main() {
+52 -20
View File
@@ -62,28 +62,60 @@ func generateSchemas(v *viper.Viper) error {
return errors.Wrap(err, "failed to get workdir")
}
preflightContents, err := ioutil.ReadFile(filepath.Join(workdir, "config", "crds", "troubleshoot.replicated.com_preflights.yaml"))
if err != nil {
return errors.Wrap(err, "failed to read preflight crd")
}
if err := generateSchemaFromCRD(preflightContents, filepath.Join(workdir, v.GetString("output-dir"), "preflight-troubleshoot-v1beta1.json")); err != nil {
return errors.Wrap(err, "failed to write preflight schema")
files := []struct {
inFilename string
outFilename string
}{
{
"troubleshoot.replicated.com_preflights.yaml",
"preflight-troubleshoot-v1beta1.json",
},
{
"troubleshoot.replicated.com_analyzers.yaml",
"analyzer-troubleshoot-v1beta1.json",
},
{
"troubleshoot.replicated.com_collectors.yaml",
"collector-troubleshoot-v1beta1.json",
},
{
"troubleshoot.replicated.com_redactors.yaml",
"redactor-troubleshoot-v1beta1.json",
},
{
"troubleshoot.replicated.com_supportbundles.yaml",
"supportbundle-troubleshoot-v1beta1.json",
},
{
"troubleshoot.sh_analyzers.yaml",
"analyzer-troubleshoot-v1beta2.json",
},
{
"troubleshoot.sh_collectors.yaml",
"collector-troubleshoot-v1beta2.json",
},
{
"troubleshoot.sh_preflights.yaml",
"preflight-troubleshoot-v1beta2.json",
},
{
"troubleshoot.sh_redactors.yaml",
"redactor-troubleshoot-v1beta2.json",
},
{
"troubleshoot.sh_supportbundles.yaml",
"supportbundle-troubleshoot-v1beta2.json",
},
}
analyzersContents, err := ioutil.ReadFile(filepath.Join(workdir, "config", "crds", "troubleshoot.replicated.com_analyzers.yaml"))
if err != nil {
return errors.Wrap(err, "failed to read analyzers crd")
}
if err := generateSchemaFromCRD(analyzersContents, filepath.Join(workdir, v.GetString("output-dir"), "analyzer-troubleshoot-v1beta1.json")); err != nil {
return errors.Wrap(err, "failed to write analyzers schema")
}
collectorsContents, err := ioutil.ReadFile(filepath.Join(workdir, "config", "crds", "troubleshoot.replicated.com_collectors.yaml"))
if err != nil {
return errors.Wrap(err, "failed to read collectors crd")
}
if err := generateSchemaFromCRD(collectorsContents, filepath.Join(workdir, v.GetString("output-dir"), "collector-troubleshoot-v1beta1.json")); err != nil {
return errors.Wrap(err, "failed to write collectors schema")
for _, file := range files {
contents, err := ioutil.ReadFile(filepath.Join(workdir, "config", "crds", file.inFilename))
if err != nil {
return errors.Wrapf(err, "failed to read crd from %s", file.inFilename)
}
if err := generateSchemaFromCRD(contents, filepath.Join(workdir, v.GetString("output-dir"), file.outFilename)); err != nil {
return errors.Wrapf(err, "failed to write crd schema to %s", file.outFilename)
}
}
return nil
+274
View File
@@ -0,0 +1,274 @@
package cli
import (
"fmt"
"io/ioutil"
"os"
"path"
"time"
"github.com/pkg/errors"
ui "github.com/replicatedhq/termui/v3"
"github.com/replicatedhq/termui/v3/widgets"
"github.com/replicatedhq/troubleshoot/cmd/util"
analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze"
)
var (
selectedResult = 0
table = widgets.NewTable()
isShowingSaved = false
)
func showInteractiveResults(supportBundleName string, analyzeResults []*analyzerunner.AnalyzeResult) error {
if err := ui.Init(); err != nil {
return errors.Wrap(err, "failed to create terminal ui")
}
defer ui.Close()
drawUI(supportBundleName, analyzeResults)
uiEvents := ui.PollEvents()
for {
select {
case e := <-uiEvents:
switch e.ID {
case "<C-c>":
return nil
case "q":
if isShowingSaved == true {
isShowingSaved = false
ui.Clear()
drawUI(supportBundleName, analyzeResults)
} else {
return nil
}
case "s":
filename, err := save(analyzeResults)
if err != nil {
// show
} else {
showSaved(filename)
go func() {
time.Sleep(time.Second * 5)
isShowingSaved = false
ui.Clear()
drawUI(supportBundleName, analyzeResults)
}()
}
case "<Resize>":
ui.Clear()
drawUI(supportBundleName, analyzeResults)
case "<Down>":
if selectedResult < len(analyzeResults)-1 {
selectedResult++
} else {
selectedResult = 0
table.SelectedRow = 0
}
table.ScrollDown()
ui.Clear()
drawUI(supportBundleName, analyzeResults)
case "<Up>":
if selectedResult > 0 {
selectedResult--
} else {
selectedResult = len(analyzeResults) - 1
table.SelectedRow = len(analyzeResults)
}
table.ScrollUp()
ui.Clear()
drawUI(supportBundleName, analyzeResults)
}
}
}
}
func drawUI(supportBundleName string, analyzeResults []*analyzerunner.AnalyzeResult) {
drawGrid(analyzeResults)
drawHeader(supportBundleName)
drawFooter()
}
func drawGrid(analyzeResults []*analyzerunner.AnalyzeResult) {
drawAnalyzersTable(analyzeResults)
drawDetails(analyzeResults[selectedResult])
}
func drawHeader(supportBundleName string) {
termWidth, _ := ui.TerminalDimensions()
title := widgets.NewParagraph()
title.Text = fmt.Sprintf("%s Support Bundle Analysis", util.AppName(supportBundleName))
title.TextStyle.Fg = ui.ColorWhite
title.TextStyle.Bg = ui.ColorClear
title.TextStyle.Modifier = ui.ModifierBold
title.Border = false
left := termWidth/2 - 2*len(title.Text)/3
right := termWidth/2 + (termWidth/2 - left)
title.SetRect(left, 0, right, 1)
ui.Render(title)
}
func drawFooter() {
termWidth, termHeight := ui.TerminalDimensions()
instructions := widgets.NewParagraph()
instructions.Text = "[q] quit [s] save [↑][↓] scroll"
instructions.Border = false
left := 0
right := termWidth
top := termHeight - 1
bottom := termHeight
instructions.SetRect(left, top, right, bottom)
ui.Render(instructions)
}
func drawAnalyzersTable(analyzeResults []*analyzerunner.AnalyzeResult) {
termWidth, termHeight := ui.TerminalDimensions()
table.SetRect(0, 3, termWidth/2, termHeight-6)
table.FillRow = true
table.Border = true
table.Rows = [][]string{}
table.ColumnWidths = []int{termWidth}
for i, analyzeResult := range analyzeResults {
title := analyzeResult.Title
if analyzeResult.IsPass {
title = fmt.Sprintf("✔ %s", title)
} else if analyzeResult.IsWarn {
title = fmt.Sprintf("⚠️ %s", title)
} else if analyzeResult.IsFail {
title = fmt.Sprintf("✘ %s", title)
}
table.Rows = append(table.Rows, []string{
title,
})
if analyzeResult.IsPass {
if i == selectedResult {
table.RowStyles[i] = ui.NewStyle(ui.ColorGreen, ui.ColorClear, ui.ModifierReverse)
} else {
table.RowStyles[i] = ui.NewStyle(ui.ColorGreen, ui.ColorClear)
}
} else if analyzeResult.IsWarn {
if i == selectedResult {
table.RowStyles[i] = ui.NewStyle(ui.ColorYellow, ui.ColorClear, ui.ModifierReverse)
} else {
table.RowStyles[i] = ui.NewStyle(ui.ColorYellow, ui.ColorClear)
}
} else if analyzeResult.IsFail {
if i == selectedResult {
table.RowStyles[i] = ui.NewStyle(ui.ColorRed, ui.ColorClear, ui.ModifierReverse)
} else {
table.RowStyles[i] = ui.NewStyle(ui.ColorRed, ui.ColorClear)
}
}
}
ui.Render(table)
}
func drawDetails(analysisResult *analyzerunner.AnalyzeResult) {
termWidth, _ := ui.TerminalDimensions()
currentTop := 4
title := widgets.NewParagraph()
title.Text = analysisResult.Title
title.Border = false
if analysisResult.IsPass {
title.TextStyle = ui.NewStyle(ui.ColorGreen, ui.ColorClear, ui.ModifierBold)
} else if analysisResult.IsWarn {
title.TextStyle = ui.NewStyle(ui.ColorYellow, ui.ColorClear, ui.ModifierBold)
} else if analysisResult.IsFail {
title.TextStyle = ui.NewStyle(ui.ColorRed, ui.ColorClear, ui.ModifierBold)
}
height := estimateNumberOfLines(title.Text, termWidth/2)
title.SetRect(termWidth/2, currentTop, termWidth, currentTop+height)
ui.Render(title)
currentTop = currentTop + height + 1
message := widgets.NewParagraph()
message.Text = analysisResult.Message
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
height = estimateNumberOfLines(uri.Text, termWidth/2)
uri.SetRect(termWidth/2, currentTop, termWidth, currentTop+height)
ui.Render(uri)
currentTop = currentTop + height + 1
}
}
func estimateNumberOfLines(text string, width int) int {
lines := len(text)/width + 1
return lines
}
func showSaved(filename string) {
termWidth, termHeight := ui.TerminalDimensions()
savedMessage := widgets.NewParagraph()
savedMessage.Text = fmt.Sprintf("Support Bundle analysis results saved to\n\n%s", filename)
savedMessage.WrapText = true
savedMessage.Border = true
left := termWidth/2 - 20
right := termWidth/2 + 20
top := termHeight/2 - 4
bottom := termHeight/2 + 4
savedMessage.SetRect(left, top, right, bottom)
ui.Render(savedMessage)
isShowingSaved = true
}
func save(analyzeResults []*analyzerunner.AnalyzeResult) (string, error) {
filename := path.Join(util.HomeDir(), fmt.Sprintf("%s-results.txt", "support-bundle"))
_, err := os.Stat(filename)
if err == nil {
os.Remove(filename)
}
results := ""
for _, analyzeResult := range analyzeResults {
result := ""
if analyzeResult.IsPass {
result = "Check PASS\n"
} else if analyzeResult.IsWarn {
result = "Check WARN\n"
} else if analyzeResult.IsFail {
result = "Check FAIL\n"
}
result = result + fmt.Sprintf("Title: %s\n", analyzeResult.Title)
result = result + fmt.Sprintf("Message: %s\n", analyzeResult.Message)
if analyzeResult.URI != "" {
result = result + fmt.Sprintf("URI: %s\n", analyzeResult.URI)
}
result = result + "\n------------\n"
results = results + result
}
if err := ioutil.WriteFile(filename, []byte(results), 0644); err != nil {
return "", errors.Wrap(err, "failed to save preflight results")
}
return filename, nil
}
+11 -18
View File
@@ -5,24 +5,20 @@ import (
"os"
"strings"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/logger"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
var (
KubernetesConfigFlags *genericclioptions.ConfigFlags
)
func RootCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "troubleshoot [url]",
Use: "support-bundle [url]",
Args: cobra.MinimumNArgs(1),
Short: "Generate and manage support bundles",
Short: "Generate a support bundle",
Long: `A support bundle is an archive of files, output, metrics and state
from a server that can be used to assist when troubleshooting a server.`,
from a server that can be used to assist when troubleshooting a Kubernetes cluster.`,
SilenceUsage: true,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlags(cmd.Flags())
@@ -40,24 +36,21 @@ from a server that can be used to assist when troubleshooting a server.`,
cmd.AddCommand(Analyze())
cmd.AddCommand(VersionCmd())
cmd.Flags().String("collectors", "", "name of the collectors to use")
cmd.Flags().String("image", "", "the full name of the collector image to use")
cmd.Flags().String("pullpolicy", "", "the pull policy of the collector image")
cmd.Flags().StringSlice("redactors", []string{}, "names of the additional redactors to use")
cmd.Flags().Bool("redact", true, "enable/disable default redactions")
cmd.Flags().Bool("collect-without-permissions", false, "always run troubleshoot collectors even if some require permissions that troubleshoot does not have")
cmd.Flags().Bool("collect-without-permissions", false, "always generate a support bundle, even if it some require additional permissions")
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.")
// hidden in favor of the `insecure-skip-tls-verify` flag
cmd.Flags().Bool("allow-insecure-connections", false, "don't verify TLS certs when retrieving spec and reporting results")
cmd.Flags().Bool("allow-insecure-connections", false, "when set, do not verify TLS certs when retrieving spec and reporting results")
cmd.Flags().MarkHidden("allow-insecure-connections")
cmd.Flags().String("serviceaccount", "", "name of the service account to use. if not provided, one will be created")
viper.BindPFlags(cmd.Flags())
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
KubernetesConfigFlags = genericclioptions.NewConfigFlags(false)
KubernetesConfigFlags.AddFlags(cmd.Flags())
k8sutil.AddFlags(cmd.Flags())
return cmd
}
@@ -73,7 +66,7 @@ func initConfig() {
viper.AutomaticEnv()
}
func ensureCollectorInList(list []*troubleshootv1beta1.Collect, collector troubleshootv1beta1.Collect) []*troubleshootv1beta1.Collect {
func ensureCollectorInList(list []*troubleshootv1beta2.Collect, collector troubleshootv1beta2.Collect) []*troubleshootv1beta2.Collect {
for _, inList := range list {
if collector.ClusterResources != nil && inList.ClusterResources != nil {
return list
+442 -130
View File
@@ -1,15 +1,20 @@
package cli
import (
"archive/tar"
"bytes"
"compress/gzip"
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"sort"
"strings"
"time"
@@ -17,16 +22,21 @@ import (
"github.com/fatih/color"
"github.com/manifoldco/promptui"
"github.com/mattn/go-isatty"
"github.com/mholt/archiver"
"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/cmd/util"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme"
troubleshootclientsetscheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/convert"
"github.com/replicatedhq/troubleshoot/pkg/docrewrite"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/redact"
"github.com/replicatedhq/troubleshoot/pkg/specs"
"github.com/spf13/viper"
spin "github.com/tj/go-spin"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var (
@@ -50,42 +60,53 @@ func runTroubleshoot(v *viper.Viper, arg string) error {
return errors.Wrap(err, "failed to load collector spec")
}
multidocs := strings.Split(string(collectorContent), "---")
multidocs := strings.Split(string(collectorContent), "\n---\n")
// we suppory both raw collector kinds and supportbundle kinds here
supportBundleSpec, err := parseSupportBundleFromDoc([]byte(multidocs[0]))
if err != nil {
return errors.Wrap(err, "failed to parse collector")
}
troubleshootclientsetscheme.AddToScheme(scheme.Scheme)
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode([]byte(multidocs[0]), nil, nil)
if err != nil {
return errors.Wrapf(err, "failed to parse %s", arg)
}
collector := obj.(*troubleshootv1beta1.Collector)
additionalRedactors := &troubleshootv1beta1.Redactor{}
additionalRedactors := &troubleshootv1beta2.Redactor{}
for idx, redactor := range v.GetStringSlice("redactors") {
redactorContent, err := loadSpec(v, redactor)
if err != nil {
return errors.Wrapf(err, "failed to load redactor spec #%d", idx)
}
redactorContent, err = docrewrite.ConvertToV1Beta2(redactorContent)
if err != nil {
return errors.Wrap(err, "failed to convert to v1beta2")
}
obj, _, err := decode([]byte(redactorContent), nil, nil)
if err != nil {
return errors.Wrapf(err, "failed to parse redactors %s", redactor)
}
loopRedactors, ok := obj.(*troubleshootv1beta1.Redactor)
loopRedactors, ok := obj.(*troubleshootv1beta2.Redactor)
if !ok {
return fmt.Errorf("%s is not a troubleshootv1beta1 redactor type", redactor)
return fmt.Errorf("%s is not a troubleshootv1beta2 redactor type", redactor)
}
if loopRedactors != nil {
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, loopRedactors.Spec.Redactors...)
}
}
for i, additionalDoc := range multidocs[1:] {
obj, _, err := decode([]byte(additionalDoc), nil, nil)
for i, additionalDoc := range multidocs {
if i == 0 {
continue
}
additionalDoc, err := docrewrite.ConvertToV1Beta2([]byte(additionalDoc))
if err != nil {
return errors.Wrap(err, "failed to convert to v1beta2")
}
obj, _, err := decode(additionalDoc, nil, nil)
if err != nil {
return errors.Wrapf(err, "failed to parse additional doc %d", i)
}
multidocRedactors, ok := obj.(*troubleshootv1beta1.Redactor)
multidocRedactors, ok := obj.(*troubleshootv1beta2.Redactor)
if !ok {
continue
}
@@ -95,6 +116,7 @@ func runTroubleshoot(v *viper.Viper, arg string) error {
s := spin.New()
finishedCh := make(chan bool, 1)
progressChan := make(chan interface{}, 0) // non-zero buffer can result in missed messages
isFinishedChClosed := false
go func() {
currentDir := ""
for {
@@ -120,19 +142,90 @@ func runTroubleshoot(v *viper.Viper, arg string) error {
}
}()
defer func() {
close(finishedCh)
if !isFinishedChClosed {
close(finishedCh)
}
}()
archivePath, err := runCollectors(v, *collector, additionalRedactors, progressChan)
archivePath, err := runCollectors(v, supportBundleSpec.Spec.Collectors, additionalRedactors, progressChan)
if err != nil {
return errors.Wrap(err, "run collectors")
}
fmt.Printf("\r%s\r", cursor.ClearEntireLine())
if len(collector.Spec.AfterCollection) == 0 {
// upload if needed
fileUploaded := false
if len(supportBundleSpec.Spec.AfterCollection) > 0 {
for _, ac := range supportBundleSpec.Spec.AfterCollection {
if ac.UploadResultsTo != nil {
if err := uploadSupportBundle(ac.UploadResultsTo, archivePath); err != nil {
c := color.New(color.FgHiRed)
c.Printf("%s\r * Failed to upload support bundle: %v\n", cursor.ClearEntireLine(), err)
} else {
fileUploaded = true
}
} else if ac.Callback != nil {
if err := callbackSupportBundleAPI(ac.Callback, archivePath); err != nil {
c := color.New(color.FgHiRed)
c.Printf("%s\r * Failed to notify API that support bundle has been uploaded: %v\n", cursor.ClearEntireLine(), err)
}
}
}
}
// perform analysis, if possible
if len(supportBundleSpec.Spec.Analyzers) > 0 {
tmpDir, err := ioutil.TempDir("", "troubleshoot")
if err != nil {
c := color.New(color.FgHiRed)
c.Printf("%s\r * Failed to make directory for analysis: %v\n", cursor.ClearEntireLine(), err)
}
f, err := os.Open(archivePath)
if err != nil {
c := color.New(color.FgHiRed)
c.Printf("%s\r * Failed to open support bundle for analysis: %v\n", cursor.ClearEntireLine(), err)
}
if err := analyzer.ExtractTroubleshootBundle(f, tmpDir); err != nil {
c := color.New(color.FgHiRed)
c.Printf("%s\r * Failed to extract support bundle for analysis: %v\n", cursor.ClearEntireLine(), err)
}
analyzeResults, err := analyzer.AnalyzeLocal(tmpDir, supportBundleSpec.Spec.Analyzers)
if err != nil {
c := color.New(color.FgHiRed)
c.Printf("%s\r * Failed to analyze support bundle: %v\n", cursor.ClearEntireLine(), err)
}
interactive := isatty.IsTerminal(os.Stdout.Fd())
if interactive {
close(finishedCh) // this removes the spinner
isFinishedChClosed = true
if err := showInteractiveResults(supportBundleSpec.Name, analyzeResults); err != nil {
interactive = false
}
}
if !interactive {
data := convert.FromAnalyzerResult(analyzeResults)
formatted, err := json.MarshalIndent(data, "", " ")
if err != nil {
c := color.New(color.FgHiRed)
c.Printf("%s\r * Failed to format analysis: %v\n", cursor.ClearEntireLine(), err)
}
fmt.Printf("%s", formatted)
}
}
if !fileUploaded {
msg := archivePath
if appName := collector.Labels["applicationName"]; appName != "" {
if appName := supportBundleSpec.Labels["applicationName"]; appName != "" {
f := `A support bundle for %s has been created in this directory
named %s. Please upload it on the Troubleshoot page of
the %s Admin Console to begin analysis.`
@@ -144,23 +237,6 @@ the %s Admin Console to begin analysis.`
return nil
}
fileUploaded := false
for _, ac := range collector.Spec.AfterCollection {
if ac.UploadResultsTo != nil {
if err := uploadSupportBundle(ac.UploadResultsTo, archivePath); err != nil {
c := color.New(color.FgHiRed)
c.Printf("%s\r * Failed to upload support bundle: %v\n", cursor.ClearEntireLine(), err)
} else {
fileUploaded = true
}
} else if ac.Callback != nil {
if err := callbackSupportBundleAPI(ac.Callback, archivePath); err != nil {
c := color.New(color.FgHiRed)
c.Printf("%s\r * Failed to notify API that support bundle has been uploaded: %v\n", cursor.ClearEntireLine(), err)
}
}
}
fmt.Printf("\r%s\r", cursor.ClearEntireLine())
if fileUploaded {
fmt.Printf("A support bundle has been created and uploaded to your cluster for analysis. Please visit the Troubleshoot page to continue.\n")
@@ -173,6 +249,21 @@ the %s Admin Console to begin analysis.`
func loadSpec(v *viper.Viper, arg string) ([]byte, error) {
var err error
if strings.HasPrefix(arg, "secret/") {
// format secret/namespace-name/secret-name
pathParts := strings.Split(arg, "/")
if len(pathParts) != 3 {
return nil, errors.Errorf("path %s must have 3 components", arg)
}
spec, err := specs.LoadFromSecret(pathParts[1], pathParts[2], "support-bundle-spec")
if err != nil {
return nil, errors.Wrap(err, "failed to get spec from secret")
}
return spec, nil
}
if _, err = os.Stat(arg); err == nil {
b, err := ioutil.ReadFile(arg)
if err != nil {
@@ -184,6 +275,14 @@ func loadSpec(v *viper.Viper, arg string) ([]byte, error) {
return nil, fmt.Errorf("%s is not a URL and was not found (err %s)", arg, err)
}
spec, err := loadSpecFromURL(v, arg)
if err != nil {
return nil, errors.Wrap(err, "failed to get spec from URL")
}
return spec, nil
}
func loadSpecFromURL(v *viper.Viper, arg string) ([]byte, error) {
for {
req, err := http.NewRequest("GET", arg, nil)
if err != nil {
@@ -193,10 +292,7 @@ func loadSpec(v *viper.Viper, arg string) ([]byte, error) {
req.Header.Set("Bundle-Upload-Host", fmt.Sprintf("%s://%s", req.URL.Scheme, req.URL.Host))
resp, err := httpClient.Do(req)
if err != nil {
if strings.Contains(err.Error(), "x509") && httpClient == http.DefaultClient && canTryInsecure(v) {
httpClient = &http.Client{Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}}
if shouldRetryRequest(err) {
continue
}
return nil, errors.Wrap(err, "execute request")
@@ -212,7 +308,57 @@ func loadSpec(v *viper.Viper, arg string) ([]byte, error) {
}
}
func canTryInsecure(v *viper.Viper) bool {
func parseSupportBundleFromDoc(doc []byte) (*troubleshootv1beta2.SupportBundle, error) {
doc, err := docrewrite.ConvertToV1Beta2(doc)
if err != nil {
return nil, errors.Wrap(err, "failed to convert to v1beta2")
}
troubleshootclientsetscheme.AddToScheme(scheme.Scheme)
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode(doc, nil, nil)
if err != nil {
return nil, errors.Wrap(err, "failed to parse document")
}
collector, ok := obj.(*troubleshootv1beta2.Collector)
if ok {
supportBundle := troubleshootv1beta2.SupportBundle{
TypeMeta: metav1.TypeMeta{
APIVersion: "troubleshoot.sh/v1beta2",
Kind: "SupportBundle",
},
ObjectMeta: collector.ObjectMeta,
Spec: troubleshootv1beta2.SupportBundleSpec{
Collectors: collector.Spec.Collectors,
Analyzers: []*troubleshootv1beta2.Analyze{},
AfterCollection: collector.Spec.AfterCollection,
},
}
return &supportBundle, nil
}
supportBundle, ok := obj.(*troubleshootv1beta2.SupportBundle)
if ok {
return supportBundle, nil
}
return nil, errors.New("spec was not parseable as a troubleshoot kind")
}
func shouldRetryRequest(err error) bool {
if strings.Contains(err.Error(), "x509") && httpClient == http.DefaultClient && canTryInsecure() {
httpClient = &http.Client{Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}}
return true
}
return false
}
func canTryInsecure() bool {
if !isatty.IsTerminal(os.Stdout.Fd()) {
return false
}
@@ -229,44 +375,55 @@ func canTryInsecure(v *viper.Viper) bool {
return true
}
func runCollectors(v *viper.Viper, collector troubleshootv1beta1.Collector, additionalRedactors *troubleshootv1beta1.Redactor, progressChan chan interface{}) (string, error) {
bundlePath, err := ioutil.TempDir("", "troubleshoot")
func runCollectors(v *viper.Viper, collectors []*troubleshootv1beta2.Collect, additionalRedactors *troubleshootv1beta2.Redactor, progressChan chan interface{}) (string, error) {
tmpDir, err := ioutil.TempDir("", "troubleshoot")
if err != nil {
return "", errors.Wrap(err, "create temp dir")
}
defer os.RemoveAll(bundlePath)
defer os.RemoveAll(tmpDir)
filename, err := findFileName("support-bundle-"+time.Now().Format("2006-01-02T15_04_05"), "tar.gz")
if err != nil {
return "", errors.Wrap(err, "find file name")
}
bundlePath := filepath.Join(tmpDir, strings.TrimSuffix(filename, ".tar.gz"))
if err := os.MkdirAll(bundlePath, 0777); err != nil {
return "", errors.Wrap(err, "create bundle dir")
}
if err = writeVersionFile(bundlePath); err != nil {
return "", errors.Wrap(err, "write version file")
}
collectSpecs := make([]*troubleshootv1beta1.Collect, 0, 0)
collectSpecs = append(collectSpecs, collector.Spec.Collectors...)
collectSpecs = ensureCollectorInList(collectSpecs, troubleshootv1beta1.Collect{ClusterInfo: &troubleshootv1beta1.ClusterInfo{}})
collectSpecs = ensureCollectorInList(collectSpecs, troubleshootv1beta1.Collect{ClusterResources: &troubleshootv1beta1.ClusterResources{}})
collectSpecs := make([]*troubleshootv1beta2.Collect, 0, 0)
collectSpecs = append(collectSpecs, collectors...)
collectSpecs = ensureCollectorInList(collectSpecs, troubleshootv1beta2.Collect{ClusterInfo: &troubleshootv1beta2.ClusterInfo{}})
collectSpecs = ensureCollectorInList(collectSpecs, troubleshootv1beta2.Collect{ClusterResources: &troubleshootv1beta2.ClusterResources{}})
config, err := KubernetesConfigFlags.ToRESTConfig()
config, err := k8sutil.GetRESTConfig()
if err != nil {
return "", errors.Wrap(err, "failed to convert kube flags to rest config")
}
var collectors collect.Collectors
var cleanedCollectors collect.Collectors
for _, desiredCollector := range collectSpecs {
collector := collect.Collector{
Redact: true,
Collect: desiredCollector,
ClientConfig: config,
Namespace: v.GetString("namespace"),
PathPrefix: filepath.Base(bundlePath),
}
collectors = append(collectors, &collector)
cleanedCollectors = append(cleanedCollectors, &collector)
}
if err := collectors.CheckRBAC(); err != nil {
if err := cleanedCollectors.CheckRBAC(context.Background()); err != nil {
return "", errors.Wrap(err, "failed to check RBAC for collectors")
}
foundForbidden := false
for _, c := range collectors {
for _, c := range cleanedCollectors {
for _, e := range c.RBACErrors {
foundForbidden = true
progressChan <- e
@@ -277,13 +434,19 @@ func runCollectors(v *viper.Viper, collector troubleshootv1beta1.Collector, addi
return "", errors.New("insufficient permissions to run all collectors")
}
globalRedactors := []*troubleshootv1beta1.Redact{}
globalRedactors := []*troubleshootv1beta2.Redact{}
if additionalRedactors != nil {
globalRedactors = additionalRedactors.Spec.Redactors
}
if v.GetString("since-time") != "" || v.GetString("since") != "" {
err := parseTimeFlags(v, progressChan, &cleanedCollectors)
if err != nil {
return "", err
}
}
// Run preflights collectors synchronously
for _, collector := range collectors {
for _, collector := range cleanedCollectors {
if len(collector.RBACErrors) > 0 {
// don't skip clusterResources collector due to RBAC issues
if collector.Collect.ClusterResources == nil {
@@ -301,7 +464,8 @@ func runCollectors(v *viper.Viper, collector troubleshootv1beta1.Collector, addi
}
if result != nil {
err = saveCollectorOutput(result, bundlePath)
// results already contain the bundle dir name in their paths
err = saveCollectorOutput(result, filepath.Dir(bundlePath), collector)
if err != nil {
progressChan <- fmt.Errorf("failed to parse collector spec %q: %v", collector.GetDisplayName(), err)
continue
@@ -309,11 +473,6 @@ func runCollectors(v *viper.Viper, collector troubleshootv1beta1.Collector, addi
}
}
filename, err := findFileName("support-bundle", "tar.gz")
if err != nil {
return "", errors.Wrap(err, "find file name")
}
if err := tarSupportBundleDir(bundlePath, filename); err != nil {
return "", errors.Wrap(err, "create bundle file")
}
@@ -321,8 +480,15 @@ func runCollectors(v *viper.Viper, collector troubleshootv1beta1.Collector, addi
return filename, nil
}
func saveCollectorOutput(output map[string][]byte, bundlePath string) error {
func saveCollectorOutput(output map[string][]byte, bundlePath string, c *collect.Collector) error {
for filename, maybeContents := range output {
if c.Collect.Copy != nil {
err := untarAndSave(maybeContents, filepath.Join(bundlePath, filepath.Dir(filename)))
if err != nil {
return errors.Wrap(err, "extract copied files")
}
continue
}
fileDir, fileName := filepath.Split(filename)
outPath := filepath.Join(bundlePath, fileDir)
@@ -338,39 +504,99 @@ func saveCollectorOutput(output map[string][]byte, bundlePath string) error {
return nil
}
func uploadSupportBundle(r *troubleshootv1beta1.ResultRequest, archivePath string) error {
func untarAndSave(tarFile []byte, bundlePath string) error {
keys := make([]string, 0)
dirs := make(map[string]*tar.Header)
files := make(map[string][]byte)
fileHeaders := make(map[string]*tar.Header)
tarReader := tar.NewReader(bytes.NewBuffer(tarFile))
//Extract and separate tar contentes in file and folders, keeping header info from each one.
for {
header, err := tarReader.Next()
if err != nil {
if err != io.EOF {
return err
}
break
}
switch header.Typeflag {
case tar.TypeDir:
dirs[header.Name] = header
case tar.TypeReg:
file := new(bytes.Buffer)
_, err = io.Copy(file, tarReader)
if err != nil {
return err
}
files[header.Name] = file.Bytes()
fileHeaders[header.Name] = header
default:
return fmt.Errorf("Tar file entry %s contained unsupported file type %v", header.Name, header.FileInfo().Mode())
}
}
//Create directories from base path: <namespace>/<pod name>/containerPath
if err := os.MkdirAll(filepath.Join(bundlePath), 0777); err != nil {
return errors.Wrap(err, "create output file")
}
//Order folders stored in variable keys to start always by parent folder. That way folder info is preserved.
for k := range dirs {
keys = append(keys, k)
}
sort.Strings(keys)
//Orderly create folders.
for _, k := range keys {
if err := os.Mkdir(filepath.Join(bundlePath, k), dirs[k].FileInfo().Mode().Perm()); err != nil {
return errors.Wrap(err, "create output file")
}
}
//Populate folders with respective files and its permissions stored in the header.
for k, v := range files {
if err := ioutil.WriteFile(filepath.Join(bundlePath, k), v, fileHeaders[k].FileInfo().Mode().Perm()); err != nil {
return err
}
}
return nil
}
func uploadSupportBundle(r *troubleshootv1beta2.ResultRequest, archivePath string) error {
contentType := getExpectedContentType(r.URI)
if contentType != "" && contentType != "application/tar+gzip" {
return fmt.Errorf("cannot upload content type %s", contentType)
}
f, err := os.Open(archivePath)
if err != nil {
return errors.Wrap(err, "open file")
}
defer f.Close()
for {
f, err := os.Open(archivePath)
if err != nil {
return errors.Wrap(err, "open file")
}
defer f.Close()
fileStat, err := f.Stat()
if err != nil {
return errors.Wrap(err, "stat file")
}
fileStat, err := f.Stat()
if err != nil {
return errors.Wrap(err, "stat file")
}
req, err := http.NewRequest(r.Method, r.URI, f)
if err != nil {
return errors.Wrap(err, "create request")
}
req.ContentLength = fileStat.Size()
if contentType != "" {
req.Header.Set("Content-Type", contentType)
}
req, err := http.NewRequest(r.Method, r.URI, f)
if err != nil {
return errors.Wrap(err, "create request")
}
req.ContentLength = fileStat.Size()
if contentType != "" {
req.Header.Set("Content-Type", contentType)
}
resp, err := httpClient.Do(req)
if err != nil {
return errors.Wrap(err, "execute request")
}
resp, err := httpClient.Do(req)
if err != nil {
if shouldRetryRequest(err) {
continue
}
return errors.Wrap(err, "execute request")
}
if resp.StatusCode >= 300 {
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
if resp.StatusCode >= 300 {
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
}
break
}
// send redaction report
@@ -384,19 +610,26 @@ func uploadSupportBundle(r *troubleshootv1beta1.ResultRequest, archivePath strin
return errors.Wrap(err, "get redaction report")
}
req, err := http.NewRequest("PUT", r.RedactURI, bytes.NewReader(redactBytes))
if err != nil {
return errors.Wrap(err, "create redaction report request")
}
req.ContentLength = int64(len(redactBytes))
for {
req, err := http.NewRequest("PUT", r.RedactURI, bytes.NewReader(redactBytes))
if err != nil {
return errors.Wrap(err, "create redaction report request")
}
req.ContentLength = int64(len(redactBytes))
resp, err := httpClient.Do(req)
if err != nil {
return errors.Wrap(err, "execute redaction request")
}
resp, err := httpClient.Do(req)
if err != nil {
if shouldRetryRequest(err) {
continue
}
return errors.Wrap(err, "execute redaction request")
}
if resp.StatusCode >= 300 {
return fmt.Errorf("unexpected redaction status code %d", resp.StatusCode)
if resp.StatusCode >= 300 {
return fmt.Errorf("unexpected redaction status code %d", resp.StatusCode)
}
break
}
}
@@ -411,54 +644,133 @@ func getExpectedContentType(uploadURL string) string {
return parsedURL.Query().Get("Content-Type")
}
func callbackSupportBundleAPI(r *troubleshootv1beta1.ResultRequest, archivePath string) error {
req, err := http.NewRequest(r.Method, r.URI, nil)
if err != nil {
return errors.Wrap(err, "create request")
}
func callbackSupportBundleAPI(r *troubleshootv1beta2.ResultRequest, archivePath string) error {
for {
req, err := http.NewRequest(r.Method, r.URI, nil)
if err != nil {
return errors.Wrap(err, "create request")
}
resp, err := httpClient.Do(req)
if err != nil {
return errors.Wrap(err, "execute request")
}
resp, err := httpClient.Do(req)
if err != nil {
if shouldRetryRequest(err) {
continue
}
return errors.Wrap(err, "execute request")
}
if resp.StatusCode >= 300 {
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
}
if resp.StatusCode >= 300 {
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
}
break
}
return nil
}
func tarSupportBundleDir(inputDir, outputFilename string) error {
tarGz := archiver.TarGz{
Tar: &archiver.Tar{
ImplicitTopLevelFolder: false,
},
}
paths := []string{
filepath.Join(inputDir, VersionFilename), // version file should be first in tar archive for quick extraction
}
topLevelFiles, err := ioutil.ReadDir(inputDir)
fileWriter, err := os.Create(outputFilename)
if err != nil {
return errors.Wrap(err, "list bundle directory contents")
}
for _, f := range topLevelFiles {
if f.Name() == VersionFilename {
continue
}
paths = append(paths, filepath.Join(inputDir, f.Name()))
return errors.Wrap(err, "failed to create output file")
}
defer fileWriter.Close()
if err := tarGz.Archive(paths, outputFilename); err != nil {
return errors.Wrap(err, "create archive")
gzipWriter := gzip.NewWriter(fileWriter)
defer gzipWriter.Close()
tarWriter := tar.NewWriter(gzipWriter)
defer tarWriter.Close()
err = filepath.Walk(inputDir, func(filename string, info os.FileInfo, err error) error {
if err != nil {
return err
}
fileMode := info.Mode()
if !fileMode.IsRegular() { // support bundle can have only files
return nil
}
nameInArchive, err := filepath.Rel(inputDir, filename)
if err != nil {
return errors.Wrap(err, "failed to create relative file name")
}
// tar.FileInfoHeader call causes a crash in static builds
// https://github.com/golang/go/issues/24787
hdr := &tar.Header{
Name: nameInArchive,
ModTime: info.ModTime(),
Mode: int64(fileMode.Perm()),
Typeflag: tar.TypeReg,
Size: info.Size(),
}
err = tarWriter.WriteHeader(hdr)
if err != nil {
return errors.Wrap(err, "failed to write tar header")
}
err = func() error {
fileReader, err := os.Open(filename)
if err != nil {
return errors.Wrap(err, "failed to open source file")
}
defer fileReader.Close()
_, err = io.Copy(tarWriter, fileReader)
if err != nil {
return errors.Wrap(err, "failed to copy file into archive")
}
return nil
}()
if err != nil {
return err
}
return nil
})
if err != nil {
return errors.Wrap(err, "failed to walk source dir")
}
return nil
}
type CollectorFailure struct {
Collector *troubleshootv1beta1.Collect
Collector *troubleshootv1beta2.Collect
Failure string
}
func parseTimeFlags(v *viper.Viper, progressChan chan interface{}, collectors *collect.Collectors) error {
var (
sinceTime time.Time
err error
)
if v.GetString("since-time") != "" {
if v.GetString("since") != "" {
return errors.Errorf("at most one of `sinceTime` or `since` may be specified")
}
sinceTime, err = time.Parse(time.RFC3339, v.GetString("since-time"))
if err != nil {
return errors.Wrap(err, "unable to parse --since-time flag")
}
} else {
parsedDuration, err := time.ParseDuration(v.GetString("since"))
if err != nil {
return errors.Wrap(err, "unable to parse --since flag")
}
now := time.Now()
sinceTime = now.Add(0 - parsedDuration)
}
for _, collector := range *collectors {
if collector.Collect.Logs != nil {
if collector.Collect.Logs.Limits == nil {
collector.Collect.Logs.Limits = new(troubleshootv1beta2.LogLimits)
}
collector.Collect.Logs.Limits.SinceTime = metav1.NewTime(sinceTime)
}
}
return nil
}
-122
View File
@@ -1,122 +0,0 @@
package cli
import (
"fmt"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
func createServiceAccount(collector troubleshootv1beta1.Collector, namespace string, clientset *kubernetes.Clientset) (string, error) {
name := fmt.Sprintf("troubleshoot-%s", collector.Name)
serviceAccount := corev1.ServiceAccount{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ServiceAccount",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
}
_, err := clientset.CoreV1().ServiceAccounts(namespace).Create(&serviceAccount)
if err != nil {
return "", err
}
role := rbacv1.ClusterRole{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ClusterRole",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{
"namespaces",
"pods",
"services",
"secrets",
},
Verbs: metav1.Verbs{"list"},
},
{
APIGroups: []string{"apps"},
Resources: []string{"deployments"},
Verbs: metav1.Verbs{"list"},
},
{
APIGroups: []string{"extensions"},
Resources: []string{"ingresses"},
Verbs: metav1.Verbs{"list"},
},
{
APIGroups: []string{"storage.k8s.io"},
Resources: []string{"storageclasses"},
Verbs: metav1.Verbs{"list"},
},
{
APIGroups: []string{"apiextensions.k8s.io"},
Resources: []string{"customresourcedefinitions"},
Verbs: metav1.Verbs{"list"},
},
},
}
_, err = clientset.RbacV1().ClusterRoles().Create(&role)
if err != nil {
return "", err
}
roleBinding := rbacv1.ClusterRoleBinding{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ClusterRoleBinding",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: name,
Namespace: namespace,
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
Name: name,
},
}
_, err = clientset.RbacV1().ClusterRoleBindings().Create(&roleBinding)
if err != nil {
return "", err
}
return name, nil
}
func removeServiceAccount(name string, namespace string, clientset *kubernetes.Clientset) error {
if err := clientset.RbacV1().ClusterRoleBindings().Delete(name, &metav1.DeleteOptions{}); err != nil {
return err
}
if err := clientset.RbacV1().ClusterRoles().Delete(name, &metav1.DeleteOptions{}); err != nil {
return err
}
if err := clientset.CoreV1().ServiceAccounts(namespace).Delete(name, &metav1.DeleteOptions{}); err != nil {
return err
}
return nil
}
-16
View File
@@ -5,24 +5,8 @@ import (
"os"
"github.com/pkg/errors"
troubleshootclientv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
func createTroubleshootK8sClient(configFlags *genericclioptions.ConfigFlags) (*troubleshootclientv1beta1.TroubleshootV1beta1Client, error) {
config, err := configFlags.ToRESTConfig()
if err != nil {
return nil, errors.Wrap(err, "failed to convert kube flags to rest config")
}
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(config)
if err != nil {
return nil, errors.Wrap(err, "failed to create troubleshoot client")
}
return troubleshootClient, nil
}
func findFileName(basename, extension string) (string, error) {
n := 1
name := basename
+4 -4
View File
@@ -6,7 +6,7 @@ import (
"io/ioutil"
"path/filepath"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/version"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
@@ -29,10 +29,10 @@ func VersionCmd() *cobra.Command {
const VersionFilename = "version.yaml"
func writeVersionFile(path string) error {
version := troubleshootv1beta1.SupportBundleVersion{
ApiVersion: "troubleshoot.replicated.com/v1beta1",
version := troubleshootv1beta2.SupportBundleVersion{
ApiVersion: "troubleshoot.sh/v1beta2",
Kind: "SupportBundle",
Spec: troubleshootv1beta1.SupportBundleVersionSpec{
Spec: troubleshootv1beta2.SupportBundleVersionSpec{
VersionNumber: version.Version(),
},
}
+1 -1
View File
@@ -2,7 +2,7 @@ package main
import (
"github.com/replicatedhq/troubleshoot/cmd/troubleshoot/cli"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
_ "k8s.io/client-go/plugin/pkg/client/auth"
)
func main() {
+17
View File
@@ -3,6 +3,7 @@ package util
import (
"net/url"
"os"
"strings"
)
func HomeDir() string {
@@ -20,3 +21,19 @@ func IsURL(str string) bool {
return parsed.Scheme != ""
}
func AppName(name string) string {
words := strings.Split(strings.Title(strings.Replace(name, "-", " ", -1)), " ")
casedWords := []string{}
for i, word := range words {
if strings.ToLower(word) == "ai" {
casedWords = append(casedWords, "AI")
} else if strings.ToLower(word) == "io" && i > 0 {
casedWords[i-1] += ".io"
} else {
casedWords = append(casedWords, word)
}
}
return strings.Join(casedWords, " ")
}
@@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.4
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: analyzers.troubleshoot.replicated.com
spec:
@@ -42,7 +42,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
outcomes:
items:
properties:
@@ -83,7 +88,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
outcomes:
items:
properties:
@@ -126,7 +136,12 @@ spec:
customResourceDefinitionName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
outcomes:
items:
properties:
@@ -168,7 +183,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
namespace:
@@ -215,7 +235,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
outcomes:
items:
properties:
@@ -256,7 +281,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
outcomes:
items:
properties:
@@ -300,7 +330,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
ingressName:
type: string
namespace:
@@ -342,12 +377,68 @@ spec:
- namespace
- outcomes
type: object
mysql:
properties:
checkName:
type: string
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
fileName:
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
required:
- collectorName
- outcomes
type: object
nodeResources:
properties:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
filters:
properties:
cpuAllocatable:
@@ -366,6 +457,13 @@ spec:
type: string
podCapacity:
type: string
selector:
properties:
matchLabel:
additionalProperties:
type: string
type: object
type: object
type: object
outcomes:
items:
@@ -402,12 +500,119 @@ spec:
required:
- outcomes
type: object
postgres:
properties:
checkName:
type: string
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
fileName:
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
required:
- collectorName
- outcomes
type: object
redis:
properties:
checkName:
type: string
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
fileName:
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
required:
- collectorName
- outcomes
type: object
secret:
properties:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
key:
type: string
namespace:
@@ -456,7 +661,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
namespace:
@@ -503,7 +713,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
outcomes:
items:
properties:
@@ -549,7 +764,12 @@ spec:
collectorName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
fileName:
type: string
outcomes:
@@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.4
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: collectors.troubleshoot.replicated.com
spec:
@@ -41,20 +41,26 @@ spec:
properties:
method:
type: string
redactUri:
type: string
uri:
type: string
required:
- method
- redactUri
- uri
type: object
uploadResultsTo:
properties:
method:
type: string
redactUri:
type: string
uri:
type: string
required:
- method
- redactUri
- uri
type: object
type: object
@@ -67,14 +73,24 @@ spec:
collectorName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
type: object
clusterResources:
properties:
collectorName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
type: object
copy:
properties:
@@ -85,7 +101,12 @@ spec:
containerPath:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
namespace:
@@ -106,7 +127,12 @@ spec:
data:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
required:
@@ -127,7 +153,12 @@ spec:
containerName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
namespace:
@@ -147,7 +178,12 @@ spec:
collectorName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
get:
properties:
headers:
@@ -203,7 +239,12 @@ spec:
type: string
type: array
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
limits:
properties:
maxAge:
@@ -223,6 +264,54 @@ spec:
required:
- selector
type: object
mysql:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
uri:
type: string
required:
- uri
type: object
postgres:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
uri:
type: string
required:
- uri
type: object
redis:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
uri:
type: string
required:
- uri
type: object
run:
properties:
args:
@@ -236,7 +325,12 @@ spec:
type: string
type: array
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
image:
type: string
imagePullPolicy:
@@ -256,7 +350,12 @@ spec:
collectorName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
includeValue:
type: boolean
key:
@@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.4
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: preflights.troubleshoot.replicated.com
spec:
@@ -42,7 +42,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
outcomes:
items:
properties:
@@ -83,7 +88,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
outcomes:
items:
properties:
@@ -126,7 +136,12 @@ spec:
customResourceDefinitionName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
outcomes:
items:
properties:
@@ -168,7 +183,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
namespace:
@@ -215,7 +235,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
outcomes:
items:
properties:
@@ -256,7 +281,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
outcomes:
items:
properties:
@@ -300,7 +330,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
ingressName:
type: string
namespace:
@@ -342,12 +377,68 @@ spec:
- namespace
- outcomes
type: object
mysql:
properties:
checkName:
type: string
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
fileName:
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
required:
- collectorName
- outcomes
type: object
nodeResources:
properties:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
filters:
properties:
cpuAllocatable:
@@ -366,6 +457,13 @@ spec:
type: string
podCapacity:
type: string
selector:
properties:
matchLabel:
additionalProperties:
type: string
type: object
type: object
type: object
outcomes:
items:
@@ -402,12 +500,119 @@ spec:
required:
- outcomes
type: object
postgres:
properties:
checkName:
type: string
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
fileName:
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
required:
- collectorName
- outcomes
type: object
redis:
properties:
checkName:
type: string
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
fileName:
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
required:
- collectorName
- outcomes
type: object
secret:
properties:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
key:
type: string
namespace:
@@ -456,7 +661,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
namespace:
@@ -503,7 +713,12 @@ spec:
checkName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
outcomes:
items:
properties:
@@ -549,7 +764,12 @@ spec:
collectorName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
fileName:
type: string
outcomes:
@@ -601,14 +821,24 @@ spec:
collectorName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
type: object
clusterResources:
properties:
collectorName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
type: object
copy:
properties:
@@ -619,7 +849,12 @@ spec:
containerPath:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
namespace:
@@ -640,7 +875,12 @@ spec:
data:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
required:
@@ -661,7 +901,12 @@ spec:
containerName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
namespace:
@@ -681,7 +926,12 @@ spec:
collectorName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
get:
properties:
headers:
@@ -737,7 +987,12 @@ spec:
type: string
type: array
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
limits:
properties:
maxAge:
@@ -757,6 +1012,54 @@ spec:
required:
- selector
type: object
mysql:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
uri:
type: string
required:
- uri
type: object
postgres:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
uri:
type: string
required:
- uri
type: object
redis:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
uri:
type: string
required:
- uri
type: object
run:
properties:
args:
@@ -770,7 +1073,12 @@ spec:
type: string
type: array
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
image:
type: string
imagePullPolicy:
@@ -790,7 +1098,12 @@ spec:
collectorName:
type: string
exclude:
type: boolean
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
includeValue:
type: boolean
key:
@@ -0,0 +1,88 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: redactors.troubleshoot.replicated.com
spec:
group: troubleshoot.replicated.com
names:
kind: Redactor
listKind: RedactorList
plural: redactors
singular: redactor
scope: Namespaced
validation:
openAPIV3Schema:
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'
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'
type: string
metadata:
type: object
spec:
description: RedactorSpec defines the desired state of Redactor
properties:
redactors:
items:
properties:
fileSelector:
properties:
file:
type: string
files:
items:
type: string
type: array
type: object
name:
type: string
removals:
properties:
regex:
items:
properties:
redactor:
type: string
selector:
type: string
type: object
type: array
values:
items:
type: string
type: array
yamlPath:
items:
type: string
type: array
type: object
type: object
type: array
type: object
status:
description: RedactorStatus defines the observed state of Redactor
type: object
type: object
version: v1beta1
versions:
- name: v1beta1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
File diff suppressed because it is too large Load Diff
+831
View File
@@ -0,0 +1,831 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: analyzers.troubleshoot.sh
spec:
group: troubleshoot.sh
names:
kind: Analyzer
listKind: AnalyzerList
plural: analyzers
singular: analyzer
scope: Namespaced
validation:
openAPIV3Schema:
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'
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'
type: string
metadata:
type: object
spec:
description: AnalyzerSpec defines the desired state of Analyzer
properties:
analyzers:
items:
properties:
clusterVersion:
properties:
checkName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
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
required:
- outcomes
type: object
containerRuntime:
properties:
checkName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
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
required:
- outcomes
type: object
customResourceDefinition:
properties:
checkName:
type: string
customResourceDefinitionName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
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
required:
- customResourceDefinitionName
- outcomes
type: object
deploymentStatus:
properties:
checkName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
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
required:
- name
- namespace
- outcomes
type: object
distribution:
properties:
checkName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
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
required:
- outcomes
type: object
imagePullSecret:
properties:
checkName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
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
registryName:
type: string
required:
- outcomes
- registryName
type: object
ingress:
properties:
checkName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
ingressName:
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
required:
- ingressName
- namespace
- outcomes
type: object
mysql:
properties:
checkName:
type: string
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
fileName:
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
required:
- collectorName
- outcomes
type: object
nodeResources:
properties:
checkName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
filters:
properties:
cpuAllocatable:
type: string
cpuCapacity:
type: string
ephemeralStorageAllocatable:
type: string
ephemeralStorageCapacity:
type: string
memoryAllocatable:
type: string
memoryCapacity:
type: string
podAllocatable:
type: string
podCapacity:
type: string
selector:
properties:
matchLabel:
additionalProperties:
type: string
type: object
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
required:
- outcomes
type: object
postgres:
properties:
checkName:
type: string
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
fileName:
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
required:
- collectorName
- outcomes
type: object
redis:
properties:
checkName:
type: string
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
fileName:
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
required:
- collectorName
- outcomes
type: object
secret:
properties:
checkName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
key:
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
secretName:
type: string
required:
- namespace
- outcomes
- secretName
type: object
statefulsetStatus:
properties:
checkName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
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
required:
- name
- namespace
- outcomes
type: object
storageClass:
properties:
checkName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
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
storageClassName:
type: string
required:
- outcomes
- storageClassName
type: object
textAnalyze:
properties:
checkName:
type: string
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
fileName:
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
regex:
type: string
regexGroups:
type: string
required:
- outcomes
type: object
type: object
type: array
type: object
status:
description: AnalyzerStatus defines the observed state of Analyzer
type: object
type: object
version: v1beta2
versions:
- name: v1beta2
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
+387
View File
@@ -0,0 +1,387 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: collectors.troubleshoot.sh
spec:
group: troubleshoot.sh
names:
kind: Collector
listKind: CollectorList
plural: collectors
singular: collector
scope: Namespaced
validation:
openAPIV3Schema:
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'
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'
type: string
metadata:
type: object
spec:
description: CollectorSpec defines the desired state of Collector
properties:
afterCollection:
items:
properties:
callback:
properties:
method:
type: string
redactUri:
type: string
uri:
type: string
required:
- method
- redactUri
- uri
type: object
uploadResultsTo:
properties:
method:
type: string
redactUri:
type: string
uri:
type: string
required:
- method
- redactUri
- uri
type: object
type: object
type: array
collectors:
items:
properties:
clusterInfo:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
type: object
clusterResources:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
type: object
copy:
properties:
collectorName:
type: string
containerName:
type: string
containerPath:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
namespace:
type: string
selector:
items:
type: string
type: array
required:
- containerPath
- namespace
- selector
type: object
data:
properties:
collectorName:
type: string
data:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
required:
- data
type: object
exec:
properties:
args:
items:
type: string
type: array
collectorName:
type: string
command:
items:
type: string
type: array
containerName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
name:
type: string
namespace:
type: string
selector:
items:
type: string
type: array
timeout:
type: string
required:
- namespace
- selector
type: object
http:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
get:
properties:
headers:
additionalProperties:
type: string
type: object
insecureSkipVerify:
type: boolean
url:
type: string
required:
- url
type: object
name:
type: string
post:
properties:
body:
type: string
headers:
additionalProperties:
type: string
type: object
insecureSkipVerify:
type: boolean
url:
type: string
required:
- url
type: object
put:
properties:
body:
type: string
headers:
additionalProperties:
type: string
type: object
insecureSkipVerify:
type: boolean
url:
type: string
required:
- url
type: object
type: object
logs:
properties:
collectorName:
type: string
containerNames:
items:
type: string
type: array
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
limits:
properties:
maxAge:
type: string
maxLines:
format: int64
type: integer
type: object
name:
type: string
namespace:
type: string
selector:
items:
type: string
type: array
required:
- selector
type: object
mysql:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
uri:
type: string
required:
- uri
type: object
postgres:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
uri:
type: string
required:
- uri
type: object
redis:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
uri:
type: string
required:
- uri
type: object
run:
properties:
args:
items:
type: string
type: array
collectorName:
type: string
command:
items:
type: string
type: array
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
image:
type: string
imagePullPolicy:
type: string
name:
type: string
namespace:
type: string
timeout:
type: string
required:
- image
- namespace
type: object
secret:
properties:
collectorName:
type: string
exclude:
description: BoolOrString is a type that can hold an bool
or a string. When used in JSON or YAML marshalling and
unmarshalling, it produces or consumes the inner type. This
allows you to have, for example, a JSON field that can accept
a booolean string or raw bool.
type: BoolString
includeValue:
type: boolean
key:
type: string
name:
type: string
namespace:
type: string
required:
- name
type: object
type: object
type: array
type: object
status:
description: CollectorStatus defines the observed state of Collector
type: object
type: object
version: v1beta2
versions:
- name: v1beta2
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,88 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: redactors.troubleshoot.sh
spec:
group: troubleshoot.sh
names:
kind: Redactor
listKind: RedactorList
plural: redactors
singular: redactor
scope: Namespaced
validation:
openAPIV3Schema:
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'
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'
type: string
metadata:
type: object
spec:
description: RedactorSpec defines the desired state of Redactor
properties:
redactors:
items:
properties:
fileSelector:
properties:
file:
type: string
files:
items:
type: string
type: array
type: object
name:
type: string
removals:
properties:
regex:
items:
properties:
redactor:
type: string
selector:
type: string
type: object
type: array
values:
items:
type: string
type: array
yamlPath:
items:
type: string
type: array
type: object
type: object
type: array
type: object
status:
description: RedactorStatus defines the observed state of Redactor
type: object
type: object
version: v1beta2
versions:
- name: v1beta2
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Analyzer
metadata:
labels:
@@ -1,4 +1,4 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Collector
metadata:
name: collector-sample
@@ -1,4 +1,4 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: support-io
+36 -4
View File
@@ -19,10 +19,25 @@ builds:
- id: preflight
goos:
- linux
- darwin
- windows
goarch:
- amd64
env:
- CGO_ENABLED=1
main: cmd/preflight/main.go
ldflags: -s -w
-X github.com/replicatedhq/troubleshoot/pkg/version.version={{.Version}}
-X github.com/replicatedhq/troubleshoot/pkg/version.gitSHA={{.Commit}}
-X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{.Date}}
-extldflags "-static"
flags: -tags netgo -installsuffix netgo
binary: preflight
hooks: {}
- id: preflight-darwin
goos:
- darwin
goarch:
- amd64
env:
- CGO_ENABLED=0
main: cmd/preflight/main.go
@@ -37,10 +52,25 @@ builds:
- id: support-bundle
goos:
- linux
- darwin
- windows
goarch:
- amd64
env:
- CGO_ENABLED=1
main: cmd/troubleshoot/main.go
ldflags: -s -w
-X github.com/replicatedhq/troubleshoot/pkg/version.version={{.Version}}
-X github.com/replicatedhq/troubleshoot/pkg/version.gitSHA={{.Commit}}
-X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{.Date}}
-extldflags "-static"
flags: -tags netgo -installsuffix netgo
binary: support-bundle
hooks: {}
- id: support-bundle-darwin
goos:
- darwin
goarch:
- amd64
env:
- CGO_ENABLED=0
main: cmd/troubleshoot/main.go
@@ -61,11 +91,12 @@ archives:
- id: preflight
builds:
- preflight
- preflight-darwin
format: tar.gz
format_overrides:
- goos: windows
format: zip
name_template: '{{ .Binary }}_{{ .Os }}_{{ .Arch }}'
name_template: 'preflight_{{ .Os }}_{{ .Arch }}'
files:
- licence*
- LICENCE*
@@ -78,11 +109,12 @@ archives:
- id: support-bundle
builds:
- support-bundle
- support-bundle-darwin
format: tar.gz
format_overrides:
- goos: windows
format: zip
name_template: '{{ .Binary }}_{{ .Os }}_{{ .Arch }}'
name_template: 'support-bundle_{{ .Os }}_{{ .Arch }}'
files:
- licence*
- LICENCE*
+18 -19
View File
@@ -31,7 +31,7 @@ spec:
matchLabels:
os: windows
arch: amd64
{{addURIAndSha "https://github.com/replicatedhq/troubleshoot/releases/download/{{ .TagName }}/preflight.exe_windows_amd64.zip" .TagName }}
{{addURIAndSha "https://github.com/replicatedhq/troubleshoot/releases/download/{{ .TagName }}/preflight_windows_amd64.zip" .TagName }}
files:
- from: preflight.exe
to: .
@@ -40,7 +40,23 @@ spec:
bin: preflight.exe
shortDescription: Executes application preflight tests in a cluster
homepage: https://github.com/replicatedhq/troubleshoot
caveats: |
description: |
This plugin executes application-specific preflight checks and conformance
tests against a cluster, prior to installation of an application.
Application developers can create and host a Preflight manifest that
defines the minimum and desired Kubernetes environment for an application.
Before installing the application, a cluster admin can use this plugin to
execute the application preflight checksto identify any missing
components, configuration or incompatibilities between the cluster and the
desired environment.
When executing Preflight tests, the test results will be displayed in a
terminal-based UI on the workstation that executed the command.
For information on creating a Preflight manifest, view the documentation
at https://help.replicated.com/docs/troubleshoot/kubernetes/analysis/
Usage:
$ kubectl preflight <uri/path>
@@ -59,20 +75,3 @@ spec:
For application developers writing collectors and analyzers:
https://help.replicated.com/docs/troubleshoot/kubernetes/collectors/defining-collectors/
description: |
This plugin executes application-specific preflight checks and conformance
tests against a cluster, prior to installation of an application.
Application developers can create and host a Preflight manifest that
defines the minimum and desired Kubernetes environment for an application.
Before installing the application, a cluster admin can use this plugin to
execute the application preflight checksto identify any missing
components, configuration or incompatibilities between the cluster and the
desired environment.
When executing Preflight tests, the test results will be displayed in a
terminal-based UI on the workstation that executed the command.
For information on creating a Preflight manifest, view the documentation
at https://help.replicated.com/docs/troubleshoot/kubernetes/analysis/
+15 -16
View File
@@ -31,7 +31,7 @@ spec:
matchLabels:
os: windows
arch: amd64
{{addURIAndSha "https://github.com/replicatedhq/troubleshoot/releases/download/{{ .TagName }}/support-bundle.exe_windows_amd64.zip" .TagName }}
{{addURIAndSha "https://github.com/replicatedhq/troubleshoot/releases/download/{{ .TagName }}/support-bundle_windows_amd64.zip" .TagName }}
files:
- from: support-bundle.exe
to: .
@@ -40,7 +40,20 @@ spec:
bin: support-bundle.exe
shortDescription: Creates support bundles for off-cluster analysis
homepage: https://github.com/replicatedhq/troubleshoot
caveats: |
description: |
This plugin collects information about the cluster, and automatically
redacts sensitive data from being collected. This can optionally include
application-specific data. The plugin writes the collected files into a
single archive named support-bundle.tar.gz. This archive can be manually
inspected or uploaded to https://vendor.replicated.com for automated
analysis.
Application developers can create and host a Collector manifest that
defines information to be collected.
For information on creating a Collector manifest, view the documentation
at https://help.replicated.com/docs/troubleshoot/kubernetes/collectors/defining-collectors/
Usage:
$ kubectl support-bundle <uri>
@@ -59,17 +72,3 @@ spec:
For application developers writing collectors and analyzers:
https://help.replicated.com/docs/troubleshoot/kubernetes/collectors/defining-collectors/
description: |
This plugin collects information about the cluster, and automatically
redacts sensitive data from being collected. This can optionally include
application-specific data. The plugin writes the collected files into a
single archive named support-bundle.tar.gz. This archive can be manually
inspected or uploaded to https://vendor.replicated.com for automated
analysis.
Application developers can create and host a Collector manifest that
defines information to be collected.
For information on creating a Collector manifest, view the documentation
at https://help.replicated.com/docs/troubleshoot/kubernetes/collectors/defining-collectors/
+1 -1
View File
@@ -1,4 +1,4 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: sample
+1 -1
View File
@@ -1,4 +1,4 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: sample
+1 -1
View File
@@ -1,4 +1,4 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: sample
+93 -29
View File
@@ -1,20 +1,14 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: example-preflight-checks
name: example
spec:
collectors:
- secret:
name: myapp-postgres
namespace: default
key: uri
includeValue: false
analyzers:
- clusterVersion:
outcomes:
- fail:
when: "< 1.13.0"
message: The application requires at Kubernetes 1.13.0 or later, and recommends 1.15.0.
message: This application requires at least Kubernetes 1.13.0 or later, and recommends 1.15.0.
uri: https://www.kubernetes.io
- warn:
when: "< 1.15.0"
@@ -24,32 +18,102 @@ spec:
when: ">= 1.15.0"
message: Your cluster meets the recommended and required versions of Kubernetes.
- customResourceDefinition:
customResourceDefinitionName: constrainttemplates.templates.gatekeeper.sh
checkName: Gatekeeper policy runtime
checkName: Ingress
customResourceDefinitionName: ingressroutes.contour.heptio.com
outcomes:
- fail:
message: Gatekeeper is required for the application, but not found in the cluster.
uri: https://enterprise.support.io/installing/gatekeeper
message: Contour ingress not found!
- pass:
message: Found a supported version of Gatekeeper installed and running in the cluster.
- imagePullSecret:
checkName: Registry credentials for Quay.io
registryName: quay.io
message: Contour ingress found!
- containerRuntime:
outcomes:
- pass:
when: "== docker"
message: Docker container runtime was found.
- fail:
message: Did not find Docker container runtime.
- storageClass:
checkName: Required storage classes
storageClassName: "default"
outcomes:
- fail:
message: |
Cannot pull from quay.io. An image pull secret should be deployed to the cluster that has credentials to pull the images. To obtain this secret, please contact your support rep.
uri: https://enterprise.support.io/installing/registry-credentials
message: Could not find a storage class called default.
- pass:
message: Found credentials to pull from quay.io
- secret:
checkName: Postgres connection string
secretName: myapp-postgres
namespace: default
key: uri
message: All good on storage classes
- distribution:
outcomes:
- fail:
message: A secret named "myapp-postgres" must be deployed with a "uri" key
uri: https://enterprise.support.io/installing/postgres
when: "== docker-desktop"
message: The application does not support Docker Desktop Clusters
- fail:
when: "== microk8s"
message: The application does not support Microk8s Clusters
- fail:
when: "== minikube"
message: The application does not support Minikube Clusters
- pass:
message: Found a valid postgres connection string
when: "== eks"
message: EKS is a supported distribution
- pass:
when: "== gke"
message: GKE is a supported distribution
- pass:
when: "== aks"
message: AKS is a supported distribution
# Will be supported in the future
- pass:
when: "== kurl"
message: KURL is a supported distribution
- pass:
when: "== digitalocean"
message: DigitalOcean is a supported distribution
- warn:
message: Unable to determine the distribution of Kubernetes
- nodeResources:
checkName: Must have at least 3 nodes in the cluster, with 5 recommended
outcomes:
- fail:
when: "count() < 3"
message: This application requires at least 3 nodes.
uri: https://kurl.sh/docs/install-with-kurl/adding-nodes
- warn:
when: "count() < 5"
message: This application recommends at last 5 nodes.
uri: https://kurl.sh/docs/install-with-kurl/adding-nodes
- pass:
message: This cluster has enough nodes.
- nodeResources:
checkName: Every node in the cluster must have at least 10 GB of memory, with 32 GB recommended
outcomes:
- fail:
when: "min(memoryCapacity) < 10Gi"
message: All nodes must have at least 10 GB of memory.
uri: https://kurl.sh/docs/install-with-kurl/system-requirements
- warn:
when: "min(memoryCapacity) < 32Gi"
message: All nodes are recommended to have at least 32 GB of memory.
uri: https://kurl.sh/docs/install-with-kurl/system-requirements
- pass:
message: All nodes have at least 32 GB of memory.
- nodeResources:
checkName: Total CPU Cores in the cluster is 4 or greater
outcomes:
- fail:
when: "sum(cpuCapacity) < 4"
message: The cluster must contain at least 4 cores
uri: https://kurl.sh/docs/install-with-kurl/system-requirements
- pass:
message: There are at least 4 cores in the cluster
- nodeResources:
checkName: Every node in the cluster must have at least 40 GB of ephemeral storage, with 100 GB recommended
outcomes:
- fail:
when: "min(ephemeralStorageCapacity) < 40Gi"
message: All nodes must have at least 40 GB of ephemeral storage.
uri: https://kurl.sh/docs/install-with-kurl/system-requirements
- warn:
when: "min(ephemeralStorageCapacity) < 100Gi"
message: All nodes are recommended to have at least 100 GB of ephemeral storage.
uri: https://kurl.sh/docs/install-with-kurl/system-requirements
- pass:
message: All nodes have at least 100 GB of ephemeral storage.
+1 -1
View File
@@ -1,4 +1,4 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: example-preflight-checks
@@ -0,0 +1,24 @@
apiVersion: troubleshoot.sh/v1beta2
kind: Analyzer
metadata:
name: a
spec:
analyzers:
- distribution:
outcomes:
- fail:
when: "= dockerdesktop"
message: "docker for desktop is not allowed"
# - fail:
# when: "microk8s"
# message: "mickrk8s is not prod"
- fail:
when: "!= openshift"
message: "this should fail on anything other than openshift"
- warn:
when: "!= eks"
message: "YMMV on not eks"
- pass:
message: "good work"
@@ -0,0 +1,6 @@
apiVersion: troubleshoot.sh/v1beta2
kind: Collector
metadata:
name: collector-sample
spec:
collectors: []
@@ -0,0 +1,101 @@
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: example
spec:
collectors:
- logs:
selector:
- app=example
- component=nginx
namespace: repl{{ Namespace }}
limits:
maxAge: 30d
maxLines: 10000
analyzers:
- clusterVersion:
outcomes:
- fail:
when: "< 1.16.0"
message: The application requires at least Kubernetes 1.16.0 or later
uri: https://kubernetes.io
- warn:
when: "< 1.17.0"
message: Your cluster meets the minimum version of Kubernetes, but we recommend you update to 1.17.0 or later.
uri: https://kubernetes.io
- pass:
message: Your cluster meets the recommended and required versions of Kubernetes.
- customResourceDefinition:
customResourceDefinitionName: rook
outcomes:
- fail:
message: The Rook CRD was not found in the cluster.
- pass:
message: Rook is installed and available.
- containerRuntime:
outcomes:
- fail:
when: "== docker"
message: The application does not support docker
- pass:
message: A supported container runtime was found
- storageClass:
checkName: Required storage classes
storageClassName: "microk8s-hostpath"
outcomes:
- fail:
message: The microk8s storage class thing was not found
- pass:
message: All good on storage classes
- nodeResources:
checkName: Must have at least 3 nodes in the cluster
outcomes:
- fail:
when: "count() < 3"
message: This application requires at least 3 nodes
- warn:
when: "count() < 5"
message: This application recommends at last 5 nodes.
- pass:
message: This cluster has enough nodes.
- nodeResources:
checkName: Total CPU Cores in the cluster is 4 or greater
outcomes:
- fail:
when: "sum(cpuCapacity) < 4"
message: The cluster must contain at least 4 cores
- pass:
message: There are at least 4 cores in the cluster
- nodeResources:
checkName: Each node must have at least 40 GB of ephemeral storage
outcomes:
- fail:
when: "min(ephemeralStorageCapacity) < 40Gi"
message: Noees in this cluster do not have at least 40 GB of ephemeral storage.
uri: https://kurl.sh/docs/install-with-kurl/system-requirements
- warn:
when: "min(ephemeralStorageCapacity) < 100Gi"
message: Nodes in this cluster are recommended to have at least 100 GB of ephemeral storage.
uri: https://kurl.sh/docs/install-with-kurl/system-requirements
- pass:
message: The nodes in this cluster have enough ephemeral storage.
- ingress:
namespace: default
ingressName: connect-to-me
outcomes:
- fail:
message: The ingress isn't ingressing
- pass:
message: All systems ok on ingress
- deploymentStatus:
name: api
namespace: default
outcomes:
- fail:
when: "< 1"
message: The API deployment does not have any ready replicas.
- warn:
when: "= 1"
message: The API deployment has only a single ready replica.
- pass:
message: There are multiple replicas of the API deployment ready.
+1 -1
View File
@@ -1,4 +1,4 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Analyzer
metadata:
name: a
@@ -1,4 +1,4 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Collector
metadata:
name: collector-sample
+11 -21
View File
@@ -8,17 +8,14 @@ require (
github.com/blang/semver v3.5.1+incompatible
github.com/chzyer/logex v1.1.11-0.20160617073814-96a4d311aa9b // indirect
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 // indirect
github.com/emicklei/go-restful v2.9.6+incompatible // indirect
github.com/fatih/color v1.7.0
github.com/frankban/quicktest v1.7.2 // indirect
github.com/gizak/termui/v3 v3.1.0
github.com/go-openapi/spec v0.19.4 // indirect
github.com/go-redis/redis/v7 v7.2.0
github.com/go-sql-driver/mysql v1.5.0
github.com/gobwas/glob v0.2.3
github.com/golang/snappy v0.0.1 // indirect
github.com/google/go-cmp v0.3.1 // indirect
github.com/google/gofuzz v1.1.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/hashicorp/go-getter v1.3.1-0.20190627223108-da0323b9545e
@@ -28,22 +25,19 @@ require (
github.com/manifoldco/promptui v0.3.2
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.9
github.com/mholt/archiver v3.1.1+incompatible
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
github.com/nicksnyder/go-i18n v1.10.1 // indirect
github.com/nwaples/rardecode v1.0.0 // indirect
github.com/onsi/ginkgo v1.11.0 // indirect
github.com/onsi/gomega v1.9.0 // indirect
github.com/pierrec/lz4 v2.2.6+incompatible // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/procfs v0.0.5 // indirect
github.com/replicatedhq/termui/v3 v3.1.1-0.20200811145416-f40076d26851
github.com/segmentio/ksuid v1.0.3
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.4.0
github.com/stretchr/testify v1.5.1
github.com/tj/go-spin v1.1.0
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/ulikunitz/xz v0.5.6 // indirect
go.opencensus.io v0.22.0 // indirect
go.undefinedlabs.com/scopeagent v0.1.7
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect
@@ -51,14 +45,10 @@ require (
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff // indirect
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20191105091915-95d230a53780 // indirect
gopkg.in/yaml.v2 v2.2.8
k8s.io/api v0.17.4
k8s.io/apiextensions-apiserver v0.15.12-beta.0
k8s.io/apimachinery v0.17.4
k8s.io/cli-runtime v0.17.4
k8s.io/client-go v0.17.4
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c // indirect
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 // indirect
sigs.k8s.io/controller-runtime v0.4.0
sigs.k8s.io/yaml v1.2.0 // indirect
k8s.io/api v0.18.3
k8s.io/apiextensions-apiserver v0.18.2
k8s.io/apimachinery v0.18.3
k8s.io/cli-runtime v0.18.0
k8s.io/client-go v0.18.2
sigs.k8s.io/controller-runtime v0.5.1-0.20200402191424-df180accb901
)
+81 -180
View File
@@ -10,18 +10,21 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
@@ -31,6 +34,7 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412 h1:vOVO0ypMfTt6tZacyI0kp+iCZb1XSNiYDqnzBWYgfe4=
github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412/go.mod h1:AI9hp1tkp10pAlK5TCwL+7yWbRgtDm9jhToq6qij2xs=
github.com/alecthomas/gometalinter v2.0.11+incompatible h1:ENdXMllZNSVDTJUUVIzBW9CSEpntTrQa76iRsEFLX/M=
@@ -38,6 +42,7 @@ github.com/alecthomas/gometalinter v2.0.11+incompatible/go.mod h1:qfIpQGGz3d+Nmg
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
@@ -52,6 +57,7 @@ github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
@@ -68,41 +74,37 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWs
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-oidc v0.0.0-20180117170138-065b426bd416/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.0.0-20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s=
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 h1:pEtiCjIXx3RvGjlUJuCNxNOw0MNblyR9Wi+vJGBFh+8=
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
@@ -113,23 +115,17 @@ github.com/emicklei/go-restful v2.9.6+incompatible h1:tfrHha8zJ01ywiOEC1miGY8st1
github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/frankban/quicktest v1.7.2 h1:2QxQoC1TS09S7fhCPsrvqYdvP1H5M1P1ih5ABm3BTYk=
github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc=
github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
@@ -144,57 +140,52 @@ github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54=
github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q=
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs=
github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
@@ -202,17 +193,13 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
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 v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
@@ -221,14 +208,10 @@ github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:tluoj9z5200j
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -238,9 +221,6 @@ github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -259,25 +239,24 @@ github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE0
github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc h1:cJlkeAx1QYgO5N80aF5xRGstVsRQwgLR7uA2FnP1ZjY=
github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v0.0.0-20170330212424-2500245aa611/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
@@ -307,10 +286,7 @@ github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
@@ -322,8 +298,6 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@@ -354,7 +328,6 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
@@ -363,8 +336,6 @@ github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU=
github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
@@ -379,37 +350,29 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
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 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nicksnyder/go-i18n v1.10.1 h1:isfg77E/aCD7+0lD/D00ebR2MV5vgeQ276WYyDaCRQc=
github.com/nicksnyder/go-i18n v1.10.1/go.mod h1:e4Di5xjP9oTVrC6y3C7C0HoSYXjSbhh/dU0eUV32nB4=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo=
@@ -422,51 +385,42 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pierrec/lz4 v2.2.6+incompatible h1:6aCX4/YZ9v8q69hTyiR7dNLnTA3fgtKHVVW5BCd5Znw=
github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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 v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
github.com/replicatedhq/termui/v3 v3.1.1-0.20200811145416-f40076d26851 h1:eRlNDHxGfVkPCRXbA4BfQJvt5DHjFiTtWy3R/t4djyY=
github.com/replicatedhq/termui/v3 v3.1.1-0.20200811145416-f40076d26851/go.mod h1:JDxG6+uubnk9/BZ2yUsyAJJwlptjrnmB2MPF5d2Xe/8=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/segmentio/ksuid v1.0.3 h1:FoResxvleQwYiPAVKe1tMUlEirodZqlqglIuFsdDntY=
github.com/segmentio/ksuid v1.0.3/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
@@ -492,7 +446,6 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
@@ -502,7 +455,7 @@ github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.0-20180319062004-c439c4fa0937/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
@@ -518,14 +471,13 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tj/go-spin v1.1.0 h1:lhdWZsvImxvZ3q1C5OIB7d72DuOwP4O2NdBg9PyzNds=
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -539,28 +491,27 @@ github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/undefinedlabs/go-mpatch v0.0.0-20200122175732-0044123dbb98 h1:Z/megzdoMbuZ0H/oLmfTw92PAEfyTi1DkvAr8AUzFgw=
github.com/undefinedlabs/go-mpatch v0.0.0-20200122175732-0044123dbb98/go.mod h1:TyJZDQ/5AgyN7FSLiBJ8RO9u2c6wbtRvK827b6AVqY4=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.undefinedlabs.com/scopeagent v0.1.7 h1:TPALQQNkPah8NgVjPKKhaBNZL2hg+hY2GRPfRMSt48c=
@@ -568,28 +519,23 @@ go.undefinedlabs.com/scopeagent v0.1.7/go.mod h1:XCD8wnuBZ+eskhynYNP+uvBiFan9cFr
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -597,7 +543,6 @@ golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -609,12 +554,10 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -622,7 +565,6 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
@@ -633,7 +575,6 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
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/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180117170059-2c42eef0765b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -646,28 +587,23 @@ golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
@@ -680,25 +616,20 @@ golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181122213734-04b5d21e00f1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac h1:MQEvx39qSf8vyrx3XRaOe+j1UDIzKwkYOVObRgGPVqI=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff h1:XdBG6es/oFDr1HwaxkxgVve7NB281QhxgK/i4voubFs=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
@@ -708,11 +639,9 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170731182057-09f6ed296fc6/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -721,19 +650,16 @@ google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@@ -743,27 +669,22 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/natefinch/lumberjack.v2 v2.0.0-20150622162204-20b71e5b60d7/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.0.0-20180411045311-89060dee6a84/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -773,69 +694,49 @@ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48=
k8s.io/api v0.15.12-beta.0/go.mod h1:SeTuJQed4C1QTmlWawryIM+feNPRReglVrvv+KsbpGk=
k8s.io/api v0.17.4 h1:HbwOhDapkguO8lTAE8OX3hdF2qp8GtpC9CW/MQATXXo=
k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA=
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783 h1:V6ndwCPoao1yZ52agqOKaUAl7DYWVGiXjV7ePA2i610=
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY=
k8s.io/apiextensions-apiserver v0.15.12-beta.0 h1:6+6t58za7f5ZwtuCz3zQAkCtn5pKYaSTbPTb3PO3vJA=
k8s.io/apiextensions-apiserver v0.15.12-beta.0/go.mod h1:8Rm/f9mZoGC5Voo4UJjDjsjIoMLAIOVnp2aQ/WCbZiA=
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4=
k8s.io/apimachinery v0.15.12-beta.0/go.mod h1:ZRw+v83FjgEqlzqaBkxL3XB21MSLYdzjsY9Bgxclhdw=
k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw=
k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg=
k8s.io/apiserver v0.15.12-beta.0/go.mod h1:8jB/rq3MrxjXmRNKxH5Uo+TmUndNG+fF91H8wIw2SKs=
k8s.io/cli-runtime v0.17.4 h1:ZIJdxpBEszZqUhydrCoiI5rLXS2J/1AF5xFok2QJ9bc=
k8s.io/cli-runtime v0.17.4/go.mod h1:IVW4zrKKx/8gBgNNkhiUIc7nZbVVNhc1+HcQh+PiNHc=
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
k8s.io/client-go v0.15.12-beta.0/go.mod h1:ytfQFnpRiHki2QY+iyAYxMbq+Cc/aj6VVmPMDYzleQE=
k8s.io/client-go v0.17.4 h1:VVdVbpTY70jiNHS1eiFkUt7ZIJX3txd29nDxxXH4en8=
k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc=
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE=
k8s.io/code-generator v0.15.12-beta.0/go.mod h1:G8bQwmHm2eafm5bgtX67XDZQ8CWKSGu9DekI+yN4Y5I=
k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA=
k8s.io/component-base v0.15.12-beta.0/go.mod h1:jRj5HNzJOrJYcmdWD76c3tlTJXuBMzz5vWHVSqm5e3s=
k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8=
k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
k8s.io/api v0.18.3 h1:2AJaUQdgUZLoDZHrun21PW2Nx9+ll6cUzvn3IKhSIn0=
k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA=
k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo=
k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8=
k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apimachinery v0.18.3 h1:pOGcbVAhxADgUYnjS08EFXs9QMl8qaH5U4fr5LGUrSk=
k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw=
k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
k8s.io/cli-runtime v0.18.0 h1:jG8XpSqQ5TrV0N+EZ3PFz6+gqlCk71dkggWCCq9Mq34=
k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ=
k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8=
k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE=
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
k8s.io/code-generator v0.18.2 h1:C1Nn2JiMf244CvBDKVPX0W2mZFJkVBg54T8OV7/Imso=
k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c=
k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20190822140433-26a664648505 h1:ZY6yclUKVbZ+SdWnkfY+Je5vrMpKOxmGeKRbsXVmqYM=
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120 h1:RPscN6KhmG54S33L+lr3GS+oD1jmchIU0ll519K6FA4=
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM=
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg=
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
sigs.k8s.io/controller-runtime v0.5.1-0.20200402191424-df180accb901 h1:qwHvTyQQBjATQeKPdJ0TwcXdtScjyI6GuiAQ5CSqEvM=
sigs.k8s.io/controller-runtime v0.5.1-0.20200402191424-df180accb901/go.mod h1:j4echH3Y/UPHRpXS65rxGXujda8iWOheMQvDh1uNgaY=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190302045857-e85c7b244fd2/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM=
sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
+96 -18
View File
@@ -4,7 +4,7 @@ import (
"strconv"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/multitype"
)
@@ -28,6 +28,10 @@ func isExcluded(excludeVal multitype.BoolOrString) (bool, error) {
return excludeVal.BoolVal, nil
}
if excludeVal.StrVal == "" {
return false, nil
}
parsed, err := strconv.ParseBool(excludeVal.StrVal)
if err != nil {
return false, errors.Wrap(err, "failed to parse bool string")
@@ -36,7 +40,7 @@ func isExcluded(excludeVal multitype.BoolOrString) (bool, error) {
return parsed, nil
}
func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileContents, findFiles getChildCollectedFileContents) (*AnalyzeResult, error) {
func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
if analyzer.ClusterVersion != nil {
isExcluded, err := isExcluded(analyzer.ClusterVersion.Exclude)
if err != nil {
@@ -45,7 +49,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeClusterVersion(analyzer.ClusterVersion, getFile)
result, err := analyzeClusterVersion(analyzer.ClusterVersion, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.StorageClass != nil {
isExcluded, err := isExcluded(analyzer.StorageClass.Exclude)
@@ -55,7 +63,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeStorageClass(analyzer.StorageClass, getFile)
result, err := analyzeStorageClass(analyzer.StorageClass, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.CustomResourceDefinition != nil {
isExcluded, err := isExcluded(analyzer.CustomResourceDefinition.Exclude)
@@ -65,7 +77,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeCustomResourceDefinition(analyzer.CustomResourceDefinition, getFile)
result, err := analyzeCustomResourceDefinition(analyzer.CustomResourceDefinition, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.Ingress != nil {
isExcluded, err := isExcluded(analyzer.Ingress.Exclude)
@@ -75,7 +91,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeIngress(analyzer.Ingress, getFile)
result, err := analyzeIngress(analyzer.Ingress, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.Secret != nil {
isExcluded, err := isExcluded(analyzer.Secret.Exclude)
@@ -85,7 +105,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeSecret(analyzer.Secret, getFile)
result, err := analyzeSecret(analyzer.Secret, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.ImagePullSecret != nil {
isExcluded, err := isExcluded(analyzer.ImagePullSecret.Exclude)
@@ -95,7 +119,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeImagePullSecret(analyzer.ImagePullSecret, findFiles)
result, err := analyzeImagePullSecret(analyzer.ImagePullSecret, findFiles)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.DeploymentStatus != nil {
isExcluded, err := isExcluded(analyzer.DeploymentStatus.Exclude)
@@ -105,7 +133,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeDeploymentStatus(analyzer.DeploymentStatus, getFile)
result, err := analyzeDeploymentStatus(analyzer.DeploymentStatus, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.StatefulsetStatus != nil {
isExcluded, err := isExcluded(analyzer.StatefulsetStatus.Exclude)
@@ -115,7 +147,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeStatefulsetStatus(analyzer.StatefulsetStatus, getFile)
result, err := analyzeStatefulsetStatus(analyzer.StatefulsetStatus, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.ContainerRuntime != nil {
isExcluded, err := isExcluded(analyzer.ContainerRuntime.Exclude)
@@ -125,7 +161,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeContainerRuntime(analyzer.ContainerRuntime, getFile)
result, err := analyzeContainerRuntime(analyzer.ContainerRuntime, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.Distribution != nil {
isExcluded, err := isExcluded(analyzer.Distribution.Exclude)
@@ -135,7 +175,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeDistribution(analyzer.Distribution, getFile)
result, err := analyzeDistribution(analyzer.Distribution, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.NodeResources != nil {
isExcluded, err := isExcluded(analyzer.NodeResources.Exclude)
@@ -145,7 +189,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeNodeResources(analyzer.NodeResources, getFile)
result, err := analyzeNodeResources(analyzer.NodeResources, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.TextAnalyze != nil {
isExcluded, err := isExcluded(analyzer.TextAnalyze.Exclude)
@@ -155,7 +203,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeTextAnalyze(analyzer.TextAnalyze, getFile)
multiResult, err := analyzeTextAnalyze(analyzer.TextAnalyze, findFiles)
if err != nil {
return nil, err
}
return multiResult, nil
}
if analyzer.Postgres != nil {
isExcluded, err := isExcluded(analyzer.Postgres.Exclude)
@@ -165,7 +217,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzePostgres(analyzer.Postgres, getFile)
result, err := analyzePostgres(analyzer.Postgres, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.Mysql != nil {
isExcluded, err := isExcluded(analyzer.Mysql.Exclude)
@@ -175,7 +231,11 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeMysql(analyzer.Mysql, getFile)
result, err := analyzeMysql(analyzer.Mysql, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.Redis != nil {
isExcluded, err := isExcluded(analyzer.Redis.Exclude)
@@ -185,8 +245,26 @@ func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileCont
if isExcluded {
return nil, nil
}
return analyzeRedis(analyzer.Redis, getFile)
result, err := analyzeRedis(analyzer.Redis, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.Collectd != nil {
isExcluded, err := isExcluded(analyzer.Collectd.Exclude)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
result, err := analyzeCollectd(analyzer.Collectd, findFiles)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
return nil, errors.New("invalid analyzer")
}
+3 -3
View File
@@ -6,11 +6,11 @@ import (
"github.com/blang/semver"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
func analyzeClusterVersion(analyzer *troubleshootv1beta1.ClusterVersion, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
func analyzeClusterVersion(analyzer *troubleshootv1beta2.ClusterVersion, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
clusterInfo, err := getCollectedFileContents("cluster-info/cluster_version.json")
if err != nil {
return nil, errors.Wrap(err, "failed to get contents of cluster_version.json")
@@ -29,7 +29,7 @@ func analyzeClusterVersion(analyzer *troubleshootv1beta1.ClusterVersion, getColl
return analyzeClusterVersionResult(k8sVersion, analyzer.Outcomes, analyzer.CheckName)
}
func analyzeClusterVersionResult(k8sVersion semver.Version, outcomes []*troubleshootv1beta1.Outcome, checkName string) (*AnalyzeResult, error) {
func analyzeClusterVersionResult(k8sVersion semver.Version, outcomes []*troubleshootv1beta2.Outcome, checkName string) (*AnalyzeResult, error) {
for _, outcome := range outcomes {
when := ""
message := ""
+6 -6
View File
@@ -5,28 +5,28 @@ import (
"testing"
"github.com/blang/semver"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"go.undefinedlabs.com/scopeagent"
)
func Test_analyzeClusterVersionResult(t *testing.T) {
outcomes := []*troubleshootv1beta1.Outcome{
outcomes := []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta1.SingleOutcome{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "< 1.13.0",
Message: "Sentry requires at Kubernetes 1.13.0 or later, and recommends 1.15.0.",
URI: "https://www.kubernetes.io",
},
},
{
Warn: &troubleshootv1beta1.SingleOutcome{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "< 1.15.0",
Message: "Your cluster meets the minimum version of Kubernetes, but we recommend you update to 1.15.0 or later.",
URI: "https://www.kubernetes.io",
},
},
{
Pass: &troubleshootv1beta1.SingleOutcome{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "Your cluster meets the recommended and required versions of Kubernetes.",
},
},
@@ -34,7 +34,7 @@ func Test_analyzeClusterVersionResult(t *testing.T) {
type args struct {
k8sVersion semver.Version
outcomes []*troubleshootv1beta1.Outcome
outcomes []*troubleshootv1beta2.Outcome
checkName string
}
tests := []struct {
+316
View File
@@ -0,0 +1,316 @@
package analyzer
import (
"archive/tar"
"bytes"
"io"
"io/ioutil"
"math"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/rrd"
)
type CollectdSummary struct {
Load float64
}
func analyzeCollectd(analyzer *troubleshootv1beta2.CollectdAnalyze, getCollectedFileContents func(string) (map[string][]byte, error)) (*AnalyzeResult, error) {
rrdArchives, err := getCollectedFileContents("/collectd/rrd/*.tar")
if err != nil {
return nil, errors.Wrap(err, "failed to find rrd archives")
}
tmpDir, err := ioutil.TempDir("", "rrd")
if err != nil {
return nil, errors.Wrap(err, "failed to create temp rrd dir")
}
defer os.RemoveAll(tmpDir)
for name, data := range rrdArchives {
destDir := filepath.Join(tmpDir, filepath.Base(name))
if err := extractRRDFiles(data, destDir); err != nil {
return nil, errors.Wrap(err, "failed to extract rrd file")
}
}
loadFiles, err := findRRDLoadFiles(tmpDir)
if err != nil {
return nil, errors.Wrap(err, "failed to find load files")
}
collectdSummary := CollectdSummary{
Load: 0,
}
// load files are always present, so this loop can be used for all host metrics
for _, loadFile := range loadFiles {
pathParts := strings.Split(loadFile, string(filepath.Separator))
if len(pathParts) < 3 {
continue
}
// .../<hostname>/load/load.rrd
hostname := pathParts[len(pathParts)-3]
hostDir := strings.TrimSuffix(loadFile, "/load/load.rrd")
hostLoad, err := getHostLoad(analyzer, loadFile, hostDir)
if err != nil {
return nil, errors.Wrapf(err, "failed to find analyze %s files", hostname)
}
collectdSummary.Load = math.Max(collectdSummary.Load, hostLoad)
}
result, err := getCollectdAnalyzerOutcome(analyzer, collectdSummary)
if err != nil {
return nil, errors.Wrap(err, "failed to generate outcome")
}
return result, nil
}
func extractRRDFiles(archiveData []byte, dst string) error {
tarReader := tar.NewReader(bytes.NewReader(archiveData))
for {
header, err := tarReader.Next()
if err == io.EOF {
return nil
} else if err != nil {
return errors.Wrap(err, "failed to read rrd archive")
}
if header.Typeflag != tar.TypeReg {
continue
}
dstFileName := filepath.Join(dst, header.Name)
if err := os.MkdirAll(filepath.Dir(dstFileName), 0755); err != nil {
return errors.Wrap(err, "failed to create dest path")
}
err = func() error {
f, err := os.Create(dstFileName)
if err != nil {
return errors.Wrap(err, "failed to create dest file")
}
defer f.Close()
_, err = io.Copy(f, tarReader)
if err != nil {
return errors.Wrap(err, "failed to copy")
}
return nil
}()
if err != nil {
return errors.Wrap(err, "failed to write dest file")
}
}
}
func findRRDLoadFiles(rootDir string) ([]string, error) {
files := make([]string, 0)
err := filepath.Walk(rootDir, func(filename string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if filepath.Base(filename) == "load.rrd" {
files = append(files, filename)
}
return nil
})
if err != nil {
return nil, errors.Wrap(err, "failed to find rrd load files")
}
return files, nil
}
func getHostLoad(analyzer *troubleshootv1beta2.CollectdAnalyze, loadFile string, hostRoot string) (float64, error) {
numberOfCPUs := 0
err := filepath.Walk(hostRoot, func(filename string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
return nil
}
if strings.HasPrefix(filepath.Base(filename), "cpu-") {
numberOfCPUs++
}
return nil
})
if err != nil {
return 0, errors.Wrap(err, "failed to find rrd files")
}
if numberOfCPUs == 0 {
numberOfCPUs = 1 // what else can we do here? return an error?
}
fileInfo, err := rrd.Info(loadFile)
if err != nil {
return 0, errors.Wrap(err, "failed to get rrd info")
}
// Query RRD data. Start and end have to be multiples of step.
window := 7 * 24 * time.Hour
step := 1 * time.Hour
lastUpdate := int64(fileInfo["last_update"].(uint))
endSeconds := int64(lastUpdate/int64(step.Seconds())) * int64(step.Seconds())
end := time.Unix(int64(endSeconds), 0)
start := end.Add(-window)
fetchResult, err := rrd.Fetch(loadFile, "MAX", start, end, step)
if err != nil {
return 0, errors.Wrap(err, "failed to fetch load data")
}
defer fetchResult.FreeValues()
values := fetchResult.Values()
maxLoad := float64(0)
for i := 0; i < len(values); i += 3 { // +3 because "shortterm", "midterm", "longterm"
v := values[i+1] // midterm
if math.IsNaN(v) {
continue
}
maxLoad = math.Max(maxLoad, values[i+1])
}
return maxLoad / float64(numberOfCPUs), nil
}
func getCollectdAnalyzerOutcome(analyzer *troubleshootv1beta2.CollectdAnalyze, collectdSummary CollectdSummary) (*AnalyzeResult, error) {
collectorName := analyzer.CollectorName
if collectorName == "" {
collectorName = "rrd"
}
title := analyzer.CheckName
if title == "" {
title = collectorName
}
result := &AnalyzeResult{
Title: title,
IconKey: "host_load_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/rrd-analyze.svg",
}
for _, outcome := range analyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return result, nil
}
isMatch, err := compareCollectdConditionalToActual(outcome.Fail.When, collectdSummary)
if err != nil {
return result, errors.Wrap(err, "failed to compare rrd fail conditional")
}
if isMatch {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return result, nil
}
} else if outcome.Warn != nil {
if outcome.Pass.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return result, nil
}
isMatch, err := compareCollectdConditionalToActual(outcome.Warn.When, collectdSummary)
if err != nil {
return result, errors.Wrap(err, "failed to compare rrd warn conditional")
}
if isMatch {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return result, nil
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return result, nil
}
isMatch, err := compareCollectdConditionalToActual(outcome.Pass.When, collectdSummary)
if err != nil {
return result, errors.Wrap(err, "failed to compare rrd pass conditional")
}
if isMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return result, nil
}
}
}
return result, nil
}
func compareCollectdConditionalToActual(conditional string, collectdSummary CollectdSummary) (bool, error) {
parts := strings.Split(strings.TrimSpace(conditional), " ")
if len(parts) != 3 {
return false, errors.New("unable to parse conditional")
}
switch parts[0] {
case "load":
expected, err := strconv.ParseFloat(parts[2], 64)
if err != nil {
return false, errors.Wrap(err, "failed to parse float")
}
switch parts[1] {
case "=", "==", "===":
return collectdSummary.Load == expected, nil
case "!=", "!==":
return collectdSummary.Load != expected, nil
case "<":
return collectdSummary.Load < expected, nil
case ">":
return collectdSummary.Load > expected, nil
case "<=":
return collectdSummary.Load <= expected, nil
case ">=":
return collectdSummary.Load >= expected, nil
}
return false, errors.Errorf("unknown rrd comparator: %q", parts[0])
}
return false, nil
}
+2 -2
View File
@@ -5,10 +5,10 @@ import (
"strings"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)
func commonStatus(outcomes []*troubleshootv1beta1.Outcome, title, iconKey string, iconURI string, readyReplicas int) (*AnalyzeResult, error) {
func commonStatus(outcomes []*troubleshootv1beta2.Outcome, title, iconKey string, iconURI string, readyReplicas int) (*AnalyzeResult, error) {
result := &AnalyzeResult{
Title: title,
IconKey: iconKey,
+7 -3
View File
@@ -6,11 +6,11 @@ import (
"strings"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
corev1 "k8s.io/api/core/v1"
)
func analyzeContainerRuntime(analyzer *troubleshootv1beta1.ContainerRuntime, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
func analyzeContainerRuntime(analyzer *troubleshootv1beta2.ContainerRuntime, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
collected, err := getCollectedFileContents("cluster-resources/nodes.json")
if err != nil {
return nil, errors.Wrap(err, "failed to get contents of nodes.json")
@@ -26,8 +26,12 @@ func analyzeContainerRuntime(analyzer *troubleshootv1beta1.ContainerRuntime, get
foundRuntimes = append(foundRuntimes, node.Status.NodeInfo.ContainerRuntimeVersion)
}
title := analyzer.CheckName
if title == "" {
title = "Container Runtime"
}
result := &AnalyzeResult{
Title: "Container Runtime",
Title: title,
IconKey: "kubernetes_container_runtime",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/container-runtime.svg?w=23&h=16",
}
+6 -6
View File
@@ -3,7 +3,7 @@ package analyzer
import (
"testing"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.undefinedlabs.com/scopeagent"
@@ -72,22 +72,22 @@ func Test_compareRuntimeConditionalToActual(t *testing.T) {
func Test_containerRuntime(t *testing.T) {
tests := []struct {
name string
analyzer troubleshootv1beta1.ContainerRuntime
analyzer troubleshootv1beta2.ContainerRuntime
expectResult AnalyzeResult
files map[string][]byte
}{
{
name: "no containerd, when it's containerd",
analyzer: troubleshootv1beta1.ContainerRuntime{
Outcomes: []*troubleshootv1beta1.Outcome{
analyzer: troubleshootv1beta2.ContainerRuntime{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "!= containerd",
Message: "pass",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "containerd detected",
},
},
+2 -2
View File
@@ -4,11 +4,11 @@ import (
"encoding/json"
"fmt"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
)
func analyzeCustomResourceDefinition(analyzer *troubleshootv1beta1.CustomResourceDefinition, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
func analyzeCustomResourceDefinition(analyzer *troubleshootv1beta2.CustomResourceDefinition, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
crdData, err := getCollectedFileContents("cluster-resources/custom-resource-definitions.json")
if err != nil {
return nil, err
+8 -4
View File
@@ -35,14 +35,18 @@ func compareDatabaseConditionalToActual(conditional string, result *collect.Data
return false, errors.New("unable to parse postgres connected analyzer")
case "version":
expectedRange, err := semver.ParseRange(fmt.Sprintf("%s %s", parts[1], parts[2]))
expected, err := semver.ParseTolerant(strings.Replace(parts[2], "x", "0", -1))
if err != nil {
return false, errors.Wrap(err, "failed to parse semver range")
return false, errors.Wrap(err, "failed to parse expected version")
}
actual, err := semver.ParseTolerant(strings.Replace(result.Version, "x", "0", -1))
if err != nil {
return false, errors.Wrap(err, "failed to parse postgres db actual version")
}
actual, err := semver.Parse(result.Version)
expectedRange, err := semver.ParseRange(fmt.Sprintf("%s %s", parts[1], expected.String()))
if err != nil {
return false, errors.Wrap(err, "failed to parse actual psotgres version")
return false, errors.Wrap(err, "failed to parse semver range")
}
return expectedRange(actual), nil
+5 -5
View File
@@ -3,15 +3,15 @@ package analyzer
import (
"encoding/json"
"fmt"
"path"
"path/filepath"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
appsv1 "k8s.io/api/apps/v1"
)
func analyzeDeploymentStatus(analyzer *troubleshootv1beta1.DeploymentStatus, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
collected, err := getCollectedFileContents(path.Join("cluster-resources", "deployments", fmt.Sprintf("%s.json", analyzer.Namespace)))
func analyzeDeploymentStatus(analyzer *troubleshootv1beta2.DeploymentStatus, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
collected, err := getCollectedFileContents(filepath.Join("cluster-resources", "deployments", fmt.Sprintf("%s.json", analyzer.Namespace)))
if err != nil {
return nil, errors.Wrap(err, "failed to read collected deployments from namespace")
}
@@ -24,7 +24,7 @@ func analyzeDeploymentStatus(analyzer *troubleshootv1beta1.DeploymentStatus, get
var status *appsv1.DeploymentStatus
for _, deployment := range deployments {
if deployment.Name == analyzer.Name {
status = &deployment.Status
status = deployment.Status.DeepCopy()
}
}
+15 -15
View File
@@ -3,7 +3,7 @@ package analyzer
import (
"testing"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.undefinedlabs.com/scopeagent"
@@ -12,22 +12,22 @@ import (
func Test_deploymentStatus(t *testing.T) {
tests := []struct {
name string
analyzer troubleshootv1beta1.DeploymentStatus
analyzer troubleshootv1beta2.DeploymentStatus
expectResult AnalyzeResult
files map[string][]byte
}{
{
name: "1/1, pass when = 1",
analyzer: troubleshootv1beta1.DeploymentStatus{
Outcomes: []*troubleshootv1beta1.Outcome{
analyzer: troubleshootv1beta2.DeploymentStatus{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "= 1",
Message: "pass",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "fail",
},
},
@@ -50,16 +50,16 @@ func Test_deploymentStatus(t *testing.T) {
},
{
name: "1/1, pass when = 2",
analyzer: troubleshootv1beta1.DeploymentStatus{
Outcomes: []*troubleshootv1beta1.Outcome{
analyzer: troubleshootv1beta2.DeploymentStatus{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Pass: &troubleshootv1beta2.SingleOutcome{
When: "= 2",
Message: "pass",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "fail",
},
},
@@ -82,22 +82,22 @@ func Test_deploymentStatus(t *testing.T) {
},
{
name: "1/1, pass when >= 2, warn when = 1, fail when 0",
analyzer: troubleshootv1beta1.DeploymentStatus{
Outcomes: []*troubleshootv1beta1.Outcome{
analyzer: troubleshootv1beta2.DeploymentStatus{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Pass: &troubleshootv1beta2.SingleOutcome{
When: ">= 2",
Message: "pass",
},
},
{
Warn: &troubleshootv1beta1.SingleOutcome{
Warn: &troubleshootv1beta2.SingleOutcome{
When: "= 1",
Message: "warn",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "fail",
},
},
+41 -8
View File
@@ -2,10 +2,11 @@ package analyzer
import (
"encoding/json"
"fmt"
"strings"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -19,6 +20,8 @@ type providers struct {
openShift bool
kurl bool
aks bool
ibm bool
minikube bool
}
type Provider int
@@ -33,6 +36,8 @@ const (
openShift Provider = iota
kurl Provider = iota
aks Provider = iota
ibm Provider = iota
minikube Provider = iota
)
func CheckOpenShift(foundProviders *providers, apiResources []*metav1.APIResourceList, provider string) string {
@@ -68,6 +73,10 @@ func ParseNodesForProviders(nodes []corev1.Node) (providers, string) {
foundProviders.aks = true
stringProvider = "aks"
}
if k == "minikube.k8s.io/version" {
foundProviders.minikube = true
stringProvider = "minikube"
}
}
if node.Status.NodeInfo.OSImage == "Docker Desktop" {
@@ -87,6 +96,10 @@ func ParseNodesForProviders(nodes []corev1.Node) (providers, string) {
foundProviders.gke = true
stringProvider = "gke"
}
if strings.HasPrefix(node.Spec.ProviderID, "ibm:") {
foundProviders.ibm = true
stringProvider = "ibm"
}
}
if foundMaster {
@@ -100,7 +113,8 @@ func ParseNodesForProviders(nodes []corev1.Node) (providers, string) {
return foundProviders, stringProvider
}
func analyzeDistribution(analyzer *troubleshootv1beta1.Distribution, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
func analyzeDistribution(analyzer *troubleshootv1beta2.Distribution, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
var unknownDistribution string
collected, err := getCollectedFileContents("cluster-resources/nodes.json")
if err != nil {
return nil, errors.Wrap(err, "failed to get contents of nodes.json")
@@ -123,9 +137,12 @@ func analyzeDistribution(analyzer *troubleshootv1beta1.Distribution, getCollecte
}
_ = CheckOpenShift(&foundProviders, apiResources, "")
}
title := analyzer.CheckName
if title == "" {
title = "Kubernetes Distribution"
}
result := &AnalyzeResult{
Title: "Kubernetes Distribution",
Title: title,
IconKey: "kubernetes_distribution",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/distribution.svg?w=20&h=14",
}
@@ -141,7 +158,7 @@ func analyzeDistribution(analyzer *troubleshootv1beta1.Distribution, getCollecte
return result, nil
}
isMatch, err := compareDistributionConditionalToActual(outcome.Fail.When, foundProviders)
isMatch, err := compareDistributionConditionalToActual(outcome.Fail.When, foundProviders, &unknownDistribution)
if err != nil {
return result, errors.Wrap(err, "failed to compare distribution conditional")
}
@@ -162,7 +179,7 @@ func analyzeDistribution(analyzer *troubleshootv1beta1.Distribution, getCollecte
return result, nil
}
isMatch, err := compareDistributionConditionalToActual(outcome.Warn.When, foundProviders)
isMatch, err := compareDistributionConditionalToActual(outcome.Warn.When, foundProviders, &unknownDistribution)
if err != nil {
return result, errors.Wrap(err, "failed to compare distribution conditional")
}
@@ -183,7 +200,7 @@ func analyzeDistribution(analyzer *troubleshootv1beta1.Distribution, getCollecte
return result, nil
}
isMatch, err := compareDistributionConditionalToActual(outcome.Pass.When, foundProviders)
isMatch, err := compareDistributionConditionalToActual(outcome.Pass.When, foundProviders, &unknownDistribution)
if err != nil {
return result, errors.Wrap(err, "failed to compare distribution conditional")
}
@@ -199,10 +216,17 @@ func analyzeDistribution(analyzer *troubleshootv1beta1.Distribution, getCollecte
}
}
result.IsWarn = true
if unknownDistribution != "" {
result.Message = unknownDistribution
} else {
result.Message = "None of the conditionals were met"
}
return result, nil
}
func compareDistributionConditionalToActual(conditional string, actual providers) (bool, error) {
func compareDistributionConditionalToActual(conditional string, actual providers, unknownDistribution *string) (bool, error) {
parts := strings.Split(strings.TrimSpace(conditional), " ")
// we can make this a lot more flexible
@@ -220,6 +244,7 @@ func compareDistributionConditionalToActual(conditional string, actual providers
normalizedName := mustNormalizeDistributionName(parts[1])
if normalizedName == unknown {
*unknownDistribution += fmt.Sprintf("- Unknown distribution: %s ", parts[1])
return false, nil
}
@@ -241,6 +266,10 @@ func compareDistributionConditionalToActual(conditional string, actual providers
isMatch = actual.kurl
case aks:
isMatch = actual.aks
case ibm:
isMatch = actual.ibm
case minikube:
isMatch = actual.minikube
}
switch parts[0] {
@@ -271,6 +300,10 @@ func mustNormalizeDistributionName(raw string) Provider {
return kurl
case "aks":
return aks
case "ibm", "ibmcloud", "ibm cloud":
return ibm
case "minikube":
return minikube
}
return unknown
+2 -1
View File
@@ -9,6 +9,7 @@ import (
)
func Test_compareDistributionConditionalToActual(t *testing.T) {
var unknownDistribution string
tests := []struct {
name string
conditional string
@@ -46,7 +47,7 @@ func Test_compareDistributionConditionalToActual(t *testing.T) {
defer scopetest.End()
req := require.New(t)
actual, err := compareDistributionConditionalToActual(test.conditional, test.input)
actual, err := compareDistributionConditionalToActual(test.conditional, test.input, &unknownDistribution)
req.NoError(err)
assert.Equal(t, test.expected, actual)
+101 -31
View File
@@ -10,8 +10,9 @@ import (
getter "github.com/hashicorp/go-getter"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
troubleshootscheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme"
"github.com/replicatedhq/troubleshoot/pkg/docrewrite"
"github.com/replicatedhq/troubleshoot/pkg/logger"
"k8s.io/client-go/kubernetes/scheme"
)
@@ -20,6 +21,31 @@ type fileContentProvider struct {
rootDir string
}
// Analyze local will analyze a locally available (already downloaded) bundle
func AnalyzeLocal(localBundlePath string, analyzers []*troubleshootv1beta2.Analyze) ([]*AnalyzeResult, error) {
rootDir, err := FindBundleRootDir(localBundlePath)
if err != nil {
return nil, errors.Wrap(err, "failed to find root dir")
}
fcp := fileContentProvider{rootDir: rootDir}
analyzeResults := []*AnalyzeResult{}
for _, analyzer := range analyzers {
analyzeResult, err := Analyze(analyzer, fcp.getFileContents, fcp.getChildFileContents)
if err != nil {
logger.Printf("an analyzer failed to run: %v\n", err)
continue
}
if analyzeResult != nil {
analyzeResults = append(analyzeResults, analyzeResult...)
}
}
return analyzeResults, nil
}
func DownloadAndAnalyze(bundleURL string, analyzersSpec string) ([]*AnalyzeResult, error) {
tmpDir, err := ioutil.TempDir("", "troubleshoot-k8s")
if err != nil {
@@ -31,12 +57,17 @@ func DownloadAndAnalyze(bundleURL string, analyzersSpec string) ([]*AnalyzeResul
return nil, errors.Wrap(err, "failed to download bundle")
}
_, err = os.Stat(filepath.Join(tmpDir, "version.yaml"))
rootDir, err := FindBundleRootDir(tmpDir)
if err != nil {
return nil, errors.Wrap(err, "failed to find root dir")
}
_, err = os.Stat(filepath.Join(rootDir, "version.yaml"))
if err != nil {
return nil, errors.Wrap(err, "failed to read version.yaml")
}
analyzers := []*troubleshootv1beta1.Analyze{}
analyzers := []*troubleshootv1beta2.Analyze{}
if analyzersSpec == "" {
defaultAnalyzers, err := getDefaultAnalyzers()
@@ -52,22 +83,7 @@ func DownloadAndAnalyze(bundleURL string, analyzersSpec string) ([]*AnalyzeResul
analyzers = parsedAnalyzers
}
fcp := fileContentProvider{rootDir: tmpDir}
analyzeResults := []*AnalyzeResult{}
for _, analyzer := range analyzers {
analyzeResult, err := Analyze(analyzer, fcp.getFileContents, fcp.getChildFileContents)
if err != nil {
logger.Printf("an analyzer failed to run: %v\n", err)
continue
}
if analyzeResult != nil {
analyzeResults = append(analyzeResults, analyzeResult)
}
}
return analyzeResults, nil
return AnalyzeLocal(rootDir, analyzers)
}
func downloadTroubleshootBundle(bundleURL string, destDir string) error {
@@ -77,7 +93,7 @@ func downloadTroubleshootBundle(bundleURL string, destDir string) error {
return errors.Wrap(err, "failed to open support bundle")
}
defer f.Close()
return extractTroubleshootBundle(f, destDir)
return ExtractTroubleshootBundle(f, destDir)
}
pwd, err := os.Getwd()
@@ -85,7 +101,7 @@ func downloadTroubleshootBundle(bundleURL string, destDir string) error {
return errors.Wrap(err, "failed to get workdir")
}
tmpDir, err := ioutil.TempDir("", "getter")
tmpDir, err := ioutil.TempDir("", "troubleshoot")
if err != nil {
return errors.Wrap(err, "failed to create tmp dir")
}
@@ -107,10 +123,10 @@ func downloadTroubleshootBundle(bundleURL string, destDir string) error {
}
defer f.Close()
return extractTroubleshootBundle(f, destDir)
return ExtractTroubleshootBundle(f, destDir)
}
func extractTroubleshootBundle(reader io.Reader, destDir string) error {
func ExtractTroubleshootBundle(reader io.Reader, destDir string) error {
gzReader, err := gzip.NewReader(reader)
if err != nil {
return errors.Wrap(err, "failed to create gzip reader")
@@ -134,6 +150,12 @@ func extractTroubleshootBundle(reader io.Reader, destDir string) error {
}
case tar.TypeReg:
name := filepath.Join(destDir, header.Name)
dirName := filepath.Dir(name)
if err := os.MkdirAll(dirName, 0755); err != nil {
return errors.Wrapf(err, "failed to mkdir for file %s", header.Name)
}
file, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, os.FileMode(header.Mode))
if err != nil {
return errors.Wrap(err, "failed to open tar file")
@@ -149,21 +171,26 @@ func extractTroubleshootBundle(reader io.Reader, destDir string) error {
return nil
}
func parseAnalyzers(spec string) ([]*troubleshootv1beta1.Analyze, error) {
func parseAnalyzers(spec string) ([]*troubleshootv1beta2.Analyze, error) {
troubleshootscheme.AddToScheme(scheme.Scheme)
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode([]byte(spec), nil, nil)
convertedSpec, err := docrewrite.ConvertToV1Beta2([]byte(spec))
if err != nil {
return nil, errors.Wrap(err, "failed to convert to v1beta2")
}
obj, _, err := decode(convertedSpec, nil, nil)
if err != nil {
return nil, errors.Wrap(err, "failed to decode analyzers")
}
analyzer := obj.(*troubleshootv1beta1.Analyzer)
analyzer := obj.(*troubleshootv1beta2.Analyzer)
return analyzer.Spec.Analyzers, nil
}
func getDefaultAnalyzers() ([]*troubleshootv1beta1.Analyze, error) {
spec := `apiVersion: troubleshoot.replicated.com/v1beta1
func getDefaultAnalyzers() ([]*troubleshootv1beta2.Analyze, error) {
spec := `apiVersion: troubleshoot.sh/v1beta2
kind: Analyzer
metadata:
name: defaultAnalyzers
@@ -186,12 +213,55 @@ spec:
return parseAnalyzers(spec)
}
// FindBundleRootDir detects whether the bundle is stored inside a subdirectory or not.
// returns the subdirectory path if so, otherwise, returns the path unchanged
func FindBundleRootDir(localBundlePath string) (string, error) {
f, err := os.Open(localBundlePath)
if err != nil {
return "", errors.Wrap(err, "failed to open bundle dir")
}
defer f.Close()
names, err := f.Readdirnames(0)
if err != nil {
return "", errors.Wrap(err, "failed to read dirnames")
}
if len(names) == 0 {
return "", errors.New("bundle directory is empty")
}
isInSubDir := true
for _, name := range names {
if name == "version.yaml" {
isInSubDir = false
break
}
}
if isInSubDir {
return filepath.Join(localBundlePath, names[0]), nil
}
return localBundlePath, nil
}
func (f fileContentProvider) getFileContents(fileName string) ([]byte, error) {
return ioutil.ReadFile(filepath.Join(f.rootDir, fileName))
}
func (f fileContentProvider) getChildFileContents(dirName string) (map[string][]byte, error) {
// TODO: walk sub-dirs
// return nil, errors.New("not implemnted")
return map[string][]byte{}, nil
files, err := filepath.Glob(filepath.Join(f.rootDir, dirName))
if err != nil {
return nil, errors.Wrapf(err, "invalid glob %q", dirName)
}
fileArr := map[string][]byte{}
for _, filePath := range files {
bytes, err := ioutil.ReadFile(filePath)
if err != nil {
return nil, errors.Wrapf(err, "read %q", filePath)
}
fileArr[filePath] = bytes
}
return fileArr, nil
}
+27 -11
View File
@@ -2,34 +2,41 @@ package analyzer
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)
func analyzeImagePullSecret(analyzer *troubleshootv1beta1.ImagePullSecret, getChildCollectedFileContents func(string) (map[string][]byte, error)) (*AnalyzeResult, error) {
func analyzeImagePullSecret(analyzer *troubleshootv1beta2.ImagePullSecret, getChildCollectedFileContents func(string) (map[string][]byte, error)) (*AnalyzeResult, error) {
imagePullSecrets, err := getChildCollectedFileContents("cluster-resources/image-pull-secrets")
if err != nil {
return nil, errors.Wrap(err, "failed to get file contents for image pull secrets")
}
var failOutcome *troubleshootv1beta1.Outcome
var passOutcome *troubleshootv1beta1.Outcome
var failOutcome *troubleshootv1beta2.SingleOutcome
var passOutcome *troubleshootv1beta2.SingleOutcome
for _, outcome := range analyzer.Outcomes {
if outcome.Fail != nil {
failOutcome = outcome
failOutcome = outcome.Fail
} else if outcome.Pass != nil {
passOutcome = outcome
passOutcome = outcome.Pass
}
}
title := analyzer.CheckName
if title == "" {
title = "Image Pull Secrets"
}
result := AnalyzeResult{
Title: analyzer.CheckName,
Title: title,
IconKey: "kubernetes_image_pull_secret",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/image-pull-secret.svg?w=16&h=14",
IsFail: true,
Message: failOutcome.Fail.Message,
URI: failOutcome.Fail.URI,
}
if failOutcome != nil {
result.Message = failOutcome.Message
result.URI = failOutcome.URI
}
for _, v := range imagePullSecrets {
@@ -42,11 +49,20 @@ func analyzeImagePullSecret(analyzer *troubleshootv1beta1.ImagePullSecret, getCh
if registry == analyzer.RegistryName {
result.IsPass = true
result.IsFail = false
result.Message = passOutcome.Pass.Message
result.URI = passOutcome.Pass.URI
if passOutcome != nil {
result.Message = passOutcome.Message
result.URI = passOutcome.URI
}
}
}
}
if result.Message == "" {
if result.IsPass {
result.Message = fmt.Sprintf("Credentials to pull from: %s found", analyzer.RegistryName)
} else {
result.Message = fmt.Sprintf("Credentials to pull from: %s not ound", analyzer.RegistryName)
}
}
return &result, nil
}
+4 -3
View File
@@ -3,13 +3,14 @@ package analyzer
import (
"encoding/json"
"fmt"
"path/filepath"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
)
func analyzeIngress(analyzer *troubleshootv1beta1.Ingress, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
ingressData, err := getCollectedFileContents("cluster-resources/storage-classes.json")
func analyzeIngress(analyzer *troubleshootv1beta2.Ingress, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
ingressData, err := getCollectedFileContents(filepath.Join("cluster-resources", "ingress", fmt.Sprintf("%s.json", analyzer.Namespace)))
if err != nil {
return nil, err
}
+6 -6
View File
@@ -6,11 +6,11 @@ import (
"path"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
func analyzeMysql(analyzer *troubleshootv1beta1.DatabaseAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
func analyzeMysql(analyzer *troubleshootv1beta2.DatabaseAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
collectorName := analyzer.CollectorName
if collectorName == "" {
collectorName = "mysql"
@@ -28,13 +28,13 @@ func analyzeMysql(analyzer *troubleshootv1beta1.DatabaseAnalyze, getCollectedFil
return nil, errors.Wrap(err, "failed to unmarshal databased connection result")
}
checkName := analyzer.CheckName
if checkName == "" {
checkName = collectorName
title := analyzer.CheckName
if title == "" {
title = collectorName
}
result := &AnalyzeResult{
Title: checkName,
Title: title,
IconKey: "kubernetes_mysql_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/mysql-analyze.svg",
}
+64 -27
View File
@@ -8,12 +8,12 @@ import (
"strings"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
)
func analyzeNodeResources(analyzer *troubleshootv1beta1.NodeResources, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
func analyzeNodeResources(analyzer *troubleshootv1beta2.NodeResources, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
collected, err := getCollectedFileContents("cluster-resources/nodes.json")
if err != nil {
return nil, errors.Wrap(err, "failed to get contents of nodes.json")
@@ -94,9 +94,19 @@ func analyzeNodeResources(analyzer *troubleshootv1beta1.NodeResources, getCollec
return result, nil
}
func compareNodeResourceConditionalToActual(conditional string, matchingNodes []corev1.Node, totalNodeCount int) (bool, error) {
func compareNodeResourceConditionalToActual(conditional string, matchingNodes []corev1.Node, totalNodeCount int) (res bool, err error) {
res = false
err = nil
defer func() {
if r := recover(); r != nil {
err = errors.Errorf("failed to evaluate %q: %v", conditional, r)
}
}()
if conditional == "" {
return true, nil
res = true
return
}
parts := strings.Fields(strings.TrimSpace(conditional))
@@ -106,7 +116,8 @@ func compareNodeResourceConditionalToActual(conditional string, matchingNodes []
}
if len(parts) != 3 {
return false, errors.New("unable to parse nodeResources conditional")
err = errors.New("unable to parse nodeResources conditional")
return
}
operator := parts[1]
@@ -117,6 +128,8 @@ func compareNodeResourceConditionalToActual(conditional string, matchingNodes []
parsedDesiredValue, err := strconv.Atoi(parts[2])
if err == nil {
desiredValue = parsedDesiredValue
} else {
err = nil // try parsing as a resource
}
reg := regexp.MustCompile(`(?P<function>.*)\((?P<property>.*)\)`)
@@ -128,7 +141,8 @@ func compareNodeResourceConditionalToActual(conditional string, matchingNodes []
}
if match == nil || len(match) != 3 {
return false, errors.New("conditional does not match pattern of function(property?)")
err = errors.New("conditional does not match pattern of function(property?)")
return
}
function := match[1]
@@ -151,70 +165,86 @@ func compareNodeResourceConditionalToActual(conditional string, matchingNodes []
case "=", "==", "===":
if _, ok := actualValue.(int); ok {
if _, ok := desiredValue.(int); ok {
return actualValue.(int) == desiredValue.(int), nil
res = actualValue.(int) == desiredValue.(int)
return
}
}
if _, ok := desiredValue.(string); ok {
return actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == 0, nil
res = actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == 0
return
}
return actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == 0, nil
res = actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == 0
return
case "<":
if _, ok := actualValue.(int); ok {
if _, ok := desiredValue.(int); ok {
return actualValue.(int) < desiredValue.(int), nil
res = actualValue.(int) < desiredValue.(int)
return
}
}
if _, ok := desiredValue.(string); ok {
return actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == -1, nil
res = actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == -1
return
}
return actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == -1, nil
res = actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == -1
return
case ">":
if _, ok := actualValue.(int); ok {
if _, ok := desiredValue.(int); ok {
return actualValue.(int) > desiredValue.(int), nil
res = actualValue.(int) > desiredValue.(int)
return
}
}
if _, ok := desiredValue.(string); ok {
return actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == 1, nil
res = actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == 1
return
}
return actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == 1, nil
res = actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == 1
return
case "<=":
if _, ok := actualValue.(int); ok {
if _, ok := desiredValue.(int); ok {
return actualValue.(int) <= desiredValue.(int), nil
res = actualValue.(int) <= desiredValue.(int)
return
}
}
if _, ok := desiredValue.(string); ok {
return actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == 0 ||
actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == -1, nil
res = actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == 0 ||
actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == -1
return
}
return actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == 0 ||
actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == -1, nil
res = actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == 0 ||
actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == -1
return
case ">=":
if _, ok := actualValue.(int); ok {
if _, ok := desiredValue.(int); ok {
return actualValue.(int) >= desiredValue.(int), nil
res = actualValue.(int) >= desiredValue.(int)
return
}
}
if _, ok := desiredValue.(string); ok {
return actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == 0 ||
actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == 1, nil
res = actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == 0 ||
actualValue.(*resource.Quantity).Cmp(resource.MustParse(desiredValue.(string))) == 1
return
}
return actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == 0 ||
actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == 1, nil
res = actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == 0 ||
actualValue.(*resource.Quantity).Cmp(resource.MustParse(strconv.Itoa(desiredValue.(int)))) == 1
return
}
return false, errors.New("unexpected conditional in nodeResources")
err = errors.New("unexpected conditional in nodeResources")
return
}
func getQuantity(node corev1.Node, property string) *resource.Quantity {
@@ -283,12 +313,19 @@ func findMax(nodes []corev1.Node, property string) *resource.Quantity {
return max
}
func nodeMatchesFilters(node corev1.Node, filters *troubleshootv1beta1.NodeResourceFilters) (bool, error) {
func nodeMatchesFilters(node corev1.Node, filters *troubleshootv1beta2.NodeResourceFilters) (bool, error) {
if filters == nil {
return true, nil
}
// all filters must pass for this to pass
if filters.Selector != nil {
for k, v := range filters.Selector.MatchLabel {
if l, found := node.Labels[k]; !found || l != v {
return false, errors.Errorf("failed to match label %s", k)
}
}
}
if filters.CPUCapacity != "" {
parsed, err := resource.ParseQuantity(filters.CPUCapacity)
+55 -6
View File
@@ -3,7 +3,7 @@ package analyzer
import (
"testing"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
@@ -67,6 +67,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
totalNodeCount int
matchingNodes []corev1.Node
expected bool
isError bool
}{
{
name: "=",
@@ -74,6 +75,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "count()",
@@ -81,6 +83,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "<",
@@ -88,6 +91,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "count() <",
@@ -95,6 +99,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: ">",
@@ -102,6 +107,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: false,
isError: false,
},
{
name: "count() >",
@@ -109,6 +115,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "count() >= 1 (true)",
@@ -116,6 +123,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "count() <= 2 (true)",
@@ -123,6 +131,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "count() <= 1 (false)",
@@ -130,6 +139,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: false,
isError: false,
},
{
name: "min(memoryCapacity) < 4Gi (true)",
@@ -137,6 +147,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "min(memoryCapacity) >= 4Gi (false)",
@@ -144,6 +155,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: false,
isError: false,
},
{
name: "min(memoryAllocatable) == 16Ki (true)",
@@ -151,6 +163,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "min(cpuCapacity) == 2 (true)",
@@ -158,6 +171,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "min(cpuAllocatable) == 1.5 (true)",
@@ -165,6 +179,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "min(podCapacity) == 15 (true)",
@@ -172,6 +187,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "min(podAllocatable) == 12 (true)",
@@ -179,6 +195,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "min(ephemeralStorageCapacity) <= 20Gi (true)",
@@ -186,6 +203,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "min(ephemeralStorageCapacity) > 20Gi (false)",
@@ -193,6 +211,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: false,
isError: false,
},
{
name: "min(ephemeralStorageAllocatable) == 12316009748 (true)",
@@ -200,6 +219,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "max(memoryCapacity) == 7951376Ki (true)",
@@ -207,6 +227,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "max(memoryAllocatable) == 7848976Ki (true)",
@@ -214,6 +235,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "max(cpuCapacity) == 12 (false)",
@@ -221,6 +243,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: false,
isError: false,
},
{
name: "max(cpuCapacity) == 4 (true)",
@@ -228,6 +251,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "max(cpuAllocatable) == 3 (true)",
@@ -235,6 +259,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "max(podCapacity) == 29 (true)",
@@ -242,6 +267,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "max(podAllocatable) == 14 (true)",
@@ -249,6 +275,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "max(ephemeralStorageCapacity) == 20959212Ki (true)",
@@ -256,6 +283,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "max(ephemeralStorageAllocatable) == 19316009748 (true)",
@@ -263,6 +291,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "sum(memoryCapacity) > 7951376Ki (true)",
@@ -270,6 +299,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "sum(memoryAllocatable) > 7848976Ki (true)",
@@ -277,6 +307,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "sum(cpuCapacity) > 5 (true)",
@@ -284,6 +315,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "sum(cpuAllocatable) == 4.5 (true)",
@@ -291,6 +323,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "sum(podCapacity) == 44 (true)",
@@ -298,6 +331,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "sum(podAllocatable) == 26 (true)",
@@ -305,6 +339,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "sum(ephemeralStorageCapacity) > 20959212Ki (true)",
@@ -312,6 +347,7 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "sum(ephemeralStorageAllocatable) > 19316009748 (true)",
@@ -319,6 +355,15 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: true,
isError: false,
},
{
name: "sum(ephemeralStorageAllocatable) > 19316009748 (error)",
conditional: "sum(ephemeralStorageAllocatable) > \"19316009748\"",
matchingNodes: nodeData,
totalNodeCount: len(nodeData),
expected: false,
isError: true,
},
}
@@ -327,7 +372,11 @@ func Test_compareNodeResourceConditionalToActual(t *testing.T) {
req := require.New(t)
actual, err := compareNodeResourceConditionalToActual(test.conditional, test.matchingNodes, test.totalNodeCount)
req.NoError(err)
if test.isError {
req.Error(err)
} else {
req.NoError(err)
}
assert.Equal(t, test.expected, actual)
@@ -362,13 +411,13 @@ func Test_nodeMatchesFilters(t *testing.T) {
tests := []struct {
name string
node corev1.Node
filters *troubleshootv1beta1.NodeResourceFilters
filters *troubleshootv1beta2.NodeResourceFilters
expectResult bool
}{
{
name: "true when empty filters",
node: node,
filters: &troubleshootv1beta1.NodeResourceFilters{},
filters: &troubleshootv1beta2.NodeResourceFilters{},
expectResult: true,
},
{
@@ -379,7 +428,7 @@ func Test_nodeMatchesFilters(t *testing.T) {
{
name: "false when allocatable memory is too high",
node: node,
filters: &troubleshootv1beta1.NodeResourceFilters{
filters: &troubleshootv1beta2.NodeResourceFilters{
MemoryAllocatable: "16Gi",
},
expectResult: false,
@@ -387,7 +436,7 @@ func Test_nodeMatchesFilters(t *testing.T) {
{
name: "true when allocatable memory is available",
node: node,
filters: &troubleshootv1beta1.NodeResourceFilters{
filters: &troubleshootv1beta2.NodeResourceFilters{
MemoryAllocatable: "4Gi",
},
expectResult: true,
+6 -6
View File
@@ -6,11 +6,11 @@ import (
"path"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
func analyzePostgres(analyzer *troubleshootv1beta1.DatabaseAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
func analyzePostgres(analyzer *troubleshootv1beta2.DatabaseAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
collectorName := analyzer.CollectorName
if collectorName == "" {
collectorName = "postgres"
@@ -28,13 +28,13 @@ func analyzePostgres(analyzer *troubleshootv1beta1.DatabaseAnalyze, getCollected
return nil, errors.Wrap(err, "failed to unmarshal databased connection result")
}
checkName := analyzer.CheckName
if checkName == "" {
checkName = collectorName
title := analyzer.CheckName
if title == "" {
title = collectorName
}
result := &AnalyzeResult{
Title: checkName,
Title: title,
IconKey: "kubernetes_postgres_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/postgres-analyze.svg",
}
+6 -6
View File
@@ -6,11 +6,11 @@ import (
"path"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
func analyzeRedis(analyzer *troubleshootv1beta1.DatabaseAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
func analyzeRedis(analyzer *troubleshootv1beta2.DatabaseAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
collectorName := analyzer.CollectorName
if collectorName == "" {
collectorName = "redis"
@@ -28,13 +28,13 @@ func analyzeRedis(analyzer *troubleshootv1beta1.DatabaseAnalyze, getCollectedFil
return nil, errors.Wrap(err, "failed to unmarshal database connection result")
}
checkName := analyzer.CheckName
if checkName == "" {
checkName = collectorName
title := analyzer.CheckName
if title == "" {
title = collectorName
}
result := &AnalyzeResult{
Title: checkName,
Title: title,
IconKey: "kubernetes_redis_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/redis-analyze.svg",
}
+3 -3
View File
@@ -4,11 +4,11 @@ import (
"encoding/json"
"fmt"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
func analyzeSecret(analyzer *troubleshootv1beta1.AnalyzeSecret, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
func analyzeSecret(analyzer *troubleshootv1beta2.AnalyzeSecret, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
secretData, err := getCollectedFileContents(fmt.Sprintf("secrets/%s/%s.json", analyzer.Namespace, analyzer.SecretName))
if err != nil {
return nil, err
@@ -30,7 +30,7 @@ func analyzeSecret(analyzer *troubleshootv1beta1.AnalyzeSecret, getCollectedFile
IconURI: "https://troubleshoot.sh/images/analyzer-icons/secret.svg?w=13&h=16",
}
var failOutcome *troubleshootv1beta1.Outcome
var failOutcome *troubleshootv1beta2.Outcome
for _, outcome := range analyzer.Outcomes {
if outcome.Fail != nil {
failOutcome = outcome
+5 -5
View File
@@ -3,15 +3,15 @@ package analyzer
import (
"encoding/json"
"fmt"
"path"
"path/filepath"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
appsv1 "k8s.io/api/apps/v1"
)
func analyzeStatefulsetStatus(analyzer *troubleshootv1beta1.StatefulsetStatus, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
collected, err := getCollectedFileContents(path.Join("cluster-resources", "statefulsets", fmt.Sprintf("%s.json", analyzer.Namespace)))
func analyzeStatefulsetStatus(analyzer *troubleshootv1beta2.StatefulsetStatus, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
collected, err := getCollectedFileContents(filepath.Join("cluster-resources", "statefulsets", fmt.Sprintf("%s.json", analyzer.Namespace)))
if err != nil {
return nil, errors.Wrap(err, "failed to read collected statefulsets from namespace")
}
@@ -24,7 +24,7 @@ func analyzeStatefulsetStatus(analyzer *troubleshootv1beta1.StatefulsetStatus, g
var status *appsv1.StatefulSetStatus
for _, statefulset := range statefulsets {
if statefulset.Name == analyzer.Name {
status = &statefulset.Status
status = statefulset.Status.DeepCopy()
}
}
+15 -4
View File
@@ -4,11 +4,11 @@ import (
"encoding/json"
"fmt"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
storagev1beta1 "k8s.io/api/storage/v1beta1"
)
func analyzeStorageClass(analyzer *troubleshootv1beta1.StorageClass, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
func analyzeStorageClass(analyzer *troubleshootv1beta2.StorageClass, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
storageClassesData, err := getCollectedFileContents("cluster-resources/storage-classes.json")
if err != nil {
return nil, err
@@ -21,7 +21,11 @@ func analyzeStorageClass(analyzer *troubleshootv1beta1.StorageClass, getCollecte
title := analyzer.CheckName
if title == "" {
title = fmt.Sprintf("Storage class %s", analyzer.StorageClassName)
if analyzer.StorageClassName != "" {
title = fmt.Sprintf("Storage class %s", analyzer.StorageClassName)
} else {
title = "Default Storage Class"
}
}
result := AnalyzeResult{
@@ -31,7 +35,8 @@ func analyzeStorageClass(analyzer *troubleshootv1beta1.StorageClass, getCollecte
}
for _, storageClass := range storageClasses {
if storageClass.Name == analyzer.StorageClassName {
val, _ := storageClass.Annotations["storageclass.kubernetes.io/is-default-class"]
if (storageClass.Name == analyzer.StorageClassName) || (analyzer.StorageClassName == "" && val == "true") {
result.IsPass = true
for _, outcome := range analyzer.Outcomes {
if outcome.Pass != nil {
@@ -39,6 +44,9 @@ func analyzeStorageClass(analyzer *troubleshootv1beta1.StorageClass, getCollecte
result.URI = outcome.Pass.URI
}
}
if analyzer.StorageClassName == "" && result.Message == "" {
result.Message = "Default Storage Class found"
}
return &result, nil
}
@@ -51,6 +59,9 @@ func analyzeStorageClass(analyzer *troubleshootv1beta1.StorageClass, getCollecte
result.URI = outcome.Fail.URI
}
}
if analyzer.StorageClassName == "" && result.Message == "" {
result.Message = "No Default Storage Class found"
}
return &result, nil
}
+69 -31
View File
@@ -8,10 +8,10 @@ import (
"strings"
"github.com/pkg/errors"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)
func analyzeTextAnalyze(analyzer *troubleshootv1beta1.TextAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
func analyzeTextAnalyze(analyzer *troubleshootv1beta2.TextAnalyze, getCollectedFileContents func(string) (map[string][]byte, error)) ([]*AnalyzeResult, error) {
fullPath := filepath.Join(analyzer.CollectorName, analyzer.FileName)
collected, err := getCollectedFileContents(fullPath)
if err != nil {
@@ -23,59 +23,97 @@ func analyzeTextAnalyze(analyzer *troubleshootv1beta1.TextAnalyze, getCollectedF
checkName = analyzer.CollectorName
}
if len(collected) == 0 {
return []*AnalyzeResult{
{
Title: checkName,
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
IsFail: false,
Message: "No matching files",
},
}, nil
}
results := []*AnalyzeResult{}
if analyzer.RegexPattern != "" {
return analyzeRegexPattern(analyzer.RegexPattern, collected, analyzer.Outcomes, checkName)
for _, fileContents := range collected {
result, err := analyzeRegexPattern(analyzer.RegexPattern, fileContents, analyzer.Outcomes, checkName)
if err != nil {
return nil, err
}
if result != nil {
results = append(results, result)
}
}
}
if analyzer.RegexGroups != "" {
return analyzeRegexGroups(analyzer.RegexGroups, collected, analyzer.Outcomes, checkName)
for _, fileContents := range collected {
result, err := analyzeRegexGroups(analyzer.RegexGroups, fileContents, analyzer.Outcomes, checkName)
if err != nil {
return nil, err
}
if result != nil {
results = append(results, result)
}
}
}
return &AnalyzeResult{
Title: checkName,
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
IsFail: true,
Message: "Invalid analyzer",
if len(results) > 0 {
return results, nil
}
return []*AnalyzeResult{
{
Title: checkName,
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
IsFail: true,
Message: "Invalid analyzer",
},
}, nil
}
func analyzeRegexPattern(pattern string, collected []byte, outcomes []*troubleshootv1beta1.Outcome, checkName string) (*AnalyzeResult, error) {
func analyzeRegexPattern(pattern string, collected []byte, outcomes []*troubleshootv1beta2.Outcome, checkName string) (*AnalyzeResult, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, errors.Wrapf(err, "failed to compile regex: %s", pattern)
}
var failOutcome *troubleshootv1beta1.Outcome
var passOutcome *troubleshootv1beta1.Outcome
var failOutcome *troubleshootv1beta2.SingleOutcome
var passOutcome *troubleshootv1beta2.SingleOutcome
for _, outcome := range outcomes {
if outcome.Fail != nil {
failOutcome = outcome
failOutcome = outcome.Fail
} else if outcome.Pass != nil {
passOutcome = outcome
passOutcome = outcome.Pass
}
}
if re.MatchString(string(collected)) {
return &AnalyzeResult{
Title: checkName,
IsPass: true,
Message: passOutcome.Pass.Message,
URI: passOutcome.Pass.URI,
}, nil
}
return &AnalyzeResult{
result := AnalyzeResult{
Title: checkName,
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
IsFail: true,
Message: failOutcome.Fail.Message,
URI: failOutcome.Fail.URI,
}, nil
}
if re.MatchString(string(collected)) {
result.IsPass = true
if passOutcome != nil {
result.Message = passOutcome.Message
result.URI = passOutcome.URI
}
return &result, nil
}
result.IsFail = true
if failOutcome != nil {
result.Message = failOutcome.Message
result.URI = failOutcome.URI
}
return &result, nil
}
func analyzeRegexGroups(pattern string, collected []byte, outcomes []*troubleshootv1beta1.Outcome, checkName string) (*AnalyzeResult, error) {
func analyzeRegexGroups(pattern string, collected []byte, outcomes []*troubleshootv1beta2.Outcome, checkName string) (*AnalyzeResult, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, errors.Wrapf(err, "failed to compile regex: %s", pattern)
+192 -76
View File
@@ -2,9 +2,11 @@ package analyzer
import (
"fmt"
"path/filepath"
"strings"
"testing"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.undefinedlabs.com/scopeagent"
@@ -13,21 +15,21 @@ import (
func Test_textAnalyze(t *testing.T) {
tests := []struct {
name string
analyzer troubleshootv1beta1.TextAnalyze
expectResult AnalyzeResult
analyzer troubleshootv1beta2.TextAnalyze
expectResult []AnalyzeResult
files map[string][]byte
}{
{
name: "success case 1",
analyzer: troubleshootv1beta1.TextAnalyze{
Outcomes: []*troubleshootv1beta1.Outcome{
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "pass",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "fail",
},
},
@@ -36,12 +38,16 @@ func Test_textAnalyze(t *testing.T) {
FileName: "cfile-1.txt",
RegexPattern: "succeeded",
},
expectResult: AnalyzeResult{
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "text-collector-1",
Message: "pass",
expectResult: []AnalyzeResult{
{
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "text-collector-1",
Message: "pass",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
"text-collector-1/cfile-1.txt": []byte("Yes it all succeeded"),
@@ -49,15 +55,15 @@ func Test_textAnalyze(t *testing.T) {
},
{
name: "failure case 1",
analyzer: troubleshootv1beta1.TextAnalyze{
Outcomes: []*troubleshootv1beta1.Outcome{
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "success",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "fail",
},
},
@@ -66,14 +72,16 @@ func Test_textAnalyze(t *testing.T) {
FileName: "cfile-2.txt",
RegexPattern: "succeeded",
},
expectResult: AnalyzeResult{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-2",
Message: "fail",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
expectResult: []AnalyzeResult{
{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-2",
Message: "fail",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
"text-collector-2/cfile-2.txt": []byte(""),
@@ -81,15 +89,15 @@ func Test_textAnalyze(t *testing.T) {
},
{
name: "success case 2",
analyzer: troubleshootv1beta1.TextAnalyze{
Outcomes: []*troubleshootv1beta1.Outcome{
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "success",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "fail",
},
},
@@ -98,14 +106,16 @@ func Test_textAnalyze(t *testing.T) {
FileName: "cfile-3.txt",
RegexPattern: "",
},
expectResult: AnalyzeResult{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-3",
Message: "Invalid analyzer",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
expectResult: []AnalyzeResult{
{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-3",
Message: "Invalid analyzer",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
"text-collector-3/cfile-3.txt": []byte("Connection to service succeeded"),
@@ -113,15 +123,15 @@ func Test_textAnalyze(t *testing.T) {
},
{
name: "success case 3",
analyzer: troubleshootv1beta1.TextAnalyze{
Outcomes: []*troubleshootv1beta1.Outcome{
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "success",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "fail",
},
},
@@ -130,12 +140,16 @@ func Test_textAnalyze(t *testing.T) {
FileName: "cfile-5.txt",
RegexPattern: "([a-zA-Z0-9\\-_:*\\s])*succe([a-zA-Z0-9\\-_:*\\s!])*",
},
expectResult: AnalyzeResult{
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "text-collector-5",
Message: "success",
expectResult: []AnalyzeResult{
{
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "text-collector-5",
Message: "success",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
"text-collector-5/cfile-5.txt": []byte("Connection to service succeeded!"),
@@ -143,15 +157,15 @@ func Test_textAnalyze(t *testing.T) {
},
{
name: "failure case 3",
analyzer: troubleshootv1beta1.TextAnalyze{
Outcomes: []*troubleshootv1beta1.Outcome{
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "success",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "fail",
},
},
@@ -160,14 +174,16 @@ func Test_textAnalyze(t *testing.T) {
FileName: "cfile-4.txt",
RegexPattern: "succeeded",
},
expectResult: AnalyzeResult{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-4",
Message: "fail",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
expectResult: []AnalyzeResult{
{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-4",
Message: "fail",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
"text-collector-4/cfile-4.txt": []byte("A different message"),
@@ -175,15 +191,15 @@ func Test_textAnalyze(t *testing.T) {
},
{
name: "failure case 4",
analyzer: troubleshootv1beta1.TextAnalyze{
Outcomes: []*troubleshootv1beta1.Outcome{
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta1.SingleOutcome{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "success",
},
},
{
Fail: &troubleshootv1beta1.SingleOutcome{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "fail",
},
},
@@ -192,19 +208,102 @@ func Test_textAnalyze(t *testing.T) {
FileName: "cfile-6.txt",
RegexPattern: "([a-zA-Z0-9\\-_:*\\s])*succe([a-zA-Z0-9\\-_:*\\s!])*",
},
expectResult: AnalyzeResult{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-6",
Message: "fail",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
expectResult: []AnalyzeResult{
{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-6",
Message: "fail",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
"text-collector-6/cfile-6.txt": []byte("A different message"),
},
},
{
name: "multiple results case 1",
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "pass",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "fail",
},
},
},
CollectorName: "text-collector-1",
FileName: "cfile",
RegexPattern: "succeeded",
},
expectResult: []AnalyzeResult{
{
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "text-collector-1",
Message: "pass",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-1",
Message: "fail",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
"text-collector-1/cfile-1.txt": []byte("Yes it all succeeded"),
"text-collector-1/cfile-2.txt": []byte("no success here"),
"text-collector-2/cfile-3.txt": []byte("Yes it all succeeded"),
},
},
{
name: "multiple results case 2 globbing",
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "pass",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "fail",
},
},
},
CollectorName: "text-collector-1",
FileName: "cfile*.log",
RegexPattern: "succeeded",
},
expectResult: []AnalyzeResult{
{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-1",
Message: "fail",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
"text-collector-1/cfile-1.txt": []byte("Yes it all succeeded"),
"text-collector-1/cfile-2.log": []byte("no success here"),
"text-collector-2/cfile-3.txt": []byte("Yes it all succeeded"),
},
},
}
for _, test := range tests {
@@ -213,17 +312,34 @@ func Test_textAnalyze(t *testing.T) {
defer scopetest.End()
req := require.New(t)
getFiles := func(n string) ([]byte, error) {
val, ok := test.files[n]
if !ok {
getFiles := func(n string) (map[string][]byte, error) {
matching := make(map[string][]byte)
for k, v := range test.files {
if strings.HasPrefix(k, n) {
matching[k] = v
}
}
for k, v := range test.files {
if ok, _ := filepath.Match(n, k); ok {
matching[k] = v
}
}
if len(matching) == 0 {
return nil, fmt.Errorf("File not found: %s", n)
}
return val, nil
return matching, nil
}
actual, err := analyzeTextAnalyze(&test.analyzer, getFiles)
req.NoError(err)
assert.Equal(t, &test.expectResult, actual)
unPointered := []AnalyzeResult{}
for _, v := range actual {
unPointered = append(unPointered, *v)
}
assert.ElementsMatch(t, test.expectResult, unPointered)
})
}
}
@@ -0,0 +1,26 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package apis
import (
"github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)
func init() {
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
AddToSchemes = append(AddToSchemes, v1beta2.SchemeBuilder.AddToScheme)
}
@@ -84,14 +84,19 @@ type NodeResources struct {
}
type NodeResourceFilters struct {
CPUCapacity string `json:"cpuCapacity,omitempty" yaml:"cpuCapacity,omitempty"`
CPUAllocatable string `json:"cpuAllocatable,omitempty" yaml:"cpuAllocatable,omitempty"`
MemoryCapacity string `json:"memoryCapacity,omitempty" yaml:"memoryCapacity,omitempty"`
MemoryAllocatable string `json:"memoryAllocatable,omitempty" yaml:"memoryAllocatable,omitempty"`
PodCapacity string `json:"podCapacity,omitempty" yaml:"podCapacity,omitempty"`
PodAllocatable string `json:"podAllocatable,omitempty" yaml:"podAllocatable,omitempty"`
EphemeralStorageCapacity string `json:"ephemeralStorageCapacity,omitempty" yaml:"ephemeralStorageCapacity,omitempty"`
EphemeralStorageAllocatable string `json:"ephemeralStorageAllocatable,omitempty" yaml:"ephemeralStorageAllocatable,omitempty"`
CPUCapacity string `json:"cpuCapacity,omitempty" yaml:"cpuCapacity,omitempty"`
CPUAllocatable string `json:"cpuAllocatable,omitempty" yaml:"cpuAllocatable,omitempty"`
MemoryCapacity string `json:"memoryCapacity,omitempty" yaml:"memoryCapacity,omitempty"`
MemoryAllocatable string `json:"memoryAllocatable,omitempty" yaml:"memoryAllocatable,omitempty"`
PodCapacity string `json:"podCapacity,omitempty" yaml:"podCapacity,omitempty"`
PodAllocatable string `json:"podAllocatable,omitempty" yaml:"podAllocatable,omitempty"`
EphemeralStorageCapacity string `json:"ephemeralStorageCapacity,omitempty" yaml:"ephemeralStorageCapacity,omitempty"`
EphemeralStorageAllocatable string `json:"ephemeralStorageAllocatable,omitempty" yaml:"ephemeralStorageAllocatable,omitempty"`
Selector *NodeResourceSelectors `json:"selector,omitempty" yaml:"selector,omitempty"`
}
type NodeResourceSelectors struct {
MatchLabel map[string]string `json:"matchLabel,omitempty" yaml:"matchLabel,omitempty"`
}
type TextAnalyze struct {
@@ -52,13 +52,20 @@ type Data struct {
type Run struct {
CollectorMeta `json:",inline" yaml:",inline"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Namespace string `json:"namespace" yaml:"namespace"`
Image string `json:"image" yaml:"image"`
Command []string `json:"command,omitempty" yaml:"command,omitempty"`
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
ImagePullPolicy string `json:"imagePullPolicy,omitempty" yaml:"imagePullPolicy,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Namespace string `json:"namespace" yaml:"namespace"`
Image string `json:"image" yaml:"image"`
Command []string `json:"command,omitempty" yaml:"command,omitempty"`
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
ImagePullPolicy string `json:"imagePullPolicy,omitempty" yaml:"imagePullPolicy,omitempty"`
ImagePullSecret *ImagePullSecrets `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
}
type ImagePullSecrets struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Data map[string]string `json:"data,omitempty" yaml:"data,omitempty"`
SecretType string `json:"type,omitempty" yaml:"type,omitempty"`
}
type Exec struct {
+15 -8
View File
@@ -1,16 +1,23 @@
package v1beta1
type MultiLine struct {
type Regex struct {
Selector string `json:"selector,omitempty" yaml:"selector,omitempty"`
Redactor string `json:"redactor,omitempty" yaml:"redactor,omitempty"`
}
type FileSelector struct {
File string `json:"file,omitempty" yaml:"file,omitempty"`
Files []string `json:"files,omitempty" yaml:"files,omitempty"`
}
type Removals struct {
Values []string `json:"values,omitempty" yaml:"values,omitempty"`
Regex []Regex `json:"regex,omitempty" yaml:"regex,omitempty"`
YamlPath []string `json:"yamlPath,omitempty" yaml:"yamlPath,omitempty"`
}
type Redact struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
File string `json:"file,omitempty" yaml:"file,omitempty"`
Files []string `json:"files,omitempty" yaml:"files,omitempty"`
Values []string `json:"values,omitempty" yaml:"values,omitempty"`
Regex []string `json:"regex,omitempty" yaml:"regex,omitempty"`
MultiLine []MultiLine `json:"multiLine,omitempty" yaml:"multiLine,omitempty"`
Yaml []string `json:"yaml,omitempty" yaml:"yaml,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
FileSelector FileSelector `json:"fileSelector,omitempty" yaml:"fileSelector,omitempty"`
Removals Removals `json:"removals,omitempty" yaml:"removals,omitempty"`
}
@@ -0,0 +1,60 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// SupportBundleSpec defines the desired state of SupportBundle
type SupportBundleSpec struct {
AfterCollection []*AfterCollection `json:"afterCollection,omitempty" yaml:"afterCollection,omitempty"`
Collectors []*Collect `json:"collectors,omitempty" yaml:"collectors,omitempty"`
Analyzers []*Analyze `json:"analyzers,omitempty" yaml:"analyzers,omitempty"`
}
// SupportBundleStatus defines the observed state of SupportBundle
type SupportBundleStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// SupportBundle is the Schema for the SupportBundles API
// +k8s:openapi-gen=true
type SupportBundle struct {
metav1.TypeMeta `json:",inline" yaml:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
Spec SupportBundleSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
Status SupportBundleStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// SupportBundleList contains a list of SupportBundle
type SupportBundleList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []SupportBundle `json:"items"`
}
func init() {
SchemeBuilder.Register(&SupportBundle{}, &SupportBundleList{})
}
@@ -762,6 +762,26 @@ func (in *Exec) DeepCopy() *Exec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FileSelector) DeepCopyInto(out *FileSelector) {
*out = *in
if in.Files != nil {
in, out := &in.Files, &out.Files
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileSelector.
func (in *FileSelector) DeepCopy() *FileSelector {
if in == nil {
return nil
}
out := new(FileSelector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Get) DeepCopyInto(out *Get) {
*out = *in
@@ -842,6 +862,28 @@ func (in *ImagePullSecret) DeepCopy() *ImagePullSecret {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ImagePullSecrets) DeepCopyInto(out *ImagePullSecrets) {
*out = *in
if in.Data != nil {
in, out := &in.Data, &out.Data
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImagePullSecrets.
func (in *ImagePullSecrets) DeepCopy() *ImagePullSecrets {
if in == nil {
return nil
}
out := new(ImagePullSecrets)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Ingress) DeepCopyInto(out *Ingress) {
*out = *in
@@ -915,24 +957,14 @@ func (in *Logs) DeepCopy() *Logs {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MultiLine) DeepCopyInto(out *MultiLine) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiLine.
func (in *MultiLine) DeepCopy() *MultiLine {
if in == nil {
return nil
}
out := new(MultiLine)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeResourceFilters) DeepCopyInto(out *NodeResourceFilters) {
*out = *in
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = new(NodeResourceSelectors)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeResourceFilters.
@@ -945,6 +977,28 @@ func (in *NodeResourceFilters) DeepCopy() *NodeResourceFilters {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeResourceSelectors) DeepCopyInto(out *NodeResourceSelectors) {
*out = *in
if in.MatchLabel != nil {
in, out := &in.MatchLabel, &out.MatchLabel
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeResourceSelectors.
func (in *NodeResourceSelectors) DeepCopy() *NodeResourceSelectors {
if in == nil {
return nil
}
out := new(NodeResourceSelectors)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeResources) DeepCopyInto(out *NodeResources) {
*out = *in
@@ -963,7 +1017,7 @@ func (in *NodeResources) DeepCopyInto(out *NodeResources) {
if in.Filters != nil {
in, out := &in.Filters, &out.Filters
*out = new(NodeResourceFilters)
**out = **in
(*in).DeepCopyInto(*out)
}
}
@@ -1165,31 +1219,8 @@ func (in *Put) DeepCopy() *Put {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Redact) DeepCopyInto(out *Redact) {
*out = *in
if in.Files != nil {
in, out := &in.Files, &out.Files
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Values != nil {
in, out := &in.Values, &out.Values
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Regex != nil {
in, out := &in.Regex, &out.Regex
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.MultiLine != nil {
in, out := &in.MultiLine, &out.MultiLine
*out = make([]MultiLine, len(*in))
copy(*out, *in)
}
if in.Yaml != nil {
in, out := &in.Yaml, &out.Yaml
*out = make([]string, len(*in))
copy(*out, *in)
}
in.FileSelector.DeepCopyInto(&out.FileSelector)
in.Removals.DeepCopyInto(&out.Removals)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Redact.
@@ -1302,6 +1333,51 @@ func (in *RedactorStatus) DeepCopy() *RedactorStatus {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Regex) DeepCopyInto(out *Regex) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Regex.
func (in *Regex) DeepCopy() *Regex {
if in == nil {
return nil
}
out := new(Regex)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Removals) DeepCopyInto(out *Removals) {
*out = *in
if in.Values != nil {
in, out := &in.Values, &out.Values
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Regex != nil {
in, out := &in.Regex, &out.Regex
*out = make([]Regex, len(*in))
copy(*out, *in)
}
if in.YamlPath != nil {
in, out := &in.YamlPath, &out.YamlPath
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Removals.
func (in *Removals) DeepCopy() *Removals {
if in == nil {
return nil
}
out := new(Removals)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResultRequest) DeepCopyInto(out *ResultRequest) {
*out = *in
@@ -1331,6 +1407,11 @@ func (in *Run) DeepCopyInto(out *Run) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ImagePullSecret != nil {
in, out := &in.ImagePullSecret, &out.ImagePullSecret
*out = new(ImagePullSecrets)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Run.
@@ -1428,6 +1509,128 @@ func (in *StorageClass) DeepCopy() *StorageClass {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SupportBundle) DeepCopyInto(out *SupportBundle) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SupportBundle.
func (in *SupportBundle) DeepCopy() *SupportBundle {
if in == nil {
return nil
}
out := new(SupportBundle)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *SupportBundle) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SupportBundleList) DeepCopyInto(out *SupportBundleList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]SupportBundle, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SupportBundleList.
func (in *SupportBundleList) DeepCopy() *SupportBundleList {
if in == nil {
return nil
}
out := new(SupportBundleList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *SupportBundleList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SupportBundleSpec) DeepCopyInto(out *SupportBundleSpec) {
*out = *in
if in.AfterCollection != nil {
in, out := &in.AfterCollection, &out.AfterCollection
*out = make([]*AfterCollection, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(AfterCollection)
(*in).DeepCopyInto(*out)
}
}
}
if in.Collectors != nil {
in, out := &in.Collectors, &out.Collectors
*out = make([]*Collect, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(Collect)
(*in).DeepCopyInto(*out)
}
}
}
if in.Analyzers != nil {
in, out := &in.Analyzers, &out.Analyzers
*out = make([]*Analyze, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(Analyze)
(*in).DeepCopyInto(*out)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SupportBundleSpec.
func (in *SupportBundleSpec) DeepCopy() *SupportBundleSpec {
if in == nil {
return nil
}
out := new(SupportBundleSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SupportBundleStatus) DeepCopyInto(out *SupportBundleStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SupportBundleStatus.
func (in *SupportBundleStatus) DeepCopy() *SupportBundleStatus {
if in == nil {
return nil
}
out := new(SupportBundleStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SupportBundleVersion) DeepCopyInto(out *SupportBundleVersion) {
*out = *in
@@ -0,0 +1,146 @@
package v1beta2
import (
"github.com/replicatedhq/troubleshoot/pkg/multitype"
)
type SingleOutcome struct {
When string `json:"when,omitempty" yaml:"when,omitempty"`
Message string `json:"message,omitempty" yaml:"message,omitempty"`
URI string `json:"uri,omitempty" yaml:"uri,omitempty"`
}
type Outcome struct {
Fail *SingleOutcome `json:"fail,omitempty" yaml:"fail,omitempty"`
Warn *SingleOutcome `json:"warn,omitempty" yaml:"warn,omitempty"`
Pass *SingleOutcome `json:"pass,omitempty" yaml:"pass,omitempty"`
}
type ClusterVersion struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
}
type StorageClass struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
StorageClassName string `json:"storageClassName" yaml:"storageClassName"`
}
type CustomResourceDefinition struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
CustomResourceDefinitionName string `json:"customResourceDefinitionName" yaml:"customResourceDefinitionName"`
}
type Ingress struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
IngressName string `json:"ingressName" yaml:"ingressName"`
Namespace string `json:"namespace" yaml:"namespace"`
}
type AnalyzeSecret struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
SecretName string `json:"secretName" yaml:"secretName"`
Namespace string `json:"namespace" yaml:"namespace"`
Key string `json:"key,omitempty" yaml:"key,omitempty"`
}
type ImagePullSecret struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
RegistryName string `json:"registryName" yaml:"registryName"`
}
type DeploymentStatus struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
Namespace string `json:"namespace" yaml:"namespace"`
Name string `json:"name" yaml:"name"`
}
type StatefulsetStatus struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
Namespace string `json:"namespace" yaml:"namespace"`
Name string `json:"name" yaml:"name"`
}
type ContainerRuntime struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
}
type Distribution struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
}
type NodeResources struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
Filters *NodeResourceFilters `json:"filters,omitempty" yaml:"filters,omitempty"`
}
type NodeResourceFilters struct {
CPUCapacity string `json:"cpuCapacity,omitempty" yaml:"cpuCapacity,omitempty"`
CPUAllocatable string `json:"cpuAllocatable,omitempty" yaml:"cpuAllocatable,omitempty"`
MemoryCapacity string `json:"memoryCapacity,omitempty" yaml:"memoryCapacity,omitempty"`
MemoryAllocatable string `json:"memoryAllocatable,omitempty" yaml:"memoryAllocatable,omitempty"`
PodCapacity string `json:"podCapacity,omitempty" yaml:"podCapacity,omitempty"`
PodAllocatable string `json:"podAllocatable,omitempty" yaml:"podAllocatable,omitempty"`
EphemeralStorageCapacity string `json:"ephemeralStorageCapacity,omitempty" yaml:"ephemeralStorageCapacity,omitempty"`
EphemeralStorageAllocatable string `json:"ephemeralStorageAllocatable,omitempty" yaml:"ephemeralStorageAllocatable,omitempty"`
Selector *NodeResourceSelectors `json:"selector,omitempty" yaml:"selector,omitempty"`
}
type NodeResourceSelectors struct {
MatchLabel map[string]string `json:"matchLabel,omitempty" yaml:"matchLabel,omitempty"`
}
type TextAnalyze struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
FileName string `json:"fileName,omitempty" yaml:"fileName,omitempty"`
RegexPattern string `json:"regex,omitempty" yaml:"regex,omitempty"`
RegexGroups string `json:"regexGroups,omitempty" yaml:"regexGroups,omitempty"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
}
type DatabaseAnalyze struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
CollectorName string `json:"collectorName" yaml:"collectorName"`
FileName string `json:"fileName,omitempty" yaml:"fileName,omitempty"`
}
type CollectdAnalyze struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
CollectorName string `json:"collectorName" yaml:"collectorName"`
}
type AnalyzeMeta struct {
CheckName string `json:"checkName,omitempty" yaml:"checkName,omitempty"`
Exclude multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
}
type Analyze struct {
ClusterVersion *ClusterVersion `json:"clusterVersion,omitempty" yaml:"clusterVersion,omitempty"`
StorageClass *StorageClass `json:"storageClass,omitempty" yaml:"storageClass,omitempty"`
CustomResourceDefinition *CustomResourceDefinition `json:"customResourceDefinition,omitempty" yaml:"customResourceDefinition,omitempty"`
Ingress *Ingress `json:"ingress,omitempty" yaml:"ingress,omitempty"`
Secret *AnalyzeSecret `json:"secret,omitempty" yaml:"secret,omitempty"`
ImagePullSecret *ImagePullSecret `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
DeploymentStatus *DeploymentStatus `json:"deploymentStatus,omitempty" yaml:"deploymentStatus,omitempty"`
StatefulsetStatus *StatefulsetStatus `json:"statefulsetStatus,omitempty" yaml:"statefulsetStatus,omitempty"`
ContainerRuntime *ContainerRuntime `json:"containerRuntime,omitempty" yaml:"containerRuntime,omitempty"`
Distribution *Distribution `json:"distribution,omitempty" yaml:"distribution,omitempty"`
NodeResources *NodeResources `json:"nodeResources,omitempty" yaml:"nodeResources,omitempty"`
TextAnalyze *TextAnalyze `json:"textAnalyze,omitempty" yaml:"textAnalyze,omitempty"`
Postgres *DatabaseAnalyze `json:"postgres,omitempty" yaml:"postgres,omitempty"`
Mysql *DatabaseAnalyze `json:"mysql,omitempty" yaml:"mysql,omitempty"`
Redis *DatabaseAnalyze `json:"redis,omitempty" yaml:"redis,omitempty"`
Collectd *CollectdAnalyze `json:"collectd,omitempty" yaml:"collectd,omitempty"`
}
@@ -0,0 +1,56 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// AnalyzerSpec defines the desired state of Analyzer
type AnalyzerSpec struct {
Analyzers []*Analyze `json:"analyzers,omitempty"`
}
// AnalyzerStatus defines the observed state of Analyzer
type AnalyzerStatus struct {
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Analyzer is the Schema for the analyzers API
// +k8s:openapi-gen=true
type Analyzer struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec AnalyzerSpec `json:"spec,omitempty"`
Status AnalyzerStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// AnalyzerList contains a list of Analyzer
type AnalyzerList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Analyzer `json:"items"`
}
func init() {
SchemeBuilder.Register(&Analyzer{}, &AnalyzerList{})
}
@@ -0,0 +1,374 @@
package v1beta2
import (
"fmt"
"strings"
"github.com/replicatedhq/troubleshoot/pkg/multitype"
authorizationv1 "k8s.io/api/authorization/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type CollectorMeta struct {
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
// +optional
Exclude multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
}
type ClusterInfo struct {
CollectorMeta `json:",inline" yaml:",inline"`
}
type ClusterResources struct {
CollectorMeta `json:",inline" yaml:",inline"`
}
type Secret struct {
CollectorMeta `json:",inline" yaml:",inline"`
SecretName string `json:"name" yaml:"name"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
Key string `json:"key,omitempty" yaml:"key,omitempty"`
IncludeValue bool `json:"includeValue,omitempty" yaml:"includeValue,omitempty"`
}
type LogLimits struct {
MaxAge string `json:"maxAge,omitempty" yaml:"maxAge,omitempty"`
MaxLines int64 `json:"maxLines,omitempty" yaml:"maxLines,omitempty"`
SinceTime metav1.Time
}
type Logs struct {
CollectorMeta `json:",inline" yaml:",inline"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Selector []string `json:"selector" yaml:"selector"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
ContainerNames []string `json:"containerNames,omitempty" yaml:"containerNames,omitempty"`
Limits *LogLimits `json:"limits,omitempty" yaml:"omitempty"`
}
type Data struct {
CollectorMeta `json:",inline" yaml:",inline"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Data string `json:"data" yaml:"data"`
}
type Run struct {
CollectorMeta `json:",inline" yaml:",inline"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Namespace string `json:"namespace" yaml:"namespace"`
Image string `json:"image" yaml:"image"`
Command []string `json:"command,omitempty" yaml:"command,omitempty"`
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
ImagePullPolicy string `json:"imagePullPolicy,omitempty" yaml:"imagePullPolicy,omitempty"`
ImagePullSecret *ImagePullSecrets `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
}
type ImagePullSecrets struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Data map[string]string `json:"data,omitempty" yaml:"data,omitempty"`
SecretType string `json:"type,omitempty" yaml:"type,omitempty"`
}
type Exec struct {
CollectorMeta `json:",inline" yaml:",inline"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Selector []string `json:"selector" yaml:"selector"`
Namespace string `json:"namespace" yaml:"namespace"`
ContainerName string `json:"containerName,omitempty" yaml:"containerName,omitempty"`
Command []string `json:"command,omitempty" yaml:"command,omitempty"`
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
}
type Copy struct {
CollectorMeta `json:",inline" yaml:",inline"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Selector []string `json:"selector" yaml:"selector"`
Namespace string `json:"namespace" yaml:"namespace"`
ContainerPath string `json:"containerPath" yaml:"containerPath"`
ContainerName string `json:"containerName,omitempty" yaml:"containerName,omitempty"`
}
type HTTP struct {
CollectorMeta `json:",inline" yaml:",inline"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Get *Get `json:"get,omitempty" yaml:"get,omitempty"`
Post *Post `json:"post,omitempty" yaml:"post,omitempty"`
Put *Put `json:"put,omitempty" yaml:"put,omitempty"`
}
type Get struct {
URL string `json:"url" yaml:"url"`
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty"`
Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"`
}
type Post struct {
URL string `json:"url" yaml:"url"`
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty"`
Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"`
Body string `json:"body,omitempty" yaml:"body,omitempty"`
}
type Put struct {
URL string `json:"url" yaml:"url"`
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty"`
Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"`
Body string `json:"body,omitempty" yaml:"body,omitempty"`
}
type Database struct {
CollectorMeta `json:",inline" yaml:",inline"`
URI string `json:"uri" yaml:"uri"`
}
type Collectd struct {
CollectorMeta `json:",inline" yaml:",inline"`
Namespace string `json:"namespace" yaml:"namespace"`
Image string `json:"image" yaml:"image"`
ImagePullPolicy string `json:"imagePullPolicy,omitempty" yaml:"imagePullPolicy,omitempty"`
ImagePullSecret *ImagePullSecrets `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
HostPath string `json:"hostPath" yaml:"hostPath"`
}
type Collect struct {
ClusterInfo *ClusterInfo `json:"clusterInfo,omitempty" yaml:"clusterInfo,omitempty"`
ClusterResources *ClusterResources `json:"clusterResources,omitempty" yaml:"clusterResources,omitempty"`
Secret *Secret `json:"secret,omitempty" yaml:"secret,omitempty"`
Logs *Logs `json:"logs,omitempty" yaml:"logs,omitempty"`
Run *Run `json:"run,omitempty" yaml:"run,omitempty"`
Exec *Exec `json:"exec,omitempty" yaml:"exec,omitempty"`
Data *Data `json:"data,omitempty" yaml:"data,omitempty"`
Copy *Copy `json:"copy,omitempty" yaml:"copy,omitempty"`
HTTP *HTTP `json:"http,omitempty" yaml:"http,omitempty"`
Postgres *Database `json:"postgres,omitempty" yaml:"postgres,omitempty"`
Mysql *Database `json:"mysql,omitempty" yaml:"mysql,omitempty"`
Redis *Database `json:"redis,omitempty" yaml:"redis,omitempty"`
Collectd *Collectd `json:"collectd,omitempty" yaml:"collectd,omitempty"`
}
func (c *Collect) AccessReviewSpecs(overrideNS string) []authorizationv1.SelfSubjectAccessReviewSpec {
result := make([]authorizationv1.SelfSubjectAccessReviewSpec, 0)
if c.ClusterInfo != nil {
// NOOP
} else if c.ClusterResources != nil {
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: "",
Verb: "list",
Group: "",
Version: "",
Resource: "Namespace",
Subresource: "",
Name: "",
},
NonResourceAttributes: nil,
})
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: "",
Verb: "list",
Group: "",
Version: "",
Resource: "Node",
Subresource: "",
Name: "",
},
NonResourceAttributes: nil,
})
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: "",
Verb: "list",
Group: "apiextensions.k8s.io",
Version: "",
Resource: "CustomResourceDefinition",
Subresource: "",
Name: "",
},
NonResourceAttributes: nil,
})
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: "",
Verb: "list",
Group: "storage.k8s.io",
Version: "",
Resource: "StorageClasses",
Subresource: "",
Name: "",
},
NonResourceAttributes: nil,
})
} else if c.Secret != nil {
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: pickNamespaceOrDefault(c.Secret.Namespace, overrideNS),
Verb: "get",
Group: "",
Version: "",
Resource: "Secret",
Subresource: "",
Name: c.Secret.SecretName,
},
NonResourceAttributes: nil,
})
} else if c.Logs != nil {
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: pickNamespaceOrDefault(c.Logs.Namespace, overrideNS),
Verb: "list",
Group: "",
Version: "",
Resource: "Pod",
Subresource: "",
Name: "",
},
NonResourceAttributes: nil,
})
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: pickNamespaceOrDefault(c.Logs.Namespace, overrideNS),
Verb: "get",
Group: "",
Version: "",
Resource: "Pod",
Subresource: "log",
Name: "",
},
NonResourceAttributes: nil,
})
} else if c.Run != nil {
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: pickNamespaceOrDefault(c.Run.Namespace, overrideNS),
Verb: "create",
Group: "",
Version: "",
Resource: "Pod",
Subresource: "",
Name: "",
},
NonResourceAttributes: nil,
})
} else if c.Exec != nil {
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: pickNamespaceOrDefault(c.Exec.Namespace, overrideNS),
Verb: "list",
Group: "",
Version: "",
Resource: "Pod",
Subresource: "",
Name: "",
},
NonResourceAttributes: nil,
})
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: pickNamespaceOrDefault(c.Exec.Namespace, overrideNS),
Verb: "get",
Group: "",
Version: "",
Resource: "Pod",
Subresource: "exec",
Name: "",
},
NonResourceAttributes: nil,
})
} else if c.Copy != nil {
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: pickNamespaceOrDefault(c.Copy.Namespace, overrideNS),
Verb: "list",
Group: "",
Version: "",
Resource: "Pod",
Subresource: "",
Name: "",
},
NonResourceAttributes: nil,
})
result = append(result, authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: pickNamespaceOrDefault(c.Copy.Namespace, overrideNS),
Verb: "get",
Group: "",
Version: "",
Resource: "Pod",
Subresource: "exec",
Name: "",
},
NonResourceAttributes: nil,
})
} else if c.HTTP != nil {
// NOOP
}
return result
}
func (c *Collect) GetName() string {
var collector, name, selector string
if c.ClusterInfo != nil {
collector = "cluster-info"
}
if c.ClusterResources != nil {
collector = "cluster-resources"
}
if c.Secret != nil {
collector = "secret"
name = c.Secret.CollectorName
}
if c.Logs != nil {
collector = "logs"
name = c.Logs.CollectorName
selector = strings.Join(c.Logs.Selector, ",")
}
if c.Run != nil {
collector = "run"
name = c.Run.CollectorName
}
if c.Exec != nil {
collector = "exec"
name = c.Exec.CollectorName
selector = strings.Join(c.Exec.Selector, ",")
}
if c.Copy != nil {
collector = "copy"
name = c.Copy.CollectorName
selector = strings.Join(c.Copy.Selector, ",")
}
if c.HTTP != nil {
collector = "http"
name = c.HTTP.CollectorName
}
if c.Collectd != nil {
collector = "rrd"
name = c.Collectd.CollectorName
}
if collector == "" {
return "<none>"
}
if name != "" {
return fmt.Sprintf("%s/%s", collector, name)
}
if selector != "" {
return fmt.Sprintf("%s/%s", collector, selector)
}
return collector
}
func pickNamespaceOrDefault(collectorNS string, overrideNS string) string {
if overrideNS != "" {
return overrideNS
}
if collectorNS != "" {
return collectorNS
}
return "default"
}
@@ -0,0 +1,70 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type ResultRequest struct {
URI string `json:"uri" yaml:"uri"`
Method string `json:"method" yaml:"method"`
RedactURI string `json:"redactUri" yaml:"redactUri"` // the URI to POST redaction reports to
}
type AfterCollection struct {
UploadResultsTo *ResultRequest `json:"uploadResultsTo,omitempty" yaml:"uploadResultsTo,omitempty"`
Callback *ResultRequest `json:"callback,omitempty" yaml:"callback,omitempty"`
}
// CollectorSpec defines the desired state of Collector
type CollectorSpec struct {
Collectors []*Collect `json:"collectors,omitempty" yaml:"collectors,omitempty"`
AfterCollection []*AfterCollection `json:"afterCollection,omitempty" yaml:"afterCollection,omitempty"`
}
// CollectorStatus defines the observed state of Collector
type CollectorStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Collector is the Schema for the collectors API
// +k8s:openapi-gen=true
type Collector struct {
metav1.TypeMeta `json:",inline" yaml:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
Spec CollectorSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
Status CollectorStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// CollectorList contains a list of Collector
type CollectorList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Collector `json:"items"`
}
func init() {
SchemeBuilder.Register(&Collector{}, &CollectorList{})
}
+23
View File
@@ -0,0 +1,23 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package v1beta2 contains API Schema definitions for the troubleshoot v1beta2 API group
// +k8s:openapi-gen=true
// +k8s:deepcopy-gen=package,register
// +k8s:conversion-gen=github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot
// +k8s:defaulter-gen=TypeMeta
// +groupName=troubleshoot.sh
package v1beta2
@@ -0,0 +1,60 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// PreflightSpec defines the desired state of Preflight
type PreflightSpec struct {
UploadResultsTo string `json:"uploadResultsTo,omitempty" yaml:"uploadResultsTo,omitempty"`
Collectors []*Collect `json:"collectors,omitempty" yaml:"collectors,omitempty"`
Analyzers []*Analyze `json:"analyzers,omitempty" yaml:"analyzers,omitempty"`
}
// PreflightStatus defines the observed state of Preflight
type PreflightStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Preflight is the Schema for the preflights API
// +k8s:openapi-gen=true
type Preflight struct {
metav1.TypeMeta `json:",inline" yaml:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
Spec PreflightSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
Status PreflightStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// PreflightList contains a list of Preflight
type PreflightList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Preflight `json:"items"`
}
func init() {
SchemeBuilder.Register(&Preflight{}, &PreflightList{})
}
@@ -0,0 +1,23 @@
package v1beta2
type Regex struct {
Selector string `json:"selector,omitempty" yaml:"selector,omitempty"`
Redactor string `json:"redactor,omitempty" yaml:"redactor,omitempty"`
}
type FileSelector struct {
File string `json:"file,omitempty" yaml:"file,omitempty"`
Files []string `json:"files,omitempty" yaml:"files,omitempty"`
}
type Removals struct {
Values []string `json:"values,omitempty" yaml:"values,omitempty"`
Regex []Regex `json:"regex,omitempty" yaml:"regex,omitempty"`
YamlPath []string `json:"yamlPath,omitempty" yaml:"yamlPath,omitempty"`
}
type Redact struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
FileSelector FileSelector `json:"fileSelector,omitempty" yaml:"fileSelector,omitempty"`
Removals Removals `json:"removals,omitempty" yaml:"removals,omitempty"`
}
@@ -0,0 +1,56 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// RedactorSpec defines the desired state of Redactor
type RedactorSpec struct {
Redactors []*Redact `json:"redactors,omitempty"`
}
// RedactorStatus defines the observed state of Redactor
type RedactorStatus struct {
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Redactor is the Schema for the redaction API
// +k8s:openapi-gen=true
type Redactor struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec RedactorSpec `json:"spec,omitempty"`
Status RedactorStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// RedactorList contains a list of Redactor
type RedactorList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Redactor `json:"items"`
}
func init() {
SchemeBuilder.Register(&Redactor{}, &RedactorList{})
}
+46
View File
@@ -0,0 +1,46 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// NOTE: Boilerplate only. Ignore this file.
// Package v1beta2 contains API Schema definitions for the troubleshoot v1beta2 API group
// +k8s:openapi-gen=true
// +k8s:deepcopy-gen=package,register
// +k8s:conversion-gen=github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot
// +k8s:defaulter-gen=TypeMeta
// +groupName=troubleshoot.sh
package v1beta2
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: "troubleshoot.sh", Version: "v1beta2"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
// AddToScheme is required by pkg/client/...
AddToScheme = SchemeBuilder.AddToScheme
)
// Resource is required by pkg/client/listers/...
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
@@ -0,0 +1,60 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// SupportBundleSpec defines the desired state of SupportBundle
type SupportBundleSpec struct {
AfterCollection []*AfterCollection `json:"afterCollection,omitempty" yaml:"afterCollection,omitempty"`
Collectors []*Collect `json:"collectors,omitempty" yaml:"collectors,omitempty"`
Analyzers []*Analyze `json:"analyzers,omitempty" yaml:"analyzers,omitempty"`
}
// SupportBundleStatus defines the observed state of SupportBundle
type SupportBundleStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// SupportBundle is the Schema for the SupportBundles API
// +k8s:openapi-gen=true
type SupportBundle struct {
metav1.TypeMeta `json:",inline" yaml:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
Spec SupportBundleSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
Status SupportBundleStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// SupportBundleList contains a list of SupportBundle
type SupportBundleList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []SupportBundle `json:"items"`
}
func init() {
SchemeBuilder.Register(&SupportBundle{}, &SupportBundleList{})
}
+11
View File
@@ -0,0 +1,11 @@
package v1beta2
type SupportBundleVersionSpec struct {
VersionNumber string `json:"versionNumber" yaml:"versionNumber"`
}
type SupportBundleVersion struct {
ApiVersion string `json:"apiVersion" yaml:"apiVersion"`
Kind string `json:"kind" yaml:"kind"`
Spec SupportBundleVersionSpec `json:"spec" yaml:"spec"`
}
File diff suppressed because it is too large Load Diff
+15 -1
View File
@@ -21,6 +21,7 @@ import (
"fmt"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2"
discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest"
flowcontrol "k8s.io/client-go/util/flowcontrol"
@@ -29,6 +30,7 @@ import (
type Interface interface {
Discovery() discovery.DiscoveryInterface
TroubleshootV1beta1() troubleshootv1beta1.TroubleshootV1beta1Interface
TroubleshootV1beta2() troubleshootv1beta2.TroubleshootV1beta2Interface
}
// Clientset contains the clients for groups. Each group has exactly one
@@ -36,6 +38,7 @@ type Interface interface {
type Clientset struct {
*discovery.DiscoveryClient
troubleshootV1beta1 *troubleshootv1beta1.TroubleshootV1beta1Client
troubleshootV1beta2 *troubleshootv1beta2.TroubleshootV1beta2Client
}
// TroubleshootV1beta1 retrieves the TroubleshootV1beta1Client
@@ -43,6 +46,11 @@ func (c *Clientset) TroubleshootV1beta1() troubleshootv1beta1.TroubleshootV1beta
return c.troubleshootV1beta1
}
// TroubleshootV1beta2 retrieves the TroubleshootV1beta2Client
func (c *Clientset) TroubleshootV1beta2() troubleshootv1beta2.TroubleshootV1beta2Interface {
return c.troubleshootV1beta2
}
// Discovery retrieves the DiscoveryClient
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
if c == nil {
@@ -58,7 +66,7 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
configShallowCopy := *c
if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
if configShallowCopy.Burst <= 0 {
return nil, fmt.Errorf("Burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
}
configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
}
@@ -68,6 +76,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
if err != nil {
return nil, err
}
cs.troubleshootV1beta2, err = troubleshootv1beta2.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
if err != nil {
@@ -81,6 +93,7 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
func NewForConfigOrDie(c *rest.Config) *Clientset {
var cs Clientset
cs.troubleshootV1beta1 = troubleshootv1beta1.NewForConfigOrDie(c)
cs.troubleshootV1beta2 = troubleshootv1beta2.NewForConfigOrDie(c)
cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
return &cs
@@ -90,6 +103,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset {
func New(c rest.Interface) *Clientset {
var cs Clientset
cs.troubleshootV1beta1 = troubleshootv1beta1.New(c)
cs.troubleshootV1beta2 = troubleshootv1beta2.New(c)
cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
return &cs
@@ -21,6 +21,8 @@ import (
clientset "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
faketroubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1/fake"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2"
faketroubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
@@ -79,3 +81,8 @@ var _ clientset.Interface = &Clientset{}
func (c *Clientset) TroubleshootV1beta1() troubleshootv1beta1.TroubleshootV1beta1Interface {
return &faketroubleshootv1beta1.FakeTroubleshootV1beta1{Fake: &c.Fake}
}
// TroubleshootV1beta2 retrieves the TroubleshootV1beta2Client
func (c *Clientset) TroubleshootV1beta2() troubleshootv1beta2.TroubleshootV1beta2Interface {
return &faketroubleshootv1beta2.FakeTroubleshootV1beta2{Fake: &c.Fake}
}
@@ -19,6 +19,7 @@ package fake
import (
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
@@ -31,6 +32,7 @@ var codecs = serializer.NewCodecFactory(scheme)
var parameterCodec = runtime.NewParameterCodec(scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
troubleshootv1beta1.AddToScheme,
troubleshootv1beta2.AddToScheme,
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
@@ -19,6 +19,7 @@ package scheme
import (
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
@@ -31,6 +32,7 @@ var Codecs = serializer.NewCodecFactory(Scheme)
var ParameterCodec = runtime.NewParameterCodec(Scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
troubleshootv1beta1.AddToScheme,
troubleshootv1beta2.AddToScheme,
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
@@ -18,6 +18,7 @@ limitations under the License.
package v1beta1
import (
"context"
"time"
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
@@ -36,15 +37,15 @@ type AnalyzersGetter interface {
// AnalyzerInterface has methods to work with Analyzer resources.
type AnalyzerInterface interface {
Create(*v1beta1.Analyzer) (*v1beta1.Analyzer, error)
Update(*v1beta1.Analyzer) (*v1beta1.Analyzer, error)
UpdateStatus(*v1beta1.Analyzer) (*v1beta1.Analyzer, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1beta1.Analyzer, error)
List(opts v1.ListOptions) (*v1beta1.AnalyzerList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Analyzer, err error)
Create(ctx context.Context, analyzer *v1beta1.Analyzer, opts v1.CreateOptions) (*v1beta1.Analyzer, error)
Update(ctx context.Context, analyzer *v1beta1.Analyzer, opts v1.UpdateOptions) (*v1beta1.Analyzer, error)
UpdateStatus(ctx context.Context, analyzer *v1beta1.Analyzer, opts v1.UpdateOptions) (*v1beta1.Analyzer, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.Analyzer, error)
List(ctx context.Context, opts v1.ListOptions) (*v1beta1.AnalyzerList, error)
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Analyzer, err error)
AnalyzerExpansion
}
@@ -63,20 +64,20 @@ func newAnalyzers(c *TroubleshootV1beta1Client, namespace string) *analyzers {
}
// Get takes name of the analyzer, and returns the corresponding analyzer object, and an error if there is any.
func (c *analyzers) Get(name string, options v1.GetOptions) (result *v1beta1.Analyzer, err error) {
func (c *analyzers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Analyzer, err error) {
result = &v1beta1.Analyzer{}
err = c.client.Get().
Namespace(c.ns).
Resource("analyzers").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Do(ctx).
Into(result)
return
}
// List takes label and field selectors, and returns the list of Analyzers that match those selectors.
func (c *analyzers) List(opts v1.ListOptions) (result *v1beta1.AnalyzerList, err error) {
func (c *analyzers) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.AnalyzerList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
@@ -87,13 +88,13 @@ func (c *analyzers) List(opts v1.ListOptions) (result *v1beta1.AnalyzerList, err
Resource("analyzers").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Do(ctx).
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested analyzers.
func (c *analyzers) Watch(opts v1.ListOptions) (watch.Interface, error) {
func (c *analyzers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
@@ -104,87 +105,90 @@ func (c *analyzers) Watch(opts v1.ListOptions) (watch.Interface, error) {
Resource("analyzers").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
Watch(ctx)
}
// Create takes the representation of a analyzer and creates it. Returns the server's representation of the analyzer, and an error, if there is any.
func (c *analyzers) Create(analyzer *v1beta1.Analyzer) (result *v1beta1.Analyzer, err error) {
func (c *analyzers) Create(ctx context.Context, analyzer *v1beta1.Analyzer, opts v1.CreateOptions) (result *v1beta1.Analyzer, err error) {
result = &v1beta1.Analyzer{}
err = c.client.Post().
Namespace(c.ns).
Resource("analyzers").
VersionedParams(&opts, scheme.ParameterCodec).
Body(analyzer).
Do().
Do(ctx).
Into(result)
return
}
// Update takes the representation of a analyzer and updates it. Returns the server's representation of the analyzer, and an error, if there is any.
func (c *analyzers) Update(analyzer *v1beta1.Analyzer) (result *v1beta1.Analyzer, err error) {
func (c *analyzers) Update(ctx context.Context, analyzer *v1beta1.Analyzer, opts v1.UpdateOptions) (result *v1beta1.Analyzer, err error) {
result = &v1beta1.Analyzer{}
err = c.client.Put().
Namespace(c.ns).
Resource("analyzers").
Name(analyzer.Name).
VersionedParams(&opts, scheme.ParameterCodec).
Body(analyzer).
Do().
Do(ctx).
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *analyzers) UpdateStatus(analyzer *v1beta1.Analyzer) (result *v1beta1.Analyzer, err error) {
func (c *analyzers) UpdateStatus(ctx context.Context, analyzer *v1beta1.Analyzer, opts v1.UpdateOptions) (result *v1beta1.Analyzer, err error) {
result = &v1beta1.Analyzer{}
err = c.client.Put().
Namespace(c.ns).
Resource("analyzers").
Name(analyzer.Name).
SubResource("status").
VersionedParams(&opts, scheme.ParameterCodec).
Body(analyzer).
Do().
Do(ctx).
Into(result)
return
}
// Delete takes name of the analyzer and deletes it. Returns an error if one occurs.
func (c *analyzers) Delete(name string, options *v1.DeleteOptions) error {
func (c *analyzers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("analyzers").
Name(name).
Body(options).
Do().
Body(&opts).
Do(ctx).
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *analyzers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
func (c *analyzers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
var timeout time.Duration
if listOptions.TimeoutSeconds != nil {
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
if listOpts.TimeoutSeconds != nil {
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("analyzers").
VersionedParams(&listOptions, scheme.ParameterCodec).
VersionedParams(&listOpts, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Body(&opts).
Do(ctx).
Error()
}
// Patch applies the patch and returns the patched analyzer.
func (c *analyzers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Analyzer, err error) {
func (c *analyzers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Analyzer, err error) {
result = &v1beta1.Analyzer{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("analyzers").
SubResource(subresources...).
Name(name).
SubResource(subresources...).
VersionedParams(&opts, scheme.ParameterCodec).
Body(data).
Do().
Do(ctx).
Into(result)
return
}
@@ -18,6 +18,7 @@ limitations under the License.
package v1beta1
import (
"context"
"time"
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
@@ -36,15 +37,15 @@ type CollectorsGetter interface {
// CollectorInterface has methods to work with Collector resources.
type CollectorInterface interface {
Create(*v1beta1.Collector) (*v1beta1.Collector, error)
Update(*v1beta1.Collector) (*v1beta1.Collector, error)
UpdateStatus(*v1beta1.Collector) (*v1beta1.Collector, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1beta1.Collector, error)
List(opts v1.ListOptions) (*v1beta1.CollectorList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Collector, err error)
Create(ctx context.Context, collector *v1beta1.Collector, opts v1.CreateOptions) (*v1beta1.Collector, error)
Update(ctx context.Context, collector *v1beta1.Collector, opts v1.UpdateOptions) (*v1beta1.Collector, error)
UpdateStatus(ctx context.Context, collector *v1beta1.Collector, opts v1.UpdateOptions) (*v1beta1.Collector, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.Collector, error)
List(ctx context.Context, opts v1.ListOptions) (*v1beta1.CollectorList, error)
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Collector, err error)
CollectorExpansion
}
@@ -63,20 +64,20 @@ func newCollectors(c *TroubleshootV1beta1Client, namespace string) *collectors {
}
// Get takes name of the collector, and returns the corresponding collector object, and an error if there is any.
func (c *collectors) Get(name string, options v1.GetOptions) (result *v1beta1.Collector, err error) {
func (c *collectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Collector, err error) {
result = &v1beta1.Collector{}
err = c.client.Get().
Namespace(c.ns).
Resource("collectors").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Do(ctx).
Into(result)
return
}
// List takes label and field selectors, and returns the list of Collectors that match those selectors.
func (c *collectors) List(opts v1.ListOptions) (result *v1beta1.CollectorList, err error) {
func (c *collectors) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.CollectorList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
@@ -87,13 +88,13 @@ func (c *collectors) List(opts v1.ListOptions) (result *v1beta1.CollectorList, e
Resource("collectors").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Do(ctx).
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested collectors.
func (c *collectors) Watch(opts v1.ListOptions) (watch.Interface, error) {
func (c *collectors) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
@@ -104,87 +105,90 @@ func (c *collectors) Watch(opts v1.ListOptions) (watch.Interface, error) {
Resource("collectors").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
Watch(ctx)
}
// Create takes the representation of a collector and creates it. Returns the server's representation of the collector, and an error, if there is any.
func (c *collectors) Create(collector *v1beta1.Collector) (result *v1beta1.Collector, err error) {
func (c *collectors) Create(ctx context.Context, collector *v1beta1.Collector, opts v1.CreateOptions) (result *v1beta1.Collector, err error) {
result = &v1beta1.Collector{}
err = c.client.Post().
Namespace(c.ns).
Resource("collectors").
VersionedParams(&opts, scheme.ParameterCodec).
Body(collector).
Do().
Do(ctx).
Into(result)
return
}
// Update takes the representation of a collector and updates it. Returns the server's representation of the collector, and an error, if there is any.
func (c *collectors) Update(collector *v1beta1.Collector) (result *v1beta1.Collector, err error) {
func (c *collectors) Update(ctx context.Context, collector *v1beta1.Collector, opts v1.UpdateOptions) (result *v1beta1.Collector, err error) {
result = &v1beta1.Collector{}
err = c.client.Put().
Namespace(c.ns).
Resource("collectors").
Name(collector.Name).
VersionedParams(&opts, scheme.ParameterCodec).
Body(collector).
Do().
Do(ctx).
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *collectors) UpdateStatus(collector *v1beta1.Collector) (result *v1beta1.Collector, err error) {
func (c *collectors) UpdateStatus(ctx context.Context, collector *v1beta1.Collector, opts v1.UpdateOptions) (result *v1beta1.Collector, err error) {
result = &v1beta1.Collector{}
err = c.client.Put().
Namespace(c.ns).
Resource("collectors").
Name(collector.Name).
SubResource("status").
VersionedParams(&opts, scheme.ParameterCodec).
Body(collector).
Do().
Do(ctx).
Into(result)
return
}
// Delete takes name of the collector and deletes it. Returns an error if one occurs.
func (c *collectors) Delete(name string, options *v1.DeleteOptions) error {
func (c *collectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("collectors").
Name(name).
Body(options).
Do().
Body(&opts).
Do(ctx).
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *collectors) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
func (c *collectors) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
var timeout time.Duration
if listOptions.TimeoutSeconds != nil {
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
if listOpts.TimeoutSeconds != nil {
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("collectors").
VersionedParams(&listOptions, scheme.ParameterCodec).
VersionedParams(&listOpts, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Body(&opts).
Do(ctx).
Error()
}
// Patch applies the patch and returns the patched collector.
func (c *collectors) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Collector, err error) {
func (c *collectors) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Collector, err error) {
result = &v1beta1.Collector{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("collectors").
SubResource(subresources...).
Name(name).
SubResource(subresources...).
VersionedParams(&opts, scheme.ParameterCodec).
Body(data).
Do().
Do(ctx).
Into(result)
return
}
@@ -18,6 +18,8 @@ limitations under the License.
package fake
import (
"context"
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
@@ -38,7 +40,7 @@ var analyzersResource = schema.GroupVersionResource{Group: "troubleshoot.replica
var analyzersKind = schema.GroupVersionKind{Group: "troubleshoot.replicated.com", Version: "v1beta1", Kind: "Analyzer"}
// Get takes name of the analyzer, and returns the corresponding analyzer object, and an error if there is any.
func (c *FakeAnalyzers) Get(name string, options v1.GetOptions) (result *v1beta1.Analyzer, err error) {
func (c *FakeAnalyzers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Analyzer, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(analyzersResource, c.ns, name), &v1beta1.Analyzer{})
@@ -49,7 +51,7 @@ func (c *FakeAnalyzers) Get(name string, options v1.GetOptions) (result *v1beta1
}
// List takes label and field selectors, and returns the list of Analyzers that match those selectors.
func (c *FakeAnalyzers) List(opts v1.ListOptions) (result *v1beta1.AnalyzerList, err error) {
func (c *FakeAnalyzers) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.AnalyzerList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(analyzersResource, analyzersKind, c.ns, opts), &v1beta1.AnalyzerList{})
@@ -71,14 +73,14 @@ func (c *FakeAnalyzers) List(opts v1.ListOptions) (result *v1beta1.AnalyzerList,
}
// Watch returns a watch.Interface that watches the requested analyzers.
func (c *FakeAnalyzers) Watch(opts v1.ListOptions) (watch.Interface, error) {
func (c *FakeAnalyzers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(analyzersResource, c.ns, opts))
}
// Create takes the representation of a analyzer and creates it. Returns the server's representation of the analyzer, and an error, if there is any.
func (c *FakeAnalyzers) Create(analyzer *v1beta1.Analyzer) (result *v1beta1.Analyzer, err error) {
func (c *FakeAnalyzers) Create(ctx context.Context, analyzer *v1beta1.Analyzer, opts v1.CreateOptions) (result *v1beta1.Analyzer, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(analyzersResource, c.ns, analyzer), &v1beta1.Analyzer{})
@@ -89,7 +91,7 @@ func (c *FakeAnalyzers) Create(analyzer *v1beta1.Analyzer) (result *v1beta1.Anal
}
// Update takes the representation of a analyzer and updates it. Returns the server's representation of the analyzer, and an error, if there is any.
func (c *FakeAnalyzers) Update(analyzer *v1beta1.Analyzer) (result *v1beta1.Analyzer, err error) {
func (c *FakeAnalyzers) Update(ctx context.Context, analyzer *v1beta1.Analyzer, opts v1.UpdateOptions) (result *v1beta1.Analyzer, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(analyzersResource, c.ns, analyzer), &v1beta1.Analyzer{})
@@ -101,7 +103,7 @@ func (c *FakeAnalyzers) Update(analyzer *v1beta1.Analyzer) (result *v1beta1.Anal
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeAnalyzers) UpdateStatus(analyzer *v1beta1.Analyzer) (*v1beta1.Analyzer, error) {
func (c *FakeAnalyzers) UpdateStatus(ctx context.Context, analyzer *v1beta1.Analyzer, opts v1.UpdateOptions) (*v1beta1.Analyzer, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(analyzersResource, "status", c.ns, analyzer), &v1beta1.Analyzer{})
@@ -112,7 +114,7 @@ func (c *FakeAnalyzers) UpdateStatus(analyzer *v1beta1.Analyzer) (*v1beta1.Analy
}
// Delete takes name of the analyzer and deletes it. Returns an error if one occurs.
func (c *FakeAnalyzers) Delete(name string, options *v1.DeleteOptions) error {
func (c *FakeAnalyzers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(analyzersResource, c.ns, name), &v1beta1.Analyzer{})
@@ -120,15 +122,15 @@ func (c *FakeAnalyzers) Delete(name string, options *v1.DeleteOptions) error {
}
// DeleteCollection deletes a collection of objects.
func (c *FakeAnalyzers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(analyzersResource, c.ns, listOptions)
func (c *FakeAnalyzers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(analyzersResource, c.ns, listOpts)
_, err := c.Fake.Invokes(action, &v1beta1.AnalyzerList{})
return err
}
// Patch applies the patch and returns the patched analyzer.
func (c *FakeAnalyzers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Analyzer, err error) {
func (c *FakeAnalyzers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Analyzer, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(analyzersResource, c.ns, name, pt, data, subresources...), &v1beta1.Analyzer{})
@@ -18,6 +18,8 @@ limitations under the License.
package fake
import (
"context"
v1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
@@ -38,7 +40,7 @@ var collectorsResource = schema.GroupVersionResource{Group: "troubleshoot.replic
var collectorsKind = schema.GroupVersionKind{Group: "troubleshoot.replicated.com", Version: "v1beta1", Kind: "Collector"}
// Get takes name of the collector, and returns the corresponding collector object, and an error if there is any.
func (c *FakeCollectors) Get(name string, options v1.GetOptions) (result *v1beta1.Collector, err error) {
func (c *FakeCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Collector, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(collectorsResource, c.ns, name), &v1beta1.Collector{})
@@ -49,7 +51,7 @@ func (c *FakeCollectors) Get(name string, options v1.GetOptions) (result *v1beta
}
// List takes label and field selectors, and returns the list of Collectors that match those selectors.
func (c *FakeCollectors) List(opts v1.ListOptions) (result *v1beta1.CollectorList, err error) {
func (c *FakeCollectors) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.CollectorList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(collectorsResource, collectorsKind, c.ns, opts), &v1beta1.CollectorList{})
@@ -71,14 +73,14 @@ func (c *FakeCollectors) List(opts v1.ListOptions) (result *v1beta1.CollectorLis
}
// Watch returns a watch.Interface that watches the requested collectors.
func (c *FakeCollectors) Watch(opts v1.ListOptions) (watch.Interface, error) {
func (c *FakeCollectors) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(collectorsResource, c.ns, opts))
}
// Create takes the representation of a collector and creates it. Returns the server's representation of the collector, and an error, if there is any.
func (c *FakeCollectors) Create(collector *v1beta1.Collector) (result *v1beta1.Collector, err error) {
func (c *FakeCollectors) Create(ctx context.Context, collector *v1beta1.Collector, opts v1.CreateOptions) (result *v1beta1.Collector, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(collectorsResource, c.ns, collector), &v1beta1.Collector{})
@@ -89,7 +91,7 @@ func (c *FakeCollectors) Create(collector *v1beta1.Collector) (result *v1beta1.C
}
// Update takes the representation of a collector and updates it. Returns the server's representation of the collector, and an error, if there is any.
func (c *FakeCollectors) Update(collector *v1beta1.Collector) (result *v1beta1.Collector, err error) {
func (c *FakeCollectors) Update(ctx context.Context, collector *v1beta1.Collector, opts v1.UpdateOptions) (result *v1beta1.Collector, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(collectorsResource, c.ns, collector), &v1beta1.Collector{})
@@ -101,7 +103,7 @@ func (c *FakeCollectors) Update(collector *v1beta1.Collector) (result *v1beta1.C
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeCollectors) UpdateStatus(collector *v1beta1.Collector) (*v1beta1.Collector, error) {
func (c *FakeCollectors) UpdateStatus(ctx context.Context, collector *v1beta1.Collector, opts v1.UpdateOptions) (*v1beta1.Collector, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(collectorsResource, "status", c.ns, collector), &v1beta1.Collector{})
@@ -112,7 +114,7 @@ func (c *FakeCollectors) UpdateStatus(collector *v1beta1.Collector) (*v1beta1.Co
}
// Delete takes name of the collector and deletes it. Returns an error if one occurs.
func (c *FakeCollectors) Delete(name string, options *v1.DeleteOptions) error {
func (c *FakeCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(collectorsResource, c.ns, name), &v1beta1.Collector{})
@@ -120,15 +122,15 @@ func (c *FakeCollectors) Delete(name string, options *v1.DeleteOptions) error {
}
// DeleteCollection deletes a collection of objects.
func (c *FakeCollectors) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(collectorsResource, c.ns, listOptions)
func (c *FakeCollectors) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(collectorsResource, c.ns, listOpts)
_, err := c.Fake.Invokes(action, &v1beta1.CollectorList{})
return err
}
// Patch applies the patch and returns the patched collector.
func (c *FakeCollectors) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Collector, err error) {
func (c *FakeCollectors) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Collector, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(collectorsResource, c.ns, name, pt, data, subresources...), &v1beta1.Collector{})

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