├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cmd └── kubectl-doctor.go ├── docs └── example.svg ├── doctor.yaml ├── glide.lock ├── glide.yaml ├── go.mod ├── go.sum └── pkg ├── client └── client.go ├── plugin └── cmd.go └── triage ├── component_triage.go ├── deployments_triage.go ├── endpoints_triage.go ├── ingress_triage.go ├── job_triage.go ├── node_triage.go ├── pv_triage.go ├── pvc_triage.go ├── replicaset_triage.go └── triage.go /.gitignore: -------------------------------------------------------------------------------- 1 | kubectl-doctor 2 | *~ 3 | .idea/ 4 | 5 | # Binaries for programs and plugins 6 | *.exe 7 | *.exe~ 8 | *.dll 9 | *.so 10 | *.dylib 11 | 12 | # Test binary, built with `go test -c` 13 | *.test 14 | 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | # Ignore vendor files 19 | vendor/* 20 | 21 | # Ignore binary releases 22 | releases/* 23 | 24 | # Ignore garbage 25 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2019 EMIR OZER 179 | 180 | kubectl-doctor 181 | 182 | Licensed under the Apache License, Version 2.0 (the "License"); 183 | you may not use this file except in compliance with the License. 184 | You may obtain a copy of the License at 185 | 186 | http://www.apache.org/licenses/LICENSE-2.0 187 | 188 | Unless required by applicable law or agreed to in writing, software 189 | distributed under the License is distributed on an "AS IS" BASIS, 190 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 191 | See the License for the specific language governing permissions and 192 | limitations under the License. 193 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME=kubectl-doctor 2 | PACKAGE_NAME=github.com/emirozer/$(NAME) 3 | TAG=$(shell git describe --abbrev=0 --tags) 4 | 5 | 6 | all: build 7 | 8 | $(GOPATH)/bin/golint$(suffix): 9 | go get github.com/golang/lint/golint 10 | 11 | $(GOPATH)/bin/goveralls$(suffix): 12 | go get github.com/mattn/goveralls 13 | 14 | bin: 15 | mkdir bin 16 | 17 | dep: 18 | glide up -v 19 | 20 | build: bin 21 | go build -o kubectl-doctor cmd/kubectl-doctor.go 22 | cp ./kubectl-doctor /usr/local/bin/plugins 23 | mv ./kubectl-doctor ./bin 24 | 25 | lint: $(GOPATH)/bin/golint$(suffix) 26 | golint 27 | 28 | vet: 29 | go vet 30 | 31 | test: vet 32 | go test -race -v -cover ./... 33 | 34 | clean: 35 | rm -fr dist bin 36 | rm /usr/local/bin/plugins/kubectl-doctor 37 | 38 | fmt: 39 | gofmt -w $(GOFMT_FILES) 40 | 41 | release: 42 | mkdir dist 43 | env GOOS=linux GOARCH=amd64 go build -o kubectl-doctor cmd/kubectl-doctor.go ; mv kubectl-doctor dist/kubectl-doctor_linux_amd64 44 | env GOOS=linux GOARCH=arm go build -o kubectl-doctor cmd/kubectl-doctor.go ; mv kubectl-doctor dist/kubectl-doctor_linux_arm 45 | env GOOS=darwin GOARCH=amd64 go build -o kubectl-doctor cmd/kubectl-doctor.go ; mv kubectl-doctor dist/kubectl-doctor_darwin_amd64 46 | env GOOS=darwin GOARCH=arm64 go build -o kubectl-doctor cmd/kubectl-doctor.go ; mv kubectl-doctor dist/kubectl-doctor_darwin_arm64 47 | env GOOS=windows GOARCH=amd64 go build -o kubectl-doctor cmd/kubectl-doctor.go ; mv kubectl-doctor dist/kubectl-doctor_windows_amd64.exe 48 | env GOOS=freebsd GOARCH=amd64 go build -o kubectl-doctor cmd/kubectl-doctor.go ; mv kubectl-doctor dist/kubectl-doctor_freebsd_amd64 49 | 50 | dist/$(NAME)-checksum-%: 51 | cd dist && sha256sum $@.zip 52 | 53 | checksums: dist/$(NAME)-checksum-darwin-amd64 dist/$(NAME)-checksum-windows-386 dist/$(NAME)-checksum-windows-amd64 dist/$(NAME)-checksum-linux-amd64 54 | 55 | 56 | .PHONY: fmt clean lint build 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes CLI Plugin - Doctor 2 | 3 | This plugin is inspired from [brew](http://brew.sh/) doctor :) It will scan your currently `target`ed k8s cluster to see if there are anomalies or useful action points that it can report back to you. 4 | 5 | This plugin does *not* change any state or configuration, it merely just scans and gathers information than reports back anomalies in yaml format. 6 | 7 | ![Demo](./docs/example.svg) 8 | 9 | ## Install 10 | 1. Download a zip that contains the binary from [releases](https://github.com/emirozer/kubectl-doctor/releases) that is compatible with your os/arch 11 | 2. Unzip to get `kubectl-doctor` (or `kubectl-doctor.exe` if windows) 12 | 3. Add it to your `PATH` 13 | 14 | ## Usage 15 | When the plugin binary is found from `PATH` you can just execute it through `kubectl` CLI 16 | ```shell 17 | kubectl doctor 18 | ``` 19 | 20 | ## Current list of anomaly checks 21 | 22 | * core component health (etcd cluster members, scheduler, controller-manager) 23 | * orphan endpoints (endpoints with no ipv4 attached) 24 | * persistent-volume available & unclaimed 25 | * persistent-volume-claim in lost state 26 | * k8s nodes that are not in ready state 27 | * orphan replicasets (desired number of replicas are bigger than 0 but the available replicas are 0) 28 | * leftover replicasets (desired number of replicas and the available # of replicas are 0) 29 | * orphan deployments (desired number of replicas are bigger than 0 but the available replicas are 0) 30 | * leftover deployments (desired number of replicas and the available # of replicas are 0) 31 | * leftover cronjobs (last active date is more than 30 days) 32 | -------------------------------------------------------------------------------- /cmd/kubectl-doctor.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/emirozer/kubectl-doctor/pkg/plugin" 7 | "github.com/spf13/pflag" 8 | _ "k8s.io/client-go/plugin/pkg/client/auth" 9 | ) 10 | 11 | func main() { 12 | flags := pflag.NewFlagSet("kubectl-doctor", pflag.ExitOnError) 13 | pflag.CommandLine = flags 14 | 15 | // bypass to DoctorCmd 16 | cmd := plugin.NewDoctorCmd() 17 | if err := cmd.Execute(); err != nil { 18 | os.Exit(1) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /docs/example.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 82 | 103 | 104 | 105 | kubectl-doctor|master kubectl-doctor|master . kubectl-doctor|master ./ kubectl-doctor|master ./b kubectl-doctor|master ./bi kubectl-doctor|master ./bin/ kubectl-doctor|master ./bin/kubectl-doctor kubectl-doctor|master ./bin/kubectl-doctor kubectl-doctor|master ./bin/kubectl-doctor WARN[0000] KUBECONFIG env var not found falling back to auto discovery! INFO[0000] Going for a full scan as no flags are set! INFO[0000] Retrieving necessary clientset for targeted k8s cluster. INFO[0000] INFO[0000] Fetched namespaces: [default kube-public kube-system] INFO[0000] Starting triage of cluster crucial component health checks. INFO[0000] Starting triage of nodes that form the cluster. INFO[0000] Starting triage of cluster-wide Endpoints resources. INFO[0000] Starting triage of cluster-wide pvc resources. INFO[0000] Starting triage of cluster-wide pv resources. INFO[0000] Starting triage of deployment resources across cluster INFO[0000] Starting triage of replicasets resources across cluster INFO[0000] Starting triage of cronjob resources across cluster INFO[0000] Triage report coming up in yaml format: --- TriageReport:- Resource: Endpoints AnomalyType: Found orphaned endpoints! Anomalies: - cadvisor - kube-controller-manager - kube-scheduler - logstash- Resource: PV AnomalyType: Found PV in Available & Unclaimed State! - nfs-screengrabs-data-labe2esuperset- Resource: ReplicaSets AnomalyType: 'Found leftover replicasets in namespace: kube-system' - coredns-788d98cc7b - dns-autoscaler-64cd8f866 - kubernetes-dashboard-67cc88998d - kubernetes-dashboard-6c664cf6c5 - kubernetes-dashboard-74c7f88bc5 - kubernetes-dashboard-79cfdfb4b6 - kubernetes-dashboard-7bc5c7d5c7 - kubernetes-dashboard-7f797f9cf7 - metrics-server-5b9455f59 - metrics-server-6779dddb9d - metrics-server-68585868b8 - metrics-server-74b99bc86d - metrics-server-77b5b684fb - metrics-server-77d9f5dd46 - metrics-server-7d49894b55 - metrics-server-85c45c6c5c - metrics-server-9fb55c4f8 - metrics-server-cc7467959 - nginx-ingress-contro - nginx-ingress-controller-57fd9d7494 - nginx-ingress-controller-595687c8c5 - nginx-ingress-controller-595cfd8467 - nginx-ingress-controller-5f75dd55f9 - nginx-ingress-controller-6cc9968cd9 - nginx-ingress-controller-6cff4fd476 - nginx-ingress-controller-747b497d9c - nginx-ingress-controller-7cfb57bbb9 - nginx-ingress-controller-866976474b - nginx-ingress-controller-897b77d5 - pghoard-fd6846dc - tiller-deploy-c6df96654- Resource: CronJobs AnomalyType: 'Found leftover cronjobs in namespace: kube-system' - etcd-backup - kube-job-cleanerkubectl-doctor|master kubectl-doctor|master e kubectl-doctor|master ex kubectl-doctor|master ext kubectl-doctor|master exi kubectl-doctor|master exit kubectl-doctor|master exit 106 | -------------------------------------------------------------------------------- /doctor.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: krew.googlecontainertools.github.com/v1alpha2 2 | kind: Plugin 3 | metadata: 4 | name: doctor 5 | spec: 6 | version: "v0.3.0" 7 | platforms: 8 | - uri: https://github.com/emirozer/kubectl-doctor/releases/download/0.3.1/kubectl-doctor_darwin_amd64 9 | sha256: 80e2f80b0552424ac2513a7aea27e93c2e4f72d2e59a3f7b55fce3869e8ef79f 10 | bin: kubectl-doctor 11 | files: 12 | - from: "*" 13 | to: "." 14 | selector: 15 | matchLabels: 16 | os: darwin 17 | arch: amd64 18 | - uri: https://github.com/emirozer/kubectl-doctor/releases/download/0.3.1/kubectl-doctor_darwin_arm64 19 | sha256: 9da5fed6df4597e0ce7d05dee789f0c36ee969619790cbaaedc1d0f645b3bd21 20 | bin: kubectl-doctor 21 | files: 22 | - from: "*" 23 | to: "." 24 | selector: 25 | matchLabels: 26 | os: darwin 27 | arch: arm64 28 | - uri: https://github.com/emirozer/kubectl-doctor/releases/download/0.3.1/kubectl-doctor_linux_amd64 29 | sha256: 0abd3ac3a02b1a9a04af7fb2fa7808ad937a1b48e7974414172da225e2737033 30 | bin: kubectl-doctor 31 | files: 32 | - from: "*" 33 | to: "." 34 | selector: 35 | matchLabels: 36 | os: linux 37 | arch: amd64 38 | - uri: https://github.com/emirozer/kubectl-doctor/releases/download/0.3.1/kubectl-doctor_linux_arm 39 | sha256: 58f48989a379f959bb8b114833c57540d3d3dde8ac2d5e3dda8f72d047af8aa1 40 | bin: kubectl-doctor 41 | files: 42 | - from: "*" 43 | to: "." 44 | selector: 45 | matchLabels: 46 | os: linux 47 | arch: arm 48 | - uri: https://github.com/emirozer/kubectl-doctor/releases/download/0.3.1/kubectl-doctor_windows_amd64.exe 49 | sha256: 0a0778c11b0d0ce2a46eafd600a2597473ebf9e1916b845c257ffade50d4654c 50 | bin: kubectl-doctor.exe 51 | files: 52 | - from: "*" 53 | to: "." 54 | selector: 55 | matchLabels: 56 | os: windows 57 | arch: amd64 58 | shortDescription: Scans your cluster and reports anomalies. 59 | homepage: https://github.com/emirozer/kubectl-doctor 60 | description: | 61 | This plugin is inspired by brew doctor. 62 | It will scan the active kube-context for anomalies or 63 | useful action points that it can report back to you. 64 | This plugin does not change any state or configuration. 65 | 66 | Please check the repository for an example report: 67 | 68 | * https://github.com/emirozer/kubectl-doctor 69 | caveats: | 70 | This plugin needs higher privileges on core API group. 71 | Potentially a ClusterRole that can get cluster-scoped resources. 72 | Such as nodes / all namespaces etc. 73 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: 612fdaeffecb9eed52955c77fb147d240bf2aae7e749d67b91f9795d85d8d584 2 | updated: 2019-08-28T13:58:56.45378+02:00 3 | imports: 4 | - name: cloud.google.com/go 5 | version: 3b1ae45394a234c385be014e9a488f2bb6eef821 6 | subpackages: 7 | - compute/metadata 8 | - internal 9 | - name: github.com/Azure/go-ansiterm 10 | version: d6e3b3328b783f23731bc4d058875b0371ff8109 11 | subpackages: 12 | - winterm 13 | - name: github.com/Azure/go-autorest 14 | version: ea233b6412b0421a65dc6160e16c893364664a95 15 | subpackages: 16 | - autorest 17 | - autorest/adal 18 | - autorest/azure 19 | - autorest/date 20 | - logger 21 | - version 22 | - name: github.com/davecgh/go-spew 23 | version: 782f4967f2dc4564575ca782fe2d04090b5faca8 24 | subpackages: 25 | - spew 26 | - name: github.com/dgrijalva/jwt-go 27 | version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20 28 | - name: github.com/docker/docker 29 | version: a9fbbdc8dd8794b20af358382ab780559bca589d 30 | subpackages: 31 | - api 32 | - api/types 33 | - api/types/blkiodev 34 | - api/types/container 35 | - api/types/events 36 | - api/types/filters 37 | - api/types/image 38 | - api/types/mount 39 | - api/types/network 40 | - api/types/registry 41 | - api/types/strslice 42 | - api/types/swarm 43 | - api/types/swarm/runtime 44 | - api/types/time 45 | - api/types/versions 46 | - api/types/volume 47 | - client 48 | - daemon/logger/jsonfilelog/jsonlog 49 | - pkg/jsonmessage 50 | - pkg/mount 51 | - pkg/parsers 52 | - pkg/parsers/operatingsystem 53 | - pkg/stdcopy 54 | - pkg/sysinfo 55 | - pkg/term 56 | - pkg/term/windows 57 | - name: github.com/docker/spdystream 58 | version: 449fdfce4d962303d702fec724ef0ad181c92528 59 | subpackages: 60 | - spdy 61 | - name: github.com/emicklei/go-restful 62 | version: ff4f55a206334ef123e4f79bbf348980da81ca46 63 | subpackages: 64 | - log 65 | - name: github.com/evanphx/json-patch 66 | version: 5858425f75500d40c52783dce87d085a483ce135 67 | - name: github.com/exponent-io/jsonpath 68 | version: d6023ce2651d8eafb5c75bb0c7167536102ec9f5 69 | - name: github.com/ghodss/yaml 70 | version: c7ce16629ff4cd059ed96ed06419dd3856fd3577 71 | - name: github.com/go-openapi/jsonpointer 72 | version: ef5f0afec364d3b9396b7b77b43dbe26bf1f8004 73 | - name: github.com/go-openapi/jsonreference 74 | version: 8483a886a90412cd6858df4ea3483dce9c8e35a3 75 | - name: github.com/go-openapi/spec 76 | version: 5bae59e25b21498baea7f9d46e9c147ec106a42e 77 | - name: github.com/go-openapi/swag 78 | version: 5899d5c5e619fda5fa86e14795a835f473ca284c 79 | - name: github.com/gogo/protobuf 80 | version: 342cbe0a04158f6dcb03ca0079991a51a4248c02 81 | subpackages: 82 | - proto 83 | - sortkeys 84 | - name: github.com/golang/protobuf 85 | version: b4deda0973fb4c70b50d226b1af49f3da59f5265 86 | subpackages: 87 | - proto 88 | - ptypes 89 | - ptypes/any 90 | - ptypes/duration 91 | - ptypes/timestamp 92 | - name: github.com/google/btree 93 | version: 7d79101e329e5a3adf994758c578dab82b90c017 94 | - name: github.com/google/gofuzz 95 | version: 24818f796faf91cd76ec7bddd72458fbced7a6c1 96 | - name: github.com/googleapis/gnostic 97 | version: 0c5108395e2debce0d731cf0287ddf7242066aba 98 | subpackages: 99 | - OpenAPIv2 100 | - compiler 101 | - extensions 102 | - name: github.com/gophercloud/gophercloud 103 | version: c818fa66e4c88b30db28038fe3f18f2f4a0db9a8 104 | subpackages: 105 | - openstack 106 | - openstack/identity/v2/tenants 107 | - openstack/identity/v2/tokens 108 | - openstack/identity/v3/tokens 109 | - openstack/utils 110 | - pagination 111 | - name: github.com/gregjones/httpcache 112 | version: 787624de3eb7bd915c329cba748687a3b22666a6 113 | subpackages: 114 | - diskcache 115 | - name: github.com/imdario/mergo 116 | version: 9316a62528ac99aaecb4e47eadd6dc8aa6533d58 117 | - name: github.com/inconshreveable/mousetrap 118 | version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 119 | - name: github.com/json-iterator/go 120 | version: ab8a2e0c74be9d3be70b3184d9acc634935ded82 121 | - name: github.com/konsorten/go-windows-terminal-sequences 122 | version: f55edac94c9bbba5d6182a4be46d86a2c9b5b50e 123 | - name: github.com/mailru/easyjson 124 | version: 2f5df55504ebc322e4d52d34df6a1f5b503bf26d 125 | subpackages: 126 | - buffer 127 | - jlexer 128 | - jwriter 129 | - name: github.com/MakeNowJust/heredoc 130 | version: bb23615498cded5e105af4ce27de75b089cbe851 131 | - name: github.com/mitchellh/go-wordwrap 132 | version: ad45545899c7b13c020ea92b2072220eefad42b8 133 | - name: github.com/modern-go/concurrent 134 | version: bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94 135 | - name: github.com/modern-go/reflect2 136 | version: 94122c33edd36123c84d5368cfb2b69df93a0ec8 137 | - name: github.com/peterbourgon/diskv 138 | version: 5f041e8faa004a95c88a202771f4cc3e991971e6 139 | - name: github.com/pkg/errors 140 | version: 27936f6d90f9c8e1145f11ed52ffffbfdb9e0af7 141 | - name: github.com/PuerkitoBio/purell 142 | version: 8a290539e2e8629dbc4e6bad948158f790ec31f4 143 | - name: github.com/PuerkitoBio/urlesc 144 | version: 5bd2802263f21d8788851d5305584c82a5c75d7e 145 | - name: github.com/russross/blackfriday 146 | version: 300106c228d52c8941d4b3de6054a6062a86dda3 147 | - name: github.com/shurcooL/sanitized_anchor_name 148 | version: 10ef21a441db47d8b13ebcc5fd2310f636973c77 149 | - name: github.com/sirupsen/logrus 150 | version: 2a22dbedbad1fd454910cd1f44f210ef90c28464 151 | - name: github.com/spf13/cobra 152 | version: 67fc4837d267bc9bfd6e47f77783fcc3dffc68de 153 | - name: github.com/spf13/pflag 154 | version: 24fa6976df40757dce6aea913e7b81ade90530e1 155 | - name: golang.org/x/crypto 156 | version: de0752318171da717af4ce24d0a2e8626afaeb11 157 | subpackages: 158 | - ssh/terminal 159 | - name: golang.org/x/net 160 | version: 65e2d4e15006aab9813ff8769e768bbf4bb667a0 161 | subpackages: 162 | - context 163 | - context/ctxhttp 164 | - http/httpguts 165 | - http2 166 | - http2/hpack 167 | - idna 168 | - name: golang.org/x/oauth2 169 | version: a6bd8cefa1811bd24b86f8902872e4e8225f74c4 170 | subpackages: 171 | - google 172 | - internal 173 | - jws 174 | - jwt 175 | - name: golang.org/x/sys 176 | version: 95c6576299259db960f6c5b9b69ea52422860fce 177 | subpackages: 178 | - unix 179 | - windows 180 | - name: golang.org/x/text 181 | version: b19bf474d317b857955b12035d2c5acb57ce8b01 182 | subpackages: 183 | - cases 184 | - encoding 185 | - encoding/internal 186 | - encoding/internal/identifier 187 | - encoding/unicode 188 | - internal 189 | - internal/tag 190 | - internal/utf8internal 191 | - language 192 | - runes 193 | - secure/bidirule 194 | - secure/precis 195 | - transform 196 | - unicode/bidi 197 | - unicode/norm 198 | - width 199 | - name: golang.org/x/time 200 | version: f51c12702a4d776e4c1fa9b0fabab841babae631 201 | subpackages: 202 | - rate 203 | - name: google.golang.org/appengine 204 | version: 54a98f90d1c46b7731eb8fb305d2a321c30ef610 205 | subpackages: 206 | - internal 207 | - internal/app_identity 208 | - internal/base 209 | - internal/datastore 210 | - internal/log 211 | - internal/modules 212 | - internal/remote_api 213 | - internal/urlfetch 214 | - urlfetch 215 | - name: gopkg.in/inf.v0 216 | version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 217 | - name: gopkg.in/yaml.v2 218 | version: 5420a8b6744d3b0345ab293f6fcba19c978f1183 219 | - name: k8s.io/api 220 | version: 40a48860b5abbba9aa891b02b32da429b08d96a0 221 | subpackages: 222 | - admission/v1beta1 223 | - admissionregistration/v1beta1 224 | - apps/v1 225 | - apps/v1beta1 226 | - apps/v1beta2 227 | - auditregistration/v1alpha1 228 | - authentication/v1 229 | - authentication/v1beta1 230 | - authorization/v1 231 | - authorization/v1beta1 232 | - autoscaling/v1 233 | - autoscaling/v2beta1 234 | - autoscaling/v2beta2 235 | - batch/v1 236 | - batch/v1beta1 237 | - batch/v2alpha1 238 | - certificates/v1beta1 239 | - coordination/v1 240 | - coordination/v1beta1 241 | - core/v1 242 | - events/v1beta1 243 | - extensions/v1beta1 244 | - imagepolicy/v1alpha1 245 | - networking/v1 246 | - networking/v1beta1 247 | - node/v1alpha1 248 | - node/v1beta1 249 | - policy/v1beta1 250 | - rbac/v1 251 | - rbac/v1alpha1 252 | - rbac/v1beta1 253 | - scheduling/v1 254 | - scheduling/v1alpha1 255 | - scheduling/v1beta1 256 | - settings/v1alpha1 257 | - storage/v1 258 | - storage/v1alpha1 259 | - storage/v1beta1 260 | - name: k8s.io/apimachinery 261 | version: d7deff9243b165ee192f5551710ea4285dcfd615 262 | subpackages: 263 | - pkg/api/apitesting 264 | - pkg/api/apitesting/fuzzer 265 | - pkg/api/apitesting/roundtrip 266 | - pkg/api/equality 267 | - pkg/api/errors 268 | - pkg/api/meta 269 | - pkg/api/resource 270 | - pkg/api/validation 271 | - pkg/apis/meta/fuzzer 272 | - pkg/apis/meta/internalversion 273 | - pkg/apis/meta/v1 274 | - pkg/apis/meta/v1/unstructured 275 | - pkg/apis/meta/v1/unstructured/unstructuredscheme 276 | - pkg/apis/meta/v1/validation 277 | - pkg/apis/meta/v1beta1 278 | - pkg/conversion 279 | - pkg/conversion/queryparams 280 | - pkg/fields 281 | - pkg/labels 282 | - pkg/runtime 283 | - pkg/runtime/schema 284 | - pkg/runtime/serializer 285 | - pkg/runtime/serializer/json 286 | - pkg/runtime/serializer/protobuf 287 | - pkg/runtime/serializer/recognizer 288 | - pkg/runtime/serializer/streaming 289 | - pkg/runtime/serializer/versioning 290 | - pkg/selection 291 | - pkg/types 292 | - pkg/util/cache 293 | - pkg/util/clock 294 | - pkg/util/diff 295 | - pkg/util/errors 296 | - pkg/util/framer 297 | - pkg/util/httpstream 298 | - pkg/util/httpstream/spdy 299 | - pkg/util/intstr 300 | - pkg/util/json 301 | - pkg/util/mergepatch 302 | - pkg/util/naming 303 | - pkg/util/net 304 | - pkg/util/remotecommand 305 | - pkg/util/runtime 306 | - pkg/util/sets 307 | - pkg/util/strategicpatch 308 | - pkg/util/validation 309 | - pkg/util/validation/field 310 | - pkg/util/wait 311 | - pkg/util/yaml 312 | - pkg/version 313 | - pkg/watch 314 | - third_party/forked/golang/json 315 | - third_party/forked/golang/netutil 316 | - third_party/forked/golang/reflect 317 | - name: k8s.io/cli-runtime 318 | version: 2899ed30580fdbc8286718edb4382b529463099d 319 | subpackages: 320 | - pkg/genericclioptions 321 | - pkg/kustomize 322 | - pkg/kustomize/k8sdeps 323 | - pkg/kustomize/k8sdeps/configmapandsecret 324 | - pkg/kustomize/k8sdeps/kunstruct 325 | - pkg/kustomize/k8sdeps/kv 326 | - pkg/kustomize/k8sdeps/transformer 327 | - pkg/kustomize/k8sdeps/transformer/hash 328 | - pkg/kustomize/k8sdeps/transformer/patch 329 | - pkg/kustomize/k8sdeps/validator 330 | - pkg/printers 331 | - pkg/resource 332 | - name: k8s.io/client-go 333 | version: 6ee68ca5fd8355d024d02f9db0b3b667e8357a0f 334 | subpackages: 335 | - discovery 336 | - discovery/cached/disk 337 | - dynamic 338 | - kubernetes 339 | - kubernetes/scheme 340 | - kubernetes/typed/admissionregistration/v1beta1 341 | - kubernetes/typed/apps/v1 342 | - kubernetes/typed/apps/v1beta1 343 | - kubernetes/typed/apps/v1beta2 344 | - kubernetes/typed/auditregistration/v1alpha1 345 | - kubernetes/typed/authentication/v1 346 | - kubernetes/typed/authentication/v1beta1 347 | - kubernetes/typed/authorization/v1 348 | - kubernetes/typed/authorization/v1beta1 349 | - kubernetes/typed/autoscaling/v1 350 | - kubernetes/typed/autoscaling/v2beta1 351 | - kubernetes/typed/autoscaling/v2beta2 352 | - kubernetes/typed/batch/v1 353 | - kubernetes/typed/batch/v1beta1 354 | - kubernetes/typed/batch/v2alpha1 355 | - kubernetes/typed/certificates/v1beta1 356 | - kubernetes/typed/coordination/v1 357 | - kubernetes/typed/coordination/v1beta1 358 | - kubernetes/typed/core/v1 359 | - kubernetes/typed/events/v1beta1 360 | - kubernetes/typed/extensions/v1beta1 361 | - kubernetes/typed/networking/v1 362 | - kubernetes/typed/networking/v1beta1 363 | - kubernetes/typed/node/v1alpha1 364 | - kubernetes/typed/node/v1beta1 365 | - kubernetes/typed/policy/v1beta1 366 | - kubernetes/typed/rbac/v1 367 | - kubernetes/typed/rbac/v1alpha1 368 | - kubernetes/typed/rbac/v1beta1 369 | - kubernetes/typed/scheduling/v1 370 | - kubernetes/typed/scheduling/v1alpha1 371 | - kubernetes/typed/scheduling/v1beta1 372 | - kubernetes/typed/settings/v1alpha1 373 | - kubernetes/typed/storage/v1 374 | - kubernetes/typed/storage/v1alpha1 375 | - kubernetes/typed/storage/v1beta1 376 | - pkg/apis/clientauthentication 377 | - pkg/apis/clientauthentication/v1alpha1 378 | - pkg/apis/clientauthentication/v1beta1 379 | - pkg/version 380 | - plugin/pkg/client/auth 381 | - plugin/pkg/client/auth/azure 382 | - plugin/pkg/client/auth/exec 383 | - plugin/pkg/client/auth/gcp 384 | - plugin/pkg/client/auth/oidc 385 | - plugin/pkg/client/auth/openstack 386 | - rest 387 | - rest/watch 388 | - restmapper 389 | - scale 390 | - scale/scheme 391 | - scale/scheme/appsint 392 | - scale/scheme/appsv1beta1 393 | - scale/scheme/appsv1beta2 394 | - scale/scheme/autoscalingv1 395 | - scale/scheme/extensionsint 396 | - scale/scheme/extensionsv1beta1 397 | - third_party/forked/golang/template 398 | - tools/auth 399 | - tools/clientcmd 400 | - tools/clientcmd/api 401 | - tools/clientcmd/api/latest 402 | - tools/clientcmd/api/v1 403 | - tools/metrics 404 | - tools/reference 405 | - tools/remotecommand 406 | - transport 407 | - transport/spdy 408 | - util/cert 409 | - util/connrotation 410 | - util/exec 411 | - util/flowcontrol 412 | - util/homedir 413 | - util/jsonpath 414 | - util/keyutil 415 | - name: k8s.io/klog 416 | version: 8e90cee79f823779174776412c13478955131846 417 | - name: k8s.io/kube-openapi 418 | version: b3a7cee44a305be0a69e1b9ac03018307287e1b0 419 | subpackages: 420 | - pkg/common 421 | - pkg/util/proto 422 | - pkg/util/proto/validation 423 | - name: k8s.io/kubernetes 424 | version: 641856db18352033a0d96dbc99153fa3b27298e5 425 | subpackages: 426 | - pkg/kubectl/cmd/util 427 | - pkg/kubectl/cmd/util/openapi 428 | - pkg/kubectl/cmd/util/openapi/validation 429 | - pkg/kubectl/scheme 430 | - pkg/kubectl/util/templates 431 | - pkg/kubectl/util/term 432 | - pkg/kubectl/validation 433 | - pkg/util/interrupt 434 | - pkg/version 435 | - name: k8s.io/utils 436 | version: c2654d5206da6b7b6ace12841e8f359bb89b443c 437 | subpackages: 438 | - buffer 439 | - exec 440 | - integer 441 | - trace 442 | - name: sigs.k8s.io/kustomize 443 | version: a6f65144121d1955266b0cd836ce954c04122dc8 444 | subpackages: 445 | - pkg/commands/build 446 | - pkg/constants 447 | - pkg/expansion 448 | - pkg/factory 449 | - pkg/fs 450 | - pkg/git 451 | - pkg/gvk 452 | - pkg/ifc 453 | - pkg/ifc/transformer 454 | - pkg/image 455 | - pkg/internal/error 456 | - pkg/loader 457 | - pkg/patch 458 | - pkg/patch/transformer 459 | - pkg/resid 460 | - pkg/resmap 461 | - pkg/resource 462 | - pkg/target 463 | - pkg/transformers 464 | - pkg/transformers/config 465 | - pkg/transformers/config/defaultconfig 466 | - pkg/types 467 | - name: sigs.k8s.io/yaml 468 | version: fd68e9863619f6ec2fdd8625fe1f02e7c877e480 469 | testImports: [] 470 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github/emirozer/kubectl-doctor 2 | import: 3 | - package: k8s.io/client-go 4 | version: v11.0.0 5 | subpackages: 6 | - rest -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/emirozer/kubectl-doctor 2 | 3 | go 1.12 4 | 5 | require ( 6 | cloud.google.com/go v0.0.0-20160913182117-3b1ae45394a2 7 | github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 8 | github.com/Azure/go-autorest v11.1.0+incompatible 9 | github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd 10 | github.com/PuerkitoBio/purell v1.1.0 11 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 12 | github.com/coreos/go-semver v0.2.0 13 | github.com/davecgh/go-spew v1.1.1 14 | github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda 15 | github.com/docker/docker v0.0.0-20180612054059-a9fbbdc8dd87 16 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 17 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 18 | github.com/evanphx/json-patch v4.2.0+incompatible 19 | github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d 20 | github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4 21 | github.com/go-openapi/jsonpointer v0.17.0 22 | github.com/go-openapi/jsonreference v0.17.0 23 | github.com/go-openapi/spec v0.17.2 24 | github.com/go-openapi/swag v0.17.0 25 | github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415 26 | github.com/golang/protobuf v1.2.0 27 | github.com/google/btree v0.0.0-20160524151835-7d79101e329e 28 | github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf 29 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d 30 | github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 31 | github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7 32 | github.com/imdario/mergo v0.3.5 33 | github.com/inconshreveable/mousetrap v1.0.0 34 | github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be 35 | github.com/konsorten/go-windows-terminal-sequences v1.0.2 36 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 37 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 38 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd 39 | github.com/modern-go/reflect2 v1.0.1 40 | github.com/peterbourgon/diskv v2.0.1+incompatible 41 | github.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9 42 | github.com/russross/blackfriday v1.5.2 43 | github.com/shurcooL/sanitized_anchor_name v0.0.0-20151028001915-10ef21a441db 44 | github.com/sirupsen/logrus v0.0.0-20190518135202-2a22dbedbad1 45 | github.com/spf13/cobra v0.0.4 46 | github.com/spf13/pflag v1.0.3 47 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 48 | golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 49 | golang.org/x/oauth2 v0.0.0-20170412232759-a6bd8cefa181 50 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894 51 | golang.org/x/text v0.3.0 52 | golang.org/x/time v0.0.0-20161028155119-f51c12702a4d 53 | google.golang.org/appengine v1.5.0 54 | gopkg.in/inf.v0 v0.9.0 55 | gopkg.in/yaml.v2 v2.2.2 56 | k8s.io/api v0.0.0-20190313235455-40a48860b5ab 57 | k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1 58 | k8s.io/cli-runtime v0.0.0-20190314001948-2899ed30580f 59 | k8s.io/client-go v11.0.0+incompatible 60 | k8s.io/klog v0.0.0-20190306015804-8e90cee79f82 61 | k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 62 | k8s.io/kubernetes v1.14.0 63 | k8s.io/utils v0.0.0-20190221042446-c2654d5206da 64 | sigs.k8s.io/kustomize v2.0.3+incompatible 65 | sigs.k8s.io/yaml v1.1.0 66 | ) 67 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.0.0-20160913182117-3b1ae45394a2 h1:BD5IDvMK5RQqnDi7Fbizwoad9Uus/Hs/xJ1zMYVXlS8= 2 | cloud.google.com/go v0.0.0-20160913182117-3b1ae45394a2/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= 4 | github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= 5 | github.com/Azure/go-autorest v11.1.0+incompatible h1:9DfMsQdUMEtg1jKRTjtkNZsvOuZXJOMl4dN1kiQwAc8= 6 | github.com/Azure/go-autorest v11.1.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= 7 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 8 | github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= 9 | github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= 10 | github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 11 | github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4= 12 | github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 13 | github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 14 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= 15 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 16 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 17 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 18 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 19 | github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= 20 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 21 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 22 | github.com/davecgh/go-spew v0.0.0-20170626231645-782f4967f2dc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 23 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 24 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 25 | github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda h1:NyywMz59neOoVRFDz+ccfKWxn784fiHMDnZSy6T+JXY= 26 | github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 27 | github.com/docker/docker v0.0.0-20180612054059-a9fbbdc8dd87 h1:a9PI9K38c+lqsMzO5itpsaXd9BhUYWTC9GM7TN5Vn0U= 28 | github.com/docker/docker v0.0.0-20180612054059-a9fbbdc8dd87/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 29 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= 30 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= 31 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= 32 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 33 | github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= 34 | github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 35 | github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= 36 | github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= 37 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 38 | github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4 h1:bRzFpEzvausOAt4va+I/22BZ1vXDtERngp0BNYDKej0= 39 | github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 40 | github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0= 41 | github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= 42 | github.com/go-openapi/jsonreference v0.17.0 h1:yJW3HCkTHg7NOA+gZ83IPHzUSnUzGXhGmsdiCcMexbA= 43 | github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= 44 | github.com/go-openapi/spec v0.17.2 h1:eb2NbuCnoe8cWAxhtK6CfMWUYmiFEZJ9Hx3Z2WRwJ5M= 45 | github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= 46 | github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880= 47 | github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= 48 | github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415 h1:WSBJMqJbLxsn+bTCPyPYZfqHdJmc8MK4wrBjMft6BAM= 49 | github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 50 | github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 51 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 52 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 53 | github.com/google/btree v0.0.0-20160524151835-7d79101e329e h1:JHB7F/4TJCrYBW8+GZO8VkWDj1jxcWuCl6uxKODiyi4= 54 | github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 55 | github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= 56 | github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= 57 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= 58 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= 59 | github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 h1:L9JPKrtsHMQ4VCRQfHvbbHBfB2Urn8xf6QZeXZ+OrN4= 60 | github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= 61 | github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7 h1:6TSoaYExHper8PYsJu23GWVNOyYRCSnIFyxKgLSZ54w= 62 | github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 63 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 64 | github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= 65 | github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 66 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 67 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 68 | github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE= 69 | github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 70 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 71 | github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= 72 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 73 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 74 | github.com/mailru/easyjson v0.0.0-20170624190925-2f5df55504eb/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 75 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= 76 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 77 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 78 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= 79 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 80 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 81 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 82 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 83 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 84 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 85 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 86 | github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= 87 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 88 | github.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9 h1:dIsTcVF0w9viTLHXUEkDI7cXITMe+M/MRRM2MwisVow= 89 | github.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 90 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 91 | github.com/russross/blackfriday v0.0.0-20151117072312-300106c228d5/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 92 | github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= 93 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 94 | github.com/shurcooL/sanitized_anchor_name v0.0.0-20151028001915-10ef21a441db/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 95 | github.com/sirupsen/logrus v0.0.0-20190518135202-2a22dbedbad1 h1:/gZayC/W/zHiCRBsDcQhz6/q0ELIurWv9EmN480eZzo= 96 | github.com/sirupsen/logrus v0.0.0-20190518135202-2a22dbedbad1/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 97 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 98 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 99 | github.com/spf13/cobra v0.0.4 h1:S0tLZ3VOKl2Te0hpq8+ke0eSJPfCnNTPiDlsfwi1/NE= 100 | github.com/spf13/cobra v0.0.4/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 101 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 102 | github.com/spf13/pflag v0.0.0-20181223182923-24fa6976df40/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 103 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 104 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 105 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 106 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 107 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 108 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 109 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 110 | golang.org/x/crypto v0.0.0-20180808211826-de0752318171/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 111 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= 112 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 113 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 114 | golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 115 | golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 h1:bfLnR+k0tq5Lqt6dflRLcZiz6UaXCMt3vhYJ1l4FQ80= 116 | golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 117 | golang.org/x/oauth2 v0.0.0-20170412232759-a6bd8cefa181 h1:/4OaQ4bC66Oq9JDhUnxTjBGt8XBhDuwgMRXHgvfcCUY= 118 | golang.org/x/oauth2 v0.0.0-20170412232759-a6bd8cefa181/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 119 | golang.org/x/sys v0.0.0-20171031081856-95c657629925/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 120 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 121 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= 122 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 123 | golang.org/x/text v0.0.0-20170810154203-b19bf474d317/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 124 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 125 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 126 | golang.org/x/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g= 127 | golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 128 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 129 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 130 | gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o= 131 | gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 132 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 133 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 134 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 135 | k8s.io/api v0.0.0-20190313235455-40a48860b5ab h1:DG9A67baNpoeweOy2spF1OWHhnVY5KR7/Ek/+U1lVZc= 136 | k8s.io/api v0.0.0-20190313235455-40a48860b5ab/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= 137 | k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1 h1:IS7K02iBkQXpCeieSiyJjGoLSdVOv2DbPaWHJ+ZtgKg= 138 | k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= 139 | k8s.io/cli-runtime v0.0.0-20190314001948-2899ed30580f h1:gRAqn9Z3rp62UwLU3PdC7Lhmsvd3e9PXLsq7EG+bq1s= 140 | k8s.io/cli-runtime v0.0.0-20190314001948-2899ed30580f/go.mod h1:qWnH3/b8sp/l7EvlDh7ulDU3UWA4P4N1NFbEEP791tM= 141 | k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o= 142 | k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= 143 | k8s.io/klog v0.0.0-20190306015804-8e90cee79f82 h1:SHucoAy7lRb+w5oC/hbXyZg+zX+Wftn6hD4tGzHCVqA= 144 | k8s.io/klog v0.0.0-20190306015804-8e90cee79f82/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 145 | k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI= 146 | k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= 147 | k8s.io/kubernetes v1.14.0 h1:6T2iAEoOYQnzQb3WvPlUkcczEEXZ7+YPlAO8olwujRw= 148 | k8s.io/kubernetes v1.14.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= 149 | k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4= 150 | k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= 151 | sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= 152 | sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= 153 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 154 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 155 | -------------------------------------------------------------------------------- /pkg/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "flag" 5 | "github.com/pkg/errors" 6 | "os" 7 | "path/filepath" 8 | "runtime" 9 | "strings" 10 | 11 | log "github.com/sirupsen/logrus" 12 | "k8s.io/client-go/kubernetes" 13 | restclient "k8s.io/client-go/rest" 14 | "k8s.io/client-go/tools/clientcmd" 15 | // utilities for kubernetes integration 16 | _ "k8s.io/client-go/plugin/pkg/client/auth" 17 | ) 18 | 19 | func init() { 20 | 21 | log.SetFormatter(&log.TextFormatter{}) 22 | log.SetOutput(os.Stdout) 23 | // Only log the info severity or above. 24 | log.SetLevel(log.InfoLevel) 25 | } 26 | 27 | // InitClient - Kubernetes Client 28 | func InitClient() *kubernetes.Clientset { 29 | // determine which kubeconfig to use 30 | var kubeconfig *string 31 | var kubeconfigbase string 32 | var kubecontext *string 33 | 34 | kubeconfigFromEnv, err := tryGetKubeConfigFromEnvVar() 35 | if err != nil { 36 | log.Warn(err.Error()) 37 | } else { 38 | return kubeconfigFromEnv 39 | } 40 | 41 | // creating a client from env didn't work try auto discover 42 | if home := homeDir(); home != "" { 43 | kubeconfigbase = filepath.Join(home, ".kube", "config") 44 | } 45 | 46 | kubeconfig = flag.String( 47 | "kubeconfig", 48 | kubeconfigbase, 49 | "(optional) absolute path to the kubeconfig file", 50 | ) 51 | kubecontext = flag.String( 52 | "context", 53 | "", 54 | "(optional) name of kube context", 55 | ) 56 | 57 | flag.Parse() 58 | 59 | config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( 60 | &clientcmd.ClientConfigLoadingRules{ExplicitPath: *kubeconfig}, 61 | &clientcmd.ConfigOverrides{ 62 | CurrentContext: *kubecontext, 63 | }).ClientConfig() 64 | if err != nil { 65 | panic(err.Error()) 66 | } 67 | 68 | csBackup, err := getClientSetFromConfig(config) 69 | if err != nil { 70 | panic(err.Error()) 71 | } 72 | return csBackup 73 | } 74 | 75 | func tryGetKubeConfigFromEnvVar() (*kubernetes.Clientset, error) { 76 | env_config := os.Getenv("KUBECONFIG") 77 | var delimeter string 78 | 79 | if env_config != "" { 80 | if runtime.GOOS == "windows" { 81 | delimeter = ";" 82 | } else { 83 | delimeter = ":" 84 | } 85 | if strings.Contains(env_config, delimeter) { 86 | // kubeconfig env var is a list, handle that 87 | log.WithFields(log.Fields{ 88 | "kubeconfiglist": clientcmd.NewDefaultClientConfigLoadingRules().Precedence, 89 | }).Warn("discovered a list of kubeconfigs & will respect current-context!") 90 | for _, i := range clientcmd.NewDefaultClientConfigLoadingRules().Precedence { 91 | // if a problem occurs here it generally means that we are trying to build a client 92 | // that does not respect the current-context so if that happens just pass that kubeconfig file 93 | config, err := clientcmd.BuildConfigFromFlags("", i) 94 | if err != nil { 95 | continue 96 | } 97 | cs, err := getClientSetFromConfig(config) 98 | if err != nil { 99 | continue 100 | } 101 | return cs, nil 102 | } 103 | 104 | } 105 | 106 | config, err := clientcmd.BuildConfigFromFlags("", env_config) 107 | cs, err := getClientSetFromConfig(config) 108 | if err != nil { 109 | return nil, err 110 | } 111 | return cs, nil 112 | 113 | } else { 114 | return nil, errors.New("KUBECONFIG env var not found falling back to auto discovery!") 115 | } 116 | } 117 | 118 | func getClientSetFromConfig(config *restclient.Config) (*kubernetes.Clientset, error) { 119 | clientset, err := kubernetes.NewForConfig(config) 120 | if err != nil { 121 | return nil, err 122 | } 123 | return clientset, nil 124 | } 125 | 126 | func homeDir() string { 127 | if h := os.Getenv("HOME"); h != "" { 128 | return h 129 | } 130 | // windows case 131 | return os.Getenv("USERPROFILE") 132 | } 133 | -------------------------------------------------------------------------------- /pkg/plugin/cmd.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/coreos/go-semver/semver" 8 | "github.com/emirozer/kubectl-doctor/pkg/client" 9 | "github.com/emirozer/kubectl-doctor/pkg/triage" 10 | "github.com/pkg/errors" 11 | log "github.com/sirupsen/logrus" 12 | "github.com/spf13/cobra" 13 | "gopkg.in/yaml.v2" 14 | "k8s.io/apimachinery/pkg/apis/meta/v1" 15 | "k8s.io/cli-runtime/pkg/genericclioptions" 16 | "k8s.io/client-go/kubernetes" 17 | coreclient "k8s.io/client-go/kubernetes/typed/core/v1" 18 | restclient "k8s.io/client-go/rest" 19 | cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" 20 | ) 21 | 22 | const ( 23 | example = ` 24 | # triage everything in the cluster 25 | kubectl doctor 26 | ` 27 | longDesc = ` 28 | kubectl-doctor plugin will scan the given k8s cluster for any kind of anomalies and reports back to its user. 29 | example anomalies: 30 | * deployments that are older than 30d with 0 available, 31 | * deployments that do not have minimum availability, 32 | * kubernetes nodes cpu usage or memory usage too high. or too low to report scaledown possiblity 33 | ` 34 | 35 | usageError = "expects no flags .. 'doctor' for doctor command" 36 | ) 37 | 38 | const K8S_CLIENT_VERSION = "11.0.0" 39 | 40 | var ( 41 | clientset *kubernetes.Clientset 42 | ) 43 | 44 | func init() { 45 | log.SetFormatter(&log.TextFormatter{}) 46 | log.SetOutput(os.Stdout) 47 | // Only log the info severity or above. 48 | log.SetLevel(log.InfoLevel) 49 | clientset = client.InitClient() 50 | } 51 | 52 | // DoctorOptions specify what the doctor is going to do 53 | type DoctorOptions struct { 54 | FetchedNamespaces []string 55 | 56 | // Doctor options 57 | DeploymentOnly bool 58 | FullScan bool 59 | Flags *genericclioptions.ConfigFlags 60 | CoreClient coreclient.CoreV1Interface 61 | RESTClient *restclient.RESTClient 62 | KubeCli *kubernetes.Clientset 63 | Args []string 64 | Config *restclient.Config 65 | } 66 | 67 | // NewDoctorOptions new doctor options initializer 68 | func NewDoctorOptions() *DoctorOptions { 69 | return &DoctorOptions{ 70 | Flags: genericclioptions.NewConfigFlags(true), 71 | } 72 | } 73 | 74 | // NewDoctorCmd returns a cobra command wrapping DoctorOptions 75 | func NewDoctorCmd() *cobra.Command { 76 | 77 | opts := NewDoctorOptions() 78 | 79 | cmd := &cobra.Command{ 80 | Use: "doctor", 81 | Short: "start triage for current targeted kubernetes cluster", 82 | Long: longDesc, 83 | Example: example, 84 | Run: func(c *cobra.Command, args []string) { 85 | argsLenAtDash := c.ArgsLenAtDash() 86 | cmdutil.CheckErr(opts.Complete(c, args, argsLenAtDash)) 87 | cmdutil.CheckErr(opts.Validate()) 88 | cmdutil.CheckErr(opts.Run()) 89 | }, 90 | } 91 | cmd.Flags().BoolVar(&opts.DeploymentOnly, "deployment-only", false, 92 | "Only triage deployments in a given namespace") 93 | 94 | opts.Flags.AddFlags(cmd.Flags()) 95 | 96 | return cmd 97 | } 98 | 99 | // Complete populate default values from KUBECONFIG file, sets up the clients 100 | func (o *DoctorOptions) Complete(cmd *cobra.Command, args []string, argsLenAtDash int) error { 101 | 102 | o.Args = args 103 | if len(args) == 0 { 104 | log.Info("Going for a full scan as no flags are set!") 105 | o.FullScan = true 106 | } 107 | o.KubeCli = clientset 108 | 109 | var err error 110 | 111 | configLoader := o.Flags.ToRawKubeConfigLoader() 112 | 113 | matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(o.Flags) 114 | f := cmdutil.NewFactory(matchVersionKubeConfigFlags) 115 | 116 | o.RESTClient, err = f.RESTClient() 117 | if err != nil { 118 | return err 119 | } 120 | log.Info("Retrieving necessary clientset for targeted k8s cluster.") 121 | o.CoreClient = clientset.CoreV1() 122 | 123 | fetchedNamespaces, _ := o.CoreClient.Namespaces().List(v1.ListOptions{}) 124 | for _, i := range fetchedNamespaces.Items { 125 | o.FetchedNamespaces = append(o.FetchedNamespaces, i.GetName()) 126 | } 127 | log.Info("") 128 | log.Info("Fetched namespaces: ", o.FetchedNamespaces) 129 | log.Info("") 130 | 131 | o.Config, err = configLoader.ClientConfig() 132 | if err != nil { 133 | return err 134 | } 135 | 136 | return nil 137 | } 138 | 139 | // Validate validate before the run that the namespace list cannot be empty(somehow?) 140 | func (o *DoctorOptions) Validate() error { 141 | if len(o.FetchedNamespaces) == 0 { 142 | return errors.New("namespace must be specified/retrieved properly!") 143 | } 144 | 145 | // compat check 146 | serverVersion, err := o.KubeCli.ServerVersion() 147 | if err != nil { 148 | return err 149 | } 150 | // vx.x.x to x.x.x 151 | serverVersionStr := serverVersion.String()[1:] 152 | 153 | serverSemVer := semver.New(serverVersionStr) 154 | if serverSemVer.Major != 1 || serverSemVer.Minor != 14 { 155 | log.Warn("doctor's client-go version: " + K8S_CLIENT_VERSION + " is not fully compatible with your k8s server version: " + serverVersionStr) 156 | log.Warn("https://github.com/kubernetes/client-go#compatibility-matrix") 157 | log.Warn("doctor run will be based on best-effort delivery") 158 | } 159 | return nil 160 | } 161 | 162 | // Run doctor run 163 | func (o *DoctorOptions) Run() error { 164 | // report setup 165 | report := make(map[interface{}][]*triage.Triage) 166 | report["TriageReport"] = make([]*triage.Triage, 0) 167 | 168 | // triage cluster crucial components starts 169 | log.Info("Starting triage of cluster crucial component health checks.") 170 | componentsTriage, err := triage.TriageComponents(o.CoreClient) 171 | if err != nil { 172 | return err 173 | } 174 | if len(componentsTriage.Anomalies) > 0 { 175 | report["TriageReport"] = append(report["TriageReport"], componentsTriage) 176 | } 177 | // triage cluster crucial components ends 178 | 179 | // triage nodes stars 180 | log.Info("Starting triage of nodes that form the cluster.") 181 | nodesTriage, err := triage.TriageNodes(o.CoreClient) 182 | if err != nil { 183 | return err 184 | } 185 | if len(nodesTriage.Anomalies) > 0 { 186 | report["TriageReport"] = append(report["TriageReport"], nodesTriage) 187 | } 188 | // triage nodes ends 189 | 190 | // triage endpoints starts 191 | log.Info("Starting triage of cluster-wide Endpoints resources.") 192 | 193 | endpointsTriage, err := triage.TriageEndpoints(o.CoreClient) 194 | if err != nil { 195 | return err 196 | } 197 | if len(endpointsTriage.Anomalies) > 0 { 198 | report["TriageReport"] = append(report["TriageReport"], endpointsTriage) 199 | } 200 | // triage endpoints ends 201 | 202 | // triage pvc starts 203 | log.Info("Starting triage of cluster-wide pvc resources.") 204 | pvcTriage, err := triage.TriagePVC(o.CoreClient) 205 | if err != nil { 206 | return err 207 | } 208 | if len(pvcTriage.Anomalies) > 0 { 209 | report["TriageReport"] = append(report["TriageReport"], pvcTriage) 210 | } 211 | // triage pvc ends 212 | 213 | // triage pv starts 214 | log.Info("Starting triage of cluster-wide pv resources.") 215 | pvTriage, err := triage.TriagePV(o.CoreClient) 216 | if err != nil { 217 | return err 218 | } 219 | if len(pvTriage.Anomalies) > 0 { 220 | report["TriageReport"] = append(report["TriageReport"], pvTriage) 221 | } 222 | // triage pv ends 223 | 224 | // triage deployments starts 225 | log.Info("Starting triage of deployment resources across cluster") 226 | for _, ns := range o.FetchedNamespaces { 227 | odeploymentTriage, err := triage.OrphanedDeployments(o.KubeCli, ns) 228 | if err != nil { 229 | return err 230 | } 231 | if len(odeploymentTriage.Anomalies) > 0 { 232 | report["TriageReport"] = append(report["TriageReport"], odeploymentTriage) 233 | } 234 | 235 | ldeploymentTriage, err := triage.LeftOverDeployments(o.KubeCli, ns) 236 | if err != nil { 237 | return err 238 | } 239 | if len(ldeploymentTriage.Anomalies) > 0 { 240 | report["TriageReport"] = append(report["TriageReport"], ldeploymentTriage) 241 | } 242 | 243 | } 244 | 245 | // triage deployments ends 246 | 247 | // triage replicasets starts 248 | log.Info("Starting triage of replicasets resources across cluster") 249 | for _, ns := range o.FetchedNamespaces { 250 | orsTriage, err := triage.OrphanedReplicaSet(o.KubeCli, ns) 251 | if err != nil { 252 | return err 253 | } 254 | if len(orsTriage.Anomalies) > 0 { 255 | report["TriageReport"] = append(report["TriageReport"], orsTriage) 256 | } 257 | lrsTriage, err := triage.LeftOverReplicaSet(o.KubeCli, ns) 258 | if err != nil { 259 | return err 260 | } 261 | if len(lrsTriage.Anomalies) > 0 { 262 | report["TriageReport"] = append(report["TriageReport"], lrsTriage) 263 | } 264 | } 265 | 266 | // triage replicasets ends 267 | 268 | // triage jobs starts 269 | log.Info("Starting triage of cronjob resources across cluster") 270 | var jobsTriage *triage.Triage 271 | for _, ns := range o.FetchedNamespaces { 272 | jobsTriage, err = triage.LeftoverJobs(o.KubeCli, ns) 273 | if err != nil { 274 | return err 275 | } 276 | if len(jobsTriage.Anomalies) > 0 { 277 | report["TriageReport"] = append(report["TriageReport"], jobsTriage) 278 | } 279 | } 280 | // triage jobs end 281 | 282 | // triage ingresses starts 283 | log.Info("Starting triage of ingress resources across cluster") 284 | var ingressTriage *triage.Triage 285 | for _, ns := range o.FetchedNamespaces { 286 | ingressTriage, err = triage.LeftoverIngresses(o.KubeCli, ns) 287 | if err != nil { 288 | return err 289 | } 290 | if len(ingressTriage.Anomalies) > 0 { 291 | report["TriageReport"] = append(report["TriageReport"], ingressTriage) 292 | } 293 | } 294 | // triage ingresses ends 295 | 296 | // yaml outputter 297 | if len(report["TriageReport"]) > 0 { 298 | log.Info("Triage report coming up in yaml format:") 299 | d, err := yaml.Marshal(&report) 300 | if err != nil { 301 | log.Fatalf("error: %v", err) 302 | } 303 | fmt.Println("\n---\n", string(d)) 304 | } else { 305 | log.Info("Triage finished, cluster all clear, no anomalies detected!") 306 | } 307 | 308 | return nil 309 | 310 | } 311 | -------------------------------------------------------------------------------- /pkg/triage/component_triage.go: -------------------------------------------------------------------------------- 1 | package triage 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | coreclient "k8s.io/client-go/kubernetes/typed/core/v1" 6 | ) 7 | 8 | const componentHealthy = "True" 9 | 10 | // TriageComponents gets a coreclient and checks if core components are in healthy state 11 | // such as etcd cluster members, scheduler, controller-manager 12 | func TriageComponents(coreClient coreclient.CoreV1Interface) (*Triage, error) { 13 | components, err := coreClient.ComponentStatuses().List(v1.ListOptions{}) 14 | if err != nil { 15 | if err.Error() != KUBE_RESOURCE_NOT_FOUND { 16 | return nil, err 17 | } 18 | } 19 | 20 | listOfTriages := make([]string, 0) 21 | for _, i := range components.Items { 22 | for _, y := range i.Conditions { 23 | if y.Status != componentHealthy { 24 | listOfTriages = append(listOfTriages, i.GetName()) 25 | } 26 | } 27 | } 28 | return NewTriage("ComponentStatuses", "Found unhealthy components!", listOfTriages), nil 29 | } 30 | -------------------------------------------------------------------------------- /pkg/triage/deployments_triage.go: -------------------------------------------------------------------------------- 1 | package triage 2 | 3 | import ( 4 | "strings" 5 | "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | "k8s.io/client-go/kubernetes" 7 | ) 8 | 9 | // OrphanedDeployments gets a kubernetes.Clientset and a specific namespace string 10 | // then proceeds to search if there are leftover deployments 11 | // the criteria is that the desired number of replicas are bigger than 0 but the available replicas are 0 12 | func OrphanedDeployments(kubeCli *kubernetes.Clientset, namespace string) (*Triage, error) { 13 | listOfTriages := make([]string, 0) 14 | deployments, err := kubeCli.ExtensionsV1beta1().Deployments(namespace).List(v1.ListOptions{}) 15 | if err != nil { 16 | if ! strings.Contains(err.Error(), KUBE_RESOURCE_NOT_FOUND) { 17 | return nil, err 18 | } 19 | } 20 | for _, i := range deployments.Items { 21 | if i.Status.Replicas > 0 && i.Status.AvailableReplicas == 0 { 22 | listOfTriages = append(listOfTriages, i.GetName()) 23 | } 24 | } 25 | return NewTriage("Deployments", "Found orphan deployments in namespace: "+namespace, listOfTriages), nil 26 | } 27 | 28 | // LeftOverDeployments gets a kubernetes.Clientset and a specific namespace string 29 | // then proceeds to search if there are leftover deployments 30 | // the criteria is that both the desired number of replicas and the available # of replicas are 0 31 | func LeftOverDeployments(kubeCli *kubernetes.Clientset, namespace string) (*Triage, error) { 32 | listOfTriages := make([]string, 0) 33 | deployments, err := kubeCli.ExtensionsV1beta1().Deployments(namespace).List(v1.ListOptions{}) 34 | if err != nil { 35 | if ! strings.Contains(err.Error(), KUBE_RESOURCE_NOT_FOUND) { 36 | return nil, err 37 | } 38 | } 39 | 40 | for _, i := range deployments.Items { 41 | if i.Status.Replicas == 0 && i.Status.AvailableReplicas == 0 { 42 | listOfTriages = append(listOfTriages, i.GetName()) 43 | } 44 | } 45 | return NewTriage("Deployments", "Found leftover deployments in namespace: "+namespace, listOfTriages), nil 46 | } 47 | -------------------------------------------------------------------------------- /pkg/triage/endpoints_triage.go: -------------------------------------------------------------------------------- 1 | package triage 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | coreclient "k8s.io/client-go/kubernetes/typed/core/v1" 6 | ) 7 | 8 | // TriageEndpoints gets a coreclient for k8s and scans through all endpoints to see if they are leftover/unused 9 | func TriageEndpoints(coreClient coreclient.CoreV1Interface) (*Triage, error) { 10 | endpoints, err := coreClient.Endpoints("").List(v1.ListOptions{}) 11 | if err != nil { 12 | if err.Error() != KUBE_RESOURCE_NOT_FOUND { 13 | return nil, err 14 | } 15 | } 16 | 17 | listOfTriages := make([]string, 0) 18 | for _, i := range endpoints.Items { 19 | if len(i.Subsets) == 0 { 20 | listOfTriages = append(listOfTriages, i.GetName()) 21 | } 22 | } 23 | return NewTriage("Endpoints", "Found orphaned endpoints!", listOfTriages), nil 24 | } 25 | -------------------------------------------------------------------------------- /pkg/triage/ingress_triage.go: -------------------------------------------------------------------------------- 1 | package triage 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | 6 | "k8s.io/client-go/kubernetes" 7 | ) 8 | 9 | // LeftoverIngresses gets a kubernetes.Clientset and a specific namespace string 10 | // then proceeds to search if there are leftover ingresses 11 | func LeftoverIngresses(kubeCli *kubernetes.Clientset, namespace string) (*Triage, error) { 12 | listOfTriages := make([]string, 0) 13 | 14 | ingresses, err := kubeCli.NetworkingV1beta1().Ingresses(namespace).List(v1.ListOptions{}) 15 | if err != nil { 16 | if err.Error() != KUBE_RESOURCE_NOT_FOUND { 17 | return nil, err 18 | } 19 | } 20 | 21 | for _, i := range ingresses.Items { 22 | if i.Status.LoadBalancer.Size() <= 0 { 23 | listOfTriages = append(listOfTriages, i.GetName()) 24 | } 25 | 26 | } 27 | return NewTriage("Ingress", "Found leftover ingresses in namespace: "+namespace, listOfTriages), nil 28 | } 29 | -------------------------------------------------------------------------------- /pkg/triage/job_triage.go: -------------------------------------------------------------------------------- 1 | package triage 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | "k8s.io/client-go/kubernetes" 6 | "time" 7 | ) 8 | 9 | // LeftoverJobs gets a kubernetes.Clientset and a specific namespace string 10 | // then proceeds to search if there are leftover cronjobs that were inactive for more than a month 11 | func LeftoverJobs(kubeCli *kubernetes.Clientset, namespace string) (*Triage, error) { 12 | listOfTriages := make([]string, 0) 13 | 14 | jobs, err := kubeCli.BatchV1beta1().CronJobs(namespace).List(v1.ListOptions{}) 15 | if err != nil { 16 | if err.Error() != KUBE_RESOURCE_NOT_FOUND { 17 | return nil, err 18 | } 19 | } 20 | 21 | currentTime := time.Now() 22 | for _, i := range jobs.Items { 23 | if i.Status.LastScheduleTime != nil { 24 | if currentTime.Sub(i.Status.LastScheduleTime.Local()).Hours()/24 > 30 { 25 | listOfTriages = append(listOfTriages, i.GetName()) 26 | } 27 | } 28 | 29 | } 30 | return NewTriage("CronJobs", "Found leftover cronjobs in namespace: "+namespace, listOfTriages), nil 31 | } 32 | -------------------------------------------------------------------------------- /pkg/triage/node_triage.go: -------------------------------------------------------------------------------- 1 | package triage 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | coreclient "k8s.io/client-go/kubernetes/typed/core/v1" 6 | ) 7 | 8 | const targetReason = "KubeletReady" 9 | 10 | // TriageNodes gets a coreclient for k8s and checks if there are any nodes in the cluster 11 | // that are not in Ready state(unoperational nodes) 12 | func TriageNodes(coreClient coreclient.CoreV1Interface) (*Triage, error) { 13 | listOfTriages := make([]string, 0) 14 | nodes, err := coreClient.Nodes().List(v1.ListOptions{}) 15 | if err != nil { 16 | if err.Error() != KUBE_RESOURCE_NOT_FOUND { 17 | return nil, err 18 | } 19 | } 20 | 21 | for _, i := range nodes.Items { 22 | for _, y := range i.Status.Conditions { 23 | if y.Reason == targetReason { 24 | if y.Status != "True" { 25 | listOfTriages = append(listOfTriages, i.GetName()) 26 | } 27 | } 28 | } 29 | } 30 | return NewTriage("Nodes", "Found node/s that are not in Ready state!", listOfTriages), nil 31 | } 32 | -------------------------------------------------------------------------------- /pkg/triage/pv_triage.go: -------------------------------------------------------------------------------- 1 | package triage 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | coreclient "k8s.io/client-go/kubernetes/typed/core/v1" 6 | ) 7 | 8 | const pvAvailable = "Available" 9 | 10 | // TriagePV gets a coreclient and checks if there are any pvs that are Available and Unclaimed 11 | func TriagePV(coreClient coreclient.CoreV1Interface) (*Triage, error) { 12 | listOfTriages := make([]string, 0) 13 | pvs, err := coreClient.PersistentVolumes().List(v1.ListOptions{}) 14 | if err != nil { 15 | if err.Error() != KUBE_RESOURCE_NOT_FOUND { 16 | return nil, err 17 | } 18 | } 19 | 20 | for _, i := range pvs.Items { 21 | if i.Status.Phase == pvAvailable { 22 | listOfTriages = append(listOfTriages, i.GetName()) 23 | } 24 | } 25 | return NewTriage("PV", "Found PV in Available & Unclaimed State!", listOfTriages), nil 26 | } 27 | -------------------------------------------------------------------------------- /pkg/triage/pvc_triage.go: -------------------------------------------------------------------------------- 1 | package triage 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | coreclient "k8s.io/client-go/kubernetes/typed/core/v1" 6 | ) 7 | 8 | const pvcLostPhase = "Lost" 9 | 10 | // TriagePVC gets a coreclient and checks if there are any pvcs that are in lost state 11 | func TriagePVC(coreClient coreclient.CoreV1Interface) (*Triage, error) { 12 | listOfTriages := make([]string, 0) 13 | pvcs, err := coreClient.PersistentVolumeClaims("").List(v1.ListOptions{}) 14 | if err != nil { 15 | if err.Error() != KUBE_RESOURCE_NOT_FOUND { 16 | return nil, err 17 | } 18 | } 19 | 20 | for _, i := range pvcs.Items { 21 | if i.Status.Phase == pvcLostPhase { 22 | listOfTriages = append(listOfTriages, i.GetName()) 23 | } 24 | } 25 | return NewTriage("PVC", "Found PVC in Lost State!", listOfTriages), nil 26 | } 27 | -------------------------------------------------------------------------------- /pkg/triage/replicaset_triage.go: -------------------------------------------------------------------------------- 1 | package triage 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | "k8s.io/client-go/kubernetes" 6 | ) 7 | 8 | // OrphanedReplicaSet gets a kubernetes.Clientset and a specific namespace string 9 | // then proceeds to search if there are orphan replicasets 10 | // the criteria is that the desired number of replicas are bigger than 0 but the available replicas are 0 11 | func OrphanedReplicaSet(kubeCli *kubernetes.Clientset, namespace string) (*Triage, error) { 12 | listOfTriages := make([]string, 0) 13 | rs, err := kubeCli.AppsV1beta2().ReplicaSets(namespace).List(v1.ListOptions{}) 14 | if err != nil { 15 | if err.Error() != KUBE_RESOURCE_NOT_FOUND { 16 | return nil, err 17 | } 18 | } 19 | 20 | for _, i := range rs.Items { 21 | if i.Status.Replicas > 0 && i.Status.AvailableReplicas == 0 { 22 | listOfTriages = append(listOfTriages, i.GetName()) 23 | } 24 | } 25 | return NewTriage("ReplicaSets", "Found orphan replicasets in namespace: "+namespace, listOfTriages), nil 26 | } 27 | 28 | // LeftOverReplicaSet gets a kubernetes.Clientset and a specific namespace string 29 | // then proceeds to search if there are left over replicasets 30 | // the criteria is that both the desired number of replicas and the available # of replicas are 0 31 | func LeftOverReplicaSet(kubeCli *kubernetes.Clientset, namespace string) (*Triage, error) { 32 | listOfTriages := make([]string, 0) 33 | rs, err := kubeCli.AppsV1beta2().ReplicaSets(namespace).List(v1.ListOptions{}) 34 | if err != nil { 35 | if err.Error() != KUBE_RESOURCE_NOT_FOUND { 36 | return nil, err 37 | } 38 | } 39 | 40 | for _, i := range rs.Items { 41 | if i.Status.Replicas == 0 && i.Status.AvailableReplicas == 0 { 42 | listOfTriages = append(listOfTriages, i.GetName()) 43 | } 44 | } 45 | return NewTriage("ReplicaSets", "Found leftover replicasets in namespace: "+namespace, listOfTriages), nil 46 | } 47 | -------------------------------------------------------------------------------- /pkg/triage/triage.go: -------------------------------------------------------------------------------- 1 | package triage 2 | 3 | // used to determine if the err from kube api is only because that type of resource is not in the targeted namespace 4 | const KUBE_RESOURCE_NOT_FOUND string = "the server could not find the requested resource" 5 | 6 | type Triage struct { 7 | ResourceType string `yaml:"Resource"` 8 | AnomalyType string `yaml:"AnomalyType"` 9 | Anomalies []string `yaml:"Anomalies"` 10 | } 11 | 12 | func NewTriage(resourceType string, anomalyType string, anomalies []string) *Triage { 13 | return &Triage{ 14 | ResourceType: resourceType, 15 | AnomalyType: anomalyType, 16 | Anomalies: anomalies, 17 | } 18 | } 19 | --------------------------------------------------------------------------------