Compare commits

...
502 Commits
Author SHA1 Message Date
divolgin e8dba3cad9 Merge pull request #309 from replicatedhq/divolgin/secrets
Create pull secrets if they don't have names
2021-01-08 12:21:04 -08:00
divolgin 3e2d90ee9b Create pull secrets if they don't have names 2021-01-08 20:01:47 +00:00
Salah Aldeen Al Saleh b05eb6bf75 fix subdirectory missing in support bundle issue (#308) 2020-12-08 12:52:29 -08:00
Andrew Lavery 2e16424b44 Merge pull request #307 from replicatedhq/laverya/fix-node-resources-label-filtering
fix node resources label filtering
2020-12-04 10:14:23 -05:00
Andrew Lavery f19ac98055 add tests for analyzeNodeResources, including label filtering 2020-12-03 19:19:57 -05:00
Andrew Lavery 55fbd92673 not finding a label is not an error 2020-12-03 19:17:47 -05:00
Andrew Lavery 2f2edb5356 Merge pull request #302 from replicatedhq/laverya/dont-rbac-check-excluded-collectors
check whether a collector is excluded before checking RBAC for it
2020-11-19 19:44:41 -05:00
Andrew Lavery 0ec1c43afd update setup-kind action 2020-11-19 18:57:52 -05:00
Andrew Lavery b43ef17b8f remve set-env and add-path from CI 2020-11-19 18:37:57 -05:00
Andrew Lavery 858d00ce41 check whether a collector is excluded before checking RBAC for it 2020-11-19 18:31:56 -05:00
emosbaugh c81576f25f Merge pull request #299 from replicatedhq/ceph-no-name
Ceph collector does not need a name property
2020-11-13 13:03:05 -08:00
Ethan Mosbaugh 1e8e20a295 Ceph collector does not need a name property 2020-11-13 21:01:02 +00:00
emosbaugh 4cdfa5b112 Merge pull request #298 from replicatedhq/ceph-format
Fix Ceph collector file extension
2020-11-13 12:17:48 -08:00
Ethan Mosbaugh 8ef720ba9e Fix Ceph collector file extension 2020-11-13 20:02:15 +00:00
divolgin 7d2e0d0aa3 Merge pull request #297 from replicatedhq/divolgin/fix
fixing build
2020-11-13 09:54:03 -08:00
divolgin 88685ae5f7 fixing build 2020-11-13 17:49:43 +00:00
divolgin 364a63533a Merge pull request #296 from replicatedhq/divolgin/releaser
Revert changes for static linking
2020-11-13 09:37:31 -08:00
divolgin 6749f339e8 Revert changes for static linking 2020-11-13 17:34:08 +00:00
emosbaugh 2bf19eaddf Ceph collectors and analyzers (#295)
* Ceph collectors and analyzers

* updating based on prior pr

* fixes

* fixes
2020-11-13 09:12:42 -08:00
divolgin fd6af65f7e Merge pull request #294 from replicatedhq/divolgin/no-rrd
Remove RRD dependency and switch to dynamic linking
2020-11-12 11:28:07 -08:00
divolgin 9e669b3f13 Remove RRD dependency and switch to dynamic linking 2020-11-12 19:13:25 +00:00
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 bde882047f Merge pull request #199 from replicatedhq/laverya/redaction-reports
add yaml/json annotations for redaction reports
2020-05-20 15:03:25 -04:00
Andrew Lavery 0d84c62c78 add yaml/json annotations for redaction reports 2020-05-20 14:58:37 -04:00
Andrew Lavery f5183c62d6 Merge pull request #198 from replicatedhq/laverya/redaction-reports
redaction reports framework
2020-05-20 14:43:29 -04:00
Andrew Lavery 5a2c153026 add tests for redaction reports 2020-05-20 14:36:07 -04:00
Andrew Lavery 669cb2a145 send redaction report 2020-05-20 13:08:10 -04:00
Andrew Lavery fb2f028fb5 add methods to get and clear redactions 2020-05-20 10:28:46 -04:00
Andrew Lavery d37ace6e51 better naming scheme that does not leak secrets 2020-05-19 17:21:55 -04:00
Andrew Lavery b6e92e1bdf record yaml redactions 2020-05-14 18:49:30 -04:00
Andrew Lavery 27369acbf4 plumbing for literal/regex/multiline reports 2020-05-14 16:00:58 -04:00
Andrew Lavery 22d397cc7b Merge pull request #190 from MikaelSmith/error-on-connect-fail
Report errors connecting to the database
2020-05-13 19:38:20 -04:00
Andrew Lavery 428db221c3 Merge pull request #197 from replicatedhq/laverya/automate-krew-releases
automate krew releases
2020-05-13 19:09:53 -04:00
Andrew Lavery f6a5d40bfd Merge pull request #195 from replicatedhq/laverya/goreleaser-action-v2
use v2 of the goreleaser-action workflow
2020-05-13 19:07:55 -04:00
Andrew Lavery 8bc627d20f automate krew releases
this might work, might not - the action is only intended for one app per repo, but since it's all in one release I think it'll be fine
2020-05-13 19:04:27 -04:00
Andrew Lavery 7c95363afe Merge pull request #196 from replicatedhq/laverya/remove-unused-replace
remove an unused replace statement
2020-05-13 18:15:13 -04:00
Andrew Lavery f7aebd0745 remove an unused replace statement 2020-05-13 16:55:41 -04:00
Andrew Lavery 523332268b Merge pull request #194 from replicatedhq/laverya/go-1.14
use go 1.14 in ci
2020-05-13 16:52:41 -04:00
Andrew Lavery ca6f118c62 use v2 of the goreleaser-action workflow 2020-05-13 15:49:44 -04:00
Andrew Lavery 769d616ede use go 1.14 in ci 2020-05-13 15:35:10 -04:00
Salah Aldeen Al Saleh 620343e3a1 convert type BoolString to oneOf for api-schema (#193) 2020-05-11 12:57:27 -07:00
Marc Campbell 994978ccd8 Merge pull request #192 from replicatedhq/pg-collector
Parse postgres version output
2020-05-08 06:36:29 -07:00
Marc Campbell 0f31088bd1 Parse postgres version output 2020-05-07 19:41:59 -07:00
Michael Smith 4b9680be8b Report errors connecting to the database
If the postgres collector encounters an error, report it as a failure in
the analyzer. Otherwise the error is completely suppressed.

Fixes #187.
2020-05-07 17:04:10 -07:00
Andrew Lavery 20033b12d5 Merge pull request #188 from replicatedhq/laverya/improve-unexpected-operator-error
include the operator in the 'unexpected operator' error message
2020-05-07 18:39:59 -04:00
Andrew Lavery f795f909b3 include all the data 2020-05-07 18:15:33 -04:00
Andrew Lavery b3828b175d include the operator in the 'unexpected operator' error message 2020-05-07 18:04:37 -04:00
Andrew Lavery fa84d3fbd3 Merge pull request #180 from MikaelSmith/fix-allocatable-sum
Fix sum(memoryAllocatable) and sum(podAllocatable)
2020-05-07 17:42:32 -04:00
Andrew Lavery f377b7c1a8 Merge pull request #185 from replicatedhq/laverya/add-multiline-and-yaml-to-example
add multiline redactor and yaml to example spec
2020-04-27 22:06:38 -04:00
Andrew Lavery 6d3321cb43 Merge pull request #186 from replicatedhq/laverya/redact-yaml-star-map-support
support * when redacting yaml maps
2020-04-27 22:03:40 -04:00
Andrew Lavery 15b100e0ee support * when redacting yaml maps 2020-04-27 22:01:04 -04:00
Andrew Lavery cd84cfa629 add multiline redactor and yaml to example spec 2020-04-27 21:47:52 -04:00
Andrew Lavery 94ccf284b8 Merge pull request #184 from replicatedhq/laverya/custom-multiline-and-yaml-redactors
custom multiline and yaml redactors
2020-04-27 21:20:35 -04:00
Andrew Lavery 2f092ae4a1 add multiline redactor test 2020-04-27 20:52:01 -04:00
Andrew Lavery 56150c3345 add support for redacting items within yaml documents 2020-04-27 20:46:52 -04:00
Andrew Lavery 8f594e8764 add support for custom multiline redactors, add yaml redactors to spec 2020-04-27 16:50:25 -04:00
Andrew Lavery 7752a33268 Merge pull request #183 from replicatedhq/laverya/allow-specifying-multiple-redact-specs
allow specifying multiple redact specs
2020-04-27 16:28:58 -04:00
Andrew Lavery 94e27eb5e1 allow specifying multiple redact specs 2020-04-27 14:37:51 -04:00
Andrew Lavery 33580e9611 Merge pull request #182 from replicatedhq/laverya/no-regex-panic-when-not-found
analyzeRegexGroups should not have out of range error upon no match
2020-04-23 15:29:23 -04:00
Andrew Lavery 09bc81e144 analyzeRegexGroups should not have out of range error upon no match 2020-04-23 15:26:01 -04:00
Andrew Lavery 7626f7d7d7 Merge pull request #181 from replicatedhq/laverya/use-glob-file-matching
use gobwas/glob, not filepath, file matching
2020-04-23 13:23:31 -04:00
Andrew Lavery 37f75c1a3f use gobwas/glob, not filepath, file matching 2020-04-23 13:13:46 -04:00
Michael Smith 8f8b8b2c78 Fix sum(memoryAllocatable) and sum(podAllocatable)
Fixes `sum(memoryAllocatable)` and `sum(podAllocatable)`. These
previously tested the wrong resource (cpuAllocatable).

Also refactor comparison functions to unify resolving properties and add
more testing.

Fixes #179.
2020-04-21 11:39:31 -07:00
Andrew Lavery b1e1dbb42a Merge pull request #178 from replicatedhq/laverya/support-multidoc-collector-and-redactor-input
allow providing redactors as multidoc yaml after collectors
2020-04-21 14:15:51 -04:00
Michael Smith 1605b56090 Simplify tests by reusing same node data
Remove repetition across test cases that made it hard to read. Tweaks
tests where needed so they can all use the same node data. Also corrects
a test where the description didn't match the test.
2020-04-21 11:08:03 -07:00
Andrew Lavery a58913d1f6 allow providing redactors as multidoc yaml after collectors 2020-04-20 17:08:39 -04:00
Andrew Lavery eb84fc64e1 Merge pull request #177 from replicatedhq/laverya/remove-per-collector-redactors
remove per-collector redactors
2020-04-20 15:56:59 -04:00
Andrew Lavery 087505f8e5 fix example yaml 2020-04-20 15:44:19 -04:00
Andrew Lavery 8b54c28b4b remove per-collector redactors 2020-04-20 14:59:15 -04:00
Andrew Lavery 7be40ddf16 Merge pull request #176 from replicatedhq/laverya/include-collect-global-redactors
honor global redactors from collect spec
2020-04-17 13:15:50 -04:00
Andrew Lavery de9b832357 honor global redactors from collect spec 2020-04-17 13:06:52 -04:00
Andrew Lavery fc65ceb5b4 Merge pull request #175 from replicatedhq/laverya/custom-redactors
last-mile and per-collector redactor specs
2020-04-16 14:22:10 -04:00
Andrew Lavery d016e3269c add global and per-collector redactors
add redact type, and begin wiring global redactors

use per-collector redactors

add a test of the 'data' collector and redaction

handle literal string replacements

remove redundant types and redact calls

add proper redactor type, foundations of global redactors

accept global redactors from the CLI, include sample redaction spec
2020-04-16 14:03:00 -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
Andrew Lavery f06cd9e4a2 Merge pull request #172 from replicatedhq/laverya/refactor-byte-array-interfaces
refactor byte array interfaces
2020-04-14 18:14:57 -04:00
Andrew Lavery 7dc93ddade don't redact nil entries, discard them 2020-04-14 17:59:12 -04:00
Andrew Lavery a7c4f04a63 convert remaining collectors to map[string][]byte format 2020-04-14 17:15:53 -04:00
Andrew Lavery 527fe16bcc reduce marshal/unmarshal passes by returning map[string][]byte
WIP
2020-04-14 16:37:51 -04:00
Andrew Lavery 406e1e6d1a Merge pull request #168 from replicatedhq/laverya/collect-logs-from-all-containers-in-pod
collect logs from all containers in a pod if no container is specified
2020-04-08 18:46:06 -04:00
Andrew Lavery cbaf96691e collect logs from all containers in a pod if no container is specified 2020-04-08 16:05:44 -04:00
Andrew Lavery 4dfb02e7f7 Merge pull request #167 from replicatedhq/laverya/include-container-in-copy-path-if-provided
include the container name in the file copy path destination if provided
2020-04-06 18:29:45 -04:00
Andrew Lavery e19a5dee38 Merge pull request #166 from replicatedhq/laverya/respect-secret-includevalue-flag
support the 'includeValue' flag for secrets
2020-04-06 18:09:43 -04:00
Andrew Lavery 2c495b8a7a include the container name in the file copy path destination if provided 2020-04-06 18:02:37 -04:00
Andrew Lavery c3646bacd9 don't use a default filename in paths 2020-04-06 18:01:13 -04:00
Andrew Lavery 3844a3dbd3 don't override namespace 2020-04-06 17:44:57 -04:00
Andrew Lavery 544c832d6e support the 'includeValue' flag for secrets 2020-04-06 17:03:19 -04:00
Marc Campbell 38aef191ab Merge pull request #162 from ashwathishiva/fix_win_path_bug
textanalyze: fixes path handling in windows as well
2020-03-26 16:07:05 -07:00
Andrew Lavery 545ce24c04 Merge pull request #164 from replicatedhq/laverya/fix-build-after-k8s-1.18
fix build after k8s 1.18 was released
2020-03-26 18:42:30 -04:00
Andrew Lavery c262320cea fix build after k8s 1.18 was released 2020-03-26 18:50:23 +00:00
Ashwathi Shiva d1c45f1456 fixes path handling in windows as well 2020-03-26 00:58:40 -04:00
Andrew Lavery c4f841d420 Merge pull request #161 from replicatedhq/laverya/check-filepath-before-attempting-url
attempt to read the file at the provided path before trying url
2020-03-25 17:59:23 -04:00
Andrew Lavery 75ab7bb917 fix downloadAnalyzerSpec function too 2020-03-25 17:32:52 -04:00
Andrew Lavery 9692d5a457 attempt to read the file at the provided path before trying url 2020-03-25 17:27:16 -04:00
Marc Campbell 2775a6393e Docker login 2020-03-23 11:20:50 -07:00
Marc Campbell b0c46c5ac8 Rename 2020-03-23 11:03:01 -07:00
Marc Campbell e9ad961c0e Merge pull request #160 from replicatedhq/build-on-actions
Move all to github actions
2020-03-23 10:43:09 -07:00
Marc Campbell bd71222715 Removing more unused code 2020-03-23 10:01:30 -07:00
Marc Campbell 818f8b26dd Move all to github actions 2020-03-23 09:33:23 -07:00
divolgin 9dbf282b5b Merge pull request #156 from replicatedhq/divolgin/previous
Include previous logs from pods if available
2020-03-23 09:19:33 -07:00
Marc Campbell 374856798f Merge branch 'master' of github.com:replicatedhq/troubleshoot 2020-03-23 09:17:05 -07:00
Marc Campbell d754b3bb9f Go mod 2020-03-23 09:16:19 -07:00
Marc Campbell 225ebfbcbe Merge pull request #157 from replicatedhq/postgres-analyzer
Database analyzers (pg, mysql, redis)
2020-03-23 08:58:50 -07:00
Marc Campbell 821a132980 add some omitempty 2020-03-23 08:58:02 -07:00
Marc Campbell 4e3a627b21 Remove broken mysql example 2020-03-23 08:51:59 -07:00
Marc Campbell 206e5c01d7 Fix makefile 2020-03-23 08:37:53 -07:00
Marc Campbell e54ad171d8 Removing incorrectly generate client code 2020-03-23 08:36:58 -07:00
Marc Campbell 8f78827002 Redis 2020-03-20 11:03:49 -07:00
Marc Campbell 77eba55ee8 Abstract, add mysql 2020-03-20 07:19:03 -07:00
Marc Campbell 562a565f1f Adding postgres analyzer 2020-03-19 18:28:13 -07:00
divolgin db8533e69c Include previous logs from pods if available 2020-03-18 17:53:43 +00:00
Marc Campbell f37bbdb14c Merge pull request #153 from replicatedhq/export-function
Export function
2020-03-17 14:36:38 -07:00
mzaneri 35808423b2 Merge pull request #155 from replicatedhq/mike-provider-refactor-working
REFACTOR analyzeDistribution to allow vendoring to KOTS
2020-03-12 16:38:45 -07:00
mzaneri 86b745d6c2 Update distribution.go 2020-03-12 16:01:27 -07:00
mzaneri c773ebd894 Update distribution.go 2020-03-12 14:56:03 -07:00
Michael 2ba2849a38 Merge branch 'mike-provider-refactor-working' of github.com:replicatedhq/troubleshoot into mike-provider-refactor-working 2020-03-12 18:01:19 +00:00
Michael a1c5247c8b REFACTOR create helper functions for analyzeDistribution 2020-03-12 18:01:05 +00:00
Michael 47ff99bfe2 Merge branch 'mike-provider-refactor-working' of github.com:replicatedhq/troubleshoot into mike-provider-refactor-working 2020-03-12 17:55:12 +00:00
Michael 6d19794dc5 REFACTOR create helper functions for analyzeDistribution 2020-03-12 17:54:34 +00:00
Michael 0c3cc7afce FIX change string to only be empty on eks masters 2020-03-11 23:33:29 +00:00
Michael 5798a4aa11 change analyzers 2020-03-11 22:10:53 +00:00
Michael ae64a4d591 REFACTOR create helper functions for analyzeDistribution 2020-03-11 17:58:52 +00:00
Marc Campbell 983aaaacea Defining types in a package 2020-03-10 17:32:16 +00:00
Marc Campbell e74101070d Refactor 2020-03-10 01:07:57 +00:00
Andrew Lavery d9625477d2 Merge pull request #151 from replicatedhq/laverya/no-go-i18n-version-replace
move multitype package here from kots
2020-03-09 17:43:12 -07:00
Andrew Lavery 1c66eed2cd move multitype package here from kots 2020-03-09 17:37:27 -07:00
Marc Campbell 1dda832933 Export runCollectors 2020-03-10 00:04:23 +00:00
divolgin d019f87284 Merge pull request #149 from replicatedhq/divolgin/fix-tests
5 is definitely not more than 20....
2020-03-09 12:14:22 -07:00
divolgin 983481da07 5 is definitely not more than 20.... 2020-03-09 19:07:46 +00:00
Marc Campbell 7462e1cdee Merge pull request #148 from replicatedhq/equality
Fix equality in regex analyzer
2020-03-07 13:32:39 -08:00
Marc Campbell 6563290de9 More clear solutino: 2020-03-07 21:30:41 +00:00
Marc Campbell ff627dcf83 Fix equality in regex analyzer 2020-03-07 21:28:17 +00:00
Jeremy Cohn 2158dee58d Merge pull request #147 from replicatedhq/jeremy/fix-analyze
Added spec support to `analyze` function in support-bundle
2020-03-06 14:27:48 -08:00
Jeremy Cohn f6a56a9def A few minor updates in the analyze usage 2020-03-06 14:26:38 -07:00
Jeremy Cohn 63a81314bb Updated to allow an analyze spec from URL
This commit includes the following changes:
1. Updated to allow a analyze spec from URL
2. Removed spec flag
The analyzer spec is a positional argument consistent with preflight.
2020-03-06 13:33:29 -07:00
Jeremy Cohn 1d352298e1 Added spec support to analyze function in support-bundle 2020-03-05 17:43:34 -08:00
Marc Campbell 26d8211a08 Merge pull request #146 from replicatedhq/remove-invalid-tests
Remove unused code
2020-03-05 17:20:47 -08:00
Marc Campbell 6c212e24f0 Better marshaling 2020-03-06 01:15:49 +00:00
Marc Campbell c9b01f981d Remove unused code 2020-03-06 00:49:53 +00:00
Marc Campbell 4fca3ff4fc Merge pull request #145 from replicatedhq/json-yaml-json-yaml
BoolString can only unmarshal as json
2020-03-05 16:36:23 -08:00
Marc Campbell d81cff0d67 BoolString can only unmarshal as json 2020-03-06 00:33:39 +00:00
Marc Campbell 62d12d8fff Merge pull request #144 from replicatedhq/boolstring
Use bool string for exclude value
2020-03-05 15:42:46 -08:00
Marc Campbell 8126cb3d3d Fix 2020-03-05 23:41:55 +00:00
Marc Campbell 6277101574 Use bool string for exclude value 2020-03-05 23:35:21 +00:00
Marc Campbell 346c751bdf Merge pull request #134 from replicatedhq/openapischema
Generate OpenAPISchema
2020-03-05 13:42:21 -08:00
divolgin 812aa9bc30 Merge pull request #143 from replicatedhq/divolgin/kots-kurl
Better integration with embedded kots clusters
2020-03-03 14:43:18 -08:00
divolgin 5a7b155093 Better integration with embedded kots clusters 2020-03-03 22:30:47 +00:00
Frank Shotwell c6ecab39cc Merge pull request #141 from replicatedhq/scope-cleanup
set SCOPE_LOG_ROOT_PATH to /dev/null to avoid creating lots of logfiles
2020-02-28 11:33:09 -08:00
Salah Al Saleh 648564eb4d Fix text_analyze_test (#142) 2020-02-25 16:42:00 -08:00
Frank Shotwell 1b9a220e2d set SCOPE_LOG_ROOT_PATH to /dev/null to avoid creating lots of logfiles 2020-02-21 19:34:26 +00:00
GraysonNull 45c7d95ebe Merge pull request #140 from GraysonNull/grayson/icon-uri
add IconURI to insights
2020-02-19 13:02:18 -06:00
GraysonNull 42288a4ff3 add IconURI to insights 2020-02-18 21:23:44 +00:00
divolgin 2eb6518792 Merge pull request #137 from replicatedhq/divolgin/insecure
Don't verify TLS certs on support bundle requests
2020-02-14 15:15:37 -08:00
divolgin 78d2364199 Don't verify TLS certs on support bundle requests 2020-02-14 23:08:42 +00:00
divolgin 1f9e478a78 Merge pull request #135 from replicatedhq/divolgin/icons
New icons for analyzers
2020-02-14 10:20:41 -08:00
Frank Shotwell 0d3c58eccd Merge pull request #136 from replicatedhq/detect-aks
Add AKS detection
2020-02-13 13:17:36 -08:00
Frank Shotwell 1a117220cb add aks detection 2020-02-12 17:57:38 +00:00
divolgin 1c79403934 New icons for analyzers 2020-02-11 23:52:29 +00:00
Marc Campbell d1ab5a6991 Docs say exclude, code says when 2020-02-10 22:13:07 +00:00
Marc Campbell c91a8ee60d Filenames that match kubeval 2020-02-10 21:31:36 +00:00
Marc Campbell 899d5d7672 Generate OpenAPISchema 2020-02-10 21:17:11 +00:00
Marc Campbell b466188679 Merge pull request #133 from replicatedhq/node-aggregate-analyzers
Update example
2020-02-06 18:11:06 -05:00
Marc Campbell f15579a3b2 Update example 2020-02-06 23:10:40 +00:00
Marc Campbell 1424faacab Merge pull request #132 from replicatedhq/node-aggregate-analyzers
Node aggregate analyzers
2020-02-06 18:06:21 -05:00
Marc Campbell e94b152733 strings.fields 2020-02-06 19:54:02 +00:00
Marc Campbell 717ba13614 Sum, min, max and count aggregation functions 2020-02-06 19:30:50 +00:00
Marc Campbell 6765e6c030 Working on aggregation functions 2020-02-06 19:10:22 +00:00
Marc Campbell eea2315c52 Aggregate analyzers for node troubleshoot: 2020-02-06 10:06:09 -08:00
Frank Shotwell 1e834430c2 Merge pull request #131 from replicatedhq/scope-build
pass through necessary envs for scope
2020-02-05 09:24:33 -08:00
Frank Shotwell 729caa4a7b pass through necessary envs for scope 2020-02-04 21:10:48 +00:00
Frank Shotwell 130aa88f3a Merge pull request #128 from replicatedhq/scope
Add scope test instrumentation
2020-01-31 12:31:21 -08:00
Frank Shotwell f8870e5fc0 move scope instrumentation to handle table-driven tests 2020-01-31 06:25:07 +00:00
Marc Campbell 4472f10ae7 Merge pull request #127 from replicatedhq/node-analyzers
Node analyzers
2020-01-30 14:17:16 -08:00
Frank Shotwell 10b2941c57 Add scope test instrumentation 2020-01-30 21:01:06 +00:00
Marc Campbell 879c3a67d7 Node resource analyzer 2020-01-29 23:16:40 +00:00
Marc Campbell 299f3e0da7 Add node analyzers 2020-01-29 13:49:02 -08:00
emosbaugh 3982e2023b Treat empty preflight when condition as true (#125)
* Treat empty preflight when condition as true

* add test

* fix
2020-01-23 11:05:54 -08:00
emosbaugh 8eddab69ce Collect statefulsets (#123)
* Collect statefulsets

* go version
2020-01-22 16:43:47 -08:00
Andrew Lavery 58b642426b Merge pull request #122 from replicatedhq/laverya/properly-identify-openshift-clusters-on-eks
EKS clusters do not have masters within the node list
2020-01-21 12:17:46 -08:00
Andrew Lavery 6fe9e2e7e6 EKS clusters do not have masters within the node list 2020-01-21 12:10:29 -08:00
Jeremy Cohn ecfe34f24c Merge pull request #121 from replicatedhq/kurl-installations
Identify kurl clusters with distribution analyzer
2020-01-21 12:04:06 -08:00
Jeremy Cohn 190eeef657 Accidentally pushed these go.mod and go.sum changes 2020-01-21 03:29:32 +00:00
Jeremy Cohn db48de2494 Identify kurl clusters with distribution analyzer 2020-01-21 03:11:39 +00:00
Andrew Lavery 5d280aee02 Merge pull request #120 from replicatedhq/laverya/fix-DownloadAndAnalyze-calls
fix order of arguments in calls to DownloadAndAnalyze
2020-01-20 18:24:33 -08:00
Andrew Lavery f67cd31e80 fix order of arguments in calls to DownloadAndAnalyze 2020-01-20 18:17:15 -08:00
Grant Miller 8f87fd0f1e Merge pull request #118 from replicatedhq/grantmiller-patch-1
Community w/ slack link
2020-01-13 14:46:57 -08:00
Grant Miller 46a114478b Community w/ slack link 2020-01-13 14:44:36 -08:00
Andrew Lavery f8c9cbf381 Merge pull request #117 from replicatedhq/laverya/simplify-auth-cani
minor simplification of authCanI code in resources collector
2020-01-10 15:09:01 -08:00
Andrew Lavery 26f3540d98 minor simplification of authCanI code in resources collector 2020-01-10 15:03:47 -08:00
Marc Campbell 2208b7672e Merge pull request #116 from replicatedhq/limit-range-collector
Add limitranges to cluster-resource collector
2020-01-10 09:43:18 -08:00
Marc Campbell b24032519e Add limitranges to cluster-resource collector 2020-01-10 17:41:20 +00:00
divolgin 1ce5e7b70b Merge pull request #115 from replicatedhq/divolgin/analyzers
Treat analyzer errors as preflight check fails
2020-01-10 08:51:35 -08:00
divolgin 052c932e05 Treat analyzer errors as preflight check fails 2020-01-09 22:48:54 +00:00
Andrew Lavery 5f60b1f0cb Merge pull request #114 from laverya/dont-require-resources.json
if resources.json does not exist, do not fail the distribution analyzer
2020-01-09 12:10:14 -08:00
Andrew Lavery 0379274bb8 if cluster-resources/resources.json, do not fail the distribution analyzer
that file was not collected in v0.9.15 and earlier, so we shouldn't require it - particularly since the microk8s/dockerdesktop/eks/gke/DO detection doesn't depend on it
2020-01-09 12:07:36 -08:00
Andrew Lavery ff97a4eabf Merge pull request #113 from laverya/distribution-analyzer-openshift-support
identify openshift clusters with distribution analyzer
2020-01-08 16:03:52 -08:00
Andrew Lavery 033524a83a identify openshift clusters with distribution analyzer 2020-01-08 15:38:32 -08:00
Andrew Lavery 64675883eb Merge pull request #112 from laverya/include-resources-and-groups-in-troubleshoot-defaults
include ServerGroupsAndResources in troubleshoot cluster-resources
2020-01-08 15:17:50 -08:00
Andrew Lavery 0a04256d62 include ServerGroupsAndResources in troubleshoot cluster-resources
also run cluster-resources collector even when some rbac permissions are missing

print progress message when skipping a collector due to rbac issues

include a sample troubleshoot yaml spec in the repo
2020-01-08 15:10:32 -08:00
Andrew Lavery 30f4d5e3f8 Merge pull request #111 from laverya/include-precise-version-in-troubleshoot-version.yaml
include binary version info in 'version.yaml' troubleshoot file
2020-01-07 17:32:58 -08:00
Andrew Lavery 0fcf473f85 include versionNumber as spec.versionNumber to follow k8s struct standards 2020-01-07 17:21:59 -08:00
Andrew Lavery f9b9dea1c6 only include version number, not full build info 2020-01-07 17:04:41 -08:00
Andrew Lavery 1928f00b0f include binary version info in 'version.yaml' troubleshoot file 2020-01-07 16:28:57 -08:00
Grant Miller d6b5ae9b7c Merge pull request #110 from grantmiller/master
readme adjustments
2020-01-07 12:59:36 -08:00
Grant Miller 3c8e4de10d more details 2020-01-07 12:56:41 -08:00
divolgin 600a67510b Merge pull request #107 from replicatedhq/laverya/rbac-wip
Check RBAC before running collectors
2019-12-31 13:33:17 -08:00
Andrew Lavery 55f2ed44bf Check RBAC before running collectors 2019-12-31 21:32:42 +00:00
divolgin 0993f6efd4 Merge pull request #109 from replicatedhq/divolgin/show-errors
include errors in messages on failures
2019-12-26 09:08:13 -08:00
divolgin 89250b0a87 include errors in messages on failures 2019-12-26 16:32:17 +00:00
divolgin 46cebad731 Merge pull request #108 from replicatedhq/divolgin/usage
Don't print usage on error and no double-logging
2019-12-24 14:09:06 -08:00
divolgin 8e1cb615a5 Don't print usage on error and no double-logging 2019-12-24 22:04:43 +00:00
Marc Campbell 777d907f58 Merge pull request #106 from replicatedhq/two-regexes
Merging both types of regex analyzers together
2019-12-24 12:44:09 -08:00
Marc Campbell 0aafbb79a6 go mod! 2019-12-24 20:00:46 +00:00
Marc Campbell 47c686f352 Replace 2019-12-24 19:50:05 +00:00
Marc Campbell 0ad7a87e4d Bump makefile 2019-12-24 19:46:43 +00:00
Marc Campbell 4a31134d3a go mod tidy 2019-12-24 19:45:01 +00:00
Marc Campbell 2b97e524e3 No more controllers 2019-12-24 19:38:47 +00:00
Marc Campbell 3955dab9bc Removing the CRD 2019-12-24 19:26:26 +00:00
Marc Campbell 9de6b3fffd Merge pull request #99 from ashwathishiva/custom_text_analyzer
Adding a text analyzer
2019-12-23 12:45:32 -08:00
divolgin 90b7ac986d Merge pull request #105 from replicatedhq/divolgin/krew
Bump krew plugins to v0.9.14
2019-12-19 14:59:16 -08:00
divolgin 9a59d1b018 Bump krew plugins to v0.9.14 2019-12-19 22:56:21 +00:00
Marc Campbell af9defc753 Merge pull request #101 from replicatedhq/revert-100-regex-analyzer
Revert "Regex analyzer"
2019-12-18 17:31:28 -08:00
Marc Campbell 806b2fbb41 Revert "Regex analyzer" 2019-12-18 17:30:16 -08:00
Marc Campbell 8ca92b42c4 Merge pull request #100 from replicatedhq/regex-analyzer
Regex analyzer
2019-12-18 17:02:16 -08:00
Marc Campbell 1ef9368e7b Tests on the regex analuzer 2019-12-19 01:00:46 +00:00
Marc Campbell b22120acd8 remove debug 2019-12-19 00:49:37 +00:00
Marc Campbell 518f826fc2 Regex analyzer 2019-12-19 00:49:16 +00:00
Ashwathi Shiva 6a64146012 Added tests 2019-12-18 18:48:12 -05:00
Marc Campbell a0529b09cd Merge pull request #98 from replicatedhq/cani
Add rbac policies for the current user (operator) in the bundle
2019-12-18 13:56:26 -08:00
Marc Campbell f5fe442349 Printing rbac policies per namespace 2019-12-18 19:49:39 +00:00
Marc Campbell e2748c7b24 RBAC 2019-12-18 19:46:02 +00:00
Marc Campbell c962dc59f0 Merge pull request #97 from replicatedhq/build-fix
one more fix
2019-12-18 11:10:14 -08:00
Marc Campbell 8ba2c576c3 one more fix 2019-12-18 18:42:03 +00:00
Marc Campbell fd47d23ac0 Merge pull request #96 from replicatedhq/build-fix
Build fix
2019-12-18 10:03:52 -08:00
Marc Campbell 59a2281290 More cleanup 2019-12-18 18:01:39 +00:00
Marc Campbell 1465af2c09 Fix build 2019-12-18 17:59:43 +00:00
Marc Campbell 7f82ec1f09 Add snapshot release back 2019-12-18 17:45:13 +00:00
Marc Campbell 4338b22505 Merge pull request #95 from replicatedhq/github-actions
GitHub actions
2019-12-18 08:01:22 -08:00
Marc Campbell 1757af58eb only push event 2019-12-18 15:18:43 +00:00
Marc Campbell d8bae5dc8b spelling 2019-12-18 15:18:01 +00:00
Marc Campbell ca9106e7c9 Merge pull request #93 from replicatedhq/no-collector-cmd
Remove manager and collector commands
2019-12-18 07:14:43 -08:00
Marc Campbell 57094dbb44 json preflights 2019-12-18 15:12:11 +00:00
Marc Campbell 7ac973647d Add json output 2019-12-18 15:12:11 +00:00
Marc Campbell 94f812c7bd Preflights 2019-12-18 15:12:11 +00:00
Marc Campbell e5f0e09236 Work on github actions 2019-12-18 15:12:11 +00:00
Marc Campbell 1ee662944f Merge pull request #92 from replicatedhq/optional-collectors
Optional collectors and analyzers
2019-12-18 06:55:56 -08:00
Marc Campbell 7ab1734cb6 Remove manager and collector commands 2019-12-18 01:52:06 +00:00
Marc Campbell c61acdc5de Regenerate 2019-12-18 01:40:06 +00:00
Marc Campbell 98f926b46c Support for optional collectors and analyzers 2019-12-18 01:38:51 +00:00
Marc Campbell d0a0d925bb Cleaning up name / collectorName 2019-12-18 01:12:16 +00:00
Andrew Lavery fd257b5618 Merge pull request #91 from bearium/stdout_results
Added Namespace for Collection and Allows for tests to output to stdout
2019-12-13 15:01:54 -08:00
Ashwathi Shiva f35f5e4d57 removed unused import 2019-12-12 22:38:28 -05:00
Ashwathi Shiva 71e66c7402 modified output string 2019-12-12 22:22:20 -05:00
bearium 3afe32cd1e change global fail to return result 2019-12-11 07:34:15 -05:00
Ashwathi Shiva 714667d641 Initial version of a custom text analyzer 2019-12-10 22:56:35 -05:00
Marc Campbell 029282451e Merge pull request #89 from replicatedhq/version-command
Add version command
2019-12-06 07:22:41 -08:00
bearium 68c76867c7 go mod tidy 2019-12-02 10:36:48 -05:00
bearium e13cb35c82 added std out support and namespace functionality 2019-12-02 09:56:07 -05:00
Marc Campbell 9c4fa8ac14 Add version command 2019-11-30 00:03:43 +00:00
Frank Shotwell 1d3831f6d1 Merge pull request #86 from replicatedhq/gke
add missing gke test
2019-11-22 15:56:26 -08:00
Frank Shotwell 5812414093 add missing gke test 2019-11-22 15:40:38 -08:00
Marc Campbell 94e468ade3 Merge pull request #85 from replicatedhq/docker-desktop
More friendly parsing of Docker Desktop
2019-11-22 15:20:02 -08:00
Marc Campbell 3dc78d2ce8 More friendly parsing of Docker Desktop 2019-11-22 23:14:28 +00:00
divolgin c4dd568267 Merge pull request #84 from replicatedhq/divolgin/krew
Adding changes made in the krew-index repo
2019-11-18 16:02:07 -08:00
divolgin 00fa236525 Adding changes made in the krew-index repo 2019-11-18 23:57:16 +00:00
divolgin ec24967e50 Merge pull request #83 from laverya/update-release-tarball-naming
update tarball naming scheme to bin_os_arch, no longer include version
2019-11-14 14:05:02 -08:00
Andrew Lavery 430fdaa9e4 update tarball naming scheme to bin_os_arch, no longer include version 2019-11-14 13:48:52 -08:00
divolgin 58ed4c8fc2 Merge pull request #82 from replicatedhq/divolgin/data
support for data collector
2019-11-12 13:03:40 -08:00
divolgin 353569d59a support for data collector 2019-11-12 20:53:50 +00:00
Marc Campbell cd43fd6f56 Merge pull request #81 from replicatedhq/collector-node
Collector node
2019-11-08 15:29:03 -08:00
Marc Campbell ed8453ce6b Distribution analyzer 2019-11-08 23:15:58 +00:00
Marc Campbell 5744d608a4 Container runtime analyzer 2019-11-08 17:25:11 +00:00
Marc Campbell 0e3bcd29e4 Add nodes to the bundle 2019-11-08 16:57:33 +00:00
Marc Campbell 2a3e4e7d75 Merge pull request #79 from replicatedhq/regen
Updated from controller-gen
2019-11-07 18:00:46 -08:00
Marc Campbell 67b25e319b Updated from controller-gen 2019-11-08 01:31:20 +00:00
Marc Campbell b0bbaa4e6b Merge pull request #78 from replicatedhq/status-analyzer
Status analyzer
2019-11-07 17:22:04 -08:00
Marc Campbell 8a1e129dbe Add names so the analysis UI renders 2019-11-08 00:22:57 +00:00
Marc Campbell b926ff6fa2 Additional test 2019-11-07 17:19:01 +00:00
Marc Campbell fb5f1522ea Deployment and statefulset 2019-11-07 02:07:05 +00:00
Marc Campbell 6e7715a145 Deployment and Statefulset status analyzers 2019-11-07 01:54:32 +00:00
divolgin c0415dd100 Merge pull request #77 from replicatedhq/divolgin/krew
bump krew manifests to 0.9.11
2019-11-05 11:16:03 -08:00
divolgin 1d031d6897 bump krew manifests to 0.9.11 2019-11-04 15:32:25 +00:00
divolgin b19ac90512 Merge pull request #76 from replicatedhq/divolgin/containerz
Change containers to containerNames to not break kustomize
2019-10-31 16:09:27 -07:00
divolgin 13fb6abcaf Change containers to containerNames to not break kustomize 2019-10-31 19:52:04 +00:00
Marc Campbell 218b9c006d Merge pull request #75 from replicatedhq/analyze-spec
Fix
2019-10-29 14:10:05 -07:00
Marc Campbell 0b493a533b Fix 2019-10-29 21:08:58 +00:00
Marc Campbell 5830eb34a0 Merge pull request #74 from replicatedhq/analyze-spec
Analyze spec
2019-10-29 13:00:32 -07:00
Marc Campbell 8a1bb8a422 isURL 2019-10-29 19:56:29 +00:00
Marc Campbell 14ace5294f Analyze spec 2019-10-29 19:45:25 +00:00
Marc Campbell 25ec38bdf5 Merge pull request #73 from replicatedhq/url
Replace default collector URL
2019-10-29 10:49:37 -07:00
divolgin 223a4a6e0a Replace default collector URL 2019-10-29 03:28:27 +00:00
divolgin 3b6ab41e1b Merge pull request #72 from replicatedhq/divolgin/output-names
use file paths specified by the spec instead of hardcoded ones
2019-10-25 09:26:11 -07:00
divolgin 60ca35a5a6 use file paths specified by the spec instead of hardcoded ones 2019-10-25 16:23:22 +00:00
divolgin 36e3bf294d Merge pull request #71 from replicatedhq/divolgin/output-names
support multiple containers in pod logs
2019-10-24 11:31:45 -07:00
divolgin 3ea71c1d5a support multiple containers in pod logs 2019-10-24 18:27:53 +00:00
divolgin 26baaacee8 Merge pull request #70 from replicatedhq/divolgin/krew
bump krew manifests to 0.9.9
2019-10-18 09:31:24 -07:00
divolgin 6f868877d8 bump krew manifests to 0.9.9 2019-10-18 16:23:08 +00:00
Marc Campbell 075c0f6f16 Merge pull request #69 from replicatedhq/analyze-kotsadm
Wraping errors, no synlink in kotsadm
2019-10-17 13:03:29 -04:00
divolgin 2455f87ee1 Merge pull request #68 from replicatedhq/divolgin/better-messaging
Better messaging when support bundle is uploaded
2019-10-17 09:15:58 -07:00
Marc Campbell a4c482c74c Wraping errors, no synlink in kotsadm 2019-10-17 14:46:37 +00:00
divolgin 642c1d7e5b Better messaging when support bundle is uploaded 2019-10-15 22:45:57 +00:00
divolgin e88e3a4694 Merge pull request #66 from replicatedhq/divolgin/krew
bump krew manifests to 0.9.8
2019-09-09 15:22:07 -07:00
divolgin e23f799bed bump krew manifests to 0.9.8 2019-09-09 22:16:32 +00:00
Marc Campbell 25e884c43a Proxy 2019-09-08 15:09:02 +00:00
Marc Campbell 481aab7820 Merge pull request #65 from replicatedhq/error-handling
Error handling in cluster resources
2019-09-08 08:01:39 -07:00
Marc Campbell 0b7aff6597 Error handling in cluster resources 2019-09-08 14:35:56 +00:00
Marc Campbell 4153b9ac44 One extension is enough 2019-08-28 22:13:33 +00:00
Marc Campbell 48750ba1e5 Better release name for so 2019-08-28 21:59:57 +00:00
Marc Campbell 086d5569b3 Merge pull request #64 from replicatedhq/ffi
Support CGO for calling analysis from other languages
2019-08-28 14:39:09 -07:00
Marc Campbell 996a55bf90 Support CGO for calling analysis from other languages 2019-08-28 21:30:59 +00:00
divolgin 8125d5534b Merge pull request #63 from alrs/fix-analyze-swallowed-error
pkg/analyze: Fix swallowed error
2019-08-27 13:55:03 -07:00
Lars Lehtonen 09deffb507 pkg/analyze: Fix swallowed error 2019-08-26 17:16:18 -07:00
divolgin a8bdda483d Merge pull request #62 from replicatedhq/divolgin/run
run collector times out
2019-08-22 15:55:31 -07:00
divolgin c3e2748f3c run collector times out 2019-08-22 22:39:30 +00:00
divolgin 0c7c8c3927 Merge pull request #61 from replicatedhq/krew
update krew manifests
2019-08-22 15:16:45 -07:00
divolgin 9a1bb9fa7b update krew manifests 2019-08-22 16:35:22 +00:00
divolgin 143f24b1e6 Merge pull request #60 from replicatedhq/krew
update krew manifests
2019-08-22 09:27:13 -07:00
divolgin 45d537744f update krew manifests 2019-08-22 16:02:54 +00:00
divolgin 1855c42b16 Merge pull request #59 from replicatedhq/divolgin/cli
adding support for generic CLI options. closes #54
2019-08-22 08:04:27 -07:00
divolgin 2fb3a17783 adding support for generic CLI options. closes #54 2019-08-22 14:25:08 +00:00
divolgin b195b34fbe Merge pull request #57 from replicatedhq/divolgin/file
add numbers to support bundle name as needed
2019-08-21 10:01:37 -07:00
divolgin 1c6fe9f262 add numbers to support bundle name as needed 2019-08-21 16:18:57 +00:00
divolgin ed9264ed04 Merge pull request #53 from replicatedhq/divolgin/upload
Automatically upload generated support bundle file
2019-08-21 07:18:54 -07:00
divolgin 68344b25a4 Automatically upload generated support bundle file 2019-08-20 23:54:28 +00:00
divolgin 123225c2ce Merge pull request #56 from replicatedhq/divolgin/run
errors are masked when collector fails
2019-08-20 13:31:49 -07:00
divolgin b6ca58dbcf errors are masked when collector fails 2019-08-20 20:25:55 +00:00
divolgin beb0514243 Merge pull request #52 from replicatedhq/krew
krew plugin manifests 0.9.4
2019-08-16 08:26:05 -07:00
divolgin a71f2e4e0f krew plugin manifests 0.9.4 2019-08-16 15:22:39 +00:00
Marc Campbell ff81cac235 Merge pull request #50 from replicatedhq/run-in-cli
Run in cli
2019-08-14 20:35:17 -07:00
Marc Campbell 91b33b0316 Merge pull request #49 from replicatedhq/fixes
Some fixes to troubleshoot, better error messages
2019-08-14 20:35:10 -07:00
Marc Campbell 3b1428a225 Add gke 2019-08-15 02:24:55 +00:00
Marc Campbell c6da43b73f Run in the CLI, not in the cluster 2019-08-15 02:10:24 +00:00
Marc Campbell 4d61c49619 Run preflights in the CLI 2019-08-15 01:59:57 +00:00
Marc Campbell 0f421ce104 Remove 2019-08-15 01:23:38 +00:00
Marc Campbell d37d037ad1 Some fixes to troubleshoot, better error messages 2019-08-15 01:22:17 +00:00
divolgin e6ecddf42e Merge pull request #48 from replicatedhq/krew
Updating krew plugin manifests
2019-08-08 13:42:39 -07:00
divolgin 07fedd29ad Updating krew plugin manifests 2019-08-08 20:36:36 +00:00
divolgin c83184d9f5 Merge pull request #47 from replicatedhq/fix-release
fixing release build
2019-08-08 10:43:42 -07:00
divolgin 37ab1960d5 fixing release build 2019-08-08 17:41:40 +00:00
divolgin 5811a7b0ae Merge pull request #46 from replicatedhq/make-release
fix release build
2019-08-08 10:12:32 -07:00
divolgin bc7614f875 fix release build 2019-08-08 17:11:20 +00:00
divolgin d900950c5f Merge pull request #44 from replicatedhq/support-bundle
rename troubleshoot binary to support-bundle
2019-08-08 09:24:06 -07:00
divolgin c84bc86a50 Merge pull request #45 from replicatedhq/message
Better message at the end of support bundle generation
2019-08-07 17:06:01 -07:00
divolgin 2e61a8e9a9 Better message at the end of support bundle generation 2019-08-07 23:56:08 +00:00
divolgin 012df52f11 rename troubleshoot binary to support-bundle 2019-08-07 21:23:12 +00:00
divolgin f9efac3182 Merge pull request #43 from replicatedhq/progress
Show progress when collecting support bundle
2019-08-07 13:42:27 -07:00
divolgin 920e1efc27 Show progress when collecting support bundle 2019-08-07 20:41:06 +00:00
divolgin e90b39733d Merge pull request #42 from replicatedhq/krew
updating troubleshoot krew manifest
2019-08-06 16:27:13 -07:00
divolgin 8718888af1 updating troubleshoot krew manifest 2019-08-06 23:09:34 +00:00
divolgin 64119e48c6 Merge pull request #41 from replicatedhq/krew
updating krew plugin version
2019-08-06 14:13:17 -07:00
divolgin afae2ae06f updating krew plugin version 2019-08-06 14:25:18 +00:00
281 changed files with 37586 additions and 11990 deletions
-39
View File
@@ -1,39 +0,0 @@
steps:
- label: build
commands:
- make
plugins:
- docker#v3.2.0:
image: replicated/gitops-builder:buildkite-go12-node10
always-pull: true
mount-checkout: true
workdir: /go/src/github.com/replicatedhq/troubleshoot
- wait
- commands:
- make snapshot-release
branches: "master"
plugins:
- docker#v3.2.0:
image: replicated/gitops-builder:buildkite-go12-node10
always-pull: true
mount-checkout: true
workdir: /go/src/github.com/replicatedhq/troubleshoot
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/var/lib/buildkite-agent/.docker/config.json:/home/builder/.docker/config.json"
- label: release
commands:
- if [ -z "$BUILDKITE_TAG" ]; then echo "Not a tag, not releasing"; else git tag -f "$BUILDKITE_TAG" && make release; fi
plugins:
- docker#v3.2.0:
always-pull: true
image: replicated/gitops-builder:buildkite-go12-node10
workdir: /go/src/github.com/replicatedhq/troubleshoot
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/var/lib/buildkite-agent/.docker/config.json:/home/builder/.docker/config.json"
environment:
- GITHUB_TOKEN_TROUBLESHOOT
+133
View File
@@ -0,0 +1,133 @@
on:
pull_request:
branches:
- master
push:
branches:
- "master"
tags:
- "v*.*.*"
jobs:
build:
runs-on: ubuntu-18.04
steps:
- uses: actions/setup-go@v1
with:
go-version: "1.14"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
shell: bash
- uses: actions/checkout@v2
- run: make
compile-preflight:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/setup-go@v1
with:
go-version: '1.14'
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
shell: bash
- uses: actions/checkout@master
- run: make preflight
- uses: actions/upload-artifact@v1
with:
name: preflight
path: bin/preflight
validate-preflight:
runs-on: ubuntu-latest
needs: compile-preflight
steps:
- name: Download preflight binary
uses: actions/download-artifact@v1
with:
name: preflight
path: bin/
- uses: engineerd/setup-kind@v0.5.0
- run: chmod +x bin/preflight
- run: ./bin/preflight --interactive=false --format=json https://preflight.replicated.com
compile-supportbundle:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/setup-go@v1
with:
go-version: '1.14'
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
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-latest
needs: compile-supportbundle
steps:
- uses: actions/checkout@v1
- name: Download support-bundle binary
uses: actions/download-artifact@v1
with:
name: support-bundle
path: bin/
- uses: engineerd/setup-kind@v0.5.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
needs:
- validate-preflight
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: azure/docker-login@v1
with:
username: repldeploy2
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Unshallow
run: git fetch --prune --unshallow
- uses: actions/setup-go@v1
with:
go-version: "1.14"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist --config deploy/.goreleaser.yaml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update new preflight version in krew-index
uses: rajatjindal/krew-release-bot@v0.0.38
with:
krew_template_file: deploy/krew/preflight.yaml
- name: Update new support-bundle version in krew-index
uses: rajatjindal/krew-release-bot@v0.0.38
with:
krew_template_file: deploy/krew/support-bundle.yaml
+3
View File
@@ -26,4 +26,7 @@ vendor
*.swo
*~
# scope test logs
scope-go-*.log
dist
+84 -57
View File
@@ -2,45 +2,61 @@
# Image URL to use all building/pushing image targets
IMG ?= controller:latest
export GO111MODULE=on
export GOPROXY=https://proxy.golang.org
export SCOPE_LOG_ROOT_PATH=/dev/null
all: test manager
SHELL := /bin/bash -o pipefail
VERSION_PACKAGE = github.com/replicatedhq/troubleshoot/pkg/version
VERSION ?=`git describe --tags --dirty`
DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"`
GIT_TREE = $(shell git rev-parse --is-inside-work-tree 2>/dev/null)
ifneq "$(GIT_TREE)" ""
define GIT_UPDATE_INDEX_CMD
git update-index --assume-unchanged
endef
define GIT_SHA
`git rev-parse HEAD`
endef
else
define GIT_UPDATE_INDEX_CMD
echo "Not a git repo, skipping git update-index"
endef
define GIT_SHA
""
endef
endif
define LDFLAGS
-ldflags "\
-s -w \
-X ${VERSION_PACKAGE}.version=${VERSION} \
-X ${VERSION_PACKAGE}.gitSHA=${GIT_SHA} \
-X ${VERSION_PACKAGE}.buildTime=${DATE} \
"
endef
all: test
.PHONY: ffi
ffi: fmt vet
go build ${LDFLAGS} -o bin/troubleshoot.so -buildmode=c-shared ffi/main.go
# Run tests
test: generate fmt vet manifests
test: generate fmt vet
go test ./pkg/... ./cmd/... -coverprofile cover.out
.PHONY: manager
manager: generate fmt vet
go build -o bin/manager github.com/replicatedhq/troubleshoot/cmd/manager
.PHONY: troubleshoot
troubleshoot: generate fmt vet
go build -o bin/troubleshoot github.com/replicatedhq/troubleshoot/cmd/troubleshoot
.PHONY: collector
collector: generate fmt vet
go build -o bin/collector github.com/replicatedhq/troubleshoot/cmd/collector
.PHONY: support-bundle
support-bundle: generate fmt vet
go build -tags netgo ${LDFLAGS} -o bin/support-bundle github.com/replicatedhq/troubleshoot/cmd/troubleshoot
.PHONY: preflight
preflight: generate fmt vet
go build -o bin/preflight github.com/replicatedhq/troubleshoot/cmd/preflight
go build -tags netgo ${LDFLAGS} -o bin/preflight github.com/replicatedhq/troubleshoot/cmd/preflight
.PHONY: run
run: generate fmt vet
TROUBLESHOOT_EXTERNAL_MANAGER=1 go run ./cmd/manager/main.go
.PHONY: install
install: manifests
kubectl apply -f config/crds
.PHONY: deploy
deploy: manifests
kubectl apply -f config/crds
kustomize build config/default | kubectl apply -f -
.PHONY: manifests
manifests:
controller-gen paths=./pkg/apis/... output:dir=./config/crds
.PHONY: analyze
analyze: generate fmt vet
go build -tags netgo ${LDFLAGS} -o bin/analyze github.com/replicatedhq/troubleshoot/cmd/analyze
.PHONY: fmt
fmt:
@@ -51,61 +67,72 @@ vet:
go vet ./pkg/... ./cmd/...
.PHONY: generate
generate: controller-gen # 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 -h ./hack/boilerplate.go.txt
generate: controller-gen 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
# find or download controller-gen
# download controller-gen if necessary
.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
.PHONY: contoller-gen
controller-gen:
ifeq (, $(shell which controller-gen))
go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.2.0-beta.2
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.13.5
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)
endif
.PHONY: snapshot-release
snapshot-release:
curl -sL https://git.io/goreleaser | bash -s -- --rm-dist --snapshot --config deploy/.goreleaser.snapshot.yml
docker push replicated/troubleshoot:alpha
docker push replicated/preflight:alpha
docker push replicated/troubleshoot-manager:alpha
.PHONY: release
release: export GITHUB_TOKEN = $(shell echo ${GITHUB_TOKEN_TROUBLESHOOT})
release:
curl -sL https://git.io/goreleaser | bash -s -- --rm-dist --config deploy/.goreleaser.yml
.PHONY: snapshot-release
snapshot-release:
curl -sL https://git.io/goreleaser | bash -s -- --rm-dist --snapshot --config deploy/.goreleaser.snapshot.yml
docker push replicated/troubleshoot:alpha
docker push replicated/preflight:alpha
.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 tag replicated/troubleshoot-manager:alpha localhost:32000/troubleshoot-manager:alpha
docker push localhost:32000/troubleshoot:alpha
docker push localhost:32000/preflight:alpha
docker push localhost:32000/troubleshoot-manager:alpha
.PHONY: run-preflight
run-preflight: preflight
./bin/preflight \
--image=localhost:32000/troubleshoot:alpha \
--pullpolicy=Always \
./config/samples/troubleshoot_v1beta1_preflight.yaml
./bin/preflight ./examples/preflight/sample-preflight.yaml
.PHONY: run-troubleshoot
run-troubleshoot: troubleshoot
./bin/troubleshoot \
--image=localhost:32000/troubleshoot:alpha \
--pullpolicy=Always \
./config/samples/troubleshoot_v1beta1_collector.yaml
run-troubleshoot: support-bundle
./bin/support-bundle ./examples/support-bundle/sample-supportbundle.yaml
.PHONY: run-analyze
run-analyze: analyze
./bin/analyze --analyzers ./examples/support-bundle/sample-analyzers.yaml ./support-bundle.tar.gz
+24 -6
View File
@@ -1,23 +1,41 @@
# Replicated Troubleshoot
Replicated Troubleshoot is a CLI tool that provides pre-installation cluster conformance testing and validation (preflight checks) and post-installation troubleshooting and diagnostics (support bundles).
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).
## 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.
To run a sample preflight check from a sample application, [install the preflight kubectl plugin](https://help.replicated.com/docs/troubleshoot/kubernetes/preflight/executing/) and run:
To run a sample preflight check from a sample application, install the preflight kubectl plugin:
```shell
curl https://krew.sh/preflight | bash
```
and run:
```shell
kubectl preflight https://preflight.replicated.com
```
For a full description of the supported preflight checks, visit the [docs](https://help.replicated.com/docs/troubleshoot/kubernetes/analysis/).
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 various commands. After creating a support bundle, the cluster operator will normally deliver it to the application vendor for analysis and remote debugging.
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](https://help.replicated.com/docs/troubleshoot/kubernetes/support-bundle/collecting/) and run:
To collect a sample support bundle, install the troubleshoot kubectl plugin:
```shell
kubectl troubleshoot https://troubleshoot.replicated.com
curl https://krew.sh/support-bundle | bash
```
and run:
```shell
kubectl support-bundle https://support-bundle.replicated.com
```
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).
+54
View File
@@ -0,0 +1,54 @@
package cli
import (
"os"
"strings"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/logger"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func RootCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "analyze [url]",
Args: cobra.MinimumNArgs(1),
Short: "Analyze a support bundle",
Long: `Run a series of analyzers on a support bundle archive`,
SilenceUsage: true,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlags(cmd.Flags())
},
RunE: func(cmd *cobra.Command, args []string) error {
v := viper.GetViper()
logger.SetQuiet(v.GetBool("quiet"))
return runAnalyzers(v, args[0])
},
}
cobra.OnInitialize(initConfig)
cmd.Flags().String("analyzers", "", "filename or url of the analyzers to use")
viper.BindPFlags(cmd.Flags())
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
k8sutil.AddFlags(cmd.Flags())
return cmd
}
func InitAndExecute() {
if err := RootCmd().Execute(); err != nil {
os.Exit(1)
}
}
func initConfig() {
viper.SetEnvPrefix("TROUBLESHOOT")
viper.AutomaticEnv()
}
+67
View File
@@ -0,0 +1,67 @@
package cli
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/cmd/util"
analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze"
"github.com/spf13/viper"
)
func runAnalyzers(v *viper.Viper, bundlePath string) error {
specPath := v.GetString("analyzers")
specContent := ""
var err error
if _, err = os.Stat(specPath); err == nil {
b, err := ioutil.ReadFile(specPath)
if err != nil {
return err
}
specContent = string(b)
} else {
if !util.IsURL(specPath) {
return fmt.Errorf("%s is not a URL and was not found (err %s)", specPath, err)
}
req, err := http.NewRequest("GET", specPath, nil)
if err != nil {
return err
}
req.Header.Set("User-Agent", "Replicated_Analyzer/v1beta1")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
specContent = string(body)
}
analyzeResults, err := analyzer.DownloadAndAnalyze(bundlePath, specContent)
if err != nil {
return errors.Wrap(err, "failed to download and analyze bundle")
}
for _, analyzeResult := range analyzeResults {
if analyzeResult.IsPass {
fmt.Printf("Pass: %s\n %s\n", analyzeResult.Title, analyzeResult.Message)
} else if analyzeResult.IsWarn {
fmt.Printf("Warn: %s\n %s\n", analyzeResult.Title, analyzeResult.Message)
} else if analyzeResult.IsFail {
fmt.Printf("Fail: %s\n %s\n", analyzeResult.Title, analyzeResult.Message)
}
}
return nil
}
+10
View File
@@ -0,0 +1,10 @@
package main
import (
"github.com/replicatedhq/troubleshoot/cmd/analyze/cli"
_ "k8s.io/client-go/plugin/pkg/client/auth"
)
func main() {
cli.InitAndExecute()
}
-39
View File
@@ -1,39 +0,0 @@
package cli
import (
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func RootCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "collector",
Short: "Run the cluster-side server for bundle collection",
Long: ``,
SilenceUsage: true,
}
cobra.OnInitialize(initConfig)
cmd.AddCommand(Run())
cmd.AddCommand(Server())
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
return cmd
}
func InitAndExecute() {
if err := RootCmd().Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func initConfig() {
viper.SetEnvPrefix("TROUBLESHOOT")
viper.AutomaticEnv()
}
-48
View File
@@ -1,48 +0,0 @@
package cli
import (
"io/ioutil"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func Run() *cobra.Command {
cmd := &cobra.Command{
Use: "run",
Short: "run a single collector",
Long: `...`,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("collector", cmd.Flags().Lookup("collector"))
viper.BindPFlag("redact", cmd.Flags().Lookup("redact"))
},
RunE: func(cmd *cobra.Command, args []string) error {
v := viper.GetViper()
specContents, err := ioutil.ReadFile(v.GetString("collector"))
if err != nil {
return err
}
collector := collect.Collector{
Spec: string(specContents),
Redact: v.GetBool("redact"),
}
if err := collector.RunCollectorSync(); err != nil {
return err
}
return nil
},
}
cmd.Flags().String("collector", "", "path to a single collector spec to collect")
cmd.Flags().Bool("redact", true, "enable/disable default redactions")
cmd.MarkFlagRequired("collector")
viper.BindPFlags(cmd.Flags())
return cmd
}
-43
View File
@@ -1,43 +0,0 @@
package cli
import (
"context"
"fmt"
"os"
"os/signal"
"github.com/replicatedhq/troubleshoot/pkg/server"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func Server() *cobra.Command {
cmd := &cobra.Command{
Use: "server",
Short: "run the http server",
Hidden: true,
Long: `...`,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("port", cmd.Flags().Lookup("port"))
},
RunE: func(cmd *cobra.Command, args []string) error {
v := viper.GetViper()
server.ServeCollector(context.Background(), fmt.Sprintf(":%d", v.GetInt("port")))
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
select {
case <-c:
return nil
}
},
}
cmd.Flags().Int("port", 8000, "port to listen on")
viper.BindPFlags(cmd.Flags())
return cmd
}
-7
View File
@@ -1,7 +0,0 @@
package main
import "github.com/replicatedhq/troubleshoot/cmd/collector/cli"
func main() {
cli.InitAndExecute()
}
-84
View File
@@ -1,84 +0,0 @@
/*
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 main
import (
"flag"
"os"
"github.com/replicatedhq/troubleshoot/pkg/apis"
"github.com/replicatedhq/troubleshoot/pkg/controller"
"github.com/replicatedhq/troubleshoot/pkg/webhook"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/controller-runtime/pkg/manager"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
)
func main() {
var metricsAddr string
flag.StringVar(&metricsAddr, "metrics-addr", ":8088", "The address the metric endpoint binds to.")
flag.Parse()
logf.SetLogger(logf.ZapLogger(false))
log := logf.Log.WithName("entrypoint")
// Get a config to talk to the apiserver
log.Info("setting up client for manager")
cfg, err := config.GetConfig()
if err != nil {
log.Error(err, "unable to set up client config")
os.Exit(1)
}
// Create a new Cmd to provide shared dependencies and start components
log.Info("setting up manager")
mgr, err := manager.New(cfg, manager.Options{MetricsBindAddress: metricsAddr})
if err != nil {
log.Error(err, "unable to set up overall controller manager")
os.Exit(1)
}
log.Info("Registering Components.")
// Setup Scheme for all resources
log.Info("setting up scheme")
if err := apis.AddToScheme(mgr.GetScheme()); err != nil {
log.Error(err, "unable add APIs to scheme")
os.Exit(1)
}
// Setup all Controllers
log.Info("Setting up controller")
if err := controller.AddToManager(mgr); err != nil {
log.Error(err, "unable to register controllers to the manager")
os.Exit(1)
}
log.Info("setting up webhooks")
if err := webhook.AddToManager(mgr); err != nil {
log.Error(err, "unable to register webhooks to the manager")
os.Exit(1)
}
// Start the Cmd
log.Info("Starting the Cmd.")
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
log.Error(err, "unable to run the manager")
os.Exit(1)
}
}
+11 -23
View File
@@ -5,17 +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
)
@@ -63,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>":
@@ -71,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)
}
@@ -94,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
@@ -126,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
@@ -214,13 +218,13 @@ func estimateNumberOfLines(text string, width int) int {
}
func save(preflightName string, analyzeResults []*analyzerunner.AnalyzeResult) (string, error) {
filename := path.Join(homeDir(), fmt.Sprintf("%s-results.txt", preflightName))
filename := path.Join(util.HomeDir(), fmt.Sprintf("%s-results.txt", preflightName))
_, err := os.Stat(filename)
if err == nil {
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 := ""
@@ -269,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, " ")
}
-68
View File
@@ -1,68 +0,0 @@
package cli
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"github.com/replicatedhq/troubleshoot/pkg/logger"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func receivePreflightResults(preflightJobNamespace string, preflightJobName string) error {
// poll until there are no more "running" collectors
troubleshootClient, err := createTroubleshootK8sClient()
if err != nil {
return err
}
bundlePath, err := ioutil.TempDir("", "troubleshoot")
if err != nil {
return err
}
defer os.RemoveAll(bundlePath)
receivedPreflights := []string{}
for {
job, err := troubleshootClient.PreflightJobs(preflightJobNamespace).Get(preflightJobName, metav1.GetOptions{})
if err != nil && kuberneteserrors.IsNotFound(err) {
// where did it go!
return nil
} else if err != nil {
return err
}
for _, readyPreflight := range job.Status.AnalyzersSuccessful {
alreadyReceived := false
for _, receivedPreflight := range receivedPreflights {
if receivedPreflight == readyPreflight {
alreadyReceived = true
}
}
if alreadyReceived {
continue
}
preflightResp, err := http.Get(fmt.Sprintf("http://localhost:8000/preflight/%s", readyPreflight))
if err != nil {
return err
}
defer preflightResp.Body.Close()
body, err := ioutil.ReadAll(preflightResp.Body)
if err != nil {
return err
}
logger.Printf("%s\n", body)
receivedPreflights = append(receivedPreflights, readyPreflight)
}
if len(job.Status.AnalyzersRunning) == 0 {
return nil
}
}
}
+12 -33
View File
@@ -1,12 +1,10 @@
package cli
import (
"fmt"
"os"
"path/filepath"
"strings"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
@@ -14,47 +12,41 @@ import (
func RootCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "preflight [url]",
Args: cobra.MinimumNArgs(1),
Short: "Run and retrieve preflight checks in a cluster",
Long: `A preflight check is a set of validations that can and should be run to ensure
that a cluster meets the requirements to run an application.`,
SilenceUsage: true,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlags(cmd.Flags())
},
RunE: func(cmd *cobra.Command, args []string) error {
v := viper.GetViper()
if len(args) == 0 {
return runPreflightsCRD(v)
}
return runPreflightsNoCRD(v, args[0])
return runPreflights(v, args[0])
},
}
cobra.OnInitialize(initConfig)
cmd.AddCommand(VersionCmd())
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("namespace", "default", "namespace the preflight can be found in")
cmd.Flags().String("kubecontext", filepath.Join(homeDir(), ".kube", "config"), "the kubecontext to use when connecting")
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().String("serviceaccount", "", "name of the service account to use. if not provided, one will be created")
cmd.Flags().Bool("collect-without-permissions", false, "always run preflight checks even if some require permissions that preflight does not have")
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("-", "_"))
k8sutil.AddFlags(cmd.Flags())
return cmd
}
func InitAndExecute() {
if err := RootCmd().Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
@@ -63,16 +55,3 @@ func initConfig() {
viper.SetEnvPrefix("PREFLIGHT")
viper.AutomaticEnv()
}
func ensureCollectorInList(list []*troubleshootv1beta1.Collect, collector troubleshootv1beta1.Collect) []*troubleshootv1beta1.Collect {
for _, inList := range list {
if collector.ClusterResources != nil && inList.ClusterResources != nil {
return list
}
if collector.ClusterInfo != nil && inList.ClusterInfo != nil {
return list
}
}
return append(list, &collector)
}
+203
View File
@@ -0,0 +1,203 @@
package cli
import (
"fmt"
"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"
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"
)
func runPreflights(v *viper.Viper, arg string) error {
fmt.Print(cursor.Hide())
defer fmt.Print(cursor.Show())
var preflightContent []byte
var err error
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 = b
} else {
if !util.IsURL(arg) {
return fmt.Errorf("%s is not a URL and was not found (err %s)", arg, err)
}
req, err := http.NewRequest("GET", arg, nil)
if err != nil {
return err
}
req.Header.Set("User-Agent", "Replicated_Preflight/v1beta2")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
preflightContent = body
}
preflightContent, err = docrewrite.ConvertToV1Beta2(preflightContent)
if err != nil {
return errors.Wrap(err, "failed to convert to v1beta2")
}
troubleshootclientsetscheme.AddToScheme(scheme.Scheme)
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode([]byte(preflightContent), nil, nil)
if err != nil {
return errors.Wrapf(err, "failed to parse %s", arg)
}
preflightSpec := obj.(*troubleshootv1beta2.Preflight)
s := spin.New()
finishedCh := make(chan bool, 1)
progressChan := make(chan interface{}, 0) // non-zero buffer will result in missed messages
go func() {
for {
select {
case msg, ok := <-progressChan:
if !ok {
continue
}
switch msg := msg.(type) {
case error:
c := color.New(color.FgHiRed)
c.Println(fmt.Sprintf("%s\r * %v", cursor.ClearEntireLine(), msg))
case string:
c := color.New(color.FgCyan)
c.Println(fmt.Sprintf("%s\r * %s", cursor.ClearEntireLine(), msg))
}
case <-time.After(time.Millisecond * 100):
fmt.Printf("\r \033[36mRunning Preflight checks\033[m %s ", s.Next())
case <-finishedCh:
fmt.Printf("\r%s\r", cursor.ClearEntireLine())
return
}
}
}()
defer func() {
close(finishedCh)
}()
restConfig, err := k8sutil.GetRESTConfig()
if err != nil {
return errors.Wrap(err, "failed to convert kube flags to rest config")
}
collectOpts := preflight.CollectOpts{
Namespace: v.GetString("namespace"),
IgnorePermissionErrors: v.GetBool("collect-without-permissions"),
ProgressChan: progressChan,
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 {
if preflightSpec.Spec.UploadResultsTo != "" {
err := uploadErrors(preflightSpec.Spec.UploadResultsTo, collectResults.Collectors)
if err != nil {
progressChan <- err
}
}
}
return err
}
analyzeResults := collectResults.Analyze()
if preflightSpec.Spec.UploadResultsTo != "" {
err := uploadResults(preflightSpec.Spec.UploadResultsTo, analyzeResults)
if err != nil {
progressChan <- err
}
}
finishedCh <- true
if v.GetBool("interactive") {
if len(analyzeResults) == 0 {
return errors.New("no data has been collected")
}
return showInteractiveResults(preflightSpec.Name, analyzeResults)
}
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
}
-103
View File
@@ -1,103 +0,0 @@
package cli
import (
"errors"
"fmt"
"time"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/spf13/viper"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func runPreflightsCRD(v *viper.Viper) error {
troubleshootClient, err := createTroubleshootK8sClient()
if err != nil {
return err
}
preflightName := v.GetString("preflight")
if preflightName == "" {
preflights, err := troubleshootClient.Preflights(v.GetString("namespace")).List(metav1.ListOptions{})
if err != nil {
return err
}
if len(preflights.Items) == 1 {
preflightName = preflights.Items[0].Name
}
}
if preflightName == "" {
return errors.New("unable to preflight, try using the --preflight flags")
}
// generate a unique name
now := time.Now()
suffix := fmt.Sprintf("%d", now.Unix())
preflightJobName := fmt.Sprintf("%s-job-%s", preflightName, suffix[len(suffix)-4:])
preflightJob := troubleshootv1beta1.PreflightJob{
ObjectMeta: metav1.ObjectMeta{
Name: preflightJobName,
Namespace: v.GetString("namespace"),
},
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "preflightjob.troubleshoot.replicated.com",
},
Spec: troubleshootv1beta1.PreflightJobSpec{
Preflight: troubleshootv1beta1.PreflightRef{
Name: preflightName,
Namespace: v.GetString("namespace"),
},
Image: v.GetString("image"),
ImagePullPolicy: v.GetString("pullpolicy"),
CollectorImage: v.GetString("collector-image"),
CollectorImagePullPolicy: v.GetString("collector-pullpolicy"),
},
}
if _, err := troubleshootClient.PreflightJobs(v.GetString("namespace")).Create(&preflightJob); err != nil {
return err
}
// Poll the status of the Custom Resource for it to include a callback
var found *troubleshootv1beta1.PreflightJob
start := time.Now()
for {
current, err := troubleshootClient.PreflightJobs(v.GetString("namespace")).Get(preflightJobName, metav1.GetOptions{})
if err != nil && kuberneteserrors.IsNotFound(err) {
continue
} else if err != nil {
return err
}
if current.Status.IsServerReady {
found = current
break
}
if time.Now().Sub(start) > time.Duration(time.Second*10) {
return errors.New("preflightjob failed to start")
}
time.Sleep(time.Millisecond * 200)
}
// Connect to the callback
stopChan, err := k8sutil.PortForward(v.GetString("kubecontext"), 8000, 8000, found.Status.ServerPodNamespace, found.Status.ServerPodName)
if err != nil {
return err
}
if err := receivePreflightResults(found.Namespace, found.Name); err != nil {
return err
}
// Write
close(stopChan)
return nil
}
-343
View File
@@ -1,343 +0,0 @@
package cli
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/pkg/errors"
analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
collectrunner "github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/logger"
"github.com/spf13/viper"
"github.com/tj/go-spin"
"gopkg.in/yaml.v2"
corev1 "k8s.io/api/core/v1"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
)
func runPreflightsNoCRD(v *viper.Viper, arg string) error {
preflightContent := ""
if !isURL(arg) {
if _, err := os.Stat(arg); os.IsNotExist(err) {
return fmt.Errorf("%s was not found", arg)
}
b, err := ioutil.ReadFile(arg)
if err != nil {
return err
}
preflightContent = string(b)
} else {
req, err := http.NewRequest("GET", arg, nil)
if err != nil {
return err
}
req.Header.Set("User-Agent", "Replicated_Preflight/v1beta1")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
preflightContent = string(body)
}
preflight := troubleshootv1beta1.Preflight{}
if err := yaml.Unmarshal([]byte(preflightContent), &preflight); err != nil {
return fmt.Errorf("unable to parse %s as a preflight", arg)
}
s := spin.New()
finishedCh := make(chan bool, 1)
go func() {
for {
select {
case <-finishedCh:
fmt.Printf("\r")
return
case <-time.After(time.Millisecond * 100):
fmt.Printf("\r \033[36mRunning Preflight checks\033[m %s ", s.Next())
}
}
}()
defer func() {
finishedCh <- true
}()
allCollectedData, err := runCollectors(v, preflight)
if err != nil {
return err
}
getCollectedFileContents := func(fileName string) ([]byte, error) {
contents, ok := allCollectedData[fileName]
if !ok {
return nil, fmt.Errorf("file %s was not collected", fileName)
}
return contents, nil
}
getChildCollectedFileContents := func(prefix string) (map[string][]byte, error) {
matching := make(map[string][]byte)
for k, v := range allCollectedData {
if strings.HasPrefix(k, prefix) {
matching[k] = v
}
}
return matching, nil
}
analyzeResults := []*analyzerunner.AnalyzeResult{}
for _, analyzer := range preflight.Spec.Analyzers {
analyzeResult, err := analyzerunner.Analyze(analyzer, getCollectedFileContents, getChildCollectedFileContents)
if err != nil {
logger.Printf("an analyzer failed to run: %v\n", err)
continue
}
analyzeResults = append(analyzeResults, analyzeResult)
}
finishedCh <- true
if preflight.Spec.UploadResultsTo != "" {
tryUploadResults(preflight.Spec.UploadResultsTo, preflight.Name, analyzeResults)
}
if v.GetBool("interactive") {
return showInteractiveResults(preflight.Name, analyzeResults)
}
logger.Printf("only interactive results are supported\n")
return nil
}
func runCollectors(v *viper.Viper, preflight troubleshootv1beta1.Preflight) (map[string][]byte, error) {
cfg, err := config.GetConfig()
if err != nil {
return nil, err
}
client, err := client.New(cfg, client.Options{})
if err != nil {
return nil, err
}
clientset, err := kubernetes.NewForConfig(cfg)
if err != nil {
return nil, err
}
restClient := clientset.CoreV1().RESTClient()
serviceAccountName := v.GetString("serviceaccount")
if serviceAccountName == "" {
generatedServiceAccountName, err := createServiceAccount(preflight, v.GetString("namespace"), clientset)
if err != nil {
return nil, err
}
defer removeServiceAccount(generatedServiceAccountName, v.GetString("namespace"), clientset)
serviceAccountName = generatedServiceAccountName
}
// deploy an object that "owns" everything to aid in cleanup
configMapNamespacedName := types.NamespacedName{
Name: fmt.Sprintf("preflight-%s-owner", preflight.Name),
Namespace: v.GetString("namespace"),
}
foundConfigMap := &corev1.ConfigMap{}
err = client.Get(context.Background(), configMapNamespacedName, foundConfigMap)
if err == nil || !kuberneteserrors.IsNotFound(err) {
return nil, errors.Wrap(err, "failed to get existing config map")
}
owner := corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: configMapNamespacedName.Name,
Namespace: configMapNamespacedName.Namespace,
},
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ConfigMap",
},
Data: make(map[string]string),
}
if err := client.Create(context.Background(), &owner); err != nil {
return nil, errors.Wrap(err, "failed to create config map")
}
defer func() {
if err := client.Delete(context.Background(), &owner); err != nil {
fmt.Println("failed to clean up preflight.")
}
}()
// deploy all collectors
desiredCollectors := make([]*troubleshootv1beta1.Collect, 0, 0)
for _, definedCollector := range preflight.Spec.Collectors {
desiredCollectors = append(desiredCollectors, definedCollector)
}
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterInfo: &troubleshootv1beta1.ClusterInfo{}})
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterResources: &troubleshootv1beta1.ClusterResources{}})
podsCreated := make([]*corev1.Pod, 0, 0)
podsDeleted := make([]*corev1.Pod, 0, 0)
allCollectedData := make(map[string][]byte)
resyncPeriod := time.Second
ctx := context.Background()
watchList := cache.NewListWatchFromClient(restClient, "pods", "", fields.Everything())
_, controller := cache.NewInformer(watchList, &corev1.Pod{}, resyncPeriod,
cache.ResourceEventHandlerFuncs{
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
newPod, ok := newObj.(*corev1.Pod)
if !ok {
return
}
oldPod, ok := oldObj.(*corev1.Pod)
if !ok {
return
}
labels := newPod.Labels
troubleshootRole, ok := labels["troubleshoot-role"]
if !ok || troubleshootRole != "preflight" {
return
}
preflightName, ok := labels["preflight"]
if !ok || preflightName != preflight.Name {
return
}
if oldPod.Status.Phase == newPod.Status.Phase {
return
}
if newPod.Status.Phase == corev1.PodFailed {
podsDeleted = append(podsDeleted, newPod)
return
}
if newPod.Status.Phase != corev1.PodSucceeded {
return
}
podLogOpts := corev1.PodLogOptions{}
req := clientset.CoreV1().Pods(newPod.Namespace).GetLogs(newPod.Name, &podLogOpts)
podLogs, err := req.Stream()
if err != nil {
fmt.Println("get stream")
return
}
defer podLogs.Close()
buf := new(bytes.Buffer)
_, err = io.Copy(buf, podLogs)
if err != nil {
fmt.Println("copy logs")
return
}
collectedData, err := parseCollectorOutput(buf.String())
if err != nil {
logger.Printf("parse collected data: %v\n", err)
return
}
for k, v := range collectedData {
allCollectedData[k] = v
}
if err := client.Delete(context.Background(), newPod); err != nil {
fmt.Println("delete pod")
}
podsDeleted = append(podsDeleted, newPod)
},
})
go func() {
controller.Run(ctx.Done())
}()
s := runtime.NewScheme()
s.AddKnownTypes(schema.GroupVersion{Group: "", Version: "v1"}, &corev1.ConfigMap{})
for _, collector := range desiredCollectors {
_, pod, err := collectrunner.CreateCollector(client, s, &owner, preflight.Name, v.GetString("namespace"), serviceAccountName, "preflight", collector, v.GetString("image"), v.GetString("pullpolicy"))
if err != nil {
return nil, errors.Wrap(err, "failed to create collector")
}
podsCreated = append(podsCreated, pod)
}
start := time.Now()
for {
if start.Add(time.Second * 30).Before(time.Now()) {
fmt.Println("timeout running preflight")
return nil, err
}
if len(podsDeleted) == len(podsCreated) {
break
}
time.Sleep(time.Millisecond * 200)
}
ctx.Done()
return allCollectedData, nil
}
func parseCollectorOutput(output string) (map[string][]byte, error) {
input := make(map[string]interface{})
files := make(map[string][]byte)
if err := json.Unmarshal([]byte(output), &input); err != nil {
return nil, err
}
for filename, maybeContents := range input {
fileDir, fileName := filepath.Split(filename)
switch maybeContents.(type) {
case string:
decoded, err := base64.StdEncoding.DecodeString(maybeContents.(string))
if err != nil {
return nil, err
}
files[filepath.Join(fileDir, fileName)] = decoded
case map[string]interface{}:
for k, v := range maybeContents.(map[string]interface{}) {
decoded, err := base64.StdEncoding.DecodeString(v.(string))
if err != nil {
return nil, err
}
files[filepath.Join(fileDir, fileName, k)] = decoded
}
}
}
return files, 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
}
+96
View File
@@ -0,0 +1,96 @@
package cli
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze"
)
func showStdoutResults(format string, preflightName string, analyzeResults []*analyzerunner.AnalyzeResult) error {
if format == "human" {
return showStdoutResultsHuman(preflightName, analyzeResults)
} else if format == "json" {
return showStdoutResultsJSON(preflightName, analyzeResults)
}
return errors.Errorf("unknown output format: %q", format)
}
func showStdoutResultsHuman(preflightName string, analyzeResults []*analyzerunner.AnalyzeResult) error {
var failed bool
for _, analyzeResult := range analyzeResults {
testResultfailed := outputResult(analyzeResult)
if testResultfailed {
failed = true
}
}
if failed {
fmt.Printf("--- FAIL %s\n", preflightName)
fmt.Println("FAILED")
} else {
fmt.Printf("--- PASS %s\n", preflightName)
fmt.Println("PASS")
}
return nil
}
func showStdoutResultsJSON(preflightName string, analyzeResults []*analyzerunner.AnalyzeResult) error {
type ResultOutput struct {
Title string `json:"title"`
Message string `json:"message"`
URI string `json:"uri,omitempty"`
}
type Output struct {
Pass []ResultOutput `json:"pass,omitempty"`
Warn []ResultOutput `json:"warn,omitempty"`
Fail []ResultOutput `json:"fail,omitempty"`
}
output := Output{
Pass: []ResultOutput{},
Warn: []ResultOutput{},
Fail: []ResultOutput{},
}
for _, analyzeResult := range analyzeResults {
resultOutput := ResultOutput{
Title: analyzeResult.Title,
Message: analyzeResult.Message,
URI: analyzeResult.URI,
}
if analyzeResult.IsPass {
output.Pass = append(output.Pass, resultOutput)
} else if analyzeResult.IsWarn {
output.Warn = append(output.Warn, resultOutput)
} else if analyzeResult.IsFail {
output.Fail = append(output.Fail, resultOutput)
}
}
b, err := json.MarshalIndent(output, "", " ")
if err != nil {
return errors.Wrap(err, "failed to marshal results")
}
fmt.Printf("%s\n", b)
return nil
}
func outputResult(analyzeResult *analyzerunner.AnalyzeResult) bool {
if analyzeResult.IsPass {
fmt.Printf(" --- PASS %s\n", analyzeResult.Title)
fmt.Printf(" --- %s\n", analyzeResult.Message)
} else if analyzeResult.IsWarn {
fmt.Printf(" --- WARN: %s\n", analyzeResult.Title)
fmt.Printf(" --- %s\n", analyzeResult.Message)
} else if analyzeResult.IsFail {
fmt.Printf(" --- FAIL: %s\n", analyzeResult.Title)
fmt.Printf(" --- %s\n", analyzeResult.Message)
return true
}
return false
}
+33 -23
View File
@@ -5,29 +5,18 @@ import (
"encoding/json"
"net/http"
"github.com/pkg/errors"
analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/preflight"
)
type UploadPreflightResult struct {
IsFail bool `json:"isFail,omitempty"`
IsWarn bool `json:"isWarn,omitempty"`
IsPass bool `json:"isPass,omitempty"`
Title string `json:"title"`
Message string `json:"message"`
URI string `json:"uri,omitempty"`
}
type UploadPreflightResults struct {
Results []*UploadPreflightResult `json:"results"`
}
func tryUploadResults(uri string, preflightName string, analyzeResults []*analyzerunner.AnalyzeResult) error {
uploadPreflightResults := UploadPreflightResults{
Results: []*UploadPreflightResult{},
func uploadResults(uri string, analyzeResults []*analyzerunner.AnalyzeResult) error {
uploadPreflightResults := &preflight.UploadPreflightResults{
Results: []*preflight.UploadPreflightResult{},
}
for _, analyzeResult := range analyzeResults {
uploadPreflightResult := &UploadPreflightResult{
uploadPreflightResult := &preflight.UploadPreflightResult{
IsFail: analyzeResult.IsFail,
IsWarn: analyzeResult.IsWarn,
IsPass: analyzeResult.IsPass,
@@ -39,14 +28,35 @@ func tryUploadResults(uri string, preflightName string, analyzeResults []*analyz
uploadPreflightResults.Results = append(uploadPreflightResults.Results, uploadPreflightResult)
}
b, err := json.Marshal(uploadPreflightResults)
return upload(uri, uploadPreflightResults)
}
func uploadErrors(uri string, collectors collect.Collectors) error {
errors := []*preflight.UploadPreflightError{}
for _, collector := range collectors {
for _, e := range collector.RBACErrors {
errors = append(errors, &preflight.UploadPreflightError{
Error: e.Error(),
})
}
}
results := &preflight.UploadPreflightResults{
Errors: errors,
}
return upload(uri, results)
}
func upload(uri string, payload *preflight.UploadPreflightResults) error {
b, err := json.Marshal(payload)
if err != nil {
return err
return errors.Wrap(err, "failed to marshal payload")
}
req, err := http.NewRequest("POST", uri, bytes.NewBuffer(b))
if err != nil {
return err
return errors.Wrap(err, "failed to create request")
}
req.Header.Set("Content-Type", "application/json")
@@ -54,11 +64,11 @@ func tryUploadResults(uri string, preflightName string, analyzeResults []*analyz
client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
return err
return errors.Wrap(err, "failed to execute request")
}
if resp.StatusCode > 290 {
return err
return errors.Errorf("unexpected status code: %d", resp.StatusCode)
}
return nil
-41
View File
@@ -1,41 +0,0 @@
package cli
import (
"net/url"
"os"
troubleshootclientv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
"github.com/spf13/viper"
"k8s.io/client-go/tools/clientcmd"
)
func homeDir() string {
if h := os.Getenv("HOME"); h != "" {
return h
}
return os.Getenv("USERPROFILE") // windows
}
func isURL(str string) bool {
_, err := url.ParseRequestURI(str)
if err != nil {
return false
}
return true
}
func createTroubleshootK8sClient() (*troubleshootclientv1beta1.TroubleshootV1beta1Client, error) {
v := viper.GetViper()
config, err := clientcmd.BuildConfigFromFlags("", v.GetString("kubecontext"))
if err != nil {
return nil, err
}
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(config)
if err != nil {
return nil, err
}
return troubleshootClient, nil
}
+23
View File
@@ -0,0 +1,23 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/replicatedhq/troubleshoot/pkg/version"
)
func VersionCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "version",
Short: "Print the current version and exit",
Long: `Print the current version and exit`,
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Printf("Replicated Preflight %s\n", version.Version())
return nil
},
}
return cmd
}
+4 -1
View File
@@ -1,6 +1,9 @@
package main
import "github.com/replicatedhq/troubleshoot/cmd/preflight/cli"
import (
"github.com/replicatedhq/troubleshoot/cmd/preflight/cli"
_ "k8s.io/client-go/plugin/pkg/client/auth"
)
func main() {
cli.InitAndExecute()
+165
View File
@@ -0,0 +1,165 @@
package cli
import (
"encoding/json"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
extensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
extensionsscheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
"k8s.io/client-go/kubernetes/scheme"
)
func RootCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "schemagen",
Short: "Generate openapischemas for the kinds in this project",
SilenceUsage: true,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlags(cmd.Flags())
},
RunE: func(cmd *cobra.Command, args []string) error {
v := viper.GetViper()
return generateSchemas(v)
},
}
cobra.OnInitialize(initConfig)
cmd.Flags().String("output-dir", "./schemas", "directory to save the schemas in")
viper.BindPFlags(cmd.Flags())
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
return cmd
}
func InitAndExecute() {
if err := RootCmd().Execute(); err != nil {
os.Exit(1)
}
}
func initConfig() {
viper.SetEnvPrefix("TROUBLESHOOT")
viper.AutomaticEnv()
}
func generateSchemas(v *viper.Viper) error {
// we generate schemas from the config/crds in the root of this project
// those crds can be created from controller-gen or by running `make openapischema`
workdir, err := os.Getwd()
if err != nil {
return errors.Wrap(err, "failed to get workdir")
}
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",
},
}
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
}
func generateSchemaFromCRD(crd []byte, outfile string) error {
extensionsscheme.AddToScheme(scheme.Scheme)
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode(crd, nil, nil)
if err != nil {
return errors.Wrap(err, "failed to decode crd")
}
customResourceDefinition := obj.(*extensionsv1beta1.CustomResourceDefinition)
b, err := json.MarshalIndent(customResourceDefinition.Spec.Validation.OpenAPIV3Schema, "", " ")
if err != nil {
return errors.Wrap(err, "failed to marshal json")
}
_, err = os.Stat(outfile)
if err == nil {
if err := os.Remove(outfile); err != nil {
return errors.Wrap(err, "failed to remove file")
}
}
d, _ := path.Split(outfile)
_, err = os.Stat(d)
if os.IsNotExist(err) {
if err = os.MkdirAll(d, 0755); err != nil {
return errors.Wrap(err, "failed to mkdir")
}
}
// whoa now
// working around the fact that controller-gen doesn't have tags to generate oneOf schemas, so this is hacky.
// going to work to add an issue there to support and if they accept, this terrible thing can go away
boolStringed := strings.ReplaceAll(string(b), `"type": "BoolString"`, `"oneOf": [{"type": "string"},{"type": "boolean"}]`)
err = ioutil.WriteFile(outfile, []byte(boolStringed), 0644)
if err != nil {
return errors.Wrap(err, "failed to write file")
}
return nil
}
+9
View File
@@ -0,0 +1,9 @@
package main
import (
"github.com/replicatedhq/troubleshoot/cmd/schemagen/cli"
)
func main() {
cli.InitAndExecute()
}
+57 -7
View File
@@ -1,10 +1,13 @@
package cli
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"github.com/replicatedhq/troubleshoot/cmd/util"
analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze"
"github.com/replicatedhq/troubleshoot/pkg/convert"
"github.com/replicatedhq/troubleshoot/pkg/logger"
@@ -15,11 +18,12 @@ import (
func Analyze() *cobra.Command {
cmd := &cobra.Command{
Use: "analyze",
Use: "analyze [url]",
Args: cobra.MinimumNArgs(1),
Short: "analyze a support bundle",
Long: `...`,
Long: `Analyze a support bundle using the Analyzer definitions provided`,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("url", cmd.Flags().Lookup("url"))
viper.BindPFlag("bundle", cmd.Flags().Lookup("bundle"))
viper.BindPFlag("output", cmd.Flags().Lookup("output"))
viper.BindPFlag("quiet", cmd.Flags().Lookup("quiet"))
},
@@ -28,7 +32,13 @@ func Analyze() *cobra.Command {
logger.SetQuiet(v.GetBool("quiet"))
result, err := analyzer.DownloadAndAnalyze(context.TODO(), v.GetString("url"))
specPath := args[0]
analyzerSpec, err := downloadAnalyzerSpec(specPath)
if err != nil {
return err
}
result, err := analyzer.DownloadAndAnalyze(v.GetString("bundle"), analyzerSpec)
if err != nil {
return err
}
@@ -60,8 +70,8 @@ func Analyze() *cobra.Command {
},
}
cmd.Flags().String("url", "", "URL of the support bundle to analyze")
cmd.MarkFlagRequired("url")
cmd.Flags().String("bundle", "", "filename of the support bundle to analyze")
cmd.MarkFlagRequired("bundle")
cmd.Flags().String("output", "", "output format: json, yaml")
cmd.Flags().String("compatibility", "", "output compatibility mode: support-bundle")
cmd.Flags().MarkHidden("compatibility")
@@ -71,3 +81,43 @@ func Analyze() *cobra.Command {
return cmd
}
func downloadAnalyzerSpec(specPath string) (string, error) {
specContent := ""
var err error
if _, err = os.Stat(specPath); err == nil {
if _, err := os.Stat(specPath); os.IsNotExist(err) {
return "", fmt.Errorf("%s was not found", specPath)
}
b, err := ioutil.ReadFile(specPath)
if err != nil {
return "", err
}
specContent = string(b)
} else {
if !util.IsURL(specPath) {
return "", fmt.Errorf("%s is not a URL and was not found (err %s)", specPath, err)
}
req, err := http.NewRequest("GET", specPath, nil)
if err != nil {
return "", err
}
req.Header.Set("User-Agent", "Replicated_Analyzer/v1beta1")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
specContent = string(body)
}
return specContent, 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
}
-148
View File
@@ -1,148 +0,0 @@
package cli
import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"github.com/mholt/archiver"
"github.com/replicatedhq/troubleshoot/pkg/logger"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func receiveSupportBundle(collectorJobNamespace string, collectorJobName string) error {
// poll until there are no more "running" collectors
troubleshootClient, err := createTroubleshootK8sClient()
if err != nil {
return err
}
bundlePath, err := ioutil.TempDir("", "troubleshoot")
if err != nil {
return err
}
defer os.RemoveAll(bundlePath)
versionFilename, err := writeVersionFile(bundlePath)
if err != nil {
return err
}
receivedCollectors := []string{}
for {
job, err := troubleshootClient.CollectorJobs(collectorJobNamespace).Get(collectorJobName, metav1.GetOptions{})
if err != nil && kuberneteserrors.IsNotFound(err) {
// where did it go!
return nil
} else if err != nil {
return err
}
for _, readyCollector := range job.Status.Successful {
alreadyReceived := false
for _, receivedCollector := range receivedCollectors {
if receivedCollector == readyCollector {
alreadyReceived = true
}
}
if alreadyReceived {
continue
}
collectorResp, err := http.Get(fmt.Sprintf("http://localhost:8000/collector/%s", readyCollector))
if err != nil {
return err
}
defer collectorResp.Body.Close()
body, err := ioutil.ReadAll(collectorResp.Body)
if err != nil {
return err
}
decoded, err := base64.StdEncoding.DecodeString(string(body))
if err != nil {
logger.Printf("failed to output for collector %s\n", readyCollector)
continue
}
files := make(map[string]interface{})
if err := json.Unmarshal(decoded, &files); err != nil {
logger.Printf("failed to unmarshal output for collector %s\n", readyCollector)
}
for filename, maybeContents := range files {
fileDir, fileName := filepath.Split(filename)
outPath := filepath.Join(bundlePath, fileDir)
if err := os.MkdirAll(outPath, 0777); err != nil {
return err
}
switch maybeContents.(type) {
case string:
decoded, err := base64.StdEncoding.DecodeString(maybeContents.(string))
if err != nil {
return err
}
if err := writeFile(filepath.Join(outPath, fileName), decoded); err != nil {
return err
}
case map[string]interface{}:
for k, v := range maybeContents.(map[string]interface{}) {
s, _ := filepath.Split(filepath.Join(outPath, fileName, k))
if err := os.MkdirAll(s, 0777); err != nil {
return err
}
decoded, err := base64.StdEncoding.DecodeString(v.(string))
if err != nil {
return err
}
if err := writeFile(filepath.Join(outPath, fileName, k), decoded); err != nil {
return err
}
}
}
}
receivedCollectors = append(receivedCollectors, readyCollector)
}
if len(job.Status.Running) == 0 {
tarGz := archiver.TarGz{
Tar: &archiver.Tar{
ImplicitTopLevelFolder: false,
},
}
// version file should be first in tar archive for quick extraction
paths := []string{
versionFilename,
}
for _, id := range receivedCollectors {
paths = append(paths, filepath.Join(bundlePath, id))
}
if err := tarGz.Archive(paths, "support-bundle.tar.gz"); err != nil {
return err
}
return nil
}
}
}
func writeFile(filename string, contents []byte) error {
if err := ioutil.WriteFile(filename, contents, 0644); err != nil {
return err
}
return nil
}
-80
View File
@@ -1,80 +0,0 @@
package cli
import (
"errors"
"path/filepath"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/logger"
"github.com/spf13/cobra"
"github.com/spf13/viper"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func Retrieve() *cobra.Command {
cmd := &cobra.Command{
Use: "retrieve",
Short: "retrieve a support bundle from a job in the cluster",
Long: `...`,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("collectors", cmd.Flags().Lookup("collectors"))
viper.BindPFlag("namespace", cmd.Flags().Lookup("namespace"))
viper.BindPFlag("kubecontext", cmd.Flags().Lookup("kubecontext"))
},
RunE: func(cmd *cobra.Command, args []string) error {
v := viper.GetViper()
troubleshootClient, err := createTroubleshootK8sClient()
if err != nil {
return err
}
collectorJobs, err := troubleshootClient.CollectorJobs(v.GetString("namespace")).List(metav1.ListOptions{})
if err != nil {
return err
}
var collectorJob *troubleshootv1beta1.CollectorJob
if v.GetString("collectors") == "" && len(collectorJobs.Items) == 1 {
collectorJob = &collectorJobs.Items[0]
} else {
for _, foundCollectorJob := range collectorJobs.Items {
if foundCollectorJob.Spec.Collector.Name == v.GetString("collectors") {
collectorJob = &foundCollectorJob
break
}
}
}
if collectorJob == nil {
logger.Printf("unable to find collector job\n")
return errors.New("no collectors")
}
logger.Printf("connecting to collector job %s\n", collectorJob.Name)
stopChan, err := k8sutil.PortForward(v.GetString("kubecontext"), 8000, 8000, collectorJob.Status.ServerPodNamespace, collectorJob.Status.ServerPodName)
if err != nil {
return err
}
if err := receiveSupportBundle(collectorJob.Namespace, collectorJob.Name); err != nil {
return err
}
// write a zip
close(stopChan)
return nil
},
}
cmd.Flags().String("collectors", "", "name of the collectors to use")
cmd.Flags().String("namespace", "", "namespace the collectors can be found in")
cmd.Flags().String("kubecontext", filepath.Join(homeDir(), ".kube", "config"), "the kubecontext to use when connecting")
viper.BindPFlags(cmd.Flags())
return cmd
}
+29 -22
View File
@@ -1,12 +1,12 @@
package cli
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"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"
@@ -14,11 +14,12 @@ import (
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())
},
@@ -26,38 +27,36 @@ from a server that can be used to assist when troubleshooting a server.`,
v := viper.GetViper()
logger.SetQuiet(v.GetBool("quiet"))
if len(args) == 0 {
return runTroubleshootCRD(v)
}
return runTroubleshootNoCRD(v, args[0])
return runTroubleshoot(v, args[0])
},
}
cobra.OnInitialize(initConfig)
cmd.AddCommand(Analyze())
cmd.AddCommand(VersionCmd())
cmd.Flags().String("collectors", "", "name of the collectors to use")
cmd.Flags().String("namespace", "default", "namespace the collectors can be found in")
cmd.Flags().String("kubecontext", filepath.Join(homeDir(), ".kube", "config"), "the kubecontext to use when connecting")
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 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, "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("-", "_"))
k8sutil.AddFlags(cmd.Flags())
return cmd
}
func InitAndExecute() {
if err := RootCmd().Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
@@ -67,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
@@ -79,3 +78,11 @@ func ensureCollectorInList(list []*troubleshootv1beta1.Collect, collector troubl
return append(list, &collector)
}
func writeFile(filename string, contents []byte) error {
if err := ioutil.WriteFile(filename, contents, 0644); err != nil {
return err
}
return nil
}
+777
View File
@@ -0,0 +1,777 @@
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"
cursor "github.com/ahmetalpbalkan/go-cursor"
"github.com/fatih/color"
"github.com/manifoldco/promptui"
"github.com/mattn/go-isatty"
"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/cmd/util"
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 (
httpClient *http.Client
)
func runTroubleshoot(v *viper.Viper, arg string) error {
fmt.Print(cursor.Hide())
defer fmt.Print(cursor.Show())
if v.GetBool("allow-insecure-connections") || v.GetBool("insecure-skip-tls-verify") {
httpClient = &http.Client{Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}}
} else {
httpClient = http.DefaultClient
}
collectorContent, err := loadSpec(v, arg)
if err != nil {
return errors.Wrap(err, "failed to load collector spec")
}
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
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.(*troubleshootv1beta2.Redactor)
if !ok {
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 {
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.(*troubleshootv1beta2.Redactor)
if !ok {
continue
}
additionalRedactors.Spec.Redactors = append(additionalRedactors.Spec.Redactors, multidocRedactors.Spec.Redactors...)
}
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 {
select {
case msg := <-progressChan:
switch msg := msg.(type) {
case error:
c := color.New(color.FgHiRed)
c.Println(fmt.Sprintf("%s\r * %v", cursor.ClearEntireLine(), msg))
case string:
currentDir = filepath.Base(msg)
}
case <-finishedCh:
fmt.Printf("\r%s\r", cursor.ClearEntireLine())
return
case <-time.After(time.Millisecond * 100):
if currentDir == "" {
fmt.Printf("\r%s \033[36mCollecting support bundle\033[m %s", cursor.ClearEntireLine(), s.Next())
} else {
fmt.Printf("\r%s \033[36mCollecting support bundle\033[m %s %s", cursor.ClearEntireLine(), s.Next(), currentDir)
}
}
}
}()
defer func() {
if !isFinishedChClosed {
close(finishedCh)
}
}()
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())
// 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 := 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.`
msg = fmt.Sprintf(f, appName, archivePath, appName)
}
fmt.Printf("%s\n", msg)
return nil
}
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")
fmt.Printf("A copy of this support bundle was written to the current directory, named %q\n", archivePath)
} else {
fmt.Printf("A support bundle has been created in the current directory named %q\n", archivePath)
}
return nil
}
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 {
return nil, errors.Wrap(err, "read spec file")
}
return b, nil
} else if !util.IsURL(arg) {
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 {
return nil, errors.Wrap(err, "make request")
}
req.Header.Set("User-Agent", "Replicated_Troubleshoot/v1beta1")
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 shouldRetryRequest(err) {
continue
}
return nil, errors.Wrap(err, "execute request")
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "read responce body")
}
return body, nil
}
}
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
}
prompt := promptui.Prompt{
Label: "Connection appears to be insecure. Would you like to attempt to create a support bundle anyway?",
IsConfirm: true,
}
_, err := prompt.Run()
if err != nil {
return false
}
return true
}
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(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([]*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 := k8sutil.GetRESTConfig()
if err != nil {
return "", errors.Wrap(err, "failed to convert kube flags to rest config")
}
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),
}
cleanedCollectors = append(cleanedCollectors, &collector)
}
if err := cleanedCollectors.CheckRBAC(context.Background()); err != nil {
return "", errors.Wrap(err, "failed to check RBAC for collectors")
}
foundForbidden := false
for _, c := range cleanedCollectors {
for _, e := range c.RBACErrors {
foundForbidden = true
progressChan <- e
}
}
if foundForbidden && !v.GetBool("collect-without-permissions") {
return "", errors.New("insufficient permissions to run all collectors")
}
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 cleanedCollectors {
if len(collector.RBACErrors) > 0 {
// don't skip clusterResources collector due to RBAC issues
if collector.Collect.ClusterResources == nil {
progressChan <- fmt.Sprintf("skipping collector %s with insufficient RBAC permissions", collector.GetDisplayName())
continue
}
}
progressChan <- collector.GetDisplayName()
result, err := collector.RunCollectorSync(globalRedactors)
if err != nil {
progressChan <- fmt.Errorf("failed to run collector %q: %v", collector.GetDisplayName(), err)
continue
}
if result != nil {
// 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
}
}
}
if err := tarSupportBundleDir(bundlePath, filename); err != nil {
return "", errors.Wrap(err, "create bundle file")
}
return filename, nil
}
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)
if err := os.MkdirAll(outPath, 0777); err != nil {
return errors.Wrap(err, "create output file")
}
if err := writeFile(filepath.Join(outPath, fileName), maybeContents); err != nil {
return errors.Wrap(err, "write collector output")
}
}
return nil
}
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)
}
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")
}
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 {
if shouldRetryRequest(err) {
continue
}
return errors.Wrap(err, "execute request")
}
if resp.StatusCode >= 300 {
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
}
break
}
// send redaction report
if r.RedactURI != "" {
type PutSupportBundleRedactions struct {
Redactions redact.RedactionList `json:"redactions"`
}
redactBytes, err := json.Marshal(PutSupportBundleRedactions{Redactions: redact.GetRedactionList()})
if err != nil {
return errors.Wrap(err, "get redaction report")
}
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 {
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)
}
break
}
}
return nil
}
func getExpectedContentType(uploadURL string) string {
parsedURL, err := url.Parse(uploadURL)
if err != nil {
return ""
}
return parsedURL.Query().Get("Content-Type")
}
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 {
if shouldRetryRequest(err) {
continue
}
return errors.Wrap(err, "execute request")
}
if resp.StatusCode >= 300 {
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
}
break
}
return nil
}
func tarSupportBundleDir(inputDir, outputFilename string) error {
fileWriter, err := os.Create(outputFilename)
if err != nil {
return errors.Wrap(err, "failed to create output file")
}
defer fileWriter.Close()
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
}
parentDirName := filepath.Dir(inputDir) // this is to have the files inside a subdirectory
nameInArchive, err := filepath.Rel(parentDirName, 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 *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
}
-102
View File
@@ -1,102 +0,0 @@
package cli
import (
"errors"
"fmt"
"time"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/spf13/viper"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func runTroubleshootCRD(v *viper.Viper) error {
troubleshootClient, err := createTroubleshootK8sClient()
if err != nil {
return err
}
collectorName := v.GetString("collectors")
if collectorName == "" {
collectors, err := troubleshootClient.Collectors(v.GetString("namespace")).List(metav1.ListOptions{})
if err != nil {
return err
}
if len(collectors.Items) == 1 {
collectorName = collectors.Items[0].Name
}
}
if collectorName == "" {
return errors.New("unknown collectors, try using the --collectors flags")
}
// generate a unique name
now := time.Now()
suffix := fmt.Sprintf("%d", now.Unix())
collectorJobName := fmt.Sprintf("%s-job-%s", collectorName, suffix[len(suffix)-4:])
collectorJob := troubleshootv1beta1.CollectorJob{
ObjectMeta: metav1.ObjectMeta{
Name: collectorJobName,
Namespace: v.GetString("namespace"),
},
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "collectorjob.troubleshoot.replicated.com",
},
Spec: troubleshootv1beta1.CollectorJobSpec{
Collector: troubleshootv1beta1.CollectorRef{
Name: collectorName,
Namespace: v.GetString("namespace"),
},
Image: v.GetString("image"),
ImagePullPolicy: v.GetString("pullpolicy"),
Redact: v.GetBool("redact"),
},
}
if _, err := troubleshootClient.CollectorJobs(v.GetString("namespace")).Create(&collectorJob); err != nil {
return err
}
// Poll the status of the Custom Resource for it to include a callback
var found *troubleshootv1beta1.CollectorJob
start := time.Now()
for {
current, err := troubleshootClient.CollectorJobs(v.GetString("namespace")).Get(collectorJobName, metav1.GetOptions{})
if err != nil && kuberneteserrors.IsNotFound(err) {
continue
} else if err != nil {
return err
}
if current.Status.IsServerReady {
found = current
break
}
if time.Now().Sub(start) > time.Duration(time.Second*10) {
return errors.New("collectorjob failed to start")
}
time.Sleep(time.Millisecond * 200)
}
// Connect to the callback
stopChan, err := k8sutil.PortForward(v.GetString("kubecontext"), 8000, 8000, found.Status.ServerPodNamespace, found.Status.ServerPodName)
if err != nil {
return err
}
if err := receiveSupportBundle(found.Namespace, found.Name); err != nil {
return err
}
// Write
close(stopChan)
return nil
}
-328
View File
@@ -1,328 +0,0 @@
package cli
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"time"
"github.com/mholt/archiver"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
collectrunner "github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/logger"
"github.com/spf13/viper"
"gopkg.in/yaml.v2"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
)
func runTroubleshootNoCRD(v *viper.Viper, arg string) error {
collectorContent := ""
if !isURL(arg) {
if _, err := os.Stat(arg); os.IsNotExist(err) {
return fmt.Errorf("%s was not found", arg)
}
b, err := ioutil.ReadFile(arg)
if err != nil {
return err
}
collectorContent = string(b)
} else {
req, err := http.NewRequest("GET", arg, nil)
if err != nil {
return err
}
req.Header.Set("User-Agent", "Replicated_Troubleshoot/v1beta1")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
collectorContent = string(body)
}
collector := troubleshootv1beta1.Collector{}
if err := yaml.Unmarshal([]byte(collectorContent), &collector); err != nil {
return fmt.Errorf("unable to parse %s collectors", arg)
}
archivePath, err := runCollectors(v, collector)
if err != nil {
return err
}
fmt.Printf("%s\n", archivePath)
return nil
}
func runCollectors(v *viper.Viper, collector troubleshootv1beta1.Collector) (string, error) {
cfg, err := config.GetConfig()
if err != nil {
return "", err
}
client, err := client.New(cfg, client.Options{})
if err != nil {
return "", err
}
clientset, err := kubernetes.NewForConfig(cfg)
if err != nil {
return "", err
}
restClient := clientset.CoreV1().RESTClient()
serviceAccountName := v.GetString("serviceaccount")
if serviceAccountName == "" {
generatedServiceAccountName, err := createServiceAccount(collector, v.GetString("namespace"), clientset)
if err != nil {
return "", err
}
defer removeServiceAccount(generatedServiceAccountName, v.GetString("namespace"), clientset)
serviceAccountName = generatedServiceAccountName
}
// deploy an object that "owns" everything to aid in cleanup
owner := corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("troubleshoot-%s-owner", collector.Name),
Namespace: v.GetString("namespace"),
},
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ConfigMap",
},
Data: make(map[string]string),
}
if err := client.Create(context.Background(), &owner); err != nil {
return "", err
}
defer func() {
if err := client.Delete(context.Background(), &owner); err != nil {
fmt.Println("failed to clean up preflight.")
}
}()
// deploy all collectors
desiredCollectors := make([]*troubleshootv1beta1.Collect, 0, 0)
for _, definedCollector := range collector.Spec {
desiredCollectors = append(desiredCollectors, definedCollector)
}
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterInfo: &troubleshootv1beta1.ClusterInfo{}})
desiredCollectors = ensureCollectorInList(desiredCollectors, troubleshootv1beta1.Collect{ClusterResources: &troubleshootv1beta1.ClusterResources{}})
podsCreated := make([]*corev1.Pod, 0, 0)
podsDeleted := make([]*corev1.Pod, 0, 0)
collectorDirs := []string{}
bundlePath, err := ioutil.TempDir("", "troubleshoot")
if err != nil {
return "", err
}
defer os.RemoveAll(bundlePath)
versionFilename, err := writeVersionFile(bundlePath)
if err != nil {
return "", err
}
resyncPeriod := time.Second
ctx := context.Background()
watchList := cache.NewListWatchFromClient(restClient, "pods", "", fields.Everything())
_, controller := cache.NewInformer(watchList, &corev1.Pod{}, resyncPeriod,
cache.ResourceEventHandlerFuncs{
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
newPod, ok := newObj.(*corev1.Pod)
if !ok {
return
}
oldPod, ok := oldObj.(*corev1.Pod)
if !ok {
return
}
labels := newPod.Labels
troubleshootRole, ok := labels["troubleshoot-role"]
if !ok || troubleshootRole != "troubleshoot" {
return
}
collectorName, ok := labels["troubleshoot"]
if !ok || collectorName != collector.Name {
return
}
if oldPod.Status.Phase == newPod.Status.Phase {
return
}
if newPod.Status.Phase == corev1.PodFailed {
podsDeleted = append(podsDeleted, newPod)
return
}
if newPod.Status.Phase != corev1.PodSucceeded {
return
}
podLogOpts := corev1.PodLogOptions{}
req := clientset.CoreV1().Pods(newPod.Namespace).GetLogs(newPod.Name, &podLogOpts)
podLogs, err := req.Stream()
if err != nil {
fmt.Println("get stream")
return
}
defer podLogs.Close()
buf := new(bytes.Buffer)
_, err = io.Copy(buf, podLogs)
if err != nil {
fmt.Println("copy logs")
return
}
collectorDir, err := parseAndSaveCollectorOutput(buf.String(), bundlePath)
if err != nil {
logger.Printf("parse collected data: %v\n", err)
return
}
// empty dir name will make tar fail
if collectorDir == "" {
logger.Printf("pod %s did not return any files\n", newPod.Name)
return
}
collectorDirs = append(collectorDirs, collectorDir)
if err := client.Delete(context.Background(), newPod); err != nil {
fmt.Println("delete pod error", err)
}
podsDeleted = append(podsDeleted, newPod)
},
})
go func() {
controller.Run(ctx.Done())
}()
s := runtime.NewScheme()
s.AddKnownTypes(schema.GroupVersion{Group: "", Version: "v1"}, &corev1.ConfigMap{})
for _, collect := range desiredCollectors {
_, pod, err := collectrunner.CreateCollector(client, s, &owner, collector.Name, v.GetString("namespace"), serviceAccountName, "troubleshoot", collect, v.GetString("image"), v.GetString("pullpolicy"))
if err != nil {
logger.Printf("A collector pod cannot be created: %v\n", err)
continue
}
podsCreated = append(podsCreated, pod)
}
start := time.Now()
for {
if start.Add(time.Second * 30).Before(time.Now()) {
fmt.Println("timeout running troubleshoot")
return "", err
}
if len(podsDeleted) == len(podsCreated) {
break
}
time.Sleep(time.Millisecond * 200)
}
ctx.Done()
tarGz := archiver.TarGz{
Tar: &archiver.Tar{
ImplicitTopLevelFolder: false,
},
}
// version file should be first in tar archive for quick extraction
paths := []string{
versionFilename,
}
for _, collectorDir := range collectorDirs {
paths = append(paths, collectorDir)
}
if err := tarGz.Archive(paths, "support-bundle.tar.gz"); err != nil {
return "", err
}
return "support-bundle.tar.gz", nil
}
func parseAndSaveCollectorOutput(output string, bundlePath string) (string, error) {
dir := ""
input := make(map[string]interface{})
if err := json.Unmarshal([]byte(output), &input); err != nil {
return "", err
}
for filename, maybeContents := range input {
fileDir, fileName := filepath.Split(filename)
outPath := filepath.Join(bundlePath, fileDir)
dir = outPath
if err := os.MkdirAll(outPath, 0777); err != nil {
return "", err
}
switch maybeContents.(type) {
case string:
decoded, err := base64.StdEncoding.DecodeString(maybeContents.(string))
if err != nil {
return "", err
}
if err := writeFile(filepath.Join(outPath, fileName), decoded); err != nil {
return "", err
}
case map[string]interface{}:
for k, v := range maybeContents.(map[string]interface{}) {
s, _ := filepath.Split(filepath.Join(outPath, fileName, k))
if err := os.MkdirAll(s, 0777); err != nil {
return "", err
}
decoded, err := base64.StdEncoding.DecodeString(v.(string))
if err != nil {
return "", err
}
if err := writeFile(filepath.Join(outPath, fileName, k), decoded); err != nil {
return "", err
}
}
}
}
return dir, 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
}
+15 -32
View File
@@ -1,41 +1,24 @@
package cli
import (
"net/url"
"fmt"
"os"
troubleshootclientv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
"github.com/spf13/viper"
"k8s.io/client-go/tools/clientcmd"
"github.com/pkg/errors"
)
func homeDir() string {
if h := os.Getenv("HOME"); h != "" {
return h
func findFileName(basename, extension string) (string, error) {
n := 1
name := basename
for {
filename := name + "." + extension
if _, err := os.Stat(filename); os.IsNotExist(err) {
return filename, nil
} else if err != nil {
return "", errors.Wrap(err, "check file exists")
}
name = fmt.Sprintf("%s (%d)", basename, n)
n = n + 1
}
return os.Getenv("USERPROFILE") // windows
}
func isURL(str string) bool {
_, err := url.ParseRequestURI(str)
if err != nil {
return false
}
return true
}
func createTroubleshootK8sClient() (*troubleshootclientv1beta1.TroubleshootV1beta1Client, error) {
v := viper.GetViper()
config, err := clientcmd.BuildConfigFromFlags("", v.GetString("kubecontext"))
if err != nil {
return nil, err
}
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(config)
if err != nil {
return nil, err
}
return troubleshootClient, nil
}
+33 -9
View File
@@ -1,27 +1,51 @@
package cli
import (
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
"gopkg.in/yaml.v2"
"fmt"
"io/ioutil"
"path/filepath"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/version"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
)
func writeVersionFile(path string) (string, error) {
version := troubleshootv1beta1.SupportBundleVersion{
ApiVersion: "troubleshoot.replicated.com/v1beta1",
func VersionCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "version",
Short: "Print the current version and exit",
Long: `Print the current version and exit`,
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Printf("Replicated Troubleshoot %s\n", version.Version())
return nil
},
}
return cmd
}
const VersionFilename = "version.yaml"
func writeVersionFile(path string) error {
version := troubleshootv1beta2.SupportBundleVersion{
ApiVersion: "troubleshoot.sh/v1beta2",
Kind: "SupportBundle",
Spec: troubleshootv1beta2.SupportBundleVersionSpec{
VersionNumber: version.Version(),
},
}
b, err := yaml.Marshal(version)
if err != nil {
return "", err
return err
}
filename := filepath.Join(path, "version.yaml")
filename := filepath.Join(path, VersionFilename)
err = ioutil.WriteFile(filename, b, 0644)
if err != nil {
return "", err
return err
}
return filename, nil
return nil
}
+4 -1
View File
@@ -1,6 +1,9 @@
package main
import "github.com/replicatedhq/troubleshoot/cmd/troubleshoot/cli"
import (
"github.com/replicatedhq/troubleshoot/cmd/troubleshoot/cli"
_ "k8s.io/client-go/plugin/pkg/client/auth"
)
func main() {
cli.InitAndExecute()
+39
View File
@@ -0,0 +1,39 @@
package util
import (
"net/url"
"os"
"strings"
)
func HomeDir() string {
if h := os.Getenv("HOME"); h != "" {
return h
}
return os.Getenv("USERPROFILE") // windows
}
func IsURL(str string) bool {
parsed, err := url.ParseRequestURI(str)
if err != nil {
return false
}
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, " ")
}
View File
@@ -3,397 +3,44 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.4
creationTimestamp: null
name: analyzerjobs.troubleshoot.replicated.com
spec:
group: troubleshoot.replicated.com
names:
kind: AnalyzerJob
listKind: AnalyzerJobList
plural: analyzerjobs
scope: ""
singular: analyzerjob
scope: Namespaced
validation:
openAPIV3Schema:
description: AnalyzerJob is the Schema for the analyzerjobs 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: AnalyzerJobSpec defines the desired state of AnalyzerJob
type: object
status:
description: AnalyzerJobStatus defines the observed state of AnalyzerJob
type: object
type: object
version: v1beta1
versions:
- name: v1beta1
schema:
openAPIV3Schema:
description: AnalyzerJob is the Schema for the analyzerjobs 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/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/api-conventions.md#types-kinds'
type: string
metadata:
properties:
annotations:
additionalProperties:
type: string
description: 'Annotations is an unstructured key value map stored
with a resource that may be set by external tools to store and retrieve
arbitrary metadata. They are not queryable and should be preserved
when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations'
type: object
clusterName:
description: The name of the cluster which the object belongs to.
This is used to distinguish resources with same name and namespace
in different clusters. This field is not set anywhere right now
and apiserver is going to ignore it if set in create or update request.
type: string
creationTimestamp:
description: "CreationTimestamp is a timestamp representing the server
time when this object was created. It is not guaranteed to be set
in happens-before order across separate operations. Clients may
not set this value. It is represented in RFC3339 form and is in
UTC. \n Populated by the system. Read-only. Null for lists. More
info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
format: date-time
type: string
deletionGracePeriodSeconds:
description: Number of seconds allowed for this object to gracefully
terminate before it will be removed from the system. Only set when
deletionTimestamp is also set. May only be shortened. Read-only.
format: int64
type: integer
deletionTimestamp:
description: "DeletionTimestamp is RFC 3339 date and time at which
this resource will be deleted. This field is set by the server when
a graceful deletion is requested by the user, and is not directly
settable by a client. The resource is expected to be deleted (no
longer visible from resource lists, and not reachable by name) after
the time in this field, once the finalizers list is empty. As long
as the finalizers list contains items, deletion is blocked. Once
the deletionTimestamp is set, this value may not be unset or be
set further into the future, although it may be shortened or the
resource may be deleted prior to this time. For example, a user
may request that a pod is deleted in 30 seconds. The Kubelet will
react by sending a graceful termination signal to the containers
in the pod. After that 30 seconds, the Kubelet will send a hard
termination signal (SIGKILL) to the container and after cleanup,
remove the pod from the API. In the presence of network partitions,
this object may still exist after this timestamp, until an administrator
or automated process can determine the resource is fully terminated.
If not set, graceful deletion of the object has not been requested.
\n Populated by the system when a graceful deletion is requested.
Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
format: date-time
type: string
finalizers:
description: Must be empty before the object is deleted from the registry.
Each entry is an identifier for the responsible component that will
remove the entry from the list. If the deletionTimestamp of the
object is non-nil, entries in this list can only be removed.
items:
type: string
type: array
generateName:
description: "GenerateName is an optional prefix, used by the server,
to generate a unique name ONLY IF the Name field has not been provided.
If this field is used, the name returned to the client will be different
than the name passed. This value will also be combined with a unique
suffix. The provided value has the same validation rules as the
Name field, and may be truncated by the length of the suffix required
to make the value unique on the server. \n If this field is specified
and the generated name exists, the server will NOT return a 409
- instead, it will either return 201 Created or 500 with Reason
ServerTimeout indicating a unique name could not be found in the
time allotted, and the client should retry (optionally after the
time indicated in the Retry-After header). \n Applied only if Name
is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency"
type: string
generation:
description: A sequence number representing a specific generation
of the desired state. Populated by the system. Read-only.
format: int64
type: integer
initializers:
description: "An initializer is a controller which enforces some system
invariant at object creation time. This field is a list of initializers
that have not yet acted on this object. If nil or empty, this object
has been completely initialized. Otherwise, the object is considered
uninitialized and is hidden (in list/watch and get calls) from clients
that haven't explicitly asked to observe uninitialized objects.
\n When an object is created, the system will populate this list
with the current set of initializers. Only privileged users may
set or modify this list. Once it is empty, it may not be modified
further by any user. \n DEPRECATED - initializers are an alpha field
and will be removed in v1.15."
properties:
pending:
description: Pending is a list of initializers that must execute
in order before this object is visible. When the last pending
initializer is removed, and no failing result is set, the initializers
struct will be set to nil and the object is considered as initialized
and visible to all clients.
items:
properties:
name:
description: name of the process that is responsible for
initializing this object.
type: string
required:
- name
type: object
type: array
result:
description: If result is set with the Failure field, the object
will be persisted to storage and then deleted, ensuring that
other clients can observe the deletion.
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/api-conventions.md#resources'
type: string
code:
description: Suggested HTTP return code for this status, 0
if not set.
format: int32
type: integer
details:
description: Extended data associated with the reason. Each
reason may define its own extended details. This field is
optional and the data returned is not guaranteed to conform
to any schema except that defined by the reason type.
properties:
causes:
description: The Causes array includes more details associated
with the StatusReason failure. Not all StatusReasons
may provide detailed causes.
items:
properties:
field:
description: "The field of the resource that has
caused this error, as named by its JSON serialization.
May include dot and postfix notation for nested
attributes. Arrays are zero-indexed. Fields may
appear more than once in an array of causes due
to fields having multiple errors. Optional. \n
Examples: \"name\" - the field \"name\" on the
current resource \"items[0].name\" - the field
\"name\" on the first array entry in \"items\""
type: string
message:
description: A human-readable description of the
cause of the error. This field may be presented
as-is to a reader.
type: string
reason:
description: A machine-readable description of the
cause of the error. If this value is empty there
is no information available.
type: string
type: object
type: array
group:
description: The group attribute of the resource associated
with the status StatusReason.
type: string
kind:
description: 'The kind attribute of the resource associated
with the status StatusReason. On some operations may
differ from the requested resource Kind. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
name:
description: The name attribute of the resource associated
with the status StatusReason (when there is a single
name which can be described).
type: string
retryAfterSeconds:
description: If specified, the time in seconds before
the operation should be retried. Some errors may indicate
the client must take an alternate action - for those
errors this field may indicate how long to wait before
taking the alternate action.
format: int32
type: integer
uid:
description: 'UID of the resource. (when there is a single
resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
type: string
type: object
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/api-conventions.md#types-kinds'
type: string
message:
description: A human-readable description of the status of
this operation.
type: string
metadata:
description: 'Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
properties:
continue:
description: continue may be set if the user set a limit
on the number of items returned, and indicates that
the server has more data available. The value is opaque
and may be used to issue another request to the endpoint
that served this list to retrieve the next set of available
objects. Continuing a consistent list may not be possible
if the server configuration has changed or more than
a few minutes have passed. The resourceVersion field
returned when using this continue value will be identical
to the value in the first response, unless you have
received this token from an error message.
type: string
resourceVersion:
description: 'String that identifies the server''s internal
version of this object that can be used by clients to
determine when objects have changed. Value must be treated
as opaque by clients and passed unmodified back to the
server. Populated by the system. Read-only. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency'
type: string
selfLink:
description: selfLink is a URL representing this object.
Populated by the system. Read-only.
type: string
type: object
reason:
description: A machine-readable description of why this operation
is in the "Failure" status. If this value is empty there
is no information available. A Reason clarifies an HTTP
status code but does not override it.
type: string
status:
description: 'Status of the operation. One of: "Success" or
"Failure". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status'
type: string
type: object
required:
- pending
type: object
labels:
additionalProperties:
type: string
description: 'Map of string keys and values that can be used to organize
and categorize (scope and select) objects. May match selectors of
replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels'
type: object
managedFields:
description: "ManagedFields maps workflow-id and version to the set
of fields that are managed by that workflow. This is mostly for
internal housekeeping, and users typically shouldn't need to set
or understand this field. A workflow can be the user's name, a controller's
name, or the name of a specific apply path like \"ci-cd\". The set
of fields is always in the version that the workflow used when modifying
the object. \n This field is alpha and can be changed or removed
without notice."
items:
properties:
apiVersion:
description: APIVersion defines the version of this resource
that this field set applies to. The format is "group/version"
just like the top-level APIVersion field. It is necessary
to track the version of a field set because it cannot be automatically
converted.
type: string
fields:
additionalProperties: true
description: Fields identifies a set of fields.
type: object
manager:
description: Manager is an identifier of the workflow managing
these fields.
type: string
operation:
description: Operation is the type of operation which lead to
this ManagedFieldsEntry being created. The only valid values
for this field are 'Apply' and 'Update'.
type: string
time:
description: Time is timestamp of when these fields were set.
It should always be empty if Operation is 'Apply'
format: date-time
type: string
type: object
type: array
name:
description: 'Name must be unique within a namespace. Is required
when creating resources, although some resources may allow a client
to request the generation of an appropriate name automatically.
Name is primarily intended for creation idempotence and configuration
definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
type: string
namespace:
description: "Namespace defines the space within each name must be
unique. An empty namespace is equivalent to the \"default\" namespace,
but \"default\" is the canonical representation. Not all objects
are required to be scoped to a namespace - the value of this field
for those objects will be empty. \n Must be a DNS_LABEL. Cannot
be updated. More info: http://kubernetes.io/docs/user-guide/namespaces"
type: string
ownerReferences:
description: List of objects depended by this object. If ALL objects
in the list have been deleted, this object will be garbage collected.
If this object is managed by a controller, then an entry in this
list will point to this controller, with the controller field set
to true. There cannot be more than one managing controller.
items:
properties:
apiVersion:
description: API version of the referent.
type: string
blockOwnerDeletion:
description: If true, AND if the owner has the "foregroundDeletion"
finalizer, then the owner cannot be deleted from the key-value
store until this reference is removed. Defaults to false.
To set this field, a user needs "delete" permission of the
owner, otherwise 422 (Unprocessable Entity) will be returned.
type: boolean
controller:
description: If true, this reference points to the managing
controller.
type: boolean
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
type: string
uid:
description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
type: string
required:
- apiVersion
- kind
- name
- uid
type: object
type: array
resourceVersion:
description: "An opaque value that represents the internal version
of this object that can be used by clients to determine when objects
have changed. May be used for optimistic concurrency, change detection,
and the watch operation on a resource or set of resources. Clients
must treat these values as opaque and passed unmodified back to
the server. They may only be valid for a particular resource or
set of resources. \n Populated by the system. Read-only. Value must
be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency"
type: string
selfLink:
description: SelfLink is a URL representing this object. Populated
by the system. Read-only.
type: string
uid:
description: "UID is the unique in time and space value for this object.
It is typically generated by the server on successful creation of
a resource and is not allowed to change on PUT operations. \n Populated
by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids"
type: string
type: object
spec:
type: object
status:
type: object
type: object
served: true
storage: true
status:
File diff suppressed because it is too large Load Diff
@@ -3,448 +3,95 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.4
creationTimestamp: null
name: collectorjobs.troubleshoot.replicated.com
spec:
group: troubleshoot.replicated.com
names:
kind: CollectorJob
listKind: CollectorJobList
plural: collectorjobs
scope: ""
singular: collectorjob
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
description: CollectorJob is the Schema for the collectorjobs 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: CollectorJobSpec defines the desired state of CollectorJob
properties:
collector:
properties:
name:
type: string
namespace:
type: string
required:
- name
type: object
image:
type: string
imagePullPolicy:
type: string
redact:
type: boolean
required:
- collector
type: object
status:
description: CollectorJobStatus defines the observed state of CollectorJob
properties:
failed:
items:
type: string
type: array
isServerReady:
type: boolean
running:
items:
type: string
type: array
serverPodName:
type: string
serverPodNamespace:
type: string
serverPodPort:
type: integer
successful:
items:
type: string
type: array
required:
- failed
- isServerReady
- running
- serverPodName
- serverPodNamespace
- serverPodPort
- successful
type: object
type: object
version: v1beta1
versions:
- name: v1beta1
schema:
openAPIV3Schema:
description: CollectorJob is the Schema for the collectorjobs 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/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/api-conventions.md#types-kinds'
type: string
metadata:
properties:
annotations:
additionalProperties:
type: string
description: 'Annotations is an unstructured key value map stored
with a resource that may be set by external tools to store and retrieve
arbitrary metadata. They are not queryable and should be preserved
when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations'
type: object
clusterName:
description: The name of the cluster which the object belongs to.
This is used to distinguish resources with same name and namespace
in different clusters. This field is not set anywhere right now
and apiserver is going to ignore it if set in create or update request.
type: string
creationTimestamp:
description: "CreationTimestamp is a timestamp representing the server
time when this object was created. It is not guaranteed to be set
in happens-before order across separate operations. Clients may
not set this value. It is represented in RFC3339 form and is in
UTC. \n Populated by the system. Read-only. Null for lists. More
info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
format: date-time
type: string
deletionGracePeriodSeconds:
description: Number of seconds allowed for this object to gracefully
terminate before it will be removed from the system. Only set when
deletionTimestamp is also set. May only be shortened. Read-only.
format: int64
type: integer
deletionTimestamp:
description: "DeletionTimestamp is RFC 3339 date and time at which
this resource will be deleted. This field is set by the server when
a graceful deletion is requested by the user, and is not directly
settable by a client. The resource is expected to be deleted (no
longer visible from resource lists, and not reachable by name) after
the time in this field, once the finalizers list is empty. As long
as the finalizers list contains items, deletion is blocked. Once
the deletionTimestamp is set, this value may not be unset or be
set further into the future, although it may be shortened or the
resource may be deleted prior to this time. For example, a user
may request that a pod is deleted in 30 seconds. The Kubelet will
react by sending a graceful termination signal to the containers
in the pod. After that 30 seconds, the Kubelet will send a hard
termination signal (SIGKILL) to the container and after cleanup,
remove the pod from the API. In the presence of network partitions,
this object may still exist after this timestamp, until an administrator
or automated process can determine the resource is fully terminated.
If not set, graceful deletion of the object has not been requested.
\n Populated by the system when a graceful deletion is requested.
Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
format: date-time
type: string
finalizers:
description: Must be empty before the object is deleted from the registry.
Each entry is an identifier for the responsible component that will
remove the entry from the list. If the deletionTimestamp of the
object is non-nil, entries in this list can only be removed.
items:
type: string
type: array
generateName:
description: "GenerateName is an optional prefix, used by the server,
to generate a unique name ONLY IF the Name field has not been provided.
If this field is used, the name returned to the client will be different
than the name passed. This value will also be combined with a unique
suffix. The provided value has the same validation rules as the
Name field, and may be truncated by the length of the suffix required
to make the value unique on the server. \n If this field is specified
and the generated name exists, the server will NOT return a 409
- instead, it will either return 201 Created or 500 with Reason
ServerTimeout indicating a unique name could not be found in the
time allotted, and the client should retry (optionally after the
time indicated in the Retry-After header). \n Applied only if Name
is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency"
type: string
generation:
description: A sequence number representing a specific generation
of the desired state. Populated by the system. Read-only.
format: int64
type: integer
initializers:
description: "An initializer is a controller which enforces some system
invariant at object creation time. This field is a list of initializers
that have not yet acted on this object. If nil or empty, this object
has been completely initialized. Otherwise, the object is considered
uninitialized and is hidden (in list/watch and get calls) from clients
that haven't explicitly asked to observe uninitialized objects.
\n When an object is created, the system will populate this list
with the current set of initializers. Only privileged users may
set or modify this list. Once it is empty, it may not be modified
further by any user. \n DEPRECATED - initializers are an alpha field
and will be removed in v1.15."
properties:
pending:
description: Pending is a list of initializers that must execute
in order before this object is visible. When the last pending
initializer is removed, and no failing result is set, the initializers
struct will be set to nil and the object is considered as initialized
and visible to all clients.
items:
properties:
name:
description: name of the process that is responsible for
initializing this object.
type: string
required:
- name
type: object
type: array
result:
description: If result is set with the Failure field, the object
will be persisted to storage and then deleted, ensuring that
other clients can observe the deletion.
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/api-conventions.md#resources'
type: string
code:
description: Suggested HTTP return code for this status, 0
if not set.
format: int32
type: integer
details:
description: Extended data associated with the reason. Each
reason may define its own extended details. This field is
optional and the data returned is not guaranteed to conform
to any schema except that defined by the reason type.
properties:
causes:
description: The Causes array includes more details associated
with the StatusReason failure. Not all StatusReasons
may provide detailed causes.
items:
properties:
field:
description: "The field of the resource that has
caused this error, as named by its JSON serialization.
May include dot and postfix notation for nested
attributes. Arrays are zero-indexed. Fields may
appear more than once in an array of causes due
to fields having multiple errors. Optional. \n
Examples: \"name\" - the field \"name\" on the
current resource \"items[0].name\" - the field
\"name\" on the first array entry in \"items\""
type: string
message:
description: A human-readable description of the
cause of the error. This field may be presented
as-is to a reader.
type: string
reason:
description: A machine-readable description of the
cause of the error. If this value is empty there
is no information available.
type: string
type: object
type: array
group:
description: The group attribute of the resource associated
with the status StatusReason.
type: string
kind:
description: 'The kind attribute of the resource associated
with the status StatusReason. On some operations may
differ from the requested resource Kind. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
name:
description: The name attribute of the resource associated
with the status StatusReason (when there is a single
name which can be described).
type: string
retryAfterSeconds:
description: If specified, the time in seconds before
the operation should be retried. Some errors may indicate
the client must take an alternate action - for those
errors this field may indicate how long to wait before
taking the alternate action.
format: int32
type: integer
uid:
description: 'UID of the resource. (when there is a single
resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
type: string
type: object
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/api-conventions.md#types-kinds'
type: string
message:
description: A human-readable description of the status of
this operation.
type: string
metadata:
description: 'Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
properties:
continue:
description: continue may be set if the user set a limit
on the number of items returned, and indicates that
the server has more data available. The value is opaque
and may be used to issue another request to the endpoint
that served this list to retrieve the next set of available
objects. Continuing a consistent list may not be possible
if the server configuration has changed or more than
a few minutes have passed. The resourceVersion field
returned when using this continue value will be identical
to the value in the first response, unless you have
received this token from an error message.
type: string
resourceVersion:
description: 'String that identifies the server''s internal
version of this object that can be used by clients to
determine when objects have changed. Value must be treated
as opaque by clients and passed unmodified back to the
server. Populated by the system. Read-only. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency'
type: string
selfLink:
description: selfLink is a URL representing this object.
Populated by the system. Read-only.
type: string
type: object
reason:
description: A machine-readable description of why this operation
is in the "Failure" status. If this value is empty there
is no information available. A Reason clarifies an HTTP
status code but does not override it.
type: string
status:
description: 'Status of the operation. One of: "Success" or
"Failure". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status'
type: string
type: object
required:
- pending
type: object
labels:
additionalProperties:
type: string
description: 'Map of string keys and values that can be used to organize
and categorize (scope and select) objects. May match selectors of
replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels'
type: object
managedFields:
description: "ManagedFields maps workflow-id and version to the set
of fields that are managed by that workflow. This is mostly for
internal housekeeping, and users typically shouldn't need to set
or understand this field. A workflow can be the user's name, a controller's
name, or the name of a specific apply path like \"ci-cd\". The set
of fields is always in the version that the workflow used when modifying
the object. \n This field is alpha and can be changed or removed
without notice."
items:
properties:
apiVersion:
description: APIVersion defines the version of this resource
that this field set applies to. The format is "group/version"
just like the top-level APIVersion field. It is necessary
to track the version of a field set because it cannot be automatically
converted.
type: string
fields:
additionalProperties: true
description: Fields identifies a set of fields.
type: object
manager:
description: Manager is an identifier of the workflow managing
these fields.
type: string
operation:
description: Operation is the type of operation which lead to
this ManagedFieldsEntry being created. The only valid values
for this field are 'Apply' and 'Update'.
type: string
time:
description: Time is timestamp of when these fields were set.
It should always be empty if Operation is 'Apply'
format: date-time
type: string
type: object
type: array
name:
description: 'Name must be unique within a namespace. Is required
when creating resources, although some resources may allow a client
to request the generation of an appropriate name automatically.
Name is primarily intended for creation idempotence and configuration
definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
type: string
namespace:
description: "Namespace defines the space within each name must be
unique. An empty namespace is equivalent to the \"default\" namespace,
but \"default\" is the canonical representation. Not all objects
are required to be scoped to a namespace - the value of this field
for those objects will be empty. \n Must be a DNS_LABEL. Cannot
be updated. More info: http://kubernetes.io/docs/user-guide/namespaces"
type: string
ownerReferences:
description: List of objects depended by this object. If ALL objects
in the list have been deleted, this object will be garbage collected.
If this object is managed by a controller, then an entry in this
list will point to this controller, with the controller field set
to true. There cannot be more than one managing controller.
items:
properties:
apiVersion:
description: API version of the referent.
type: string
blockOwnerDeletion:
description: If true, AND if the owner has the "foregroundDeletion"
finalizer, then the owner cannot be deleted from the key-value
store until this reference is removed. Defaults to false.
To set this field, a user needs "delete" permission of the
owner, otherwise 422 (Unprocessable Entity) will be returned.
type: boolean
controller:
description: If true, this reference points to the managing
controller.
type: boolean
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
type: string
uid:
description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
type: string
required:
- apiVersion
- kind
- name
- uid
type: object
type: array
resourceVersion:
description: "An opaque value that represents the internal version
of this object that can be used by clients to determine when objects
have changed. May be used for optimistic concurrency, change detection,
and the watch operation on a resource or set of resources. Clients
must treat these values as opaque and passed unmodified back to
the server. They may only be valid for a particular resource or
set of resources. \n Populated by the system. Read-only. Value must
be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency"
type: string
selfLink:
description: SelfLink is a URL representing this object. Populated
by the system. Read-only.
type: string
uid:
description: "UID is the unique in time and space value for this object.
It is typically generated by the server on successful creation of
a resource and is not allowed to change on PUT operations. \n Populated
by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids"
type: string
type: object
spec:
properties:
collector:
properties:
name:
type: string
namespace:
type: string
required:
- name
type: object
image:
type: string
imagePullPolicy:
type: string
redact:
type: boolean
required:
- collector
type: object
status:
properties:
failed:
items:
type: string
type: array
isServerReady:
type: boolean
running:
items:
type: string
type: array
serverPodName:
type: string
serverPodNamespace:
type: string
serverPodPort:
type: integer
successful:
items:
type: string
type: array
required:
- isServerReady
- serverPodName
- serverPodNamespace
- serverPodPort
- running
- successful
- failed
type: object
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
@@ -3,557 +3,380 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: collectors.troubleshoot.replicated.com
spec:
group: troubleshoot.replicated.com
names:
kind: Collector
listKind: CollectorList
plural: collectors
scope: ""
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: v1beta1
versions:
- name: v1beta1
schema:
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/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/api-conventions.md#types-kinds'
type: string
metadata:
properties:
annotations:
additionalProperties:
type: string
description: 'Annotations is an unstructured key value map stored
with a resource that may be set by external tools to store and retrieve
arbitrary metadata. They are not queryable and should be preserved
when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations'
type: object
clusterName:
description: The name of the cluster which the object belongs to.
This is used to distinguish resources with same name and namespace
in different clusters. This field is not set anywhere right now
and apiserver is going to ignore it if set in create or update request.
type: string
creationTimestamp:
description: "CreationTimestamp is a timestamp representing the server
time when this object was created. It is not guaranteed to be set
in happens-before order across separate operations. Clients may
not set this value. It is represented in RFC3339 form and is in
UTC. \n Populated by the system. Read-only. Null for lists. More
info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
format: date-time
type: string
deletionGracePeriodSeconds:
description: Number of seconds allowed for this object to gracefully
terminate before it will be removed from the system. Only set when
deletionTimestamp is also set. May only be shortened. Read-only.
format: int64
type: integer
deletionTimestamp:
description: "DeletionTimestamp is RFC 3339 date and time at which
this resource will be deleted. This field is set by the server when
a graceful deletion is requested by the user, and is not directly
settable by a client. The resource is expected to be deleted (no
longer visible from resource lists, and not reachable by name) after
the time in this field, once the finalizers list is empty. As long
as the finalizers list contains items, deletion is blocked. Once
the deletionTimestamp is set, this value may not be unset or be
set further into the future, although it may be shortened or the
resource may be deleted prior to this time. For example, a user
may request that a pod is deleted in 30 seconds. The Kubelet will
react by sending a graceful termination signal to the containers
in the pod. After that 30 seconds, the Kubelet will send a hard
termination signal (SIGKILL) to the container and after cleanup,
remove the pod from the API. In the presence of network partitions,
this object may still exist after this timestamp, until an administrator
or automated process can determine the resource is fully terminated.
If not set, graceful deletion of the object has not been requested.
\n Populated by the system when a graceful deletion is requested.
Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
format: date-time
type: string
finalizers:
description: Must be empty before the object is deleted from the registry.
Each entry is an identifier for the responsible component that will
remove the entry from the list. If the deletionTimestamp of the
object is non-nil, entries in this list can only be removed.
items:
type: string
type: array
generateName:
description: "GenerateName is an optional prefix, used by the server,
to generate a unique name ONLY IF the Name field has not been provided.
If this field is used, the name returned to the client will be different
than the name passed. This value will also be combined with a unique
suffix. The provided value has the same validation rules as the
Name field, and may be truncated by the length of the suffix required
to make the value unique on the server. \n If this field is specified
and the generated name exists, the server will NOT return a 409
- instead, it will either return 201 Created or 500 with Reason
ServerTimeout indicating a unique name could not be found in the
time allotted, and the client should retry (optionally after the
time indicated in the Retry-After header). \n Applied only if Name
is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency"
type: string
generation:
description: A sequence number representing a specific generation
of the desired state. Populated by the system. Read-only.
format: int64
type: integer
initializers:
description: "An initializer is a controller which enforces some system
invariant at object creation time. This field is a list of initializers
that have not yet acted on this object. If nil or empty, this object
has been completely initialized. Otherwise, the object is considered
uninitialized and is hidden (in list/watch and get calls) from clients
that haven't explicitly asked to observe uninitialized objects.
\n When an object is created, the system will populate this list
with the current set of initializers. Only privileged users may
set or modify this list. Once it is empty, it may not be modified
further by any user. \n DEPRECATED - initializers are an alpha field
and will be removed in v1.15."
properties:
pending:
description: Pending is a list of initializers that must execute
in order before this object is visible. When the last pending
initializer is removed, and no failing result is set, the initializers
struct will be set to nil and the object is considered as initialized
and visible to all clients.
items:
properties:
name:
description: name of the process that is responsible for
initializing this object.
type: string
required:
- name
type: object
type: array
result:
description: If result is set with the Failure field, the object
will be persisted to storage and then deleted, ensuring that
other clients can observe the deletion.
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/api-conventions.md#resources'
type: string
code:
description: Suggested HTTP return code for this status, 0
if not set.
format: int32
type: integer
details:
description: Extended data associated with the reason. Each
reason may define its own extended details. This field is
optional and the data returned is not guaranteed to conform
to any schema except that defined by the reason type.
properties:
causes:
description: The Causes array includes more details associated
with the StatusReason failure. Not all StatusReasons
may provide detailed causes.
items:
properties:
field:
description: "The field of the resource that has
caused this error, as named by its JSON serialization.
May include dot and postfix notation for nested
attributes. Arrays are zero-indexed. Fields may
appear more than once in an array of causes due
to fields having multiple errors. Optional. \n
Examples: \"name\" - the field \"name\" on the
current resource \"items[0].name\" - the field
\"name\" on the first array entry in \"items\""
type: string
message:
description: A human-readable description of the
cause of the error. This field may be presented
as-is to a reader.
type: string
reason:
description: A machine-readable description of the
cause of the error. If this value is empty there
is no information available.
type: string
type: object
type: array
group:
description: The group attribute of the resource associated
with the status StatusReason.
type: string
kind:
description: 'The kind attribute of the resource associated
with the status StatusReason. On some operations may
differ from the requested resource Kind. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
name:
description: The name attribute of the resource associated
with the status StatusReason (when there is a single
name which can be described).
type: string
retryAfterSeconds:
description: If specified, the time in seconds before
the operation should be retried. Some errors may indicate
the client must take an alternate action - for those
errors this field may indicate how long to wait before
taking the alternate action.
format: int32
type: integer
uid:
description: 'UID of the resource. (when there is a single
resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
type: string
type: object
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/api-conventions.md#types-kinds'
type: string
message:
description: A human-readable description of the status of
this operation.
type: string
metadata:
description: 'Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
properties:
continue:
description: continue may be set if the user set a limit
on the number of items returned, and indicates that
the server has more data available. The value is opaque
and may be used to issue another request to the endpoint
that served this list to retrieve the next set of available
objects. Continuing a consistent list may not be possible
if the server configuration has changed or more than
a few minutes have passed. The resourceVersion field
returned when using this continue value will be identical
to the value in the first response, unless you have
received this token from an error message.
type: string
resourceVersion:
description: 'String that identifies the server''s internal
version of this object that can be used by clients to
determine when objects have changed. Value must be treated
as opaque by clients and passed unmodified back to the
server. Populated by the system. Read-only. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency'
type: string
selfLink:
description: selfLink is a URL representing this object.
Populated by the system. Read-only.
type: string
type: object
reason:
description: A machine-readable description of why this operation
is in the "Failure" status. If this value is empty there
is no information available. A Reason clarifies an HTTP
status code but does not override it.
type: string
status:
description: 'Status of the operation. One of: "Success" or
"Failure". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status'
type: string
type: object
required:
- pending
type: object
labels:
additionalProperties:
type: string
description: 'Map of string keys and values that can be used to organize
and categorize (scope and select) objects. May match selectors of
replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels'
type: object
managedFields:
description: "ManagedFields maps workflow-id and version to the set
of fields that are managed by that workflow. This is mostly for
internal housekeeping, and users typically shouldn't need to set
or understand this field. A workflow can be the user's name, a controller's
name, or the name of a specific apply path like \"ci-cd\". The set
of fields is always in the version that the workflow used when modifying
the object. \n This field is alpha and can be changed or removed
without notice."
items:
properties:
apiVersion:
description: APIVersion defines the version of this resource
that this field set applies to. The format is "group/version"
just like the top-level APIVersion field. It is necessary
to track the version of a field set because it cannot be automatically
converted.
type: string
fields:
additionalProperties: true
description: Fields identifies a set of fields.
type: object
manager:
description: Manager is an identifier of the workflow managing
these fields.
type: string
operation:
description: Operation is the type of operation which lead to
this ManagedFieldsEntry being created. The only valid values
for this field are 'Apply' and 'Update'.
type: string
time:
description: Time is timestamp of when these fields were set.
It should always be empty if Operation is 'Apply'
format: date-time
type: string
type: object
type: array
name:
description: 'Name must be unique within a namespace. Is required
when creating resources, although some resources may allow a client
to request the generation of an appropriate name automatically.
Name is primarily intended for creation idempotence and configuration
definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
type: string
namespace:
description: "Namespace defines the space within each name must be
unique. An empty namespace is equivalent to the \"default\" namespace,
but \"default\" is the canonical representation. Not all objects
are required to be scoped to a namespace - the value of this field
for those objects will be empty. \n Must be a DNS_LABEL. Cannot
be updated. More info: http://kubernetes.io/docs/user-guide/namespaces"
type: string
ownerReferences:
description: List of objects depended by this object. If ALL objects
in the list have been deleted, this object will be garbage collected.
If this object is managed by a controller, then an entry in this
list will point to this controller, with the controller field set
to true. There cannot be more than one managing controller.
items:
properties:
apiVersion:
description: API version of the referent.
type: string
blockOwnerDeletion:
description: If true, AND if the owner has the "foregroundDeletion"
finalizer, then the owner cannot be deleted from the key-value
store until this reference is removed. Defaults to false.
To set this field, a user needs "delete" permission of the
owner, otherwise 422 (Unprocessable Entity) will be returned.
type: boolean
controller:
description: If true, this reference points to the managing
controller.
type: boolean
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
type: string
uid:
description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
type: string
required:
- apiVersion
- kind
- name
- uid
type: object
type: array
resourceVersion:
description: "An opaque value that represents the internal version
of this object that can be used by clients to determine when objects
have changed. May be used for optimistic concurrency, change detection,
and the watch operation on a resource or set of resources. Clients
must treat these values as opaque and passed unmodified back to
the server. They may only be valid for a particular resource or
set of resources. \n Populated by the system. Read-only. Value must
be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency"
type: string
selfLink:
description: SelfLink is a URL representing this object. Populated
by the system. Read-only.
type: string
uid:
description: "UID is the unique in time and space value for this object.
It is typically generated by the server on successful creation of
a resource and is not allowed to change on PUT operations. \n Populated
by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids"
type: string
type: object
spec:
items:
properties:
clusterInfo:
type: object
clusterResources:
type: object
copy:
properties:
collectorName:
type: string
containerName:
type: string
containerPath:
type: string
namespace:
type: string
selector:
items:
type: string
type: array
required:
- selector
- namespace
- containerPath
type: object
exec:
properties:
args:
items:
type: string
type: array
collectorName:
type: string
command:
items:
type: string
type: array
containerName:
type: string
namespace:
type: string
selector:
items:
type: string
type: array
timeout:
type: string
required:
- selector
- namespace
type: object
http:
properties:
collectorName:
type: string
get:
properties:
headers:
additionalProperties:
type: string
type: object
insecureSkipVerify:
type: boolean
url:
type: string
required:
- url
type: object
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
limits:
properties:
maxAge:
type: string
maxLines:
format: int64
type: integer
type: object
namespace:
type: string
selector:
items:
type: string
type: array
required:
- selector
type: object
run:
properties:
args:
items:
type: string
type: array
collectorName:
type: string
command:
items:
type: string
type: array
image:
type: string
imagePullPolicy:
type: string
namespace:
type: string
timeout:
type: string
required:
- namespace
- image
type: object
secret:
properties:
collectorName:
type: string
includeValue:
type: boolean
key:
type: string
name:
type: string
namespace:
type: string
required:
- name
type: object
type: object
type: array
status:
type: object
type: object
served: true
storage: true
status:
@@ -3,468 +3,115 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.4
creationTimestamp: null
name: preflightjobs.troubleshoot.replicated.com
spec:
group: troubleshoot.replicated.com
names:
kind: PreflightJob
listKind: PreflightJobList
plural: preflightjobs
scope: ""
singular: preflightjob
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
description: PreflightJob is the Schema for the preflightjobs 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: PreflightJobSpec defines the desired state of PreflightJob
properties:
collectorImage:
type: string
collectorImagePullPolicy:
type: string
imagePullPolicy:
type: string
preflight:
properties:
name:
type: string
namespace:
type: string
required:
- name
type: object
preflightImage:
type: string
required:
- preflight
type: object
status:
description: PreflightJobStatus defines the observed state of PreflightJob
properties:
analyzersFailed:
items:
type: string
type: array
analyzersRunning:
items:
type: string
type: array
analyzersSuccessful:
items:
type: string
type: array
collectorsFailed:
items:
type: string
type: array
collectorsRunning:
items:
type: string
type: array
collectorsSuccessful:
items:
type: string
type: array
isAnalyzersComplete:
type: boolean
isServerReady:
type: boolean
serverPodName:
type: string
serverPodNamespace:
type: string
serverPodPort:
type: integer
required:
- analyzersFailed
- analyzersRunning
- analyzersSuccessful
- collectorsFailed
- collectorsRunning
- collectorsSuccessful
- isAnalyzersComplete
- isServerReady
- serverPodName
- serverPodNamespace
- serverPodPort
type: object
type: object
version: v1beta1
versions:
- name: v1beta1
schema:
openAPIV3Schema:
description: PreflightJob is the Schema for the preflightjobs 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/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/api-conventions.md#types-kinds'
type: string
metadata:
properties:
annotations:
additionalProperties:
type: string
description: 'Annotations is an unstructured key value map stored
with a resource that may be set by external tools to store and retrieve
arbitrary metadata. They are not queryable and should be preserved
when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations'
type: object
clusterName:
description: The name of the cluster which the object belongs to.
This is used to distinguish resources with same name and namespace
in different clusters. This field is not set anywhere right now
and apiserver is going to ignore it if set in create or update request.
type: string
creationTimestamp:
description: "CreationTimestamp is a timestamp representing the server
time when this object was created. It is not guaranteed to be set
in happens-before order across separate operations. Clients may
not set this value. It is represented in RFC3339 form and is in
UTC. \n Populated by the system. Read-only. Null for lists. More
info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
format: date-time
type: string
deletionGracePeriodSeconds:
description: Number of seconds allowed for this object to gracefully
terminate before it will be removed from the system. Only set when
deletionTimestamp is also set. May only be shortened. Read-only.
format: int64
type: integer
deletionTimestamp:
description: "DeletionTimestamp is RFC 3339 date and time at which
this resource will be deleted. This field is set by the server when
a graceful deletion is requested by the user, and is not directly
settable by a client. The resource is expected to be deleted (no
longer visible from resource lists, and not reachable by name) after
the time in this field, once the finalizers list is empty. As long
as the finalizers list contains items, deletion is blocked. Once
the deletionTimestamp is set, this value may not be unset or be
set further into the future, although it may be shortened or the
resource may be deleted prior to this time. For example, a user
may request that a pod is deleted in 30 seconds. The Kubelet will
react by sending a graceful termination signal to the containers
in the pod. After that 30 seconds, the Kubelet will send a hard
termination signal (SIGKILL) to the container and after cleanup,
remove the pod from the API. In the presence of network partitions,
this object may still exist after this timestamp, until an administrator
or automated process can determine the resource is fully terminated.
If not set, graceful deletion of the object has not been requested.
\n Populated by the system when a graceful deletion is requested.
Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
format: date-time
type: string
finalizers:
description: Must be empty before the object is deleted from the registry.
Each entry is an identifier for the responsible component that will
remove the entry from the list. If the deletionTimestamp of the
object is non-nil, entries in this list can only be removed.
items:
type: string
type: array
generateName:
description: "GenerateName is an optional prefix, used by the server,
to generate a unique name ONLY IF the Name field has not been provided.
If this field is used, the name returned to the client will be different
than the name passed. This value will also be combined with a unique
suffix. The provided value has the same validation rules as the
Name field, and may be truncated by the length of the suffix required
to make the value unique on the server. \n If this field is specified
and the generated name exists, the server will NOT return a 409
- instead, it will either return 201 Created or 500 with Reason
ServerTimeout indicating a unique name could not be found in the
time allotted, and the client should retry (optionally after the
time indicated in the Retry-After header). \n Applied only if Name
is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency"
type: string
generation:
description: A sequence number representing a specific generation
of the desired state. Populated by the system. Read-only.
format: int64
type: integer
initializers:
description: "An initializer is a controller which enforces some system
invariant at object creation time. This field is a list of initializers
that have not yet acted on this object. If nil or empty, this object
has been completely initialized. Otherwise, the object is considered
uninitialized and is hidden (in list/watch and get calls) from clients
that haven't explicitly asked to observe uninitialized objects.
\n When an object is created, the system will populate this list
with the current set of initializers. Only privileged users may
set or modify this list. Once it is empty, it may not be modified
further by any user. \n DEPRECATED - initializers are an alpha field
and will be removed in v1.15."
properties:
pending:
description: Pending is a list of initializers that must execute
in order before this object is visible. When the last pending
initializer is removed, and no failing result is set, the initializers
struct will be set to nil and the object is considered as initialized
and visible to all clients.
items:
properties:
name:
description: name of the process that is responsible for
initializing this object.
type: string
required:
- name
type: object
type: array
result:
description: If result is set with the Failure field, the object
will be persisted to storage and then deleted, ensuring that
other clients can observe the deletion.
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/api-conventions.md#resources'
type: string
code:
description: Suggested HTTP return code for this status, 0
if not set.
format: int32
type: integer
details:
description: Extended data associated with the reason. Each
reason may define its own extended details. This field is
optional and the data returned is not guaranteed to conform
to any schema except that defined by the reason type.
properties:
causes:
description: The Causes array includes more details associated
with the StatusReason failure. Not all StatusReasons
may provide detailed causes.
items:
properties:
field:
description: "The field of the resource that has
caused this error, as named by its JSON serialization.
May include dot and postfix notation for nested
attributes. Arrays are zero-indexed. Fields may
appear more than once in an array of causes due
to fields having multiple errors. Optional. \n
Examples: \"name\" - the field \"name\" on the
current resource \"items[0].name\" - the field
\"name\" on the first array entry in \"items\""
type: string
message:
description: A human-readable description of the
cause of the error. This field may be presented
as-is to a reader.
type: string
reason:
description: A machine-readable description of the
cause of the error. If this value is empty there
is no information available.
type: string
type: object
type: array
group:
description: The group attribute of the resource associated
with the status StatusReason.
type: string
kind:
description: 'The kind attribute of the resource associated
with the status StatusReason. On some operations may
differ from the requested resource Kind. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
name:
description: The name attribute of the resource associated
with the status StatusReason (when there is a single
name which can be described).
type: string
retryAfterSeconds:
description: If specified, the time in seconds before
the operation should be retried. Some errors may indicate
the client must take an alternate action - for those
errors this field may indicate how long to wait before
taking the alternate action.
format: int32
type: integer
uid:
description: 'UID of the resource. (when there is a single
resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
type: string
type: object
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/api-conventions.md#types-kinds'
type: string
message:
description: A human-readable description of the status of
this operation.
type: string
metadata:
description: 'Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
properties:
continue:
description: continue may be set if the user set a limit
on the number of items returned, and indicates that
the server has more data available. The value is opaque
and may be used to issue another request to the endpoint
that served this list to retrieve the next set of available
objects. Continuing a consistent list may not be possible
if the server configuration has changed or more than
a few minutes have passed. The resourceVersion field
returned when using this continue value will be identical
to the value in the first response, unless you have
received this token from an error message.
type: string
resourceVersion:
description: 'String that identifies the server''s internal
version of this object that can be used by clients to
determine when objects have changed. Value must be treated
as opaque by clients and passed unmodified back to the
server. Populated by the system. Read-only. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency'
type: string
selfLink:
description: selfLink is a URL representing this object.
Populated by the system. Read-only.
type: string
type: object
reason:
description: A machine-readable description of why this operation
is in the "Failure" status. If this value is empty there
is no information available. A Reason clarifies an HTTP
status code but does not override it.
type: string
status:
description: 'Status of the operation. One of: "Success" or
"Failure". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status'
type: string
type: object
required:
- pending
type: object
labels:
additionalProperties:
type: string
description: 'Map of string keys and values that can be used to organize
and categorize (scope and select) objects. May match selectors of
replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels'
type: object
managedFields:
description: "ManagedFields maps workflow-id and version to the set
of fields that are managed by that workflow. This is mostly for
internal housekeeping, and users typically shouldn't need to set
or understand this field. A workflow can be the user's name, a controller's
name, or the name of a specific apply path like \"ci-cd\". The set
of fields is always in the version that the workflow used when modifying
the object. \n This field is alpha and can be changed or removed
without notice."
items:
properties:
apiVersion:
description: APIVersion defines the version of this resource
that this field set applies to. The format is "group/version"
just like the top-level APIVersion field. It is necessary
to track the version of a field set because it cannot be automatically
converted.
type: string
fields:
additionalProperties: true
description: Fields identifies a set of fields.
type: object
manager:
description: Manager is an identifier of the workflow managing
these fields.
type: string
operation:
description: Operation is the type of operation which lead to
this ManagedFieldsEntry being created. The only valid values
for this field are 'Apply' and 'Update'.
type: string
time:
description: Time is timestamp of when these fields were set.
It should always be empty if Operation is 'Apply'
format: date-time
type: string
type: object
type: array
name:
description: 'Name must be unique within a namespace. Is required
when creating resources, although some resources may allow a client
to request the generation of an appropriate name automatically.
Name is primarily intended for creation idempotence and configuration
definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
type: string
namespace:
description: "Namespace defines the space within each name must be
unique. An empty namespace is equivalent to the \"default\" namespace,
but \"default\" is the canonical representation. Not all objects
are required to be scoped to a namespace - the value of this field
for those objects will be empty. \n Must be a DNS_LABEL. Cannot
be updated. More info: http://kubernetes.io/docs/user-guide/namespaces"
type: string
ownerReferences:
description: List of objects depended by this object. If ALL objects
in the list have been deleted, this object will be garbage collected.
If this object is managed by a controller, then an entry in this
list will point to this controller, with the controller field set
to true. There cannot be more than one managing controller.
items:
properties:
apiVersion:
description: API version of the referent.
type: string
blockOwnerDeletion:
description: If true, AND if the owner has the "foregroundDeletion"
finalizer, then the owner cannot be deleted from the key-value
store until this reference is removed. Defaults to false.
To set this field, a user needs "delete" permission of the
owner, otherwise 422 (Unprocessable Entity) will be returned.
type: boolean
controller:
description: If true, this reference points to the managing
controller.
type: boolean
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
type: string
uid:
description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
type: string
required:
- apiVersion
- kind
- name
- uid
type: object
type: array
resourceVersion:
description: "An opaque value that represents the internal version
of this object that can be used by clients to determine when objects
have changed. May be used for optimistic concurrency, change detection,
and the watch operation on a resource or set of resources. Clients
must treat these values as opaque and passed unmodified back to
the server. They may only be valid for a particular resource or
set of resources. \n Populated by the system. Read-only. Value must
be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency"
type: string
selfLink:
description: SelfLink is a URL representing this object. Populated
by the system. Read-only.
type: string
uid:
description: "UID is the unique in time and space value for this object.
It is typically generated by the server on successful creation of
a resource and is not allowed to change on PUT operations. \n Populated
by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids"
type: string
type: object
spec:
properties:
collectorImage:
type: string
collectorImagePullPolicy:
type: string
imagePullPolicy:
type: string
preflight:
properties:
name:
type: string
namespace:
type: string
required:
- name
type: object
preflightImage:
type: string
required:
- preflight
type: object
status:
properties:
analyzersFailed:
items:
type: string
type: array
analyzersRunning:
items:
type: string
type: array
analyzersSuccessful:
items:
type: string
type: array
collectorsFailed:
items:
type: string
type: array
collectorsRunning:
items:
type: string
type: array
collectorsSuccessful:
items:
type: string
type: array
isAnalyzersComplete:
type: boolean
isServerReady:
type: boolean
serverPodName:
type: string
serverPodNamespace:
type: string
serverPodPort:
type: integer
required:
- isServerReady
- serverPodName
- serverPodNamespace
- serverPodPort
- collectorsRunning
- collectorsSuccessful
- collectorsFailed
- isAnalyzersComplete
- analyzersRunning
- analyzersSuccessful
- analyzersFailed
type: object
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
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.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
-49
View File
@@ -1,49 +0,0 @@
# Adds namespace to all resources.
namespace: troubleshoot-system
# Value of this field is prepended to the
# names of all resources, e.g. a deployment named
# "wordpress" becomes "alices-wordpress".
# Note that it should also match with the prefix (text before '-') of the namespace
# field above.
namePrefix: troubleshoot-
# Labels to add to all resources and selectors.
#commonLabels:
# someName: someValue
# Each entry in this list must resolve to an existing
# resource definition in YAML. These are the resource
# files that kustomize reads, modifies and emits as a
# YAML string, with resources separated by document
# markers ("---").
resources:
- ../rbac/rbac_role.yaml
- ../rbac/rbac_role_binding.yaml
- ../manager/manager.yaml
# Comment the following 3 lines if you want to disable
# the auth proxy (https://github.com/brancz/kube-rbac-proxy)
# which protects your /metrics endpoint.
- ../rbac/auth_proxy_service.yaml
- ../rbac/auth_proxy_role.yaml
- ../rbac/auth_proxy_role_binding.yaml
patches:
- manager_image_patch.yaml
# Protect the /metrics endpoint by putting it behind auth.
# Only one of manager_auth_proxy_patch.yaml and
# manager_prometheus_metrics_patch.yaml should be enabled.
- manager_auth_proxy_patch.yaml
# If you want your controller-manager to expose the /metrics
# endpoint w/o any authn/z, uncomment the following line and
# comment manager_auth_proxy_patch.yaml.
# Only one of manager_auth_proxy_patch.yaml and
# manager_prometheus_metrics_patch.yaml should be enabled.
#- manager_prometheus_metrics_patch.yaml
vars:
- name: WEBHOOK_SECRET_NAME
objref:
kind: Secret
name: webhook-server-secret
apiVersion: v1
@@ -1,24 +0,0 @@
# This patch inject a sidecar container which is a HTTP proxy for the controller manager,
# it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: kube-rbac-proxy
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.4.0
args:
- "--secure-listen-address=0.0.0.0:8443"
- "--upstream=http://127.0.0.1:8089/"
- "--logtostderr=true"
- "--v=10"
ports:
- containerPort: 8443
name: https
- name: manager
args:
- "--metrics-addr=127.0.0.1:8088"
-12
View File
@@ -1,12 +0,0 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
# Change the value of image field below to your controller image URL
- image: IMAGE_URL
name: manager
@@ -1,19 +0,0 @@
# This patch enables Prometheus scraping for the manager pod.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: controller-manager
namespace: system
spec:
template:
metadata:
annotations:
prometheus.io/scrape: 'true'
spec:
containers:
# Expose the prometheus metrics on default port
- name: manager
ports:
- containerPort: 8080
name: metrics
protocol: TCP
-83
View File
@@ -1,83 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
control-plane: controller-manager
controller-tools.k8s.io: "1.0"
name: system
---
apiVersion: v1
kind: Service
metadata:
name: controller-manager-service
namespace: system
labels:
control-plane: controller-manager
controller-tools.k8s.io: "1.0"
spec:
selector:
control-plane: controller-manager
controller-tools.k8s.io: "1.0"
ports:
- port: 443
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: controller-manager
namespace: system
labels:
control-plane: controller-manager
controller-tools.k8s.io: "1.0"
spec:
selector:
matchLabels:
control-plane: controller-manager
controller-tools.k8s.io: "1.0"
serviceName: controller-manager-service
template:
metadata:
labels:
control-plane: controller-manager
controller-tools.k8s.io: "1.0"
spec:
containers:
- command:
- /manager
image: controller:latest
imagePullPolicy: Always
name: manager
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: SECRET_NAME
value: $(WEBHOOK_SECRET_NAME)
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
ports:
- containerPort: 9876
name: webhook-server
protocol: TCP
volumeMounts:
- mountPath: /tmp/cert
name: cert
readOnly: true
terminationGracePeriodSeconds: 10
volumes:
- name: cert
secret:
defaultMode: 420
secretName: webhook-server-secret
---
apiVersion: v1
kind: Secret
metadata:
name: webhook-server-secret
namespace: system
-13
View File
@@ -1,13 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: proxy-role
rules:
- apiGroups: ["authentication.k8s.io"]
resources:
- tokenreviews
verbs: ["create"]
- apiGroups: ["authorization.k8s.io"]
resources:
- subjectaccessreviews
verbs: ["create"]
-12
View File
@@ -1,12 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: proxy-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: proxy-role
subjects:
- kind: ServiceAccount
name: default
namespace: system
-20
View File
@@ -1,20 +0,0 @@
apiVersion: v1
kind: Service
metadata:
annotations:
prometheus.io/port: "8443"
prometheus.io/scheme: https
prometheus.io/scrape: "true"
labels:
control-plane: controller-manager
controller-tools.k8s.io: "1.0"
name: controller-manager-metrics-service
namespace: system
spec:
ports:
- name: https
port: 8443
targetPort: https
selector:
control-plane: controller-manager
controller-tools.k8s.io: "1.0"
-283
View File
@@ -1,283 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: manager-role
rules:
- apiGroups:
- apps
resources:
- deployments
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- apps
resources:
- deployments/status
verbs:
- get
- update
- patch
- apiGroups:
- troubleshoot.replicated.com
resources:
- analyzers
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- troubleshoot.replicated.com
resources:
- analyzers/status
verbs:
- get
- update
- patch
- apiGroups:
- apps
resources:
- deployments
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- apps
resources:
- deployments/status
verbs:
- get
- update
- patch
- apiGroups:
- troubleshoot.replicated.com
resources:
- analyzerjobs
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- troubleshoot.replicated.com
resources:
- analyzerjobs/status
verbs:
- get
- update
- patch
- apiGroups:
- apps
resources:
- deployments
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- apps
resources:
- deployments/status
verbs:
- get
- update
- patch
- apiGroups:
- troubleshoot.replicated.com
resources:
- collectors
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- troubleshoot.replicated.com
resources:
- collectors/status
verbs:
- get
- update
- patch
- apiGroups:
- apps
resources:
- deployments
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- apps
resources:
- deployments/status
verbs:
- get
- update
- patch
- apiGroups:
- troubleshoot.replicated.com
resources:
- collectorjobs
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- troubleshoot.replicated.com
resources:
- collectorjobs/status
verbs:
- get
- update
- patch
- apiGroups:
- apps
resources:
- deployments
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- apps
resources:
- deployments/status
verbs:
- get
- update
- patch
- apiGroups:
- troubleshoot.replicated.com
resources:
- preflights
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- troubleshoot.replicated.com
resources:
- preflights/status
verbs:
- get
- update
- patch
- apiGroups:
- apps
resources:
- deployments
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- apps
resources:
- deployments/status
verbs:
- get
- update
- patch
- apiGroups:
- troubleshoot.replicated.com
resources:
- preflightjobs
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- troubleshoot.replicated.com
resources:
- preflightjobs/status
verbs:
- get
- update
- patch
- apiGroups:
- admissionregistration.k8s.io
resources:
- mutatingwebhookconfigurations
- validatingwebhookconfigurations
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
-13
View File
@@ -1,13 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
creationTimestamp: null
name: manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: manager-role
subjects:
- kind: ServiceAccount
name: default
namespace: system
@@ -1,9 +0,0 @@
apiVersion: troubleshoot.replicated.com/v1beta1
kind: AnalyzerJob
metadata:
labels:
controller-tools.k8s.io: "1.0"
name: analyzerjob-sample
spec:
# Add fields here
foo: bar
@@ -1,78 +0,0 @@
apiVersion: troubleshoot.replicated.com/v1beta1
kind: Collector
metadata:
name: collector-sample
spec:
- clusterInfo: {}
- clusterResources: {}
# - secret:
# name: illmannered-cricket-mysql
# namespace: default
# key: mysql-password
# - logs:
# selector:
# - name=nginx-ingress-microk8s
# namespace: default
# limits:
# maxAge: 30d
# maxLines: 10000
# - run:
# collectorName: ping-google
# namespace: default
# image: flungo/netutils
# command: ["ping"]
# args: ["www.google.com"]
# timeout: 5s
- exec:
collectorName: mysql-vars
selector:
- app=mysql
namespace: default
command: ["mysql"]
args: ["-ureplicated", "-ppassword", "-e", "show processlist"]
timeout: 60m
- exec:
collectorName: hosts
selector:
- app=graphql-api
namespace: default
command: ["cat"]
args: ["/etc/hosts"]
timeout: 60m
- exec:
collectorName: broken
selector:
- app=graphql-api
namespace: default
command: ["cat"]
args: ["/etc/hostdasddsda"]
timeout: 60m
# - copy:
# selector:
# - app=illmannered-cricket-mysql
# namespace: default
# containerPath: /etc/hosts
- http:
collectorName: test-get
get:
url: https://api.staging.replicated.com/market/v1/echo/ip
insecureSkipVerify: false
headers: {}
- http:
collectorName: test-post
post:
url: http://httpbin.org/headers
insecureSkipVerify: false
headers:
X-Custom-Header: "post-request"
- http:
collectorName: test-put
put:
url: http://httpbin.org/anything
insecureSkipVerify: false
headers:
X-Custom-Header: "put-request"
- http:
collectorName: test-broken
put:
url: ""
@@ -1,10 +0,0 @@
apiVersion: troubleshoot.replicated.com/v1beta1
kind: CollectorJob
metadata:
name: collectorjob-sample
spec:
image: localhost:32000/troubleshoot:alpha
imagePullPolicy: Always
collector:
name: collector-sample
namespace: default
@@ -1,10 +0,0 @@
apiVersion: troubleshoot.replicated.com/v1beta1
kind: PreflightJob
metadata:
name: preflightjob-sample
spec:
image: localhost:32000/troubleshoot:alpha
imagePullPolicy: Always
preflight:
name: preflight-sample
namespace: default
@@ -1,4 +1,4 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Analyzer
metadata:
labels:
@@ -0,0 +1,79 @@
apiVersion: troubleshoot.sh/v1beta2
kind: Collector
metadata:
name: collector-sample
spec:
collectors:
- clusterInfo: {}
- clusterResources: {}
# - secret:
# name: illmannered-cricket-mysql
# namespace: default
# key: mysql-password
# - logs:
# selector:
# - name=nginx-ingress-microk8s
# namespace: default
# limits:
# maxAge: 30d
# maxLines: 10000
# - run:
# collectorName: ping-google
# namespace: default
# image: flungo/netutils
# command: ["ping"]
# args: ["www.google.com"]
# timeout: 5s
- exec:
collectorName: mysql-vars
selector:
- app=mysql
namespace: default
command: ["mysql"]
args: ["-ureplicated", "-ppassword", "-e", "show processlist"]
timeout: 60m
- exec:
collectorName: hosts
selector:
- app=graphql-api
namespace: default
command: ["cat"]
args: ["/etc/hosts"]
timeout: 60m
- exec:
collectorName: broken
selector:
- app=graphql-api
namespace: default
command: ["cat"]
args: ["/etc/hostdasddsda"]
timeout: 60m
# - copy:
# selector:
# - app=illmannered-cricket-mysql
# namespace: default
# containerPath: /etc/hosts
- http:
collectorName: test-get
get:
url: https://api.staging.replicated.com/market/v1/echo/ip
insecureSkipVerify: false
headers: {}
- http:
collectorName: test-post
post:
url: http://httpbin.org/headers
insecureSkipVerify: false
headers:
X-Custom-Header: "post-request"
- http:
collectorName: test-put
put:
url: http://httpbin.org/anything
insecureSkipVerify: false
headers:
X-Custom-Header: "put-request"
- http:
collectorName: test-broken
put:
url: ""
@@ -1,4 +1,4 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: support-io
View File
-105
View File
@@ -1,105 +0,0 @@
project_name: troubleshoot
release:
github:
owner: replicatedhq
name: troubleshoot
builds:
- id: collector
goos:
- linux
goarch:
- amd64
env:
- CGO_ENABLED=0
main: cmd/collector/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: collector
hooks: {}
- id: preflight
goos:
- linux
goarch:
- amd64
env:
- CGO_ENABLED=0
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: troubleshoot
goos:
- linux
goarch:
- amd64
env:
- CGO_ENABLED=0
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: troubleshoot
hooks: {}
- id: manager
goos:
- linux
goarch:
- amd64
env:
- CGO_ENABLED=0
main: cmd/manager/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: manager
hooks: {}
archives:
- id: tar
format: tar.gz
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{.Arm }}{{ end }}-alpha'
files:
- licence*
- LICENCE*
- license*
- LICENSE*
- readme*
- README*
- changelog*
- CHANGELOG*
dockers:
- dockerfile: ./deploy/Dockerfile.troubleshoot
image_templates:
- "replicated/troubleshoot:alpha"
binaries:
- collector
- troubleshoot
- preflight
- dockerfile: ./deploy/Dockerfile.troubleshoot
image_templates:
- "replicated/preflight:alpha"
binaries:
- collector
- troubleshoot
- preflight
- dockerfile: ./deploy/Dockerfile.manager
image_templates:
- "replicated/troubleshoot-manager:alpha"
binaries:
- manager
snapshot:
name_template: SNAPSHOT-{{ .Commit }}
-109
View File
@@ -1,109 +0,0 @@
project_name: troubleshoot
release:
github:
owner: replicatedhq
name: troubleshoot
builds:
- id: collector
goos:
- linux
goarch:
- amd64
env:
- CGO_ENABLED=0
main: cmd/collector/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: collector
hooks: {}
- id: preflight
goos:
- linux
- windows
- darwin
goarch:
- amd64
env:
- CGO_ENABLED=0
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: troubleshoot
goos:
- linux
- windows
- darwin
goarch:
- amd64
env:
- CGO_ENABLED=0
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: troubleshoot
hooks: {}
- id: manager
goos:
- linux
goarch:
- amd64
env:
- CGO_ENABLED=0
main: cmd/manager/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: manager
hooks: {}
archives:
- id: tar
format: tar.gz
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{.Arm }}{{ end }}-alpha'
files:
- licence*
- LICENCE*
- license*
- LICENSE*
- readme*
- README*
- changelog*
- CHANGELOG*
dockers:
- dockerfile: ./deploy/Dockerfile.troubleshoot
image_templates:
- "replicated/troubleshoot:alpha"
binaries:
- collector
- troubleshoot
- preflight
- dockerfile: ./deploy/Dockerfile.troubleshoot
image_templates:
- "replicated/preflight:alpha"
binaries:
- collector
- troubleshoot
- preflight
- dockerfile: ./deploy/Dockerfile.manager
image_templates:
- "replicated/troubleshoot-manager:alpha"
binaries:
- manager
snapshot:
name_template: SNAPSHOT-{{ .Commit }}
@@ -4,21 +4,17 @@ release:
owner: replicatedhq
name: troubleshoot
builds:
- id: collector
- id: so
goos:
- linux
goarch:
- amd64
env:
- CGO_ENABLED=0
main: cmd/collector/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: collector
- CGO_ENABLED=1
- GO111MODULE=on
main: ffi/main.go
flags: -buildmode=c-shared
binary: troubleshoot.so
hooks: {}
- id: preflight
goos:
@@ -38,7 +34,7 @@ builds:
flags: -tags netgo -installsuffix netgo
binary: preflight
hooks: {}
- id: troubleshoot
- id: support-bundle
goos:
- linux
- darwin
@@ -54,25 +50,14 @@ builds:
-X github.com/replicatedhq/troubleshoot/pkg/version.buildTime={{.Date}}
-extldflags "-static"
flags: -tags netgo -installsuffix netgo
binary: troubleshoot
hooks: {}
- id: manager
goos:
- linux
goarch:
- amd64
env:
- CGO_ENABLED=0
main: cmd/manager/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: manager
binary: support-bundle
hooks: {}
archives:
- id: so
builds:
- so
format: tar.gz
name_template: 'troubleshoot.so_{{ .Os }}_{{ .Arch }}'
- id: preflight
builds:
- preflight
@@ -80,7 +65,7 @@ archives:
format_overrides:
- goos: windows
format: zip
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{.Arm }}{{ end }}-{{ .Major }}.{{ .Minor }}.{{ .Patch }}'
name_template: 'preflight_{{ .Os }}_{{ .Arch }}'
files:
- licence*
- LICENCE*
@@ -90,14 +75,14 @@ archives:
- README*
- changelog*
- CHANGELOG*
- id: troubleshoot
- id: support-bundle
builds:
- troubleshoot
- support-bundle
format: tar.gz
format_overrides:
- goos: windows
format: zip
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{.Arm }}{{ end }}-{{ .Major }}.{{ .Minor }}.{{ .Patch }}'
name_template: 'support-bundle_{{ .Os }}_{{ .Arch }}'
files:
- licence*
- LICENCE*
@@ -115,8 +100,7 @@ dockers:
- "replicated/troubleshoot:{{ .Major }}.{{ .Minor }}"
- "replicated/troubleshoot:{{ .Major }}.{{ .Minor }}.{{ .Patch }}"
binaries:
- collector
- troubleshoot
- support-bundle
- preflight
- dockerfile: ./deploy/Dockerfile.troubleshoot
image_templates:
@@ -125,14 +109,5 @@ dockers:
- "replicated/preflight:{{ .Major }}.{{ .Minor }}"
- "replicated/preflight:{{ .Major }}.{{ .Minor }}.{{ .Patch }}"
binaries:
- collector
- troubleshoot
- preflight
- dockerfile: ./deploy/Dockerfile.manager
image_templates:
- "replicated/troubleshoot-manager:latest"
- "replicated/troubleshoot-manager:{{ .Major }}"
- "replicated/troubleshoot-manager:{{ .Major }}.{{ .Minor }}"
- "replicated/troubleshoot-manager:{{ .Major }}.{{ .Minor }}.{{ .Patch }}"
binaries:
- manager
- support-bundle
- preflight
-4
View File
@@ -1,4 +0,0 @@
FROM ubuntu:latest
WORKDIR /
COPY manager /manager
ENTRYPOINT ["/manager"]
+1 -2
View File
@@ -5,8 +5,7 @@ RUN apt-get -qq update \
&& apt-get -qq -y install \
ca-certificates
COPY troubleshoot /troubleshoot/troubleshoot
COPY collector /troubleshoot/collector
COPY support-bundle /troubleshoot/support-bundle
COPY preflight /troubleshoot/preflight
ENV PATH="/troubleshoot:${PATH}"
+36 -34
View File
@@ -3,41 +3,60 @@ kind: Plugin
metadata:
name: preflight
spec:
version: "v0.9.1"
version: {{ .TagName }}
platforms:
- selector:
matchLabels:
os: linux
arch: amd64
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.9.1/preflight_0.9.1_linux_amd64-0.9.1.tar.gz
sha256: "2a50b896b2bd0fdd10ae0bc63ecfe417da1de2ab81255990958fa433d123ef85"
{{addURIAndSha "https://github.com/replicatedhq/troubleshoot/releases/download/{{ .TagName }}/preflight_linux_amd64.tar.gz" .TagName }}
files:
- from: "./preflight"
to: "."
bin: "preflight"
- from: preflight
to: .
- from: LICENSE
to: .
bin: preflight
- selector:
matchLabels:
os: darwin
arch: amd64
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.9.1/preflight_0.9.1_darwin_amd64-0.9.1.tar.gz
sha256: "e8d99442c12606f062edce164c7a503aca316928b796708a08f8ae3fc8e3444d"
{{addURIAndSha "https://github.com/replicatedhq/troubleshoot/releases/download/{{ .TagName }}/preflight_darwin_amd64.tar.gz" .TagName }}
files:
- from: "./preflight"
to: "."
bin: "preflight"
- from: preflight
to: .
- from: LICENSE
to: .
bin: preflight
- selector:
matchLabels:
os: windows
arch: amd64
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.9.1/preflight_0.9.1_windows_amd64-0.9.1.zip
sha256: "ac1940826bda4025cf2e8be1627f3ba9ef0df0ee259dba388cb7a167c617dfd4"
{{addURIAndSha "https://github.com/replicatedhq/troubleshoot/releases/download/{{ .TagName }}/preflight_windows_amd64.zip" .TagName }}
files:
- from: "/preflight.exe"
to: "."
bin: "preflight.exe"
- from: preflight.exe
to: .
- from: LICENSE
to: .
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>
@@ -56,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/
+74
View File
@@ -0,0 +1,74 @@
apiVersion: krew.googlecontainertools.github.com/v1alpha2
kind: Plugin
metadata:
name: support-bundle
spec:
version: {{ .TagName }}
platforms:
- selector:
matchLabels:
os: linux
arch: amd64
{{addURIAndSha "https://github.com/replicatedhq/troubleshoot/releases/download/{{ .TagName }}/support-bundle_linux_amd64.tar.gz" .TagName }}
files:
- from: support-bundle
to: .
- from: LICENSE
to: .
bin: support-bundle
- selector:
matchLabels:
os: darwin
arch: amd64
{{addURIAndSha "https://github.com/replicatedhq/troubleshoot/releases/download/{{ .TagName }}/support-bundle_darwin_amd64.tar.gz" .TagName }}
files:
- from: support-bundle
to: .
- from: LICENSE
to: .
bin: support-bundle
- selector:
matchLabels:
os: windows
arch: amd64
{{addURIAndSha "https://github.com/replicatedhq/troubleshoot/releases/download/{{ .TagName }}/support-bundle_windows_amd64.zip" .TagName }}
files:
- from: support-bundle.exe
to: .
- from: LICENSE
to: .
bin: support-bundle.exe
shortDescription: Creates support bundles for off-cluster analysis
homepage: https://github.com/replicatedhq/troubleshoot
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>
where <uri> references a set of application collectors
For example:
$ kubectl support-bundle https://troubleshoot.replicated.com
For additional options:
$ kubectl support-bundle --help
Documentation:
Full documentation on this plugin is available at:
https://help.replicated.com/docs/troubleshoot/kubernetes/support-bundle/overview/
For application developers writing collectors and analyzers:
https://help.replicated.com/docs/troubleshoot/kubernetes/collectors/defining-collectors/
-39
View File
@@ -1,39 +0,0 @@
apiVersion: krew.googlecontainertools.github.com/v1alpha2
kind: Plugin
metadata:
name: troubleshoot
spec:
version: "v0.1.0"
platforms:
- selector:
matchLabels:
os: linux
arch: amd64
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.1.0/troubleshoot_0.1.0_linux_amd64-alpha.tar.gz
sha256: "fcfee994959a4924d0f70817a4b40d2b4df9e21a5a2a106d340784ce77fbbc15"
files:
- from: "/kubectl-troubleshoot"
to: "."
bin: "./kubectl-troubleshoot"
- selector:
matchLabels:
os: darwin
arch: amd64
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.1.0/troubleshoot_0.1.0_darwin_amd64-alpha.tar.gz
sha256: "fcfee994959a4924d0f70817a4b40d2b4df9e21a5a2a106d340784ce77fbbc15"
files:
- from: "/kubectl-troubleshoot"
to: "."
bin: "./kubectl-troubleshoot"
- selector:
matchLabels:
os: windows
arch: amd64
uri: https://github.com/replicatedhq/troubleshoot/releases/download/v0.1.0/troubleshoot_0.1.0_windows_amd64-alpha.tar.gz
sha256: "fcfee994959a4924d0f70817a4b40d2b4df9e21a5a2a106d340784ce77fbbc15"
files:
- from: "/kubectl-troubleshoot.exe"
to: "."
bin: "./kubectl-troubleshoot.exe"
shortDescription: Creates support bundles for off-cluster analysis
homepage: https://github.com/replicaetdhq/troubleshoot
+54
View File
@@ -0,0 +1,54 @@
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: sample
spec:
analyzers:
- nodeResources:
checkName: Must have at least 3 nodes in the cluster
outcomes:
- fail:
when: "< 3"
message: This application requires at least 3 nodes
- warn:
when: "< 5"
message: This application recommends at last 5 nodes.
- pass:
message: This cluster has enough nodes.
- nodeResources:
checkName: Must have 3 nodes with at least 6 cores
filters:
cpuCapacity: "6"
outcomes:
- fail:
when: "< 3"
message: This application requires at least 3 nodes with 6 cores each
- pass:
message: This cluster has enough nodes with enough codes
- nodeResources:
checkName: Must have 1 node with 16 GB (available) memory and 5 cores (on a single node)
filters:
allocatableMemory: 16Gi
cpuCapacity: "5"
outcomes:
- fail:
when: "< 1"
message: This application requires at least 1 node with 16GB available memory
- pass:
message: This cluster has a node with enough memory.
- nodeResources:
checkName: There must be at least 3 nodes in the cluster
outcomes:
- fail:
when: "count() < 3"
message: This application requires at least 3 nodes in the cluster
- pass:
message: This cluster haas sufficient nodes
- nodeResources:
checkName: There must be a total of at least 32Gi of memory on all nodes
outcomes:
- fail:
when: "sum(memoryCapacity) < 32Gi"
message: This application requires that 32Gi or more memory be available to the cluster
- pass:
message: This cluster haas sufficient memory
+23
View File
@@ -0,0 +1,23 @@
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: sample
spec:
collectors:
- postgres:
collectorName: pg
uri: postgresql://user:password@hostname:5432/defaultdb?sslmode=require
analyzers:
- postgres:
checkName: Must be postgres 10.x or later
collectorName: pg
outcomes:
- fail:
when: "connected == false"
message: Cannot connect to postgres server
- fail:
when: "version < 10.x"
message: The postgres server must be at least version 10
- pass:
message: The postgres connection checks out
+23
View File
@@ -0,0 +1,23 @@
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: sample
spec:
collectors:
- redis:
collectorName: my-redis
uri: rediss://default:password@hostname:6379
analyzers:
- redis:
checkName: Must be redis 5.x or later
collectorName: my-redis
outcomes:
- fail:
when: "connected == false"
message: Cannot connect to postgres server
- fail:
when: "version < 5.0.0"
message: The redis server must be at least version 5
- pass:
message: The redis connection checks out
+97 -13
View File
@@ -1,35 +1,119 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: example-preflight-checks
name: example
spec:
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"
message: Your cluster meets the minimum version of Kubernetes, but we recommend you update to 1.15.0 or later.
uri: https://kubernetes.io
- pass:
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 is installed
checkName: Ingress
customResourceDefinitionName: ingressroutes.contour.heptio.com
outcomes:
- fail:
message: Gatekeeper is required for the application, but not found in the cluster.
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.
message: Could not find a storage class called default.
- pass:
message: Found credentials to pull from quay.io
message: All good on storage classes
- distribution:
outcomes:
- fail:
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:
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.
+20
View File
@@ -0,0 +1,20 @@
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: example-preflight-checks
spec:
collectors:
- data:
name: config/replicas.txt
data: "2"
analyzers:
- textAnalyze:
checkName: Replica Count
fileName: config/replicas.txt
regexGroups: '(?P<Replicas>\d+)'
outcomes:
- fail:
when: "Replicas < 5"
message: That's not enough replicas!
- pass:
message: You've selected at leat 5 replicas
@@ -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.
@@ -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"
+2 -27
View File
@@ -1,31 +1,6 @@
apiVersion: troubleshoot.replicated.com/v1beta1
apiVersion: troubleshoot.sh/v1beta2
kind: Collector
metadata:
name: collector-sample
spec:
- clusterInfo: {}
- clusterResources: {}
- secret:
name: illmannered-cricket-mysql
namespace: default
key: mysql-password
- logs:
selector:
- name=nginx-ingress-microk8s
namespace: default
limits:
maxAge: 30d
maxLines: 10000
- run:
collectorName: ping-google
namespace: default
image: flungo/netutils
command: ["ping"]
args: ["www.google.com"]
timeout: 5s
- http:
collectorName: test-get
get:
url: https://api.staging.replicated.com/market/v1/echo/ip
insecureSkipVerify: false
headers: {}
collectors: []
+52
View File
@@ -0,0 +1,52 @@
package main
import "C"
import (
"encoding/json"
"fmt"
analyzer "github.com/replicatedhq/troubleshoot/pkg/analyze"
"github.com/replicatedhq/troubleshoot/pkg/convert"
"github.com/replicatedhq/troubleshoot/pkg/logger"
"gopkg.in/yaml.v2"
)
//export Analyze
func Analyze(bundleURL string, analyzers string, outputFormat string, compatibility string) *C.char {
logger.SetQuiet(true)
result, err := analyzer.DownloadAndAnalyze(bundleURL, analyzers)
if err != nil {
fmt.Printf("error downloading and analyzing: %s\n", err.Error())
return C.CString("")
}
var data interface{}
switch compatibility {
case "support-bundle":
data = convert.FromAnalyzerResult(result)
default:
data = result
}
var formatted []byte
switch outputFormat {
case "json":
formatted, err = json.MarshalIndent(data, "", " ")
case "", "yaml":
formatted, err = yaml.Marshal(data)
default:
fmt.Printf("unknown output format: %s\n", outputFormat)
return C.CString("")
}
if err != nil {
fmt.Printf("error formatting output: %#v\n", err)
return C.CString("")
}
return C.CString(string(formatted))
}
func main() {}
+44 -34
View File
@@ -3,43 +3,53 @@ module github.com/replicatedhq/troubleshoot
go 1.12
require (
github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/semver v1.4.2 // indirect
github.com/Masterminds/sprig v2.20.0+incompatible // indirect
github.com/andrewchambers/go-jqpipe v0.0.0-20180509223707-2d54cef8cd94 // indirect
github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412
github.com/aws/aws-sdk-go v1.25.18 // indirect
github.com/blang/semver v3.5.1+incompatible
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v1.13.1 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
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/gin-gonic/gin v1.4.0
github.com/gizak/termui/v3 v3.1.0
github.com/golang/snappy v0.0.1 // indirect
github.com/google/uuid v1.1.1 // indirect
github.com/hashicorp/go-getter v1.3.0
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/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/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
github.com/hashicorp/go-multierror v1.0.0
github.com/huandu/xstrings v1.2.0 // indirect
github.com/manifoldco/promptui v0.3.2 // indirect
github.com/mholt/archiver v3.1.1+incompatible
github.com/nwaples/rardecode v1.0.0 // indirect
github.com/onsi/gomega v1.5.0
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/pierrec/lz4 v2.0.5+incompatible // indirect
github.com/pkg/errors v0.8.1
github.com/spf13/cobra v0.0.3
github.com/imdario/mergo v0.3.8 // indirect
github.com/lib/pq v1.3.0
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/mitchellh/go-wordwrap v1.0.0 // indirect
github.com/nicksnyder/go-i18n v1.10.1 // indirect
github.com/onsi/gomega v1.9.0 // indirect
github.com/pkg/errors v0.9.1
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.3.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/xo/dburl v0.0.0-20190203050942-98997a05b24f // indirect
golang.org/x/net v0.0.0-20190522155817-f3200d17e092
gopkg.in/yaml.v2 v2.2.2
k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b
k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
sigs.k8s.io/controller-runtime v0.2.0-beta.2
sigs.k8s.io/controller-tools v0.2.0-beta.2 // 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
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 // indirect
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.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
sigs.k8s.io/controller-tools v0.3.0 // indirect
)
+335 -280
View File
@@ -1,97 +1,124 @@
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8=
cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40=
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.41.0 h1:NFvqUTDnSNYPX5oReekmB+D+90jrJIcVImxQ3qrBVgM=
cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
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/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.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/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.20.0+incompatible h1:dJTKKuUkYW3RMFdQFXPU/s6hg10RgctmTjRcbZ98Ap8=
github.com/Masterminds/sprig v2.20.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
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=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
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=
github.com/alecthomas/gometalinter v2.0.11+incompatible/go.mod h1:qfIpQGGz3d+NmgyPBqv+LSh50emm1pt72EtcX2vKYQk=
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/andrewchambers/go-jqpipe v0.0.0-20180509223707-2d54cef8cd94 h1:mOmUCSuraMWgFv6Wr3pCkt9UZ2RBsx2XMZoIXg2Oj6w=
github.com/andrewchambers/go-jqpipe v0.0.0-20180509223707-2d54cef8cd94/go.mod h1:8KsRln2ynWOaJwxYOUwfPU/ix+H9oh3aCJ6L0DsHW3Q=
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/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30 h1:Kn3rqvbUFqSepE2OqVu0Pn1CbDw9IuMlONapol0zuwk=
github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30/go.mod h1:4AJxUpXUhv4N+ziTvIcWWXgeorXpxPZOfk9HdEVr96M=
github.com/appscode/jsonpatch v2.0.0+incompatible h1:DEsgcSnA7ui6pICc75uxDpyN8Bx4DLFTS8aRym702nE=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.15.78 h1:LaXy6lWR0YK7LKyuU0QWy2ws/LWTPfYV/UgfiBu4tvY=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
github.com/aws/aws-sdk-go v1.25.18 h1:fMEkpli4r+FS4xZRqjgjYHP+uKaSwfk2MOcDUpLYwIE=
github.com/aws/aws-sdk-go v1.25.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=
github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
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=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.1.11-0.20160617073814-96a4d311aa9b h1:i4AJWjdpwyE7iKKxsp8NbWy3gQVQ9nZEBG/Pm+cBaww=
github.com/chzyer/logex v1.1.11-0.20160617073814-96a4d311aa9b/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
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/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/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
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 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/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
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/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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
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=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.6+incompatible h1:tfrHha8zJ01ywiOEC1miGY8st1/igzWB8OmvPgoYX7w=
github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.0.0+incompatible h1:xregGRMLBeuRcwiOTHRCsPPuzCQlqhxUPbqdw+zNkLc=
github.com/evanphx/json-patch v4.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.1.0+incompatible h1:K1MDoo4AZ4wU0GIU/fPmtZg7VpzLjCxu+UwBD1FvwOc=
github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
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 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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
@@ -99,119 +126,151 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/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/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
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/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
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=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
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-logr/zapr v0.1.1 h1:qXBXPDdNncunGs7XeEpsJt8wCjYBygluzfdLO0G5baE=
github.com/go-logr/zapr v0.1.1/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.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.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.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.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.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.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.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/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.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.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=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
github.com/gobuffalo/envy v1.6.15 h1:OsV5vOpHYUpP7ZLS6sem1y40/lNX1BZj+ynMiRi21lQ=
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gogo/protobuf v1.0.0 h1:2jyBKDKU/8v3v2xVR2PtiWQviFUyiaGk2rpfyFT8rTM=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gobuffalo/flect v0.2.0 h1:EWCvMGGxOjsgwlWaP+f4+Hh6yrrte7JeFL2S6b+0hdM=
github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
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 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
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/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3 h1:I4BOK3PBMjhWfQM2zPJKK7lOBGsrsvOB7kBELP33hiE=
github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
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/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/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/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
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 h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
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=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3 h1:siORttZ36U2R/WjiJuDz8znElWBiAlO9rVt+mqJt0Cc=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
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/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0=
github.com/googleapis/gnostic v0.3.0/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.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 v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
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-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
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.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=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-getter v1.3.0 h1:pFMSFlI9l5NaeuzkpE3L7BYk9qQ9juTAgXW/H0cqxcU=
github.com/hashicorp/go-getter v1.3.0/go.mod h1:/O1k/AizTN0QmfEKknCYGvICeyKUDqCYA8vvWtGWDeQ=
github.com/hashicorp/go-getter v1.3.1-0.20190627223108-da0323b9545e h1:6krcdHPiS+aIP9XKzJzSahfjD7jG7Z+4+opm0z39V1M=
github.com/hashicorp/go-getter v1.3.1-0.20190627223108-da0323b9545e/go.mod h1:/O1k/AizTN0QmfEKknCYGvICeyKUDqCYA8vvWtGWDeQ=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw=
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -219,155 +278,152 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
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.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 v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
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=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
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=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw=
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/manifoldco/promptui v0.3.2 h1:rir7oByTERac6jhpHUPErHuopoRDvO3jxS+FdadEns8=
github.com/manifoldco/promptui v0.3.2/go.mod h1:8JU+igZ+eeiiRku4T5BjtKh2ms8sziGpSYl1gN8Bazw=
github.com/markbates/inflect v1.0.4 h1:5fh1gzTFhfae06u3hzHYO9xe3l3v3nW5Pwt3naLTP5g=
github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
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.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=
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=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
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 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
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/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.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
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 v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
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=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c h1:MUyE44mTvnI5A0xrxIxaMqoWFzPfQvtE2IWUollMDMs=
github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 h1:zNBQb37RGLmJybyMcs983HfUfpkw9OTFD9tbBfAViHE=
github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/petar/GoLLRB v0.0.0-20190514000832-33fb24c13b99/go.mod h1:HUpKUBZnpzkdx0kD/+Yfuft+uD3zHGtXF/XJB14TUr4=
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.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
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/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 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.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.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM=
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/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.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/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
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-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 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
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-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2 h1:J7U/N7eRtzjhs26d6GqMh2HBuXP8/Z64Densiiieafo=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
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/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
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=
@@ -392,6 +448,7 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
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.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=
@@ -401,80 +458,91 @@ 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.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
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=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
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/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tsenart/deadcode v0.0.0-20160724212837-210d2dc333e9 h1:vY5WqiEon0ZSTGM3ayVVi+twaHKHDFUVloaQ/wug9/c=
github.com/tsenart/deadcode v0.0.0-20160724212837-210d2dc333e9/go.mod h1:q+QjxYvZ+fpjMXqs+XEriussHjSYqeXVnAdSV1tkMYk=
github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
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/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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xo/dburl v0.0.0-20190203050942-98997a05b24f/go.mod h1:g6rdekR8vgfVZrkLWfobLTm0kVez7GAN23mWtkGCJ14=
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.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938=
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/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 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 v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
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=
go.undefinedlabs.com/scopeagent v0.1.7/go.mod h1:XCD8wnuBZ+eskhynYNP+uvBiFan9cFrWLwHnWOETXDU=
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-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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/exp v0.0.0-20190510132918-efd6b22b2522/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/lint v0.0.0-20190409202823-959b441ac422/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-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=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
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=
@@ -482,21 +550,22 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r
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=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
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-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
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/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 h1:uESlIz09WIHT2I+pasSXcpLYqYK8wHcdCetU3VuMBJE=
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-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
@@ -507,6 +576,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -517,23 +587,22 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
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-20190429190828-d89cdac9e872 h1:cGjJzUd8RgBw428LXP65YXni0aiGNA4Bl+ls8SmLOm8=
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/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-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/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.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
@@ -547,150 +616,136 @@ golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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 h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ=
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-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190501045030-23463209683d h1:D7DVZUZEUgsSIDTivnUtVeGfN5AvhDIKtdIZAqx0ieE=
golang.org/x/tools v0.0.0-20190501045030-23463209683d/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
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-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190703212419-2214986f1668/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
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=
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=
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 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
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/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
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-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=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 h1:mBVYJnbrXLA/ZCBTCe7PtEgAUP+1bg92qTaFoPHdz+8=
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
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/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
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.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 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
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=
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20191105091915-95d230a53780 h1:CEBpW6C191eozfEuWdUmIAHn7lwlLxJ7HVdr2e2Tsrw=
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20191105091915-95d230a53780/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
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/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.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.v2 v2.0.0-20170812160011-eb3733d160e7/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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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=
gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966 h1:B0J02caTR6tpSJozBJyiAzT6CtBzjclw4pgm9gg8Ys0=
gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.0.0-20190222213804-5cb15d344471 h1:MzQGt8qWQCR+39kbYRd0uQqsvSidpYqJLFeWiJ9l4OE=
k8s.io/api v0.0.0-20190222213804-5cb15d344471/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b h1:aBGgKJUM9Hk/3AE8WaZIApnTxG35kbuQba2w+SXqezo=
k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/api v0.0.0-20190703205437-39734b2a72fe h1:MFaHtAyhZcfBZocN91muHSqnwiF5yfXx7yGoehneNYg=
k8s.io/api v0.0.0-20190703205437-39734b2a72fe/go.mod h1:J5EZ0KSEjvyKOBy5BDHSF3zn82madLLWg7nUKaOHZKU=
k8s.io/api v0.0.0-20190717022910-653c86b0609b h1:WiE134uexhvhHw4DjJuYsghv792UCe2xN5SHQOayf28=
k8s.io/apiextensions-apiserver v0.0.0-20190228180357-d002e88f6236 h1:JfFtjaElBIgYKCWEtYQkcNrTpW+lMO4GJy8NP6SVQmM=
k8s.io/apiextensions-apiserver v0.0.0-20190228180357-d002e88f6236/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8 h1:q1Qvjzs/iEdXF6A1a8H3AKVFDzJNcJn3nXMs6R6qFtA=
k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
k8s.io/apimachinery v0.0.0-20190221213512-86fb29eff628 h1:UYfHH+KEF88OTg+GojQUwFTNxbxwmoktLwutUzR0GPg=
k8s.io/apimachinery v0.0.0-20190221213512-86fb29eff628/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d h1:Jmdtdt1ZnoGfWWIIik61Z7nKYgO3J+swQJtPYsP9wHA=
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/apimachinery v0.0.0-20190703205208-4cfb76a8bf76 h1:vxMYBaJgczGAIpJAOBco2eHuFYIyDdNIebt60jxLauA=
k8s.io/apimachinery v0.0.0-20190703205208-4cfb76a8bf76/go.mod h1:M2fZgZL9DbLfeJaPBCDqSqNsdsmLN+V29knYJnIXlMA=
k8s.io/client-go v0.0.0-20190228174230-b40b2a5939e4 h1:aE8wOCKuoRs2aU0OP/Rz8SXiAB0FTTku3VtGhhrkSmc=
k8s.io/client-go v0.0.0-20190228174230-b40b2a5939e4/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o=
k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible h1:U5Bt+dab9K8qaUmXINrkXO135kA11/i5Kg1RUydgaMQ=
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/code-generator v0.0.0-20181117043124-c2090bec4d9b h1:KH0fUlgdFZH8UMxJ/FDCYHpczfSQKefetq5NjL6BVF0=
k8s.io/code-generator v0.0.0-20181117043124-c2090bec4d9b/go.mod h1:MYiN+ZJZ9HkETbgVZdWw2AsuAi9PZ4V80cwfuf2axe8=
k8s.io/code-generator v0.0.0-20190703204957-583809a49343/go.mod h1:gZ0mjdzfPz3P+cILxibAJ25xFoZ1Kf0xQrFUHy66AEs=
k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
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-20190327210449-e17681d19d3a h1:QoHVuRquf80YZ+/bovwxoMO3Q/A3nt3yTgS0/0nejuk=
k8s.io/gengo v0.0.0-20190327210449-e17681d19d3a/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.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68=
k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c=
k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c h1:3KSCztE7gPitlZmWbNwue/2U0YruD65DqX3INopDAQM=
k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI=
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208 h1:5sW+fEHvlJI3Ngolx30CmubFulwH28DhKjGf70Xmtco=
k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4=
k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5 h1:VBM/0P5TWxwk+Nw6Z+lAw3DKgO76g90ETOiA6rfLV1Y=
k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a h1:2jUDc9gJja832Ftp+QbDV0tVhQHMISFn01els+2ZAcw=
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/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=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
sigs.k8s.io/controller-runtime v0.1.12 h1:ovDq28E64PeY1yR+6H7DthakIC09soiDCrKvfP2tPYo=
sigs.k8s.io/controller-runtime v0.1.12/go.mod h1:HFAYoOh6XMV+jKF1UjFwrknPbowfyHEHHRdJMf2jMX8=
sigs.k8s.io/controller-runtime v0.2.0-beta.2 h1:hOWldx1qmGI9TsU+uUsq1xTgVmUV7AZo08VAYX0dwGI=
sigs.k8s.io/controller-runtime v0.2.0-beta.2/go.mod h1:TSH2R0nSz4WAlUUlNnOFcOR/VUhfwBLlmtq2X6AiQCA=
sigs.k8s.io/controller-tools v0.1.11/go.mod h1:6g08p9m9G/So3sBc1AOQifHfhxH/mb6Sc4z0LMI8XMw=
sigs.k8s.io/controller-tools v0.2.0-beta.2 h1:ucniFzEuW7PFfFDuUxacdY4Fy4q065wPguVl+BE2/t0=
sigs.k8s.io/controller-tools v0.2.0-beta.2/go.mod h1:gC5UAnK1jbxWnDaqTi0yxKIsRsRwshzeRtTUGbM9vos=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/testing_frameworks v0.1.1 h1:cP2l8fkA3O9vekpy5Ks8mmA0NW/F7yBdXf8brkWhVrs=
sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
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-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
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=
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/controller-tools v0.3.0 h1:y3YD99XOyWaXkiF1kd41uRvfp/64teWcrEZFuHxPhJ4=
sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
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/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
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=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
+247 -19
View File
@@ -1,8 +1,11 @@
package analyzer
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"
)
type AnalyzeResult struct {
@@ -13,30 +16,255 @@ type AnalyzeResult struct {
Title string
Message string
URI string
IconKey string
IconURI string
}
type getCollectedFileContents func(string) ([]byte, error)
type getChildCollectedFileContents func(string) (map[string][]byte, error)
func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileContents, findFiles getChildCollectedFileContents) (*AnalyzeResult, error) {
if analyzer.ClusterVersion != nil {
return analyzeClusterVersion(analyzer.ClusterVersion, getFile)
}
if analyzer.StorageClass != nil {
return analyzeStorageClass(analyzer.StorageClass, getFile)
}
if analyzer.CustomResourceDefinition != nil {
return analyzeCustomResourceDefinition(analyzer.CustomResourceDefinition, getFile)
}
if analyzer.Ingress != nil {
return analyzeIngress(analyzer.Ingress, getFile)
}
if analyzer.Secret != nil {
return analyzeSecret(analyzer.Secret, getFile)
}
if analyzer.ImagePullSecret != nil {
return analyzeImagePullSecret(analyzer.ImagePullSecret, findFiles)
func isExcluded(excludeVal multitype.BoolOrString) (bool, error) {
if excludeVal.Type == multitype.Bool {
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")
}
return parsed, nil
}
func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
if analyzer.ClusterVersion != nil {
isExcluded, err := isExcluded(analyzer.ClusterVersion.Exclude)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
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)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
result, err := analyzeRedis(analyzer.Redis, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
if analyzer.CephStatus != nil {
isExcluded, err := isExcluded(analyzer.CephStatus.Exclude)
if err != nil {
return nil, err
}
if isExcluded {
return nil, nil
}
result, err := cephStatus(analyzer.CephStatus, getFile)
if err != nil {
return nil, err
}
return []*AnalyzeResult{result}, nil
}
return nil, errors.New("invalid analyzer")
}
+175
View File
@@ -0,0 +1,175 @@
package analyzer
import (
"encoding/json"
"path"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
type CephHealth string
const (
CephHealthOK CephHealth = "HEALTH_OK"
CephHealthWarn CephHealth = "HEALTH_WARN"
CephHealthErr CephHealth = "HEALTH_ERR"
)
func (a CephHealth) Compare(b CephHealth) int {
if a == b {
return 0
}
switch a {
case CephHealthOK:
return 1
case CephHealthWarn:
switch b {
case CephHealthOK:
return -1
case CephHealthErr:
return 1
}
return 1
case CephHealthErr:
switch b {
case CephHealthOK, CephHealthWarn:
return -1
}
return 1
default:
return -1
}
}
var CephStatusDefaultOutcomes = []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "Ceph is healthy",
},
},
{
Warn: &troubleshootv1beta2.SingleOutcome{
Message: "Ceph status is HEALTH_WARN",
URI: "https://rook.io/docs/rook/v1.4/ceph-common-issues.html",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "Ceph status is HEALTH_ERR",
URI: "https://rook.io/docs/rook/v1.4/ceph-common-issues.html",
},
},
}
func cephStatus(analyzer *troubleshootv1beta2.CephStatusAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
fileName := path.Join(collect.GetCephCollectorFilepath(analyzer.CollectorName, analyzer.Namespace), "status.json")
collected, err := getCollectedFileContents(fileName)
if err != nil {
return nil, errors.Wrap(err, "failed to read collected ceph status")
}
title := analyzer.CheckName
if title == "" {
title = "Ceph Status"
}
analyzeResult := &AnalyzeResult{
Title: title,
IconKey: "rook", // maybe this should be ceph?
IconURI: "https://troubleshoot.sh/images/analyzer-icons/rook.svg?w=11&h=16",
}
status := struct {
Health struct {
Status string `json:"status"`
} `json:"health"`
}{}
if err := json.Unmarshal(collected, &status); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal status.json")
}
if len(analyzer.Outcomes) == 0 {
analyzer.Outcomes = CephStatusDefaultOutcomes
}
for _, outcome := range analyzer.Outcomes {
if outcome.Fail != nil {
if outcome.Fail.When == "" {
outcome.Fail.When = string(CephHealthErr)
}
match, err := compareCephStatus(status.Health.Status, outcome.Fail.When)
if err != nil {
return nil, errors.Wrap(err, "failed to compare ceph status")
} else if match {
analyzeResult.IsFail = true
analyzeResult.Message = outcome.Fail.Message
analyzeResult.URI = outcome.Fail.URI
return analyzeResult, nil
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
outcome.Warn.When = string(CephHealthWarn)
}
match, err := compareCephStatus(status.Health.Status, outcome.Warn.When)
if err != nil {
return nil, errors.Wrap(err, "failed to compare ceph status")
} else if match {
analyzeResult.IsWarn = true
analyzeResult.Message = outcome.Warn.Message
analyzeResult.URI = outcome.Warn.URI
return analyzeResult, nil
}
} else if outcome.Pass != nil {
if outcome.Pass.When == "" {
outcome.Pass.When = string(CephHealthOK)
}
match, err := compareCephStatus(status.Health.Status, outcome.Pass.When)
if err != nil {
return nil, errors.Wrap(err, "failed to compare ceph status")
} else if match {
analyzeResult.IsPass = true
analyzeResult.Message = outcome.Pass.Message
analyzeResult.URI = outcome.Pass.URI
return analyzeResult, nil
}
}
}
return analyzeResult, nil
}
func compareCephStatus(actual, when string) (bool, error) {
parts := strings.Split(strings.TrimSpace(when), " ")
if len(parts) == 1 {
value := strings.TrimSpace(parts[0])
return value == actual, nil
}
if len(parts) != 2 {
return false, errors.New("unable to parse when range")
}
operator := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
compareResult := CephHealth(actual).Compare(CephHealth(value))
switch operator {
case "=", "==", "===":
return compareResult == 0, nil
case "<":
return compareResult == -1, nil
case ">":
return compareResult == 1, nil
case "<=":
return compareResult <= 0, nil
case ">=":
return compareResult >= 0, nil
default:
return false, errors.New("unknown operator")
}
}
+204
View File
@@ -0,0 +1,204 @@
package analyzer
import (
"fmt"
"testing"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.undefinedlabs.com/scopeagent"
)
func Test_cephStatus(t *testing.T) {
tests := []struct {
name string
analyzer troubleshootv1beta2.CephStatusAnalyze
expectResult AnalyzeResult
filePath, file string
}{
{
name: "pass case",
analyzer: troubleshootv1beta2.CephStatusAnalyze{},
expectResult: AnalyzeResult{
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "Ceph Status",
Message: "Ceph is healthy",
IconKey: "rook",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/rook.svg?w=11&h=16",
},
filePath: "ceph/status.json",
file: `{
"fsid": "96a8178c-6aa2-4adf-a309-9e8869a79611",
"health": {
"status": "HEALTH_OK"
}
}`,
},
{
name: "warn case",
analyzer: troubleshootv1beta2.CephStatusAnalyze{},
expectResult: AnalyzeResult{
IsPass: false,
IsWarn: true,
IsFail: false,
Title: "Ceph Status",
Message: "Ceph status is HEALTH_WARN",
URI: "https://rook.io/docs/rook/v1.4/ceph-common-issues.html",
IconKey: "rook",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/rook.svg?w=11&h=16",
},
filePath: "ceph/status.json",
file: `{
"fsid": "96a8178c-6aa2-4adf-a309-9e8869a79611",
"health": {
"status": "HEALTH_WARN"
}
}`,
},
{
name: "fail case",
analyzer: troubleshootv1beta2.CephStatusAnalyze{},
expectResult: AnalyzeResult{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "Ceph Status",
Message: "Ceph status is HEALTH_ERR",
URI: "https://rook.io/docs/rook/v1.4/ceph-common-issues.html",
IconKey: "rook",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/rook.svg?w=11&h=16",
},
filePath: "ceph/status.json",
file: `{
"fsid": "96a8178c-6aa2-4adf-a309-9e8869a79611",
"health": {
"status": "HEALTH_ERR"
}
}`,
},
{
name: "CollectorName and Namespace",
analyzer: troubleshootv1beta2.CephStatusAnalyze{
CollectorName: "custom-namespace",
Namespace: "namespace",
},
expectResult: AnalyzeResult{
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "Ceph Status",
Message: "Ceph is healthy",
IconKey: "rook",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/rook.svg?w=11&h=16",
},
filePath: "custom-namespace/namespace/ceph/status.json",
file: `{
"fsid": "96a8178c-6aa2-4adf-a309-9e8869a79611",
"health": {
"status": "HEALTH_OK"
}
}`,
},
{
name: "outcomes when",
analyzer: troubleshootv1beta2.CephStatusAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "== HEALTH_OK",
Message: "custom message OK",
URI: "custom uri OK",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
When: "<= HEALTH_WARN",
Message: "custom message WARN",
URI: "custom uri WARN",
},
},
},
},
expectResult: AnalyzeResult{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "Ceph Status",
Message: "custom message WARN",
URI: "custom uri WARN",
IconKey: "rook",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/rook.svg?w=11&h=16",
},
filePath: "ceph/status.json",
file: `{
"fsid": "96a8178c-6aa2-4adf-a309-9e8869a79611",
"health": {
"status": "HEALTH_WARN"
}
}`,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
scopetest := scopeagent.StartTest(t)
defer scopetest.End()
req := require.New(t)
getFile := func(n string) ([]byte, error) {
assert.Equal(t, n, test.filePath)
return []byte(test.file), nil
}
actual, err := cephStatus(&test.analyzer, getFile)
req.NoError(err)
assert.Equal(t, test.expectResult, *actual)
})
}
}
func Test_compareCephStatus(t *testing.T) {
tests := []struct {
actual string
when string
want bool
wantErr bool
}{
{
actual: "HEALTH_OK",
when: "HEALTH_OK",
want: true,
},
{
actual: "HEALTH_OK",
when: "HEALTH_WARN",
want: false,
},
{
actual: "HEALTH_OK",
when: "<= HEALTH_WARN",
want: false,
},
{
actual: "HEALTH_OK",
when: ">= HEALTH_WARN",
want: true,
},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("%s %s", tt.actual, tt.when), func(t *testing.T) {
got, err := compareCephStatus(tt.actual, tt.when)
if (err != nil) != tt.wantErr {
t.Errorf("compareCephStatus() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("compareCephStatus() = %v, want %v", got, tt.want)
}
})
}
}
+21 -8
View File
@@ -6,14 +6,14 @@ 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 top get contents of cluster_version.json")
return nil, errors.Wrap(err, "failed to get contents of cluster_version.json")
}
collectorClusterVersion := collect.ClusterVersion{}
@@ -26,19 +26,24 @@ func analyzeClusterVersion(analyzer *troubleshootv1beta1.ClusterVersion, getColl
return nil, errors.Wrap(err, "failed to parse semver from cluster_version.json")
}
result := AnalyzeResult{}
for _, outcome := range analyzer.Outcomes {
return analyzeClusterVersionResult(k8sVersion, analyzer.Outcomes, analyzer.CheckName)
}
func analyzeClusterVersionResult(k8sVersion semver.Version, outcomes []*troubleshootv1beta2.Outcome, checkName string) (*AnalyzeResult, error) {
for _, outcome := range outcomes {
when := ""
message := ""
uri := ""
title := analyzer.CheckName
title := checkName
if title == "" {
title = "Required Kubernetes Version"
}
result = AnalyzeResult{
Title: title,
result := AnalyzeResult{
Title: title,
IconKey: "kubernetes_cluster_version",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
}
if outcome.Fail != nil {
@@ -60,6 +65,14 @@ func analyzeClusterVersion(analyzer *troubleshootv1beta1.ClusterVersion, getColl
return nil, errors.New("empty outcome")
}
// When is usually empty as the final case and should be treated as true
if when == "" {
result.Message = message
result.URI = uri
return &result, nil
}
whenRange, err := semver.ParseRange(when)
if err != nil {
return nil, errors.Wrap(err, "failed to parse semver range")
+108
View File
@@ -0,0 +1,108 @@
package analyzer
import (
"reflect"
"testing"
"github.com/blang/semver"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"go.undefinedlabs.com/scopeagent"
)
func Test_analyzeClusterVersionResult(t *testing.T) {
outcomes := []*troubleshootv1beta2.Outcome{
{
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: &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: &troubleshootv1beta2.SingleOutcome{
Message: "Your cluster meets the recommended and required versions of Kubernetes.",
},
},
}
type args struct {
k8sVersion semver.Version
outcomes []*troubleshootv1beta2.Outcome
checkName string
}
tests := []struct {
name string
args args
want *AnalyzeResult
wantErr bool
}{
{
name: "fail",
args: args{
k8sVersion: semver.MustParse("1.12.5"),
outcomes: outcomes,
checkName: "Check Fail",
},
want: &AnalyzeResult{
IsFail: true,
Title: "Check Fail",
Message: "Sentry requires at Kubernetes 1.13.0 or later, and recommends 1.15.0.",
URI: "https://www.kubernetes.io",
IconKey: "kubernetes_cluster_version",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
},
},
{
name: "warn",
args: args{
k8sVersion: semver.MustParse("1.14.3"),
outcomes: outcomes,
checkName: "Check Warn",
},
want: &AnalyzeResult{
IsWarn: true,
Title: "Check Warn",
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",
IconKey: "kubernetes_cluster_version",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
},
},
{
name: "fallthrough",
args: args{
k8sVersion: semver.MustParse("1.17.0"),
outcomes: outcomes,
checkName: "Check Pass",
},
want: &AnalyzeResult{
IsPass: true,
Title: "Check Pass",
Message: "Your cluster meets the recommended and required versions of Kubernetes.",
IconKey: "kubernetes_cluster_version",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/kubernetes.svg?w=16&h=16",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
scopetest := scopeagent.StartTest(t)
defer scopetest.End()
got, err := analyzeClusterVersionResult(tt.args.k8sVersion, tt.args.outcomes, tt.args.checkName)
if (err != nil) != tt.wantErr {
t.Errorf("analyzeClusterVersionResult() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("analyzeClusterVersionResult() = %v, want %v", got, tt.want)
}
})
}
}
+124
View File
@@ -0,0 +1,124 @@
package analyzer
import (
"strconv"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)
func commonStatus(outcomes []*troubleshootv1beta2.Outcome, title, iconKey string, iconURI string, readyReplicas int) (*AnalyzeResult, error) {
result := &AnalyzeResult{
Title: title,
IconKey: iconKey,
IconURI: iconURI,
}
// ordering from the spec is important, the first one that matches returns
for _, outcome := range 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
}
match, err := compareActualToWhen(outcome.Fail.When, readyReplicas)
if err != nil {
return nil, errors.Wrap(err, "failed to parse fail range")
}
if match {
result.IsFail = true
result.Message = outcome.Fail.Message
result.URI = outcome.Fail.URI
return result, nil
}
} else if outcome.Warn != nil {
if outcome.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return result, nil
}
match, err := compareActualToWhen(outcome.Warn.When, readyReplicas)
if err != nil {
return nil, errors.Wrap(err, "failed to parse warn range")
}
if match {
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
}
match, err := compareActualToWhen(outcome.Pass.When, readyReplicas)
if err != nil {
return nil, errors.Wrap(err, "failed to parse pass range")
}
if match {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return result, nil
}
}
}
return result, nil
}
func compareActualToWhen(when string, actual int) (bool, error) {
parts := strings.Split(strings.TrimSpace(when), " ")
// we can make this a lot more flexible
if len(parts) != 2 {
return false, errors.New("unable to parse when range")
}
value, err := strconv.Atoi(parts[1])
if err != nil {
return false, errors.Wrap(err, "failed to parse when value")
}
switch parts[0] {
case "=":
fallthrough
case "==":
fallthrough
case "===":
return actual == value, nil
case "<":
return actual < value, nil
case ">":
return actual > value, nil
case "<=":
return actual <= value, nil
case ">=":
return actual >= value, nil
}
return false, errors.Errorf("unknown comparator: %q", parts[0])
}
+138
View File
@@ -0,0 +1,138 @@
package analyzer
import (
"encoding/json"
"net/url"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
corev1 "k8s.io/api/core/v1"
)
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")
}
var nodes []corev1.Node
if err := json.Unmarshal(collected, &nodes); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal node list")
}
foundRuntimes := []string{}
for _, node := range nodes {
foundRuntimes = append(foundRuntimes, node.Status.NodeInfo.ContainerRuntimeVersion)
}
title := analyzer.CheckName
if title == "" {
title = "Container Runtime"
}
result := &AnalyzeResult{
Title: title,
IconKey: "kubernetes_container_runtime",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/container-runtime.svg?w=23&h=16",
}
// ordering is important for passthrough
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
}
for _, foundRuntime := range foundRuntimes {
isMatch, err := compareRuntimeConditionalToActual(outcome.Fail.When, foundRuntime)
if err != nil {
return nil, errors.Wrap(err, "failed to compare runtime 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.Warn.When == "" {
result.IsWarn = true
result.Message = outcome.Warn.Message
result.URI = outcome.Warn.URI
return result, nil
}
for _, foundRuntime := range foundRuntimes {
isMatch, err := compareRuntimeConditionalToActual(outcome.Warn.When, foundRuntime)
if err != nil {
return nil, errors.Wrap(err, "failed to compare runtime 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
}
for _, foundRuntime := range foundRuntimes {
isMatch, err := compareRuntimeConditionalToActual(outcome.Pass.When, foundRuntime)
if err != nil {
return nil, errors.Wrap(err, "failed to compare runtime conditional")
}
if isMatch {
result.IsPass = true
result.Message = outcome.Pass.Message
result.URI = outcome.Pass.URI
return result, nil
}
}
}
}
return result, nil
}
func compareRuntimeConditionalToActual(conditional string, actual string) (bool, error) {
parts := strings.Split(strings.TrimSpace(conditional), " ")
// we can make this a lot more flexible
if len(parts) != 2 {
return false, errors.New("unable to parse conditional")
}
parsedRuntime, err := url.Parse(actual)
if err != nil {
return false, errors.Wrap(err, "failed to parse url")
}
switch parts[0] {
case "=":
fallthrough
case "==":
fallthrough
case "===":
return parts[1] == parsedRuntime.Scheme, nil
}
return false, nil
}

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