├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── agent └── Dockerfile ├── cmd └── kube-jarvis │ ├── config.go │ └── main.go ├── conf └── default.yaml ├── go.mod ├── go.sum ├── manifests ├── common │ ├── cluster-role-binding.yaml │ ├── cluster-role.yaml │ ├── config.yaml │ ├── namespace.yaml │ └── service-account.yaml └── workload │ ├── cronjob.yaml │ ├── deployment.yaml │ └── job.yaml ├── pkg ├── httpserver │ ├── fake.go │ ├── fake_test.go │ ├── server.go │ ├── server_test.go │ └── standardapi.go ├── logger │ ├── interface.go │ ├── logger.go │ └── logger_test.go ├── plugins │ ├── README.md │ ├── cluster │ │ ├── all │ │ │ └── all.go │ │ ├── cluster.go │ │ ├── custom │ │ │ ├── README.md │ │ │ ├── cluster.go │ │ │ ├── cluster_test.go │ │ │ ├── compexplorer │ │ │ │ ├── auto.go │ │ │ │ ├── auto_test.go │ │ │ │ ├── bare.go │ │ │ │ ├── bare_test.go │ │ │ │ ├── component.go │ │ │ │ ├── label.go │ │ │ │ ├── label_test.go │ │ │ │ ├── pods.go │ │ │ │ ├── pods_test.go │ │ │ │ ├── staticpod.go │ │ │ │ └── staticpod_test.go │ │ │ └── nodeexec │ │ │ │ ├── nodeexec.go │ │ │ │ ├── nodeexec_test.go │ │ │ │ ├── proxy.go │ │ │ │ ├── proxy_test.go │ │ │ │ └── yaml.go │ │ ├── fake │ │ │ └── cluster.go │ │ ├── resources.go │ │ └── resources_test.go │ ├── coordinate │ │ ├── all │ │ │ └── all.go │ │ ├── basic │ │ │ ├── README.md │ │ │ ├── default.go │ │ │ └── default_test.go │ │ ├── coordinate.go │ │ ├── cron │ │ │ ├── README.md │ │ │ ├── coordinator.go │ │ │ ├── coordinator_test.go │ │ │ ├── server.go │ │ │ └── server_test.go │ │ └── fake.go │ ├── diagnose │ │ ├── all │ │ │ └── all.go │ │ ├── common.go │ │ ├── diagnose.go │ │ ├── master │ │ │ ├── args │ │ │ │ ├── apiserver │ │ │ │ │ ├── README.md │ │ │ │ │ └── diagnostic.go │ │ │ │ ├── controller-manager │ │ │ │ │ ├── README.md │ │ │ │ │ └── diagnostic.go │ │ │ │ ├── etcd │ │ │ │ │ ├── README.md │ │ │ │ │ └── diagnostic.go │ │ │ │ └── scheduler │ │ │ │ │ ├── README.md │ │ │ │ │ └── diagnostic.go │ │ │ ├── capacity │ │ │ │ ├── README.md │ │ │ │ ├── capacity.go │ │ │ │ ├── capacity_test.go │ │ │ │ └── default.go │ │ │ └── components │ │ │ │ ├── README.md │ │ │ │ ├── components.go │ │ │ │ └── components_test.go │ │ ├── node │ │ │ ├── ha │ │ │ │ ├── README.md │ │ │ │ ├── ha.go │ │ │ │ └── ha_test.go │ │ │ ├── iptables │ │ │ │ └── iptables.go │ │ │ ├── status │ │ │ │ ├── status.go │ │ │ │ └── status_test.go │ │ │ └── sys │ │ │ │ └── sys.go │ │ ├── other │ │ │ └── example │ │ │ │ ├── README.md │ │ │ │ ├── example.go │ │ │ │ └── example_test.go │ │ └── resource │ │ │ ├── hpa │ │ │ └── ip │ │ │ │ └── ip.go │ │ │ └── workload │ │ │ ├── affinity │ │ │ ├── README.md │ │ │ ├── affinitycheck.go │ │ │ └── affinitycheck_test.go │ │ │ ├── batch │ │ │ ├── README.md │ │ │ ├── batchcheck.go │ │ │ └── batchcheck_test.go │ │ │ ├── ha │ │ │ ├── README.md │ │ │ └── ha.go │ │ │ ├── healthcheck │ │ │ ├── README.md │ │ │ ├── healthcheck.go │ │ │ └── healthcheck_test.go │ │ │ ├── pdb │ │ │ ├── README.md │ │ │ ├── pdbcheck.go │ │ │ └── pdbcheck_test.go │ │ │ ├── requestslimits │ │ │ ├── README.md │ │ │ ├── requestlimit.go │ │ │ └── requestlimit_test.go │ │ │ └── status │ │ │ ├── README.md │ │ │ ├── status.go │ │ │ └── status_test.go │ ├── export │ │ ├── all │ │ │ └── all.go │ │ ├── export.go │ │ ├── result.go │ │ ├── stdout │ │ │ ├── README.md │ │ │ ├── stdout.go │ │ │ └── stdout_test.go │ │ └── store │ │ │ ├── README.md │ │ │ ├── exporter.go │ │ │ ├── metahandler.go │ │ │ └── queryhandler.go │ ├── meta.go │ ├── meta_test.go │ └── progress.go ├── store │ ├── file.go │ ├── mem.go │ ├── mysql.go │ └── store.go ├── translate │ ├── default.go │ ├── default_test.go │ ├── fake.go │ ├── fake_test.go │ └── translator.go └── util │ ├── goroutine.go │ ├── goroutine_test.go │ ├── k8s.go │ ├── k8s_test.go │ ├── obj.go │ └── obj_test.go ├── scripts └── run.sh └── translation ├── en └── diagnostics │ ├── affinity.yaml │ ├── batch.yaml │ ├── etcd-args.yaml │ ├── example.yaml │ ├── health-check.yaml │ ├── hpa-ip.yaml │ ├── kube-apiserver-args.yaml │ ├── kube-controller-manager-args.yaml │ ├── kube-scheduler-args.yaml │ ├── master-capacity.yaml │ ├── master-components.yaml │ ├── master-status.yaml │ ├── node-ha.yaml │ ├── node-iptables.yaml │ ├── node-status.yaml │ ├── node-sys.yaml │ ├── pdb.yaml │ ├── requests-limits.yaml │ ├── workload-ha.yaml │ └── workload-status.yaml └── zh └── diagnostics ├── affinity.yaml ├── batch.yaml ├── etcd-args.yaml ├── example.yaml ├── health-check.yaml ├── hpa-ip.yaml ├── kube-apiserver-args.yaml ├── kube-controller-manager-args.yaml ├── kube-scheduler-args.yaml ├── master-capacity.yaml ├── master-status.yaml ├── node-ha.yaml ├── node-iptables.yaml ├── node-status.yaml ├── node-sys.yaml ├── pdb.yaml ├── requests-limits.yaml └── workload-status.yaml /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | .idea/* 14 | bin/ 15 | vendor -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - master 4 | os: 5 | - linux 6 | - osx 7 | dist: trusty 8 | sudo: false 9 | install: true 10 | script: 11 | - env GO111MODULE=on go test ./pkg/... -coverprofile=coverage.txt -covermode=atomic 12 | - env GO111MODULE=on go build cmd/kube-jarvis/*.go 13 | 14 | after_success: 15 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at rayhuang110@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | ADD conf /conf 3 | ADD kube-jarvis / 4 | ADD translation /translation 5 | CMD ["/kube-jarvis"] -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | go build -o bin/kube-jarvis cmd/kube-jarvis/*.go 3 | release:all 4 | mkdir kube-jarvis 5 | cp -R conf kube-jarvis/ 6 | cp -R translation kube-jarvis/ 7 | cp bin/kube-jarvis kube-jarvis/ 8 | cp -R manifests kube-jarvis/ 9 | tar cf kube-jarvis.tar.gz kube-jarvis 10 | rm -rf kube-jarvis 11 | clean: 12 | rm -f kube-jarvis.tar.gz 13 | rm -fr kube-jarvis 14 | rm -fr bin/ 15 | test: 16 | go test ./pkg/... 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kube-jarvis 2 | [![Build Status](https://travis-ci.com/RayHuangCN/kube-jarvis.svg?token=nJm3RqJv2hocVN6fWNzx&branch=master)](https://travis-ci.com/RayHuangCN/kube-jarvis.svg?token=nJm3RqJv2hocVN6fWNzx&branch=master) 3 | [![codecov](https://codecov.io/gh/RayHuangCN/kube-jarvis/branch/master/graph/badge.svg?token=rva9yuTNLO)](https://codecov.io/gh/RayHuangCN/kube-jarvis) 4 | 5 | kube-jarvis is a tool used to inspect kubernetes cluster 6 | 7 | # Features 8 | 9 | * Comprehensively check the cluster health status 10 | * Support a variety of cloud manufacturers 11 | * Highly configurable 12 | * Highly extensible 13 | * Description statements can be customized 14 | 15 | # Quick start 16 | On any node has "/$HOME/.kube/config" 17 | ```bash 18 | wget -O - https://kube-jarvis-1251707795.cos.ap-guangzhou.myqcloud.com/run.sh | bash 19 | ``` 20 | 21 | # Config struct 22 | ```yaml 23 | global: 24 | trans: "translation" # the translation file dir 25 | lang: "en" # target lang 26 | 27 | cluster: 28 | type: "custom" 29 | 30 | coordinator: 31 | type: "default" 32 | 33 | diagnostics: 34 | - type: "master-capacity" 35 | - type: "master-apiserver" 36 | - type: "node-sys" 37 | - type: "requests-limits" 38 | 39 | exporters: 40 | - type: "stdout" 41 | 42 | - type: "file" 43 | name: "for json" 44 | config: 45 | format: "json" 46 | path: "result.json" 47 | 48 | 49 | ``` 50 | 51 | # Run in docker 52 | login any node of your cluster and exec cmd: 53 | ```bash 54 | docker run -i -t docker.io/raylhuang110/kube-jarvis:latest 55 | ``` 56 | > [you can found all docker images here](https://hub.docker.com/r/raylhuang110/kube-jarvis/tags) 57 | 58 | # Run as job or cronjob 59 | create common resource (Namespaces, ServiceAccount ...) 60 | ```bash 61 | kubectl apply -f manifests/ 62 | ``` 63 | run as job 64 | ```bash 65 | kubectl apply -f manifests/workload/job.yaml 66 | ``` 67 | run as cronjob (default run at 00:00 every day) 68 | ```bash 69 | kubectl apply -f manifests/workload/cronjob.yaml 70 | ``` 71 | # Plugins 72 | we call coordinator, diagnostics, evaluators and exporters as "plugins" 73 | > [you can found all plugins lists here](./pkg/plugins/README.md) 74 | 75 | # License 76 | Apache License 2.0 - see LICENSE.md for more details 77 | -------------------------------------------------------------------------------- /agent/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN set -ex \ 4 | && echo "http://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \ 5 | && echo "http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \ 6 | && apk update \ 7 | && apk upgrade \ 8 | && apk add --no-cache \ 9 | iptables \ -------------------------------------------------------------------------------- /cmd/kube-jarvis/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package main 19 | 20 | import ( 21 | "context" 22 | "flag" 23 | "log" 24 | 25 | "tkestack.io/kube-jarvis/pkg/httpserver" 26 | _ "tkestack.io/kube-jarvis/pkg/plugins/cluster/all" 27 | _ "tkestack.io/kube-jarvis/pkg/plugins/coordinate/all" 28 | _ "tkestack.io/kube-jarvis/pkg/plugins/diagnose/all" 29 | _ "tkestack.io/kube-jarvis/pkg/plugins/export/all" 30 | ) 31 | 32 | var configFile string 33 | 34 | func init() { 35 | flag.StringVar(&configFile, "config", "conf/default.yaml", "config file") 36 | flag.Parse() 37 | } 38 | 39 | func main() { 40 | config, err := GetConfig(configFile) 41 | if err != nil { 42 | panic(err) 43 | } 44 | 45 | cls, err := config.GetCluster() 46 | if err != nil { 47 | panic(err) 48 | } 49 | 50 | store, err := config.GetStore() 51 | if err != nil { 52 | panic(err) 53 | } 54 | 55 | coordinator, err := config.GetCoordinator(cls, store) 56 | if err != nil { 57 | panic(err) 58 | } 59 | 60 | trans, err := config.GetTranslator() 61 | if err != nil { 62 | panic(err) 63 | } 64 | 65 | diagnostics, err := config.GetDiagnostics(cls, trans, store) 66 | if err != nil { 67 | panic(err) 68 | } 69 | 70 | for _, d := range diagnostics { 71 | coordinator.AddDiagnostic(d) 72 | } 73 | 74 | exporters, err := config.GetExporters(cls, trans, store) 75 | if err != nil { 76 | panic(err) 77 | } 78 | 79 | for _, e := range exporters { 80 | coordinator.AddExporter(e) 81 | } 82 | 83 | go httpserver.Default.Start(config.Logger, config.Global.HttpAddr) 84 | if err := coordinator.Run(context.Background()); err != nil { 85 | log.Fatal(err.Error()) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /conf/default.yaml: -------------------------------------------------------------------------------- 1 | global: 2 | trans: "translation" 3 | lang: "en" 4 | httpaddr: ":9005" 5 | store: 6 | type: file 7 | config: 8 | dir: "/data/kube-jarvis" 9 | 10 | cluster: 11 | # see detail of custom cluster here: 12 | type: "custom" 13 | name: "my-cluster" 14 | kubeconfig: "" 15 | config: 16 | node: 17 | autocreate: true 18 | 19 | diagnostics: 20 | - type: "master-capacity" 21 | - type: "master-components" 22 | - type: "kube-apiserver-args" 23 | - type: "kube-controller-manager-args" 24 | - type: "kube-scheduler-args" 25 | - type: "etcd-args" 26 | - type: "node-sys" 27 | - type: "node-iptables" 28 | - type: "node-status" 29 | - type: "workload-status" 30 | - type: "workload-ha" 31 | - type: "affinity" 32 | - type: "health-check" 33 | - type: "requests-limits" 34 | - type: "pdb" 35 | - type: "batch-check" 36 | - type: "hpa-ip" 37 | - type: "node-ha" 38 | 39 | exporters: 40 | - type: "stdout" 41 | 42 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module tkestack.io/kube-jarvis 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/fatih/color v1.7.0 7 | github.com/go-sql-driver/mysql v1.5.0 8 | github.com/jinzhu/gorm v1.9.12 9 | github.com/mattn/go-colorable v0.1.4 // indirect 10 | github.com/mattn/go-isatty v0.0.10 // indirect 11 | github.com/nicksnyder/go-i18n/v2 v2.0.3 12 | github.com/pkg/errors v0.8.1 13 | github.com/robfig/cron/v3 v3.0.0 14 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e 15 | golang.org/x/text v0.3.2 16 | gopkg.in/yaml.v2 v2.2.4 17 | k8s.io/api v0.0.0-20191121015604-11707872ac1c 18 | k8s.io/apimachinery v0.0.0-20191203211716-adc6f4cd9e7d 19 | k8s.io/client-go v0.0.0-20191204082520-bc9b51d240b2 20 | k8s.io/kubectl v0.0.0-20191205004521-ce1b5846dca4 21 | k8s.io/utils v0.0.0-20191114184206-e782cd3c129f 22 | ) 23 | -------------------------------------------------------------------------------- /manifests/common/cluster-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: kube-jarvis 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: kube-jarvis 9 | subjects: 10 | - kind: ServiceAccount 11 | name: kube-jarvis 12 | namespace: kube-jarvis -------------------------------------------------------------------------------- /manifests/common/cluster-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: kube-jarvis 5 | rules: 6 | - apiGroups: 7 | - '*' 8 | resources: 9 | - '*' 10 | verbs: 11 | - '*' -------------------------------------------------------------------------------- /manifests/common/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | default.yaml: | 4 | global: 5 | trans: "translation" 6 | lang: "en" 7 | httpaddr: ":9005" 8 | cluster: 9 | # see detail of custom cluster here: 10 | type: "custom" 11 | kubeconfig: "" 12 | config: 13 | node: 14 | autocreate: true 15 | 16 | exporters: 17 | - type: "stdout" 18 | 19 | 20 | kind: ConfigMap 21 | metadata: 22 | name: config 23 | namespace: kube-jarvis -------------------------------------------------------------------------------- /manifests/common/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: kube-jarvis -------------------------------------------------------------------------------- /manifests/common/service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: kube-jarvis 5 | namespace: kube-jarvis 6 | 7 | -------------------------------------------------------------------------------- /manifests/workload/cronjob.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1beta1 2 | kind: CronJob 3 | metadata: 4 | name: kube-jarvis 5 | namespace: kube-jarvis 6 | spec: 7 | schedule: "0 0 * * *" 8 | jobTemplate: 9 | spec: 10 | template: 11 | spec: 12 | containers: 13 | - name: kube-jarvis 14 | image: docker.io/raylhuang110/kube-jarvis:latest 15 | imagePullPolicy: Always 16 | volumeMounts: 17 | - name: config 18 | mountPath: /conf 19 | volumes: 20 | - name: config 21 | configMap: 22 | name: config 23 | restartPolicy: Never 24 | serviceAccountName: kube-jarvis -------------------------------------------------------------------------------- /manifests/workload/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: kube-jarvis 5 | namespace: cls-e16mmp4a 6 | labels: 7 | k8s-app: kube-jarvis 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | k8s-app: kube-jarvis 13 | template: 14 | metadata: 15 | annotations: 16 | qcloud.CNI_CONFIG_INFO: '{"eni":{"spec":{"peerVpcAppId":1251707795,"peerVpcId":"vpc-rt8vgbiv","name":"cls-e16mmp4a","cvmOwnerAppId":1253687700,"cvmOwnerUin":"100005258832"}},"useBridge":true}' 17 | tke.cloud.tencent.com/networks: tke-bridge,tke-eni,tke-route 18 | name: kube-jarvis 19 | namespace: cls-e16mmp4a 20 | labels: 21 | k8s-app: kube-jarvis 22 | label_qcloud_app: kube-jarvis 23 | spec: 24 | volumes: 25 | - configMap: 26 | defaultMode: 0 27 | name: kube-jarvis-config 28 | name: config 29 | - hostPath: 30 | path: /etc/localtime 31 | type: "" 32 | name: host-time 33 | - configMap: 34 | defaultMode: 420 35 | name: security-clientconfig 36 | name: security-clientconfig 37 | 38 | containers: 39 | - name: kube-jarvis 40 | image: ccr.ccs.tencentyun.com/ccs-dev/kube-jarvis:latest 41 | imagePullPolicy: Always 42 | resources: 43 | limits: 44 | memory: "1G" 45 | tke.cloud.tencent.com/eni: "1" 46 | requests: 47 | memory: "100M" 48 | tke.cloud.tencent.com/eni: "1" 49 | volumeMounts: 50 | - mountPath: /kubeconfig 51 | name: security-clientconfig 52 | - mountPath: /conf 53 | name: config 54 | readOnly: true 55 | - mountPath: /etc/localtime 56 | name: host-time -------------------------------------------------------------------------------- /manifests/workload/job.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: kube-jarvis 5 | namespace: kube-jarvis 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: kube-jarvis 11 | image: docker.io/raylhuang110/kube-jarvis:latest 12 | imagePullPolicy: Always 13 | volumeMounts: 14 | - name: config 15 | mountPath: /conf 16 | volumes: 17 | - name: config 18 | configMap: 19 | name: config 20 | restartPolicy: Never 21 | serviceAccountName: kube-jarvis -------------------------------------------------------------------------------- /pkg/httpserver/fake.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package httpserver 19 | 20 | import "net/http" 21 | 22 | // FakeResponseWriter is a fake http.ResponseWriter for handler testing 23 | type FakeResponseWriter struct { 24 | StatusCode int 25 | RespData []byte 26 | HeaderMap http.Header 27 | } 28 | 29 | // NewFakeResponseWriter return an new FakeResponseWriter 30 | func NewFakeResponseWriter() *FakeResponseWriter { 31 | return &FakeResponseWriter{ 32 | HeaderMap: http.Header{}, 33 | StatusCode: http.StatusOK, 34 | } 35 | } 36 | 37 | // Header return the HeaderMap of FakeResponseWriter 38 | func (f *FakeResponseWriter) Header() http.Header { 39 | return f.HeaderMap 40 | } 41 | 42 | // Write will append response data to FakeResponseWriter.RespData 43 | func (f *FakeResponseWriter) Write(data []byte) (int, error) { 44 | f.RespData = append(f.RespData, data...) 45 | return len(data), nil 46 | } 47 | 48 | // WriteHeader set the StatusCode of FakeResponseWriter 49 | func (f *FakeResponseWriter) WriteHeader(statusCode int) { 50 | f.StatusCode = statusCode 51 | } 52 | -------------------------------------------------------------------------------- /pkg/httpserver/fake_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package httpserver 19 | 20 | import ( 21 | "net/http" 22 | "testing" 23 | ) 24 | 25 | func TestFakeResponseWriter_Header(t *testing.T) { 26 | f := NewFakeResponseWriter() 27 | f.HeaderMap.Set("a", "b") 28 | h := f.Header() 29 | if h.Get("a") != "b" { 30 | t.Fatalf("want a but get %s", h.Get("a")) 31 | } 32 | } 33 | 34 | func TestFakeResponseWriter_Write(t *testing.T) { 35 | f := NewFakeResponseWriter() 36 | str := "test" 37 | n, err := f.Write([]byte(str)) 38 | if err != nil { 39 | t.Fatalf(err.Error()) 40 | } 41 | 42 | if n != len(str) { 43 | t.Fatalf("want %d, but get %d", len(str), n) 44 | } 45 | 46 | if string(f.RespData) != str { 47 | t.Fatalf("want %s, but get %s", str, string(f.RespData)) 48 | } 49 | } 50 | 51 | func TestFakeResponseWriter_WriteHeader(t *testing.T) { 52 | f := NewFakeResponseWriter() 53 | f.WriteHeader(http.StatusOK) 54 | if f.StatusCode != http.StatusOK { 55 | t.Fatalf("want 200 but get %d", f.StatusCode) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pkg/httpserver/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package httpserver 19 | 20 | import ( 21 | "net/http" 22 | "sync" 23 | 24 | "tkestack.io/kube-jarvis/pkg/logger" 25 | ) 26 | 27 | // Server is a http server of kube-jarvis 28 | // all plugins can register standard APIs or extended APIs 29 | type Server struct { 30 | handlers map[string]func(http.ResponseWriter, *http.Request) 31 | handlersLock sync.Mutex 32 | listenAndServe func(addr string, handler http.Handler) error 33 | handFunc func(pattern string, handler func(http.ResponseWriter, *http.Request)) 34 | } 35 | 36 | // NewServer create a new Server with default values 37 | func NewServer() *Server { 38 | return &Server{ 39 | handlers: map[string]func(http.ResponseWriter, *http.Request){}, 40 | handlersLock: sync.Mutex{}, 41 | listenAndServe: http.ListenAndServe, 42 | handFunc: http.HandleFunc, 43 | } 44 | } 45 | 46 | // Default is the default http server 47 | var Default = NewServer() 48 | 49 | // Start try setup a http server if any handler registered 50 | func (s *Server) Start(logger logger.Logger, addr string) { 51 | if len(s.handlers) == 0 { 52 | return 53 | } 54 | 55 | if addr == "" { 56 | addr = ":9005" 57 | } 58 | 59 | logger.Infof("http server start at %s", addr) 60 | if err := s.listenAndServe(addr, nil); err != nil { 61 | panic(err.Error()) 62 | } 63 | } 64 | 65 | // HandleFunc registered a handler for a certain path 66 | func (s *Server) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) { 67 | s.handlersLock.Lock() 68 | defer s.handlersLock.Unlock() 69 | if _, exist := s.handlers[pattern]; exist { 70 | return 71 | } 72 | 73 | s.handlers[pattern] = handler 74 | s.handFunc(pattern, handler) 75 | } 76 | -------------------------------------------------------------------------------- /pkg/httpserver/server_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package httpserver 19 | 20 | import ( 21 | "fmt" 22 | "net/http" 23 | "testing" 24 | 25 | "tkestack.io/kube-jarvis/pkg/logger" 26 | ) 27 | 28 | func TestServer_HandleFunc(t *testing.T) { 29 | s := NewServer() 30 | s.handFunc = func(pattern string, handler func(http.ResponseWriter, *http.Request)) {} 31 | s.HandleFunc("test", func(writer http.ResponseWriter, request *http.Request) {}) 32 | _, exist := s.handlers["test"] 33 | if !exist { 34 | t.Fatalf("register handler failed") 35 | } 36 | } 37 | 38 | func TestServer_Start(t *testing.T) { 39 | var cases = []struct { 40 | wantStared bool 41 | registerHandler bool 42 | }{ 43 | { 44 | wantStared: true, 45 | registerHandler: true, 46 | }, 47 | { 48 | wantStared: false, 49 | registerHandler: false, 50 | }, 51 | } 52 | 53 | for _, cs := range cases { 54 | t.Run(fmt.Sprintf("%+v", cs), func(t *testing.T) { 55 | s := NewServer() 56 | started := false 57 | s.listenAndServe = func(addr string, handler http.Handler) error { 58 | started = true 59 | return nil 60 | } 61 | 62 | if cs.registerHandler { 63 | s.HandleFunc("test", func(writer http.ResponseWriter, request *http.Request) { 64 | }) 65 | } 66 | 67 | s.Start(logger.NewLogger(), "") 68 | 69 | if cs.wantStared != started { 70 | t.Fatalf("want %v but not", cs.wantStared) 71 | } 72 | }) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pkg/httpserver/standardapi.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package httpserver 19 | 20 | import ( 21 | "tkestack.io/kube-jarvis/pkg/plugins" 22 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 23 | "tkestack.io/kube-jarvis/pkg/plugins/export" 24 | ) 25 | 26 | const ( 27 | // StandardQueryPath is the standard API path for querying target diagnostic results 28 | StandardQueryPath = "/exporter/store/query" 29 | // StandardHistoryPath is the standard API path for querying diagnostic history 30 | StandardHistoryPath = "/exporter/store/history" 31 | // StandardRunPath is the standard API path for starting diagnose immediately 32 | StandardRunPath = "/coordinator/cron/run" 33 | // StandardStatePath is the standard API path for getting current running state and progress 34 | // this API only available when the coordinator type is "cron" 35 | StandardStatePath = "/coordinator/cron/state" 36 | // StandardPeriodPath is the standard path for getting or updating running period 37 | // this API only available when the coordinator type is "cron" 38 | StandardPeriodPath = "/coordinator/cron/period" 39 | ) 40 | 41 | // HistoryRequest is the request for querying history records 42 | type HistoryRequest struct { 43 | // Offset is the offset of target records 44 | // the histories will be sorted in descending chronological order 45 | // so, offset=1 means the second recent record 46 | Offset int 47 | // Limit is the max record number of returned records 48 | Limit int 49 | } 50 | 51 | // HistoryResponse is the response of query history records 52 | type HistoryResponse struct { 53 | *export.History 54 | } 55 | 56 | // NewHistoryResponse return a HistoryResponse with default values 57 | func NewHistoryResponse() *HistoryResponse { 58 | return &HistoryResponse{ 59 | &export.History{Records: []*export.HistoryItem{}}, 60 | } 61 | } 62 | 63 | // QueryRequest is the request for querying one diagnostic report 64 | type QueryRequest struct { 65 | // ID used to specify the target report 66 | ID string 67 | // Type is the target type of diagnostic 68 | // if Type is not empty, only diagnostic with type "Type" will be returned 69 | Type string 70 | // Name is the target name of diagnostic 71 | // if Name is not empty, only diagnostic with name "Name" will be returned 72 | Name string 73 | // Level is the max HealthyLevel of target results 74 | // for example: 75 | // Level = HealthyLevelSerious 76 | // will only return results with HealthyLevel HealthyLevelSerious, and HealthyLevelFailed 77 | // if Level is empty ,HealthyLevelGood will be used 78 | Level diagnose.HealthyLevel 79 | // Offset is the offset value of the request result 80 | Offset int 81 | // Limit is the max line of results 82 | Limit int 83 | } 84 | 85 | // QueryResponse is the response of querying results 86 | type QueryResponse struct { 87 | *export.AllResult 88 | } 89 | 90 | // NewQueryResponse create an empty QueryResponse 91 | func NewQueryResponse() *QueryResponse { 92 | return &QueryResponse{ 93 | AllResult: export.NewAllResult(), 94 | } 95 | } 96 | 97 | // StateResponse is the response of querying current state 98 | type StateResponse struct { 99 | State string 100 | Progress *plugins.Progress 101 | } 102 | -------------------------------------------------------------------------------- /pkg/logger/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package logger 19 | 20 | // Logger is a abstract logger obj for kube-jarvis 21 | type Logger interface { 22 | // With create a new Logger and append "labels" to old logger's labels 23 | With(labels map[string]string) Logger 24 | Infof(format string, args ...interface{}) 25 | Debugf(format string, args ...interface{}) 26 | Errorf(format string, args ...interface{}) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/logger/logger.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package logger 19 | 20 | import ( 21 | "fmt" 22 | "log" 23 | "sort" 24 | ) 25 | 26 | type loggerInfo struct { 27 | labels map[string]string 28 | } 29 | 30 | // NewLogger create a logger that just print logs using golang fmt logger 31 | func NewLogger() Logger { 32 | return &loggerInfo{ 33 | labels: map[string]string{}, 34 | } 35 | } 36 | 37 | func (l *loggerInfo) With(labels map[string]string) Logger { 38 | nLogger := &loggerInfo{ 39 | labels: map[string]string{}, 40 | } 41 | for k, v := range l.labels { 42 | nLogger.labels[k] = v 43 | } 44 | 45 | for k, v := range labels { 46 | nLogger.labels[k] = v 47 | } 48 | 49 | return nLogger 50 | } 51 | 52 | func (l *loggerInfo) Message(prefix string, format string, args ...interface{}) string { 53 | message := prefix + " " 54 | message += fmt.Sprintf(format, args...) 55 | message += " " 56 | 57 | keys := make([]string, 0) 58 | for k := range l.labels { 59 | keys = append(keys, k) 60 | } 61 | sort.Strings(keys) 62 | 63 | for _, k := range keys { 64 | message += fmt.Sprintf("%s = %s | ", k, l.labels[k]) 65 | } 66 | 67 | return message 68 | } 69 | 70 | func (l *loggerInfo) Infof(format string, args ...interface{}) { 71 | log.Println(l.Message("[INFO]", format, args...)) 72 | } 73 | func (l *loggerInfo) Debugf(format string, args ...interface{}) { 74 | log.Println(l.Message("[DEBUG]", format, args...)) 75 | 76 | } 77 | func (l *loggerInfo) Errorf(format string, args ...interface{}) { 78 | log.Println(l.Message("[ERROR]", format, args...)) 79 | } 80 | -------------------------------------------------------------------------------- /pkg/logger/logger_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package logger 19 | 20 | import ( 21 | "testing" 22 | ) 23 | 24 | func TestLogger(t *testing.T) { 25 | lg := NewLogger() 26 | lg = lg.With(map[string]string{ 27 | "user": "test", 28 | }).With(map[string]string{ 29 | "logger": "fmt", 30 | }) 31 | 32 | lg.Infof("info") 33 | lg.Errorf("error") 34 | lg.Debugf("debug") 35 | } 36 | -------------------------------------------------------------------------------- /pkg/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | ## Cluster 3 | Cluster is the abstraction of a particular type of cluster, and it is responsible for probing and discovering the core components of the cluster and collecting cluster-related information 4 | * [custom](./cluster/custom/README.md) 5 | 6 | ## Coordinator 7 | Coordinator is responsible for coordinating the work of the other plug-ins, executing the various diagnostics, and distributing the output to the exporters 8 | * [default](./coordinate/basic/README.md) 9 | * [cron](./coordinate/basic/README.md) 10 | 11 | ## Diagnostic 12 | Diagnostic is responsible for diagnosing an aspect of the cluster, outputting diagnostic results and repair recommendations 13 | * [example](./diagnose/other/example/README.md) 14 | * [kube-apiserver-args](./diagnose/master/args/apiserver/README.md) 15 | * [kube-controller-manager-args](./diagnose/master/args/controller-manager/README.md) 16 | * [etcd-args](./diagnose/master/args/etcd/README.md) 17 | * [scheduler-args](./diagnose/master/args/scheduler/README.md) 18 | * [master-capacity](./diagnose/master/capacity/README.md) 19 | * [master-components](./diagnose/master/components/README.md) 20 | * [node-ha](./diagnose/node/ha/README.md) 21 | * [node-iptables](./diagnose/node/iptables/README.md) 22 | * [node-status](./diagnose/node/status/README.md) 23 | * [node-sys](./diagnose/node/sys/README.md) 24 | * [hpa-ip](./diagnose/resource/hpa/ip/README.md) 25 | * [affinity](./diagnose/resource/workload/affinity/README.md) 26 | * [batch-check](./diagnose/resource/workload/batch/README.md) 27 | * [workload-ha](./diagnose/resource/workload/ha/README.md) 28 | * [health-check](./diagnose/resource/workload/healthcheck/README.md) 29 | * [pdb](./diagnose/resource/workload/pdb/README.md) 30 | * [requests-limits](./diagnose/resource/workload/requestslimits/README.md) 31 | * [workload-status](./diagnose/resource/workload/status/README.md) 32 | * [node-ha](./diagnose/node/ha/README.md) 33 | 34 | ## Exporter 35 | Exporter is responsible for formatting the output or 36 | storage 37 | * [stdout](./export/stdout/README.md) 38 | * [store](./export/store/README.md) 39 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/all/all.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package all 19 | 20 | import ( 21 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 22 | "tkestack.io/kube-jarvis/pkg/plugins/cluster/custom" 23 | ) 24 | 25 | func init() { 26 | cluster.Add(custom.Type, cluster.Factory{Creator: custom.NewCluster}) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/cluster.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package cluster 19 | 20 | import ( 21 | "context" 22 | 23 | "k8s.io/client-go/kubernetes" 24 | "k8s.io/client-go/rest" 25 | "tkestack.io/kube-jarvis/pkg/logger" 26 | "tkestack.io/kube-jarvis/pkg/plugins" 27 | ) 28 | 29 | // Cluster is the abstract of target cluster 30 | // plugins can get Resources from Cluster 31 | type Cluster interface { 32 | // Complete check and complete config items 33 | Complete() error 34 | // Init do Initialization for cluster, and fetching Resources 35 | Init(ctx context.Context, progress *plugins.Progress) error 36 | // CloudType return the cloud type of Cluster 37 | CloudType() string 38 | // Resources just return fetched resources 39 | Resources() *Resources 40 | // Finish will be called once diagnostic done 41 | Finish() error 42 | } 43 | 44 | // Factory create a new Cluster 45 | type Factory struct { 46 | // Creator is a factory function to create Cluster 47 | Creator func(log logger.Logger, cli kubernetes.Interface, config *rest.Config) Cluster 48 | } 49 | 50 | // Factories store all registered Cluster Creator 51 | var Factories = map[string]Factory{} 52 | 53 | // Add register a Diagnostic Factory 54 | func Add(typ string, f Factory) { 55 | Factories[typ] = f 56 | } 57 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/README.md: -------------------------------------------------------------------------------- 1 | # Custom Cluster 2 | 3 | A custom cluster is a user-built cluster that can be highly customized through a configuration file 4 | 5 | # config 6 | ```yaml 7 | cluster: 8 | type: "custom" 9 | name: "" # the name of this cluster 10 | kubeconfig: "" # the path of kubeconfig file, use "" to use $HOMEDIR/.kube/.config or use in-cluster way 11 | # default config value 12 | config: 13 | node: # the way to fetch node machine level data 14 | type: "proxy" # via the a agent DaemonSet 15 | namespace: "kube-jarvis" # the namespace of agent 16 | daemonset: "kube-jarvis-agent" # the name of agent DaemonSet 17 | 18 | components: # the components that should to explore their information 19 | kube-apiserver: # this is the example of component "kube-apiserver" 20 | # the default components also includes as follow 21 | # "kube-apiserver", "kube-scheduler", "kube-controller-manager", "etcd", "kube-proxy" 22 | # "coredns", "kube-dns", "kubelet", "kube-proxy", "dockerd", "containerd" 23 | 24 | type: "auto" # the way used to explore this component, 25 | # Auto : try follow ways one by one 26 | # Bare : use "ps" command to explore component on nodes 27 | # Label: select pods with label selector to explore component 28 | # StaticPod: use static pod on node to explore component 29 | 30 | name: "kube-apiserver" # the real name of target component 31 | namespace: "kube-system" # the namespace of the component if use "Lable" or "StaticPod" exploring 32 | masternodes: true # only explore component on master nodes 33 | # the master nodes are nodes with label item "node-role.kubernetes.io/master" exist 34 | 35 | nodes: [] # only explore component on target nodes 36 | # will try explore component on all nodes if "masternodes" is false and "nodes" is empty 37 | 38 | labels: # the labels that used to select pod when use "Label" exploring 39 | k8s-app : "kube-apiserver" 40 | 41 | ``` 42 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/cluster_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package custom 19 | 20 | import ( 21 | "context" 22 | v1 "k8s.io/api/core/v1" 23 | "k8s.io/client-go/kubernetes/fake" 24 | "testing" 25 | "tkestack.io/kube-jarvis/pkg/logger" 26 | "tkestack.io/kube-jarvis/pkg/plugins" 27 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 28 | "tkestack.io/kube-jarvis/pkg/plugins/cluster/custom/compexplorer" 29 | ) 30 | 31 | type fakeComp struct { 32 | } 33 | 34 | func (f *fakeComp) Component() ([]cluster.Component, error) { 35 | return []cluster.Component{ 36 | { 37 | Name: "kube-apiserver", 38 | IsRunning: true, 39 | }, 40 | }, nil 41 | } 42 | 43 | func (f *fakeComp) Finish() error { 44 | return nil 45 | } 46 | 47 | type fakeNodeExecutor struct { 48 | success bool 49 | } 50 | 51 | func (f *fakeNodeExecutor) DoCmd(nodeName string, cmd []string) (string, string, error) { 52 | out := `kube-apiserver 53 | -a=123 54 | -b=321 55 | ` 56 | if !f.success { 57 | return "", "", nil 58 | } 59 | return out, "", nil 60 | } 61 | 62 | func (f *fakeNodeExecutor) Finish() error { 63 | return nil 64 | } 65 | 66 | func TestGetSysCtlMap(t *testing.T) { 67 | out := ` 68 | a = 1 69 | b = 2 3 70 | ` 71 | m := GetSysCtlMap(out) 72 | t.Logf("%+v", m) 73 | if len(m) != 2 { 74 | t.Fatalf("want 2 key") 75 | } 76 | 77 | if m["a"] != "1" { 78 | t.Fatalf("key a want value 1") 79 | } 80 | 81 | if m["b"] != "2 3" { 82 | t.Fatalf("key b want value 2") 83 | } 84 | } 85 | 86 | func TestCluster_Resources(t *testing.T) { 87 | fk := fake.NewSimpleClientset() 88 | pod := &v1.Pod{} 89 | pod.Name = "pod1" 90 | pod.Namespace = "kube-system" 91 | 92 | ns := &v1.Namespace{} 93 | ns.Name = "kube-system" 94 | if _, err := fk.CoreV1().Namespaces().Create(ns); err != nil { 95 | t.Fatalf(err.Error()) 96 | } 97 | 98 | if _, err := fk.CoreV1().Pods(pod.Namespace).Create(pod); err != nil { 99 | t.Fatalf(err.Error()) 100 | } 101 | 102 | node := &v1.Node{} 103 | node.Name = "node1" 104 | if _, err := fk.CoreV1().Nodes().Create(node); err != nil { 105 | t.Fatalf(err.Error()) 106 | } 107 | 108 | cls := NewCluster(logger.NewLogger(), fk, nil).(*Cluster) 109 | if cls.CloudType() != Type { 110 | t.Fatalf("wrong cloud type") 111 | } 112 | 113 | if err := cls.Complete(); err != nil { 114 | t.Fatalf(err.Error()) 115 | } 116 | 117 | cls.Components = map[string]*compexplorer.Auto{} 118 | cls.compExps = map[string]compexplorer.Explorer{ 119 | cluster.ComponentApiserver: &fakeComp{}, 120 | } 121 | cls.nodeExecutor = &fakeNodeExecutor{success: true} 122 | 123 | if err := cls.Init(context.Background(), plugins.NewProgress()); err != nil { 124 | t.Fatalf(err.Error()) 125 | } 126 | 127 | res := cls.Resources() 128 | if len(res.Pods.Items) != 1 { 129 | t.Fatalf("want 1 Pods") 130 | } 131 | 132 | if len(res.Machines) != 1 { 133 | t.Fatalf("want 1 Machines") 134 | } 135 | 136 | if len(res.CoreComponents) != 1 { 137 | t.Fatalf("want 1 CoreComponents") 138 | } 139 | 140 | if err := cls.Finish(); err != nil { 141 | t.Fatalf(err.Error()) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/compexplorer/auto_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package compexplorer 19 | 20 | import ( 21 | "fmt" 22 | "testing" 23 | 24 | v1 "k8s.io/api/core/v1" 25 | "k8s.io/client-go/kubernetes/fake" 26 | "tkestack.io/kube-jarvis/pkg/logger" 27 | ) 28 | 29 | func TestAuto_Init(t *testing.T) { 30 | cases := []struct { 31 | masterNodes bool 32 | nodes []string 33 | labels map[string]string 34 | }{ 35 | { 36 | masterNodes: true, 37 | nodes: []string{}, 38 | labels: map[string]string{}, 39 | }, 40 | { 41 | masterNodes: false, 42 | nodes: []string{}, 43 | }, 44 | } 45 | 46 | for _, cs := range cases { 47 | t.Run(fmt.Sprintf("%v", cs), func(t *testing.T) { 48 | fk := fake.NewSimpleClientset() 49 | for i := 0; i < 6; i++ { 50 | node := &v1.Node{} 51 | node.Name = fmt.Sprintf("10.0.0.%d", i) 52 | 53 | if i < 3 { 54 | node.Labels = map[string]string{ 55 | "node-role.kubernetes.io/master": "true", 56 | } 57 | } 58 | 59 | if _, err := fk.CoreV1().Nodes().Create(node); err != nil { 60 | t.Fatalf(err.Error()) 61 | } 62 | } 63 | 64 | a := NewAuto("kube-apiserver", cs.masterNodes) 65 | a.Nodes = cs.nodes 66 | if len(cs.labels) != 0 { 67 | a.Labels = cs.labels 68 | } 69 | if err := a.Init(logger.NewLogger(), fk, &fakeNodeExecutor{success: true}); err != nil { 70 | t.Fatalf(err.Error()) 71 | } 72 | 73 | if cs.masterNodes && len(a.Nodes) != 3 { 74 | t.Fatalf("want 3 nodes") 75 | } 76 | 77 | if !cs.masterNodes && len(cs.nodes) == 0 && len(a.Nodes) != 6 { 78 | t.Fatalf("want 6 nodes") 79 | } 80 | }) 81 | } 82 | } 83 | 84 | func TestAuto_Component(t *testing.T) { 85 | fk := fake.NewSimpleClientset() 86 | a := NewAuto("kube-apiserver", false) 87 | a.Nodes = []string{"node1"} 88 | if err := a.Init(logger.NewLogger(), fk, &fakeNodeExecutor{success: true}); err != nil { 89 | t.Fatalf(err.Error()) 90 | } 91 | 92 | cmp, err := a.Component() 93 | if err != nil { 94 | t.Fatalf(err.Error()) 95 | } 96 | 97 | if len(cmp) != 1 { 98 | t.Fatalf("want len 1 but get %d", len(cmp)) 99 | } 100 | 101 | if !cmp[0].IsRunning { 102 | t.Fatalf("IsRuning wrong") 103 | } 104 | 105 | if cmp[0].Args["a"] != "123" { 106 | t.Fatalf("want key a valuer 123 but get %s", cmp[0].Args["a"]) 107 | } 108 | 109 | if cmp[0].Args["b"] != "321" { 110 | t.Fatalf("want key a valuer 321 but get %s", cmp[0].Args["a"]) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/compexplorer/bare.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package compexplorer 19 | 20 | import ( 21 | "fmt" 22 | "strings" 23 | "sync" 24 | 25 | "golang.org/x/sync/errgroup" 26 | "tkestack.io/kube-jarvis/pkg/logger" 27 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 28 | "tkestack.io/kube-jarvis/pkg/plugins/cluster/custom/nodeexec" 29 | ) 30 | 31 | // Bare get component information by executing cmd on node 32 | type Bare struct { 33 | logger logger.Logger 34 | cmdName string 35 | nodes []string 36 | nodeExecutor nodeexec.Executor 37 | } 38 | 39 | // NewBare create and int a StaticPods ComponentExecutor 40 | func NewBare(logger logger.Logger, 41 | cmdName string, nodes []string, 42 | executor nodeexec.Executor) *Bare { 43 | return &Bare{ 44 | logger: logger, 45 | cmdName: cmdName, 46 | nodes: nodes, 47 | nodeExecutor: executor, 48 | } 49 | } 50 | 51 | // Component get cluster components 52 | func (b *Bare) Component() ([]cluster.Component, error) { 53 | cmd := fmt.Sprintf("pgrep %s && cat /proc/`pgrep %s`/cmdline | xargs -0 | tr ' ' '\\n'", 54 | b.cmdName, b.cmdName) 55 | result := make([]cluster.Component, 0) 56 | lk := sync.Mutex{} 57 | conCtl := make(chan struct{}, 200) 58 | g := errgroup.Group{} 59 | 60 | for _, tempN := range b.nodes { 61 | n := tempN 62 | g.Go(func() error { 63 | conCtl <- struct{}{} 64 | defer func() { <-conCtl }() 65 | 66 | out, _, err := b.nodeExecutor.DoCmd(n, []string{ 67 | "/bin/sh", "-c", cmd, 68 | }) 69 | if err != nil { 70 | if !strings.Contains(err.Error(), "terminated with exit code") { 71 | b.logger.Errorf("do command on node %s failed :%v", n, err) 72 | } 73 | return err 74 | } 75 | 76 | cmp := cluster.Component{ 77 | Name: b.cmdName, 78 | Node: n, 79 | Args: map[string]string{}, 80 | } 81 | 82 | lines := strings.Split(out, "\n") 83 | for i, line := range lines { 84 | line = strings.TrimSpace(line) 85 | line = strings.TrimLeft(line, "-") 86 | if line == "" { 87 | continue 88 | } 89 | 90 | if i == 0 { 91 | cmp.IsRunning = true 92 | continue 93 | } 94 | 95 | spIndex := strings.IndexAny(line, "=") 96 | if spIndex == -1 { 97 | continue 98 | } 99 | 100 | k := line[0:spIndex] 101 | v := line[spIndex+1:] 102 | cmp.Args[strings.TrimSpace(k)] = strings.TrimSpace(v) 103 | } 104 | 105 | lk.Lock() 106 | result = append(result, cmp) 107 | lk.Unlock() 108 | return nil 109 | }) 110 | } 111 | _ = g.Wait() 112 | 113 | return result, nil 114 | } 115 | 116 | // Finish will be called once every thing done 117 | func (b *Bare) Finish() error { 118 | return nil 119 | } 120 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/compexplorer/bare_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package compexplorer 19 | 20 | import ( 21 | "fmt" 22 | "testing" 23 | 24 | "tkestack.io/kube-jarvis/pkg/logger" 25 | ) 26 | 27 | type fakeNodeExecutor struct { 28 | success bool 29 | } 30 | 31 | func (f *fakeNodeExecutor) DoCmd(nodeName string, cmd []string) (string, string, error) { 32 | out := `kube-apiserver 33 | -a=123 34 | -b=321 35 | ` 36 | if !f.success { 37 | return "", "", nil 38 | } 39 | return out, "", nil 40 | } 41 | 42 | func (f *fakeNodeExecutor) Finish() error { 43 | return nil 44 | } 45 | 46 | func TestBare_Component(t *testing.T) { 47 | cases := []struct { 48 | success bool 49 | }{ 50 | { 51 | success: true, 52 | }, 53 | { 54 | success: false, 55 | }, 56 | } 57 | 58 | for _, cs := range cases { 59 | t.Run(fmt.Sprintf("%v", cs), func(t *testing.T) { 60 | f := &fakeNodeExecutor{success: cs.success} 61 | b := NewBare(logger.NewLogger(), "kube-apiserver", []string{"node1"}, f) 62 | cmp, err := b.Component() 63 | if err != nil { 64 | t.Fatalf(err.Error()) 65 | } 66 | 67 | if len(cmp) != 1 { 68 | t.Fatalf("want len 1 but get %d", len(cmp)) 69 | } 70 | 71 | if cmp[0].IsRunning != cs.success { 72 | t.Fatalf("IsRuning wrong") 73 | } 74 | 75 | if !cs.success { 76 | return 77 | } 78 | 79 | if cmp[0].Args["a"] != "123" { 80 | t.Fatalf("want key a valuer 123 but get %s", cmp[0].Args["a"]) 81 | } 82 | 83 | if cmp[0].Args["b"] != "321" { 84 | t.Fatalf("want key a valuer 321 but get %s", cmp[0].Args["a"]) 85 | } 86 | 87 | }) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/compexplorer/component.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package compexplorer 19 | 20 | import ( 21 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 22 | ) 23 | 24 | const ( 25 | // TypeStaticPod is the type of StaticPod explore 26 | TypeStaticPod = "StaticPod" 27 | // TypeLabel is the type of Label explore 28 | TypeLabel = "Label" 29 | // TypeBare is the type of Bare explore 30 | TypeBare = "Bare" 31 | // TypeAuto is the type of Auto explore 32 | TypeAuto = "Auto" 33 | ) 34 | 35 | // Explorer get component information 36 | type Explorer interface { 37 | // Component get cluster components 38 | Component() ([]cluster.Component, error) 39 | // Finish will be called once every thing done 40 | Finish() error 41 | } 42 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/compexplorer/label.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package compexplorer 19 | 20 | import ( 21 | "github.com/pkg/errors" 22 | v1 "k8s.io/api/core/v1" 23 | v12 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | "k8s.io/apimachinery/pkg/labels" 25 | "k8s.io/client-go/kubernetes" 26 | "tkestack.io/kube-jarvis/pkg/logger" 27 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 28 | "tkestack.io/kube-jarvis/pkg/plugins/cluster/custom/nodeexec" 29 | ) 30 | 31 | // LabelExp get select pod with labels and try get component from 32 | type LabelExp struct { 33 | logger logger.Logger 34 | cli kubernetes.Interface 35 | namespace string 36 | name string 37 | labels map[string]string 38 | exec nodeexec.Executor 39 | explorePods func(logger logger.Logger, name string, 40 | pods []v1.Pod, exec nodeexec.Executor) []cluster.Component 41 | } 42 | 43 | // NewLabelExp create and init a LabelExp Component 44 | func NewLabelExp(logger logger.Logger, cli kubernetes.Interface, 45 | namespace string, cmpName string, 46 | labels map[string]string, exec nodeexec.Executor) *LabelExp { 47 | if len(labels) == 0 { 48 | labels = map[string]string{ 49 | "k8s-app": cmpName, 50 | } 51 | } 52 | 53 | return &LabelExp{ 54 | logger: logger, 55 | cli: cli, 56 | name: cmpName, 57 | labels: labels, 58 | namespace: namespace, 59 | exec: exec, 60 | explorePods: ExplorePods, 61 | } 62 | } 63 | 64 | // Component get cluster components 65 | func (l *LabelExp) Component() ([]cluster.Component, error) { 66 | pods, err := l.cli.CoreV1().Pods(l.namespace).List(v12.ListOptions{ 67 | LabelSelector: labels.FormatLabels(l.labels), 68 | }) 69 | if err != nil { 70 | return nil, errors.Wrapf(err, "get pods failed") 71 | } 72 | 73 | return l.explorePods(l.logger, l.name, pods.Items, l.exec), nil 74 | } 75 | 76 | // Finish will be called once every thing done 77 | func (l *LabelExp) Finish() error { 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/compexplorer/label_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package compexplorer 19 | 20 | import ( 21 | "testing" 22 | 23 | v1 "k8s.io/api/core/v1" 24 | "k8s.io/client-go/kubernetes/fake" 25 | "tkestack.io/kube-jarvis/pkg/logger" 26 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 27 | "tkestack.io/kube-jarvis/pkg/plugins/cluster/custom/nodeexec" 28 | ) 29 | 30 | func TestDaemonSet_Component(t *testing.T) { 31 | fk := fake.NewSimpleClientset() 32 | pod1 := &v1.Pod{} 33 | pod1.Name = "p1" 34 | pod1.Namespace = "kube-system" 35 | pod1.Labels = map[string]string{ 36 | "k8s-app": "p1", 37 | } 38 | 39 | if _, err := fk.CoreV1().Pods("kube-system").Create(pod1); err != nil { 40 | t.Fatalf(err.Error()) 41 | } 42 | 43 | pod2 := &v1.Pod{} 44 | pod2.Namespace = "kube-system" 45 | pod2.Name = "p2" 46 | pod2.Labels = map[string]string{ 47 | "k8s-app": "p2", 48 | } 49 | 50 | if _, err := fk.CoreV1().Pods("kube-system").Create(pod2); err != nil { 51 | t.Fatalf(err.Error()) 52 | } 53 | 54 | l := NewLabelExp(logger.NewLogger(), fk, "kube-system", "p1", nil, nil) 55 | l.explorePods = func(logger logger.Logger, name string, pods []v1.Pod, exec nodeexec.Executor) []cluster.Component { 56 | if name != "p1" { 57 | t.Fatalf("name want p1 but get %s", name) 58 | } 59 | 60 | if len(pods) != 1 { 61 | t.Fatalf("want 1 pods but get %d", len(pods)) 62 | } 63 | 64 | if pods[0].Name != "p1" { 65 | t.Fatalf("want p1 pod but get %s", pods[0].Name) 66 | } 67 | 68 | return nil 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/compexplorer/pods.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package compexplorer 19 | 20 | import ( 21 | "strings" 22 | "sync" 23 | 24 | "golang.org/x/sync/errgroup" 25 | v1 "k8s.io/api/core/v1" 26 | "tkestack.io/kube-jarvis/pkg/logger" 27 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 28 | "tkestack.io/kube-jarvis/pkg/plugins/cluster/custom/nodeexec" 29 | ) 30 | 31 | // ExplorePod explore a component from k8s pods 32 | // if exec is not nil, a bare explore will be used for fetching component command line arguments 33 | // if bare explore failed, command line argument find in pod will be used 34 | func ExplorePods(logger logger.Logger, name string, 35 | pods []v1.Pod, exec nodeexec.Executor) []cluster.Component { 36 | result := make([]cluster.Component, 0) 37 | lk := sync.Mutex{} 38 | g := errgroup.Group{} 39 | conCtl := make(chan struct{}, 200) 40 | 41 | for _, tempPod := range pods { 42 | pod := tempPod 43 | g.Go(func() error { 44 | conCtl <- struct{}{} 45 | defer func() { <-conCtl }() 46 | 47 | r := cluster.Component{ 48 | Name: pod.Name, 49 | Node: pod.Spec.NodeName, 50 | Args: GetPodArgs(name, &pod), 51 | Pod: &pod, 52 | } 53 | 54 | // see Ready as component Running 55 | for _, c := range pod.Status.Conditions { 56 | if c.Type == v1.PodReady && c.Status == v1.ConditionTrue { 57 | r.IsRunning = true 58 | break 59 | } 60 | } 61 | 62 | // we try to get args via node executor 63 | if exec != nil && pod.Spec.NodeName != "" { 64 | bare := NewBare(logger, name, []string{pod.Spec.NodeName}, exec) 65 | cmp, err := bare.Component() 66 | if err != nil { 67 | logger.Errorf("try get component detail via node executor failed : %v", err) 68 | } else { 69 | if len(cmp) == 0 { 70 | logger.Errorf("can not found target component %s on node %s via node executor", 71 | name, pod.Spec.NodeName) 72 | } else if len(cmp[0].Args) == 0 { 73 | logger.Errorf("found target component %s on node %s ,but get empty args", 74 | name, pod.Spec.NodeName) 75 | } else { 76 | r.Args = cmp[0].Args 77 | } 78 | } 79 | } 80 | 81 | lk.Lock() 82 | result = append(result, r) 83 | lk.Unlock() 84 | return nil 85 | }) 86 | } 87 | 88 | _ = g.Wait() 89 | 90 | return result 91 | } 92 | 93 | // GetPodArgs try get args from pod 94 | func GetPodArgs(name string, pod *v1.Pod) map[string]string { 95 | result := make(map[string]string) 96 | for _, c := range pod.Spec.Containers { 97 | if c.Name == name { 98 | for _, arg := range c.Args { 99 | arg = strings.TrimLeft(arg, "-") 100 | spIndex := strings.IndexAny(arg, "=") 101 | if spIndex == -1 { 102 | continue 103 | } 104 | 105 | k := arg[0:spIndex] 106 | v := arg[spIndex+1:] 107 | result[strings.TrimSpace(k)] = strings.TrimSpace(v) 108 | } 109 | } 110 | } 111 | return result 112 | } 113 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/compexplorer/pods_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package compexplorer 19 | 20 | import ( 21 | "fmt" 22 | v1 "k8s.io/api/core/v1" 23 | "testing" 24 | "tkestack.io/kube-jarvis/pkg/logger" 25 | ) 26 | 27 | func TestExplorePods(t *testing.T) { 28 | cases := []struct { 29 | useNodeExp bool 30 | cmpRunning bool 31 | }{ 32 | { 33 | cmpRunning: false, 34 | }, 35 | { 36 | cmpRunning: true, 37 | useNodeExp: true, 38 | }, { 39 | cmpRunning: true, 40 | useNodeExp: false, 41 | }, 42 | } 43 | 44 | for _, cs := range cases { 45 | t.Run(fmt.Sprintf("%+v", cs), func(t *testing.T) { 46 | pod := v1.Pod{} 47 | pod.Name = "kube-apiserver" 48 | pod.Spec.NodeName = "10.0.0.1" 49 | pod.Spec.Containers = []v1.Container{ 50 | { 51 | Name: "kube-apiserver", 52 | Args: []string{ 53 | "-a=c1", 54 | "-b=c2", 55 | }, 56 | }, 57 | } 58 | 59 | if cs.cmpRunning { 60 | pod.Status = v1.PodStatus{ 61 | Conditions: []v1.PodCondition{ 62 | { 63 | Type: v1.PodReady, 64 | Status: v1.ConditionTrue, 65 | }, 66 | }, 67 | } 68 | } 69 | 70 | results := ExplorePods(logger.NewLogger(), "kube-apiserver", []v1.Pod{pod}, &fakeNodeExecutor{success: true}) 71 | if !cs.useNodeExp { 72 | results = ExplorePods(logger.NewLogger(), "kube-apiserver", []v1.Pod{pod}, nil) 73 | } 74 | 75 | if len(results) != 1 { 76 | t.Fatalf("want 1 result but get %d", len(results)) 77 | } 78 | 79 | cmp := results[0] 80 | if cmp.Pod == nil { 81 | t.Fatalf("component pod should not nil") 82 | } 83 | 84 | if !cs.cmpRunning { 85 | if cmp.IsRunning { 86 | t.Fatalf("component should not running") 87 | } 88 | return 89 | } 90 | 91 | if len(cmp.Args) != 2 { 92 | t.Fatalf("want 2 args") 93 | } 94 | 95 | if cs.useNodeExp { 96 | if cmp.Args["a"] != "123" { 97 | t.Fatalf("key a want value 123 but get %s", cmp.Args["a"]) 98 | } 99 | 100 | if cmp.Args["b"] != "321" { 101 | t.Fatalf("key a want value 123 but get %s", cmp.Args["b"]) 102 | } 103 | } else { 104 | if cmp.Args["a"] != "c1" { 105 | t.Fatalf("key a want value c1 but get %s", cmp.Args["a"]) 106 | } 107 | 108 | if cmp.Args["b"] != "c2" { 109 | t.Fatalf("key b want value c2 but get %s", cmp.Args["b"]) 110 | } 111 | } 112 | }) 113 | } 114 | } 115 | 116 | func TestGetPodArgs(t *testing.T) { 117 | pod := &v1.Pod{} 118 | pod.Name = "test" 119 | pod.Spec.Containers = []v1.Container{ 120 | { 121 | Name: "test", 122 | Args: []string{ 123 | "-a=123", 124 | "-b=321", 125 | }, 126 | }, 127 | } 128 | 129 | m := GetPodArgs("test", pod) 130 | if len(m) != 2 { 131 | t.Fatalf("should return 2 args") 132 | } 133 | 134 | if m["a"] != "123" { 135 | t.Fatalf("key a want 123 ,but get %s", m["a"]) 136 | } 137 | 138 | if m["b"] != "321" { 139 | t.Fatalf("key a want 123 ,but get %s", m["a"]) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/compexplorer/staticpod.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package compexplorer 19 | 20 | import ( 21 | "fmt" 22 | 23 | "github.com/pkg/errors" 24 | v12 "k8s.io/api/core/v1" 25 | k8serr "k8s.io/apimachinery/pkg/api/errors" 26 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | "k8s.io/client-go/kubernetes" 28 | "tkestack.io/kube-jarvis/pkg/logger" 29 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 30 | "tkestack.io/kube-jarvis/pkg/plugins/cluster/custom/nodeexec" 31 | ) 32 | 33 | // StaticPods get component information from static pod 34 | type StaticPods struct { 35 | logger logger.Logger 36 | cli kubernetes.Interface 37 | podName string 38 | namespace string 39 | nodes []string 40 | exec nodeexec.Executor 41 | ExplorePods func(logger logger.Logger, name string, 42 | pods []v12.Pod, exec nodeexec.Executor) []cluster.Component 43 | } 44 | 45 | // NewStaticPods create and int a StaticPods ComponentExecutor 46 | func NewStaticPods(logger logger.Logger, cli kubernetes.Interface, namespace string, 47 | podPrefix string, nodes []string, exe nodeexec.Executor) *StaticPods { 48 | return &StaticPods{ 49 | logger: logger, 50 | cli: cli, 51 | podName: podPrefix, 52 | namespace: namespace, 53 | nodes: nodes, 54 | exec: exe, 55 | ExplorePods: ExplorePods, 56 | } 57 | } 58 | 59 | // Component get cluster components 60 | func (s *StaticPods) Component() ([]cluster.Component, error) { 61 | result := make([]cluster.Component, 0) 62 | pods := make([]v12.Pod, 0) 63 | for _, n := range s.nodes { 64 | cmp := cluster.Component{ 65 | Name: s.podName, 66 | Node: n, 67 | } 68 | 69 | podName := fmt.Sprintf("%s-%s", s.podName, n) 70 | pod, err := s.cli.CoreV1().Pods(s.namespace).Get(podName, v1.GetOptions{}) 71 | if err != nil { 72 | if !k8serr.IsNotFound(err) { 73 | cmp.Error = errors.Wrapf(err, "get target pod %s failed", cmp.Name) 74 | } 75 | result = append(result, cmp) 76 | continue 77 | } 78 | 79 | pods = append(pods, *pod) 80 | } 81 | 82 | return append(result, s.ExplorePods(s.logger, s.podName, pods, s.exec)...), nil 83 | } 84 | 85 | // Finish will be called once every thing done 86 | func (s *StaticPods) Finish() error { 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/compexplorer/staticpod_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package compexplorer 19 | 20 | import ( 21 | "fmt" 22 | v1 "k8s.io/api/core/v1" 23 | "k8s.io/client-go/kubernetes/fake" 24 | "testing" 25 | "tkestack.io/kube-jarvis/pkg/logger" 26 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 27 | "tkestack.io/kube-jarvis/pkg/plugins/cluster/custom/nodeexec" 28 | ) 29 | 30 | func TestStaticPods_Component(t *testing.T) { 31 | fk := fake.NewSimpleClientset() 32 | total := 2 33 | nodes := make([]string, 0) 34 | for i := 0; i < total; i++ { 35 | n := &v1.Node{} 36 | n.Name = fmt.Sprintf("10.0.0.%d", i) 37 | if _, err := fk.CoreV1().Nodes().Create(n); err != nil { 38 | t.Fatalf("create master %s failed", n.Name) 39 | } 40 | 41 | nodes = append(nodes, n.Name) 42 | 43 | if i == 0 { 44 | continue 45 | } 46 | 47 | pod := &v1.Pod{} 48 | pod.Spec.NodeName = n.Name 49 | pod.Namespace = "kube-system" 50 | pod.Name = fmt.Sprintf("test-%s", n.Name) 51 | 52 | if _, err := fk.CoreV1().Pods("kube-system").Create(pod); err != nil { 53 | t.Fatalf(err.Error()) 54 | } 55 | } 56 | 57 | sd := NewStaticPods(logger.NewLogger(), fk, "kube-system", "test", nodes, nil) 58 | sd.ExplorePods = func(logger logger.Logger, name string, pods []v1.Pod, exec nodeexec.Executor) []cluster.Component { 59 | if name != "test" { 60 | t.Fatalf("want name test but get %s", name) 61 | } 62 | 63 | if len(pods) != 1 { 64 | t.Fatalf("want 1 pods but get %d", len(pods)) 65 | } 66 | 67 | return nil 68 | } 69 | 70 | cmp, err := sd.Component() 71 | if err != nil { 72 | t.Fatalf(err.Error()) 73 | } 74 | 75 | if len(cmp) != 1 { 76 | t.Fatalf("want 2 result but get %d", len(cmp)) 77 | } 78 | 79 | if cmp[0].IsRunning { 80 | t.Fatalf("want cmponent not running ") 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/nodeexec/nodeexec.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package nodeexec 19 | 20 | import ( 21 | "fmt" 22 | 23 | "k8s.io/client-go/kubernetes" 24 | restclient "k8s.io/client-go/rest" 25 | "tkestack.io/kube-jarvis/pkg/logger" 26 | ) 27 | 28 | var ( 29 | // UnKnowTypeErr will be returned if target node executor is not found 30 | UnKnowTypeErr = fmt.Errorf("unknow node executor type") 31 | // NoneExecutor will be returned if node executor type is "none" 32 | NoneExecutor = fmt.Errorf("none executor") 33 | ) 34 | 35 | // Executor get machine information 36 | type Executor interface { 37 | // DoCmd do cmd on node and return output 38 | DoCmd(nodeName string, cmd []string) (string, string, error) 39 | // Finish will be called once this Executor work done 40 | Finish() error 41 | } 42 | 43 | // Config is the config of node executor 44 | type Config struct { 45 | // Type is the node executor type 46 | Type string 47 | // Namespace is the namespace to install node agent if node executor type is "agent" 48 | Namespace string 49 | // DaemonSet is the DaemonSet name if node executor type is "agent" 50 | DaemonSet string 51 | // Image is the image that will be use to create node agent DaemonSet 52 | Image string 53 | // AutoCreate indicate whether to create node agent DaemonSet if it is not exist 54 | // if AutoCreate is true, agent will be deleted once cluster diagnostic done 55 | AutoCreate bool 56 | } 57 | 58 | // NewConfig return a Config with default value 59 | func NewConfig() *Config { 60 | return &Config{} 61 | } 62 | 63 | // Complete check and complete config fields 64 | func (c *Config) Complete() { 65 | if c.Type == "" { 66 | c.Type = "proxy" 67 | } 68 | 69 | if c.Namespace == "" { 70 | c.Namespace = "kube-jarvis" 71 | } 72 | 73 | if c.DaemonSet == "" { 74 | c.DaemonSet = "kube-jarvis-agent" 75 | } 76 | 77 | if c.Image == "" { 78 | c.Image = "raylhuang110/kube-jarvis-agent:latest" 79 | } 80 | } 81 | 82 | // Executor return the appropriate node executor according to config value 83 | func (c *Config) Executor(logger logger.Logger, 84 | cli kubernetes.Interface, config *restclient.Config) (Executor, error) { 85 | switch c.Type { 86 | case "proxy": 87 | return NewDaemonSetProxy(logger, cli, config, c.Namespace, c.DaemonSet, c.Image, c.AutoCreate) 88 | case "none": 89 | return nil, NoneExecutor 90 | } 91 | return nil, UnKnowTypeErr 92 | } 93 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/nodeexec/nodeexec_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package nodeexec 19 | 20 | import ( 21 | "k8s.io/client-go/kubernetes/fake" 22 | "testing" 23 | "tkestack.io/kube-jarvis/pkg/logger" 24 | ) 25 | 26 | func TestConfig_Executor(t *testing.T) { 27 | n := NewConfig() 28 | n.Complete() 29 | 30 | exe, err := n.Executor(logger.NewLogger(), fake.NewSimpleClientset(), nil) 31 | if err != nil { 32 | t.Fatalf(err.Error()) 33 | } 34 | 35 | _, ok := exe.(*DaemonSetProxy) 36 | if !ok { 37 | t.Fatalf("should return an DaemonSetProxy Executor") 38 | } 39 | 40 | n.Type = "none" 41 | _, err = n.Executor(logger.NewLogger(), fake.NewSimpleClientset(), nil) 42 | if err != NoneExecutor { 43 | t.Fatalf("should get a UnKnowTypeErr") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/custom/nodeexec/yaml.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package nodeexec 19 | 20 | var proxyYaml = `apiVersion: apps/v1 21 | kind: DaemonSet 22 | metadata: 23 | name: %s 24 | labels: 25 | k8s-app: kube-jarvis-agent 26 | namespace: %s 27 | spec: 28 | selector: 29 | matchLabels: 30 | k8s-app: kube-jarvis-agent 31 | template: 32 | metadata: 33 | labels: 34 | k8s-app: kube-jarvis-agent 35 | spec: 36 | hostNetwork: true 37 | hostPID: true 38 | hostIPC: true 39 | tolerations: 40 | - effect: NoExecute 41 | operator: Exists 42 | - effect: NoSchedule 43 | operator: Exists 44 | containers: 45 | - image: %s 46 | command: ["sleep","1000000000d"] 47 | imagePullPolicy: Always 48 | name: proxy 49 | securityContext: 50 | runAsUser: 0 51 | privileged: true 52 | volumeMounts: 53 | - name: dbus 54 | mountPath: /var/run/dbus 55 | - name: run-systemd 56 | mountPath: /run/systemd 57 | - name: etc-systemd 58 | mountPath: /etc/systemd 59 | - name: var-log 60 | mountPath: /var/log 61 | - name: var-run 62 | mountPath: /var/run 63 | - name: run 64 | mountPath: /run 65 | - name: usr-lib-systemd 66 | mountPath: /usr/lib/systemd 67 | - name: etc-machine-id 68 | mountPath: /etc/machine-id 69 | - name: etc-sudoers 70 | mountPath: /etc/sudoers.d 71 | volumes: 72 | - name: dbus 73 | hostPath: 74 | path: /var/run/dbus 75 | type: Directory 76 | - name: run-systemd 77 | hostPath: 78 | path: /run/systemd 79 | type: Directory 80 | - name: etc-systemd 81 | hostPath: 82 | path: /etc/systemd 83 | type: Directory 84 | - name: var-log 85 | hostPath: 86 | path: /var/log 87 | type: Directory 88 | - name: var-run 89 | hostPath: 90 | path: /var/run 91 | type: Directory 92 | - name: run 93 | hostPath: 94 | path: /run 95 | type: Directory 96 | - name: usr-lib-systemd 97 | hostPath: 98 | path: /usr/lib/systemd 99 | type: Directory 100 | - name: etc-machine-id 101 | hostPath: 102 | path: /etc/machine-id 103 | type: File 104 | - name: etc-sudoers 105 | hostPath: 106 | path: /etc/sudoers.d 107 | type: Directory` 108 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/fake/cluster.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package fake 19 | 20 | import ( 21 | "context" 22 | "tkestack.io/kube-jarvis/pkg/plugins" 23 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 24 | ) 25 | 26 | type Cluster struct { 27 | Res *cluster.Resources 28 | } 29 | 30 | func NewCluster() *Cluster { 31 | return &Cluster{Res: cluster.NewResources()} 32 | } 33 | 34 | // Complete check and complete config items 35 | func (c *Cluster) Complete() error { 36 | return nil 37 | } 38 | 39 | // Init Instantiation for cluster, it will fetch Resources 40 | func (c *Cluster) Init(ctx context.Context, progress *plugins.Progress) error { 41 | return nil 42 | } 43 | 44 | // Finish will be called once diagnostic done 45 | func (c *Cluster) Finish() error { 46 | return nil 47 | } 48 | 49 | // CloudType return the cloud type of Cluster 50 | func (c *Cluster) CloudType() string { 51 | return "fake" 52 | } 53 | 54 | // Machine return the low level information of a node 55 | func (c *Cluster) Resources() *cluster.Resources { 56 | return c.Res 57 | } 58 | -------------------------------------------------------------------------------- /pkg/plugins/cluster/resources_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | 19 | package cluster 20 | 21 | import ( 22 | "fmt" 23 | "testing" 24 | ) 25 | 26 | func TestResourcesFilter_Compile(t *testing.T) { 27 | cases := []struct { 28 | filter ResourcesFilter 29 | pass bool 30 | }{ 31 | { 32 | filter: ResourcesFilter{ 33 | { 34 | Namespace: "**", 35 | }, 36 | }, 37 | pass: false, 38 | }, 39 | { 40 | filter: ResourcesFilter{ 41 | { 42 | Namespace: ".*", 43 | }, 44 | }, 45 | pass: true, 46 | }, 47 | } 48 | 49 | for _, cs := range cases { 50 | t.Run(fmt.Sprintf("%+v", cs), func(t *testing.T) { 51 | err := cs.filter.Compile() 52 | if (err == nil) != cs.pass { 53 | t.Fatalf("want pass = %v, but not", cs.pass) 54 | } 55 | }) 56 | } 57 | } 58 | 59 | func TestResourcesFilter_Filtered(t *testing.T) { 60 | cases := []struct { 61 | filter ResourcesFilter 62 | ns string 63 | kind string 64 | name string 65 | filtered bool 66 | }{ 67 | { 68 | filter: ResourcesFilter{ 69 | { 70 | Namespace: "test", 71 | }, 72 | }, 73 | ns: "test", 74 | filtered: true, 75 | }, 76 | { 77 | filter: ResourcesFilter{ 78 | { 79 | Namespace: "test", 80 | Kind: "Pod", 81 | Name: "Pod1", 82 | }, 83 | }, 84 | ns: "test1", 85 | kind: "Pod2", 86 | name: "Pod2", 87 | filtered: false, 88 | }, 89 | { 90 | ns: "test", 91 | filtered: false, 92 | }, 93 | } 94 | 95 | for _, cs := range cases { 96 | t.Run(fmt.Sprintf("%+v", cs), func(t *testing.T) { 97 | if err := cs.filter.Compile(); err != nil { 98 | t.Fatalf(err.Error()) 99 | } 100 | 101 | if cs.filtered != cs.filter.Filtered(cs.ns, cs.kind, cs.name) { 102 | t.Fatalf("want %v but not", cs.filtered) 103 | } 104 | }) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /pkg/plugins/coordinate/all/all.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package all 19 | 20 | import ( 21 | "tkestack.io/kube-jarvis/pkg/plugins/coordinate" 22 | "tkestack.io/kube-jarvis/pkg/plugins/coordinate/basic" 23 | "tkestack.io/kube-jarvis/pkg/plugins/coordinate/cron" 24 | ) 25 | 26 | func init() { 27 | coordinate.Add("default", basic.NewCoordinator) 28 | coordinate.Add("cron", cron.NewCoordinator) 29 | } 30 | -------------------------------------------------------------------------------- /pkg/plugins/coordinate/basic/README.md: -------------------------------------------------------------------------------- 1 | # default coordinator 2 | 3 | default coordinator is the default coordinator, it just run diagnostics one by one and run evaluators one by one. 4 | any result will be send to all exporters 5 | 6 | # config 7 | ```yaml 8 | coordinate: 9 | type: "default" 10 | ``` -------------------------------------------------------------------------------- /pkg/plugins/coordinate/basic/default_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package basic 19 | 20 | import ( 21 | "context" 22 | "testing" 23 | 24 | logger2 "tkestack.io/kube-jarvis/pkg/logger" 25 | "tkestack.io/kube-jarvis/pkg/plugins" 26 | "tkestack.io/kube-jarvis/pkg/plugins/cluster/fake" 27 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 28 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose/other/example" 29 | "tkestack.io/kube-jarvis/pkg/plugins/export" 30 | "tkestack.io/kube-jarvis/pkg/plugins/export/stdout" 31 | "tkestack.io/kube-jarvis/pkg/store" 32 | "tkestack.io/kube-jarvis/pkg/translate" 33 | ) 34 | 35 | func TestNewDefault(t *testing.T) { 36 | logger := logger2.NewLogger() 37 | ctx := context.Background() 38 | d := NewCoordinator(logger, fake.NewCluster(), store.GetStore("mem", "")) 39 | _ = d.Complete() 40 | 41 | d.AddDiagnostic(example.NewDiagnostic(&diagnose.MetaData{ 42 | MetaData: plugins.MetaData{ 43 | Translator: translate.NewFake(), 44 | }, 45 | })) 46 | d.AddExporter(stdout.NewExporter(&export.MetaData{})) 47 | _ = d.Run(ctx) 48 | } 49 | -------------------------------------------------------------------------------- /pkg/plugins/coordinate/coordinate.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package coordinate 19 | 20 | import ( 21 | "context" 22 | 23 | "tkestack.io/kube-jarvis/pkg/logger" 24 | "tkestack.io/kube-jarvis/pkg/plugins" 25 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 26 | "tkestack.io/kube-jarvis/pkg/store" 27 | 28 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 29 | "tkestack.io/kube-jarvis/pkg/plugins/export" 30 | ) 31 | 32 | // Coordinator knows how to coordinate diagnostics,exporters,evaluators 33 | type Coordinator interface { 34 | // Complete check and complete config items 35 | Complete() error 36 | // AddDiagnostic add a diagnostic to Coordinator 37 | AddDiagnostic(dia diagnose.Diagnostic) 38 | // AddExporter add a Exporter to Coordinator 39 | AddExporter(exporter export.Exporter) 40 | // Run will do all diagnostics, evaluations, then export it by exporters 41 | Run(ctx context.Context) error 42 | // Progress return the coordination progress 43 | // if coordination has not start, an nil will be returned 44 | Progress() *plugins.Progress 45 | } 46 | 47 | // Creator is a factory to create a Coordinator 48 | type Creator func(logger logger.Logger, cls cluster.Cluster, st store.Store) Coordinator 49 | 50 | // Creators store all registered Coordinator Creator 51 | var Creators = map[string]Creator{} 52 | 53 | // Add register a Coordinator Creator 54 | func Add(typ string, creator Creator) { 55 | Creators[typ] = creator 56 | } 57 | -------------------------------------------------------------------------------- /pkg/plugins/coordinate/cron/README.md: -------------------------------------------------------------------------------- 1 | # cron coordinator 2 | 3 | cron coordinator will run kube-jarvis as a cron job 4 | this coordinator will make kube-jarvis as a http server with following handlers 5 | 6 | # config 7 | ```yaml 8 | coordinate: 9 | type: "cron" 10 | config: 11 | cron: "1 * * * * *" 12 | # this is the path that will used to save wal file, 13 | # the wal file is used to auto retry if the process is restarted at diagnostic time 14 | walpath: "/tmp/" 15 | ``` 16 | 17 | 18 | * POST "/coordinator/cron/run" : run a diagnostic immediately 19 | 20 | curl http://127.0.0.1:9005/coordinator/cron/run -X POST 21 | 22 | * POST "/coordinator/cron/period" : set cron period 23 | 24 | curl http://127.0.0.1:9005/coordinator/cron/period -d '1 * * * * *' 25 | 26 | * GET "/coordinator/cron/period": get cron period 27 | 28 | curl http://127.0.0.1:9005/coordinator/cron/period 29 | "1 * * * * *" 30 | 31 | * GET "/coordinator/cron/state" : return current coordinator state 32 | 33 | curl http://127.0.0.1:9005/coordinator/cron/state 34 | 35 | {"State":"running","Progress":{"IsDone":false,"Steps":{"diagnostic":{"Title":"Diagnosing...","Total":4,"Percent":0,"Current":0},"init_components":{"Title":"Fetching all components..","Total":10,"Percent":10,"Current":1},"init_env":{"Title":"Preparing environment","Total":2,"Percent":100,"Current":2},"init_k8s_resources":{"Title":"Fetching k8s resources..","Total":20,"Percent":100,"Current":20},"init_machines":{"Title":"Fetching all machines..","Total":4,"Percent":0,"Current":0}},"CurStep":"init_components","Total":40,"Current":23,"Percent":57.49999999999999}} 36 | 37 | -------------------------------------------------------------------------------- /pkg/plugins/coordinate/cron/coordinator_test.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "tkestack.io/kube-jarvis/pkg/logger" 9 | "tkestack.io/kube-jarvis/pkg/plugins/cluster/fake" 10 | "tkestack.io/kube-jarvis/pkg/plugins/coordinate" 11 | "tkestack.io/kube-jarvis/pkg/store" 12 | ) 13 | 14 | func TestCoordinator_Run(t *testing.T) { 15 | count := 0 16 | c := NewCoordinator(logger.NewLogger(), fake.NewCluster(), store.GetStore("mem", "")).(*Coordinator) 17 | f := &coordinate.FakeCoordinator{ 18 | RunFunc: func(ctx context.Context) error { 19 | count++ 20 | return nil 21 | }, 22 | } 23 | c.Coordinator = f 24 | c.Cron = "@every 1s" 25 | 26 | if err := c.Complete(); err != nil { 27 | t.Fatalf(err.Error()) 28 | } 29 | 30 | ctx, cl := context.WithTimeout(context.Background(), time.Second*10) 31 | defer cl() 32 | go func() { 33 | _ = c.Run(ctx) 34 | }() 35 | 36 | for { 37 | suc := c.tryStartRun() 38 | if suc { 39 | break 40 | } 41 | } 42 | 43 | for { 44 | if ctx.Err() != nil { 45 | t.Fatalf("timeout") 46 | } 47 | if count == 2 { 48 | return 49 | } 50 | time.Sleep(time.Second) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/plugins/coordinate/cron/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package cron 19 | 20 | import ( 21 | "encoding/json" 22 | "io/ioutil" 23 | "net/http" 24 | 25 | "tkestack.io/kube-jarvis/pkg/httpserver" 26 | 27 | "github.com/robfig/cron/v3" 28 | ) 29 | 30 | // runOnceHandler run inspection immediately 31 | // if inspection is already running, status code will be 409 32 | func (c *Coordinator) runOnceHandler(w http.ResponseWriter, r *http.Request) { 33 | c.logger.Infof("handle run once request") 34 | ok := c.tryStartRun() 35 | if ok { 36 | w.WriteHeader(http.StatusOK) 37 | } else { 38 | w.WriteHeader(http.StatusConflict) 39 | } 40 | } 41 | 42 | // periodHandler return or set period 43 | // current inspection period will be returned if request method is 'Get' 44 | // new period will be set if request method is 'POST' 45 | func (c *Coordinator) periodHandler(w http.ResponseWriter, r *http.Request) { 46 | if r.Method == http.MethodGet { 47 | if _, err := w.Write([]byte(c.Cron)); err != nil { 48 | c.logger.Errorf("write cron config to response failed: %v", err) 49 | w.WriteHeader(http.StatusInternalServerError) 50 | } 51 | return 52 | } 53 | 54 | c.logger.Infof("handle update cron config") 55 | data, err := ioutil.ReadAll(r.Body) 56 | if err != nil { 57 | c.logger.Errorf("handle update cron config failed : %v", err) 58 | w.WriteHeader(http.StatusBadRequest) 59 | return 60 | } 61 | 62 | newCron := cron.New(cron.WithSeconds()) 63 | if string(data) != "" { 64 | if _, err := newCron.AddFunc(string(data), c.cronDo); err != nil { 65 | c.logger.Errorf("create new cron failed : %v", err) 66 | w.WriteHeader(http.StatusBadRequest) 67 | return 68 | } 69 | } 70 | 71 | c.cronLock.Lock() 72 | defer c.cronLock.Unlock() 73 | 74 | c.Cron = string(data) 75 | if c.cronCtl != nil { 76 | c.cronCtl.Stop() 77 | } 78 | 79 | if string(data) != "" { 80 | c.cronCtl = newCron 81 | c.cronCtl.Start() 82 | c.logger.Infof("cron scheduler success update to %s", string(data)) 83 | } else { 84 | c.logger.Infof("cron scheduler success update closed") 85 | } 86 | 87 | } 88 | 89 | // stateHandler return current inspection state and inspection process 90 | func (c *Coordinator) stateHandler(w http.ResponseWriter, r *http.Request) { 91 | c.logger.Infof("handle get current state") 92 | resp := &httpserver.StateResponse{ 93 | Progress: c.Progress(), 94 | State: c.state, 95 | } 96 | 97 | data, err := json.Marshal(resp) 98 | if err != nil { 99 | c.logger.Errorf("marshal resp failed : %v", err.Error()) 100 | w.WriteHeader(http.StatusInternalServerError) 101 | return 102 | } 103 | 104 | if _, err := w.Write(data); err != nil { 105 | c.logger.Errorf("write resp failed : %v", err.Error()) 106 | w.WriteHeader(http.StatusInternalServerError) 107 | return 108 | } 109 | c.logger.Infof("return current state success: %s ", string(data)) 110 | } 111 | -------------------------------------------------------------------------------- /pkg/plugins/coordinate/fake.go: -------------------------------------------------------------------------------- 1 | package coordinate 2 | 3 | import ( 4 | "context" 5 | "tkestack.io/kube-jarvis/pkg/plugins" 6 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 7 | "tkestack.io/kube-jarvis/pkg/plugins/export" 8 | ) 9 | 10 | type FakeCoordinator struct { 11 | RunFunc func(ctx context.Context) error 12 | } 13 | 14 | // Complete check and complete config items 15 | func (f *FakeCoordinator) Complete() error { 16 | return nil 17 | } 18 | 19 | // AddDiagnostic add a diagnostic to Coordinator 20 | func (f *FakeCoordinator) AddDiagnostic(dia diagnose.Diagnostic) { 21 | 22 | } 23 | 24 | // AddExporter add a Exporter to Coordinator 25 | func (f *FakeCoordinator) AddExporter(exporter export.Exporter) { 26 | 27 | } 28 | 29 | // Run will do all diagnostics, evaluations, then export it by exporters 30 | func (f *FakeCoordinator) Run(ctx context.Context) error { 31 | if f.RunFunc != nil { 32 | return f.RunFunc(ctx) 33 | } 34 | return nil 35 | } 36 | 37 | func (f *FakeCoordinator) Progress() *plugins.Progress { 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/common.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package diagnose 19 | 20 | import ( 21 | "fmt" 22 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | "k8s.io/apimachinery/pkg/runtime/schema" 24 | "k8s.io/apimachinery/pkg/types" 25 | "tkestack.io/kube-jarvis/pkg/translate" 26 | ) 27 | 28 | func CommonDeafer(c chan *Result) { 29 | close(c) 30 | if err := recover(); err != nil { 31 | c <- &Result{ 32 | Level: HealthyLevelFailed, 33 | ObjName: "*", 34 | Title: "Failed", 35 | Desc: translate.Message(fmt.Sprintf("%v", err)), 36 | } 37 | } 38 | } 39 | 40 | type MetaObject interface { 41 | schema.ObjectKind 42 | v1.Object 43 | } 44 | 45 | func GetRootOwner(obj MetaObject, uid2obj map[types.UID]MetaObject) MetaObject { 46 | ownerReferences := obj.GetOwnerReferences() 47 | if len(ownerReferences) > 0 { 48 | for _, owner := range ownerReferences { 49 | if owner.Controller != nil && *owner.Controller == true { 50 | if parent, ok := uid2obj[owner.UID]; ok { 51 | return GetRootOwner(parent, uid2obj) 52 | } 53 | } 54 | } 55 | } 56 | return obj 57 | } 58 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/master/args/apiserver/README.md: -------------------------------------------------------------------------------- 1 | # apiserver-args diagnostic 2 | 3 | This is diagnostic detection of whether kube-apiserver'arguments are a best practice 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "kube-apiserver-args" 9 | catalogue: ["master"] 10 | ``` 11 | # supported cluster type 12 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/master/args/controller-manager/README.md: -------------------------------------------------------------------------------- 1 | # kube-controller-manager-args diagnostic 2 | 3 | This is diagnostic detection of whether kube-controller-manager'arguments are a best practice 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "kube-controller-manager-args" 9 | catalogue: ["master"] 10 | ``` 11 | # supported cluster type 12 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/master/args/etcd/README.md: -------------------------------------------------------------------------------- 1 | # etcd-args diagnostic 2 | 3 | This is diagnostic detection of whether etcd'arguments are a best practice 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "etcd-args" 9 | catalogue: ["master"] 10 | ``` 11 | # supported cluster type 12 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/master/args/etcd/diagnostic.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package etcd 19 | 20 | import ( 21 | "context" 22 | "fmt" 23 | "strconv" 24 | 25 | "tkestack.io/kube-jarvis/pkg/translate" 26 | 27 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 28 | 29 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 30 | ) 31 | 32 | const ( 33 | // DiagnosticType is type name of this Diagnostic 34 | DiagnosticType = "etcd-args" 35 | ) 36 | 37 | // Diagnostic is a example diagnostic shows how to write a diagnostic 38 | type Diagnostic struct { 39 | *diagnose.MetaData 40 | result chan *diagnose.Result 41 | } 42 | 43 | // NewDiagnostic return a example diagnostic 44 | func NewDiagnostic(meta *diagnose.MetaData) diagnose.Diagnostic { 45 | return &Diagnostic{ 46 | result: make(chan *diagnose.Result, 1000), 47 | MetaData: meta, 48 | } 49 | } 50 | 51 | // Complete check and complete config items 52 | func (d *Diagnostic) Complete() error { 53 | return nil 54 | } 55 | 56 | // StartDiagnose return a result chan that will output results 57 | func (d *Diagnostic) StartDiagnose(ctx context.Context, 58 | param diagnose.StartDiagnoseParam) (chan *diagnose.Result, error) { 59 | d.result = make(chan *diagnose.Result, 1000) 60 | go func() { 61 | defer diagnose.CommonDeafer(d.result) 62 | for _, comp := range param.Resources.CoreComponents[cluster.ComponentETCD] { 63 | d.checkQuota(param.Resources, comp) 64 | } 65 | }() 66 | return d.result, nil 67 | } 68 | 69 | func (d *Diagnostic) checkQuota(resources *cluster.Resources, info cluster.Component) { 70 | arg := "quota-backend-bytes" 71 | curVal := 2 * 1024 * 1024 * 1024 72 | curValStr := info.Args[arg] 73 | if curValStr != "" { 74 | curVal, _ = strconv.Atoi(curValStr) 75 | } 76 | targetVal := 6 * 1024 * 1024 * 1024 77 | 78 | obj := map[string]interface{}{ 79 | "Name": info.Name, 80 | "Node": info.Node, 81 | "Arg": arg, 82 | "CurVal": curVal, 83 | "TargetVal": targetVal, 84 | } 85 | 86 | level := diagnose.HealthyLevelGood 87 | desc := d.Translator.Message("good-desc", obj) 88 | proposal := translate.Message("") 89 | 90 | if curVal < targetVal { 91 | level = diagnose.HealthyLevelWarn 92 | desc = d.Translator.Message(fmt.Sprintf("%s-desc", arg), obj) 93 | proposal = d.Translator.Message(fmt.Sprintf("%s-proposal", arg), obj) 94 | } 95 | 96 | d.result <- &diagnose.Result{ 97 | Level: level, 98 | ObjName: info.Name, 99 | ObjInfo: obj, 100 | Title: d.Translator.Message(fmt.Sprintf("%s-title", arg), nil), 101 | Desc: desc, 102 | Proposal: proposal, 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/master/args/scheduler/README.md: -------------------------------------------------------------------------------- 1 | # etcd-args diagnostic 2 | 3 | This is diagnostic detection of whether etcd'arguments are a best practice 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "etcd-args" 9 | catalogue: ["master"] 10 | ``` 11 | # supported cluster type 12 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/master/capacity/README.md: -------------------------------------------------------------------------------- 1 | # master-capacity diagnostic 2 | 3 | check whether the capacity are sufficient for a specific size cluster 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "master-capacity" 9 | # default values 10 | name: "master-capacity" 11 | catalogue: ["master"] 12 | config: 13 | Capacities: 14 | - maxnodetotal: 5 15 | memory: "3.75Gi" 16 | cpu: "1000m" 17 | - maxnodetotal: 10 18 | memory: "7.5Gi" 19 | cpu: "2000m" 20 | - maxnodetotal: 100 21 | memory: "15Gi" 22 | cpu: "4000m" 23 | - maxnodetotal: 250 24 | memory: "30Gi" 25 | cpu: "8000m" 26 | - maxnodetotal: 500 27 | memory: "60Gi" 28 | cpu: "16000m" 29 | - maxnodetotal: 100000 30 | memory: "120Gi" 31 | cpu: "32000m" 32 | ``` 33 | # supported cluster type 34 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/master/capacity/default.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package capacity 19 | 20 | import "k8s.io/apimachinery/pkg/api/resource" 21 | 22 | var ( 23 | DefCapacities = []Capacity{ 24 | { 25 | MaxNodeTotal: 5, 26 | Memory: resource.MustParse("3.75Gi"), 27 | Cpu: resource.MustParse("1000m"), 28 | }, 29 | { 30 | MaxNodeTotal: 10, 31 | Memory: resource.MustParse("7.5Gi"), 32 | Cpu: resource.MustParse("2000m"), 33 | }, 34 | { 35 | MaxNodeTotal: 100, 36 | Memory: resource.MustParse("15Gi"), 37 | Cpu: resource.MustParse("4000m"), 38 | }, 39 | { 40 | MaxNodeTotal: 250, 41 | Memory: resource.MustParse("30Gi"), 42 | Cpu: resource.MustParse("8000m"), 43 | }, 44 | { 45 | MaxNodeTotal: 500, 46 | Memory: resource.MustParse("60Gi"), 47 | Cpu: resource.MustParse("16000m"), 48 | }, 49 | { 50 | MaxNodeTotal: 100000, 51 | Memory: resource.MustParse("120Gi"), 52 | Cpu: resource.MustParse("32000m"), 53 | }, 54 | } 55 | ) 56 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/master/components/README.md: -------------------------------------------------------------------------------- 1 | # master-components diagnostic 2 | 3 | Check if the core components are working properly (include k8s node components) 4 | Also check if they have been restarted within a special time 5 | 6 | # config 7 | ```yaml 8 | diagnostics: 9 | - type: "master-components" 10 | # default values 11 | name: "master-components" 12 | catalogue: ["master"] 13 | config: 14 | restarttime: "24h" 15 | components: 16 | - "kube-apiserver" 17 | - "kube-scheduler" 18 | - "kube-controller-manager" 19 | - "etcd" 20 | - "kube-proxy" 21 | - "coredns" 22 | - "kube-dns" 23 | - "kubelet" 24 | - "dockerd" 25 | - "containerd" 26 | ``` 27 | # supported cluster type 28 | * all 29 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/master/components/components_test.go: -------------------------------------------------------------------------------- 1 | package components 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | v1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "testing" 9 | "time" 10 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 11 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 12 | "tkestack.io/kube-jarvis/pkg/translate" 13 | ) 14 | 15 | func TestDiagnostic_StartDiagnose(t *testing.T) { 16 | cases := []struct { 17 | running bool 18 | restart bool 19 | err error 20 | notExist bool 21 | }{ 22 | { 23 | notExist: true, 24 | }, 25 | { 26 | err: fmt.Errorf("err"), 27 | }, 28 | { 29 | running: false, 30 | }, 31 | { 32 | running: true, 33 | restart: false, 34 | }, 35 | { 36 | running: true, 37 | restart: true, 38 | }, 39 | } 40 | 41 | for _, cs := range cases { 42 | t.Run(fmt.Sprintf("%+v", cs), func(t *testing.T) { 43 | pod := &v1.Pod{} 44 | pod.Name = "pod1" 45 | pod.Status.ContainerStatuses = []v1.ContainerStatus{ 46 | { 47 | State: v1.ContainerState{ 48 | Running: &v1.ContainerStateRunning{}, 49 | }, 50 | }, 51 | } 52 | 53 | if cs.restart { 54 | pod.Status.ContainerStatuses[0].RestartCount = 1 55 | pod.Status.ContainerStatuses[0].State.Running.StartedAt = metav1.NewTime(time.Now()) 56 | } else { 57 | pod.Status.ContainerStatuses[0].RestartCount = 1 58 | pod.Status.ContainerStatuses[0].State.Running.StartedAt = metav1.NewTime(time.Now().Add(-1 * time.Hour * 25)) 59 | } 60 | 61 | cmpName := "test" 62 | res := cluster.NewResources() 63 | if !cs.notExist { 64 | res.CoreComponents[cmpName] = []cluster.Component{{ 65 | Name: cmpName, 66 | Node: "node1", 67 | Error: cs.err, 68 | IsRunning: cs.running, 69 | Pod: pod, 70 | }} 71 | 72 | } 73 | 74 | param := &diagnose.MetaData{} 75 | param.Translator = translate.NewFake() 76 | 77 | d := NewDiagnostic(param).(*Diagnostic) 78 | d.Components = []string{cmpName} 79 | if err := d.Complete(); err != nil { 80 | t.Fatalf(err.Error()) 81 | } 82 | 83 | result, err := d.StartDiagnose(context.Background(), diagnose.StartDiagnoseParam{ 84 | CloudType: "test", 85 | Resources: res, 86 | }) 87 | 88 | if err != nil { 89 | t.Fatalf(err.Error()) 90 | } 91 | 92 | total := 0 93 | for { 94 | r, ok := <-result 95 | if !ok { 96 | break 97 | } 98 | 99 | total++ 100 | if total > 1 { 101 | t.Fatalf("should return only one result") 102 | } 103 | 104 | if cs.notExist { 105 | if r.Level != diagnose.HealthyLevelFailed { 106 | t.Fatalf("should return an failed result") 107 | } 108 | continue 109 | } 110 | 111 | if cs.err != nil { 112 | if r.Level != diagnose.HealthyLevelFailed { 113 | t.Fatalf("should return an failed result") 114 | } 115 | continue 116 | } 117 | 118 | if !cs.running { 119 | if r.Level != diagnose.HealthyLevelRisk { 120 | t.Fatalf("should return an risk result") 121 | } 122 | 123 | if r.Desc != "not-run-desc" { 124 | t.Fatalf("should get not-run-desc") 125 | } 126 | 127 | continue 128 | } 129 | 130 | if cs.running { 131 | if cs.restart { 132 | if r.Level != diagnose.HealthyLevelRisk { 133 | t.Fatalf("should return an result result") 134 | } 135 | 136 | if r.Desc != "restart-desc" { 137 | t.Fatalf("should get restart-desc") 138 | } 139 | } else { 140 | if r.Level != diagnose.HealthyLevelGood { 141 | t.Fatalf("should return an good result") 142 | } 143 | 144 | if r.Desc != "good-desc" { 145 | t.Fatalf("should get good-desc") 146 | } 147 | } 148 | } 149 | } 150 | }) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/node/ha/README.md: -------------------------------------------------------------------------------- 1 | # node-ha diagnostic 2 | 3 | check whether the cluster nodes are evenly distributed in multiple zones and there are no single point of failure. 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "node-ha" 9 | # default values 10 | name: "node-ha" 11 | catalogue: ["master"] 12 | config: 13 | ``` 14 | # supported cluster type 15 | * all 16 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/node/status/status.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "context" 5 | 6 | v1 "k8s.io/api/core/v1" 7 | "k8s.io/kubectl/pkg/describe" 8 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 9 | ) 10 | 11 | const ( 12 | // DiagnosticType is type name of this Diagnostic 13 | DiagnosticType = "node-status" 14 | ) 15 | 16 | // Diagnostic is a example diagnostic shows how to write a diagnostic 17 | type Diagnostic struct { 18 | *diagnose.MetaData 19 | result chan *diagnose.Result 20 | param *diagnose.StartDiagnoseParam 21 | } 22 | 23 | // NewDiagnostic return a node status diagnostic 24 | func NewDiagnostic(meta *diagnose.MetaData) diagnose.Diagnostic { 25 | return &Diagnostic{ 26 | result: make(chan *diagnose.Result, 1000), 27 | MetaData: meta, 28 | } 29 | } 30 | 31 | // Init do initialization 32 | func (d *Diagnostic) Init() error { 33 | return nil 34 | } 35 | 36 | // Complete check and complete config items 37 | func (d *Diagnostic) Complete() error { 38 | return nil 39 | } 40 | 41 | // StartDiagnose return a result chan that will output results 42 | func (d *Diagnostic) StartDiagnose(ctx context.Context, param diagnose.StartDiagnoseParam) (chan *diagnose.Result, error) { 43 | d.param = ¶m 44 | d.result = make(chan *diagnose.Result, 1000) 45 | go func() { 46 | defer close(d.result) 47 | for _, node := range d.param.Resources.Nodes.Items { 48 | isMaster := false 49 | isHealth := true 50 | levelGood := diagnose.HealthyLevelGood 51 | levelBad := diagnose.HealthyLevelRisk 52 | // see https://github.com/kubernetes/kubeadm/blob/master/docs/design/design_v1.9.md#mark-master 53 | // in order to be compatible with the previous 'node-role.kubernetes.io/master="true"' 54 | // so judge whether exist instead of 'node-role.kubernetes.io/master=""' 55 | if _, ok := node.Labels[describe.LabelNodeRolePrefix+"master"]; ok { 56 | isMaster = true 57 | levelBad = diagnose.HealthyLevelSerious 58 | } 59 | for _, cond := range node.Status.Conditions { 60 | if cond.Status == v1.ConditionUnknown { 61 | d.uploadResult(isMaster, node.Name, v1.NodeReady, cond.Status, levelBad) 62 | isHealth = false 63 | break 64 | } 65 | if (cond.Type == v1.NodeReady && cond.Status != v1.ConditionTrue) || (cond.Type != v1.NodeReady && cond.Status == v1.ConditionTrue) { 66 | d.uploadResult(isMaster, node.Name, cond.Type, cond.Status, levelBad) 67 | isHealth = false 68 | break 69 | } 70 | } 71 | if isHealth { 72 | d.uploadResult(isMaster, node.Name, v1.NodeReady, v1.ConditionTrue, levelGood) 73 | } 74 | } 75 | }() 76 | return d.result, nil 77 | } 78 | 79 | func (d *Diagnostic) uploadResult(isMaster bool, name string, typ v1.NodeConditionType, status v1.ConditionStatus, level diagnose.HealthyLevel) { 80 | resource := typ 81 | prefix := "node" 82 | goodFlag := "" 83 | if isMaster { 84 | prefix = "master" 85 | } 86 | if level == diagnose.HealthyLevelGood { 87 | goodFlag = "good-" 88 | } 89 | 90 | obj := map[string]interface{}{ 91 | "Node": name, 92 | "Type": typ, 93 | "Status": status, 94 | "Resource": resource, 95 | "Role": "worker", 96 | } 97 | 98 | if isMaster { 99 | obj["Role"] = "master" 100 | } 101 | 102 | title := d.Translator.Message(prefix+"-status-title", nil) 103 | desc := d.Translator.Message(prefix+"-status-"+goodFlag+"desc", obj) 104 | proposal := d.Translator.Message(prefix+"-status-"+goodFlag+"proposal", obj) 105 | 106 | d.result <- &diagnose.Result{ 107 | Level: level, 108 | Title: title, 109 | ObjName: name, 110 | ObjInfo: obj, 111 | Desc: desc, 112 | Proposal: proposal, 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/node/sys/sys.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package sys 19 | 20 | import ( 21 | "context" 22 | 23 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 24 | ) 25 | 26 | const ( 27 | // DiagnosticType is type name of this Diagnostic 28 | DiagnosticType = "node-sys" 29 | ) 30 | 31 | // Diagnostic is a example diagnostic shows how to write a diagnostic 32 | type Diagnostic struct { 33 | *diagnose.MetaData 34 | result chan *diagnose.Result 35 | param *diagnose.StartDiagnoseParam 36 | } 37 | 38 | // NewDiagnostic return a example diagnostic 39 | func NewDiagnostic(meta *diagnose.MetaData) diagnose.Diagnostic { 40 | return &Diagnostic{ 41 | result: make(chan *diagnose.Result, 1000), 42 | MetaData: meta, 43 | } 44 | } 45 | 46 | // Complete check and complete config items 47 | func (d *Diagnostic) Complete() error { 48 | return nil 49 | } 50 | 51 | // StartDiagnose return a result chan that will output results 52 | func (d *Diagnostic) StartDiagnose(ctx context.Context, 53 | param diagnose.StartDiagnoseParam) (chan *diagnose.Result, error) { 54 | d.param = ¶m 55 | d.result = make(chan *diagnose.Result, 1000) 56 | 57 | go func() { 58 | defer diagnose.CommonDeafer(d.result) 59 | for node := range d.param.Resources.Machines { 60 | // net.ipv4.tcp_tw_reuse 61 | d.diagnoseKernelParam("net.ipv4.tcp_tw_reuse", "1", node) 62 | 63 | // net.ipv4.ip_forward 64 | d.diagnoseKernelParam("net.ipv4.ip_forward", "1", node) 65 | 66 | // net.bridge.bridge-nf-call-iptables 67 | d.diagnoseKernelParam("net.bridge.bridge-nf-call-iptables", "1", node) 68 | } 69 | }() 70 | return d.result, nil 71 | } 72 | 73 | func (d *Diagnostic) diagnoseKernelParam(key string, targetVal string, node string) { 74 | m := d.param.Resources.Machines[node] 75 | curVal := m.SysCtl[key] 76 | level := diagnose.HealthyLevelGood 77 | 78 | obj := map[string]interface{}{ 79 | "Node": node, 80 | "Name": key, 81 | "CurVal": curVal, 82 | "TargetVal": targetVal, 83 | } 84 | 85 | if curVal != targetVal { 86 | level = diagnose.HealthyLevelWarn 87 | d.result <- &diagnose.Result{ 88 | Level: level, 89 | Title: d.Translator.Message("kernel-para-title", nil), 90 | ObjName: node, 91 | ObjInfo: obj, 92 | Desc: d.Translator.Message("kernel-para-desc", obj), 93 | Proposal: d.Translator.Message("kernel-para-proposal", obj), 94 | } 95 | } else { 96 | d.result <- &diagnose.Result{ 97 | Level: level, 98 | Title: d.Translator.Message("kernel-para-title", nil), 99 | ObjName: node, 100 | ObjInfo: obj, 101 | Desc: d.Translator.Message("kernel-para-good-desc", obj), 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/other/example/README.md: -------------------------------------------------------------------------------- 1 | # example diagnostic 2 | 3 | This is the example that shows how to write a diagnostic plugins 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "example" 9 | # default values 10 | name: "example" 11 | catalogue: ["other"] 12 | config: 13 | message: "message" #extra message 14 | ``` 15 | # supported cluster type 16 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/other/example/example.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package example 19 | 20 | import ( 21 | "context" 22 | 23 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 24 | ) 25 | 26 | const ( 27 | // DiagnosticType is type name of this Diagnostic 28 | DiagnosticType = "example" 29 | ) 30 | 31 | // Diagnostic is a example diagnostic shows how to write a diagnostic 32 | type Diagnostic struct { 33 | *diagnose.MetaData 34 | result chan *diagnose.Result 35 | Message string `yaml:"message"` 36 | } 37 | 38 | // NewDiagnostic return a example diagnostic 39 | func NewDiagnostic(meta *diagnose.MetaData) diagnose.Diagnostic { 40 | return &Diagnostic{ 41 | result: make(chan *diagnose.Result, 1000), 42 | MetaData: meta, 43 | } 44 | } 45 | 46 | // Complete check and complete config items 47 | func (d *Diagnostic) Complete() error { 48 | return nil 49 | } 50 | 51 | // StartDiagnose return a result chan that will output results 52 | func (d *Diagnostic) StartDiagnose(ctx context.Context, 53 | param diagnose.StartDiagnoseParam) (chan *diagnose.Result, error) { 54 | d.result = make(chan *diagnose.Result, 1000) 55 | go func() { 56 | defer diagnose.CommonDeafer(d.result) 57 | // send a risk result 58 | obj := map[string]interface{}{ 59 | "Mes": d.Message, 60 | } 61 | 62 | d.result <- &diagnose.Result{ 63 | Level: diagnose.HealthyLevelRisk, 64 | Title: "example", 65 | ObjName: "example-obj", 66 | ObjInfo: obj, 67 | Desc: d.Translator.Message("message", obj), 68 | Proposal: d.Translator.Message("proposal", nil), 69 | } 70 | 71 | // if any Error occur , send a failed result 72 | d.result <- &diagnose.Result{ 73 | Level: diagnose.HealthyLevelFailed, 74 | Title: "example", 75 | ObjName: "example-obj", 76 | ObjInfo: obj, 77 | Desc: d.Translator.Message("message", obj), 78 | Proposal: d.Translator.Message("proposal", nil), 79 | } 80 | }() 81 | return d.result, nil 82 | } 83 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/other/example/example_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package example 19 | 20 | import ( 21 | "context" 22 | "fmt" 23 | "testing" 24 | "tkestack.io/kube-jarvis/pkg/plugins" 25 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 26 | "tkestack.io/kube-jarvis/pkg/translate" 27 | ) 28 | 29 | func TestDiagnostic_StartDiagnose(t *testing.T) { 30 | cases := []struct { 31 | message string 32 | }{ 33 | { 34 | message: "mes1", 35 | }, 36 | { 37 | message: "mes2", 38 | }, 39 | } 40 | 41 | for _, cs := range cases { 42 | t.Run(fmt.Sprintf("%+v", cs), func(t *testing.T) { 43 | s := NewDiagnostic(&diagnose.MetaData{ 44 | MetaData: plugins.MetaData{ 45 | Translator: translate.NewFake(), 46 | }, 47 | }).(*Diagnostic) 48 | 49 | s.Message = cs.message 50 | result, err := s.StartDiagnose(context.Background(), diagnose.StartDiagnoseParam{}) 51 | if err != nil { 52 | t.Fatalf(err.Error()) 53 | } 54 | 55 | for { 56 | res, ok := <-result 57 | if !ok { 58 | break 59 | } 60 | t.Logf("%+v", res) 61 | } 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/resource/hpa/ip/ip.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package ip 19 | 20 | import ( 21 | "context" 22 | "math" 23 | "net" 24 | 25 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 26 | ) 27 | 28 | const ( 29 | // DiagnosticType is type name of this Diagnostic 30 | DiagnosticType = "hpa-ip" 31 | ) 32 | 33 | // Diagnostic is a example diagnostic shows how to write a diagnostic 34 | type Diagnostic struct { 35 | *diagnose.MetaData 36 | result chan *diagnose.Result 37 | param *diagnose.StartDiagnoseParam 38 | } 39 | 40 | // NewDiagnostic return a example diagnostic 41 | func NewDiagnostic(meta *diagnose.MetaData) diagnose.Diagnostic { 42 | return &Diagnostic{ 43 | result: make(chan *diagnose.Result, 1000), 44 | MetaData: meta, 45 | } 46 | } 47 | 48 | // Complete check and complete config items 49 | func (d *Diagnostic) Complete() error { 50 | return nil 51 | } 52 | 53 | // StartDiagnose return a result chan that will output results 54 | func (d *Diagnostic) StartDiagnose(ctx context.Context, param diagnose.StartDiagnoseParam) (chan *diagnose.Result, error) { 55 | d.param = ¶m 56 | d.result = make(chan *diagnose.Result, 1000) 57 | go func() { 58 | defer diagnose.CommonDeafer(d.result) 59 | var totalIPCount int 60 | var curIPCount int 61 | var hpaMaxIPCount int 62 | 63 | for _, node := range d.param.Resources.Nodes.Items { 64 | podCIDR := node.Spec.PodCIDR 65 | if podCIDR == "" { 66 | continue 67 | } 68 | _, netCIDR, _ := net.ParseCIDR(podCIDR) 69 | cur, total := netCIDR.Mask.Size() 70 | totalIPCount += int(math.Pow(2, float64(total-cur))) - 2 71 | } 72 | 73 | for _, pod := range d.param.Resources.Pods.Items { 74 | if pod.Spec.HostNetwork { 75 | continue 76 | } 77 | curIPCount += 1 78 | } 79 | 80 | deploySet := make(map[string]int) 81 | for _, deploy := range d.param.Resources.Deployments.Items { 82 | deploySet[deploy.Namespace+"/"+deploy.Name] = int(*deploy.Spec.Replicas) 83 | } 84 | 85 | hpaMaxIPCount = curIPCount 86 | for _, hpa := range d.param.Resources.HPAs.Items { 87 | if hpa.Spec.ScaleTargetRef.Kind != "Deployment" { 88 | d.Logger.Errorf("hpa %s/%s related %v, not deployment", hpa.Namespace, hpa.Name, hpa.Spec.ScaleTargetRef) 89 | continue 90 | } 91 | key := hpa.Namespace + "/" + hpa.Spec.ScaleTargetRef.Name 92 | replicas, ok := deploySet[key] 93 | if ok { 94 | hpaMaxIPCount += int(hpa.Spec.MaxReplicas) - replicas 95 | } 96 | } 97 | 98 | obj := map[string]interface{}{ 99 | "CurrentIPCount": curIPCount, 100 | "HPAMaxIPCount": hpaMaxIPCount, 101 | "ClusterIPCount": totalIPCount, 102 | } 103 | 104 | d.result <- &diagnose.Result{ 105 | Level: diagnose.HealthyLevelGood, 106 | Title: d.Translator.Message("hpa-ip-title", nil), 107 | ObjName: "*", 108 | ObjInfo: obj, 109 | Desc: d.Translator.Message("hpa-ip-desc", obj), 110 | } 111 | }() 112 | return d.result, nil 113 | } 114 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/resource/workload/affinity/README.md: -------------------------------------------------------------------------------- 1 | # affinity diagnostic 2 | 3 | This diagnostic report the healthy of workload's resources affinity configuration 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "affinity" 9 | # default values 10 | name: "affinity" 11 | catalogue: ["resource"] 12 | ``` 13 | # supported cluster type 14 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/resource/workload/batch/README.md: -------------------------------------------------------------------------------- 1 | # job/cronjob diagnostic 2 | 3 | This diagnostic report the healthy of job/cronjob's restart strategy configuration 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "batch-check" 9 | # default values 10 | name: "job/cronjob check" 11 | catalogue: ["resource"] 12 | ``` 13 | # supported cluster type 14 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/resource/workload/ha/README.md: -------------------------------------------------------------------------------- 1 | # workload-ha diagnostic 2 | 3 | This diagnostic report whether the pods of Deployment, StatefulSet, ReplicaSets or ReplicationControllers are deployed on the same node 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "workload-ha" 9 | ``` 10 | # supported cloud providers 11 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/resource/workload/healthcheck/README.md: -------------------------------------------------------------------------------- 1 | # health check diagnostic 2 | 3 | This diagnostic report the healthy of workload's resources health check configuration 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "health-check" 9 | # default values 10 | name: "health-check" 11 | catalogue: ["resource"] 12 | ``` 13 | # supported cluster type 14 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/resource/workload/healthcheck/healthcheck_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package healthcheck 19 | 20 | import ( 21 | "context" 22 | appv1 "k8s.io/api/apps/v1" 23 | "k8s.io/apimachinery/pkg/util/intstr" 24 | "testing" 25 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 26 | 27 | "tkestack.io/kube-jarvis/pkg/plugins" 28 | 29 | "tkestack.io/kube-jarvis/pkg/translate" 30 | 31 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 32 | 33 | v1 "k8s.io/api/core/v1" 34 | ) 35 | 36 | func TestHealthCheckDiagnostic_StartDiagnose(t *testing.T) { 37 | res := cluster.NewResources() 38 | res.Deployments = &appv1.DeploymentList{} 39 | res.ReplicaSets = &appv1.ReplicaSetList{} 40 | res.ReplicationControllers = &v1.ReplicationControllerList{} 41 | res.StatefulSets = &appv1.StatefulSetList{} 42 | res.DaemonSets = &appv1.DaemonSetList{} 43 | 44 | res.Pods = &v1.PodList{} 45 | 46 | pod := v1.Pod{} 47 | pod.Name = "pod1" 48 | pod.Namespace = "default" 49 | pod.UID = "pod1-uid" 50 | pod.Spec.Containers = []v1.Container{ 51 | { 52 | Name: "kubedns", 53 | Image: "1", 54 | ReadinessProbe: &v1.Probe{ 55 | Handler: v1.Handler{ 56 | HTTPGet: &v1.HTTPGetAction{ 57 | Path: "/healthcheck/kubedns", 58 | Port: intstr.FromInt(10054), 59 | Scheme: v1.URISchemeHTTP, 60 | }, 61 | }, 62 | InitialDelaySeconds: int32(60), 63 | TimeoutSeconds: int32(5), 64 | SuccessThreshold: int32(1), 65 | }, 66 | LivenessProbe: &v1.Probe{ 67 | Handler: v1.Handler{ 68 | HTTPGet: &v1.HTTPGetAction{ 69 | Path: "/healthcheck/kubedns", 70 | Port: intstr.FromInt(10054), 71 | Scheme: v1.URISchemeHTTP, 72 | }, 73 | }, 74 | InitialDelaySeconds: int32(60), 75 | TimeoutSeconds: int32(5), 76 | SuccessThreshold: int32(1), 77 | }, 78 | }, 79 | } 80 | res.Pods.Items = append(res.Pods.Items, pod) 81 | 82 | pod = v1.Pod{} 83 | pod.Name = "pod2" 84 | pod.Namespace = "default" 85 | pod.UID = "pod2-uid" 86 | pod.Spec.Containers = []v1.Container{ 87 | { 88 | Name: "kubedns", 89 | Image: "1", 90 | }, 91 | } 92 | res.Pods.Items = append(res.Pods.Items, pod) 93 | 94 | d := NewDiagnostic(&diagnose.MetaData{ 95 | MetaData: plugins.MetaData{ 96 | Translator: translate.NewFake(), 97 | }, 98 | }) 99 | 100 | if err := d.Complete(); err != nil { 101 | t.Fatalf(err.Error()) 102 | } 103 | 104 | result, _ := d.StartDiagnose(context.Background(), diagnose.StartDiagnoseParam{ 105 | Resources: res, 106 | }) 107 | 108 | total := 0 109 | for { 110 | s, ok := <-result 111 | if !ok { 112 | break 113 | } 114 | total++ 115 | 116 | t.Logf("%+v", *s) 117 | } 118 | if total != 1 { 119 | t.Fatalf("should return 1 result") 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/resource/workload/pdb/README.md: -------------------------------------------------------------------------------- 1 | # pdb diagnostic 2 | 3 | This diagnostic report the healthy of workload's resources pdb configuration 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "pdb-check" 9 | # default values 10 | name: "pdb-check" 11 | catalogue: ["resource"] 12 | ``` 13 | # supported cluster type 14 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/resource/workload/pdb/pdbcheck_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package pdb 19 | 20 | import ( 21 | "context" 22 | "testing" 23 | 24 | appv1 "k8s.io/api/apps/v1" 25 | "k8s.io/api/policy/v1beta1" 26 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | "k8s.io/apimachinery/pkg/util/intstr" 28 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 29 | 30 | "tkestack.io/kube-jarvis/pkg/plugins" 31 | 32 | "tkestack.io/kube-jarvis/pkg/translate" 33 | 34 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 35 | 36 | v1 "k8s.io/api/core/v1" 37 | ) 38 | 39 | func TestPDBCheckDiagnostic_StartDiagnose(t *testing.T) { 40 | res := cluster.NewResources() 41 | dep := appv1.Deployment{} 42 | dep.Name = "kube-dns" 43 | rs := int32(2) 44 | dep.Spec.Replicas = &rs 45 | dep.UID = "1" 46 | 47 | dep2 := appv1.Deployment{} 48 | dep2.Name = "kube-dns2" 49 | dep2.Spec.Replicas = &rs 50 | dep2.UID = "2" 51 | 52 | res.Deployments = &appv1.DeploymentList{ 53 | Items: []appv1.Deployment{ 54 | dep, 55 | dep2, 56 | }, 57 | } 58 | res.ReplicaSets = &appv1.ReplicaSetList{} 59 | res.ReplicationControllers = &v1.ReplicationControllerList{} 60 | res.StatefulSets = &appv1.StatefulSetList{} 61 | res.DaemonSets = &appv1.DaemonSetList{} 62 | 63 | res.Pods = &v1.PodList{} 64 | res.PodDisruptionBudgets = &v1beta1.PodDisruptionBudgetList{} 65 | 66 | pdb := v1beta1.PodDisruptionBudget{} 67 | pdb.Name = "test-pdb" 68 | pdb.Namespace = "default" 69 | minAvailable := intstr.FromInt(1) 70 | pdb.Spec.MinAvailable = &minAvailable 71 | pdb.Spec.Selector = &metav1.LabelSelector{ 72 | MatchLabels: map[string]string{"test-pdb-key": "test-pdb-value"}, 73 | } 74 | res.PodDisruptionBudgets.Items = append(res.PodDisruptionBudgets.Items, pdb) 75 | 76 | pod := v1.Pod{} 77 | pod.Name = "pod1" 78 | pod.Namespace = "default" 79 | pod.UID = "pod1-uid" 80 | pod.Labels = map[string]string{"test-pdb-key": "test-pdb-value"} 81 | pod.Spec.Containers = []v1.Container{ 82 | { 83 | Name: "kubedns", 84 | Image: "1", 85 | }, 86 | } 87 | 88 | owner := metav1.OwnerReference{} 89 | owner.Name = dep.Name 90 | owner.Kind = "Deployment" 91 | ctl := true 92 | owner.Controller = &ctl 93 | owner.UID = "1" 94 | 95 | pod.OwnerReferences = []metav1.OwnerReference{owner} 96 | res.Pods.Items = append(res.Pods.Items, pod) 97 | 98 | pod = v1.Pod{} 99 | pod.Name = "pod2" 100 | pod.Namespace = "default" 101 | pod.UID = "pod2-uid" 102 | pod.Spec.Containers = []v1.Container{ 103 | { 104 | Name: "kubedns", 105 | Image: "1", 106 | }, 107 | } 108 | owner2 := metav1.OwnerReference{} 109 | owner2.Name = dep2.Name 110 | owner2.Kind = "Deployment" 111 | owner2.Controller = &ctl 112 | owner2.UID = "2" 113 | pod.OwnerReferences = []metav1.OwnerReference{owner2} 114 | res.Pods.Items = append(res.Pods.Items, pod) 115 | 116 | d := NewDiagnostic(&diagnose.MetaData{ 117 | MetaData: plugins.MetaData{ 118 | Translator: translate.NewFake(), 119 | }, 120 | }) 121 | 122 | if err := d.Complete(); err != nil { 123 | t.Fatalf(err.Error()) 124 | } 125 | 126 | result, _ := d.StartDiagnose(context.Background(), diagnose.StartDiagnoseParam{ 127 | Resources: res, 128 | }) 129 | 130 | total := 0 131 | for { 132 | s, ok := <-result 133 | if !ok { 134 | break 135 | } 136 | total++ 137 | 138 | t.Logf("%+v", *s) 139 | } 140 | if total != 1 { 141 | t.Fatalf("should return 1 result") 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/resource/workload/requestslimits/README.md: -------------------------------------------------------------------------------- 1 | # requests-limits diagnostic 2 | 3 | This diagnostic report the healthy of workload's resources requests limits configuration 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "requests-limits" 9 | # default values 10 | name: "requests-limits" 11 | catalogue: ["resource"] 12 | ``` 13 | # supported cluster type 14 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/resource/workload/requestslimits/requestlimit_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package requestslimits 19 | 20 | import ( 21 | "context" 22 | "testing" 23 | 24 | appv1 "k8s.io/api/apps/v1" 25 | "tkestack.io/kube-jarvis/pkg/plugins" 26 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 27 | "tkestack.io/kube-jarvis/pkg/translate" 28 | 29 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 30 | 31 | v1 "k8s.io/api/core/v1" 32 | "k8s.io/apimachinery/pkg/api/resource" 33 | ) 34 | 35 | func Test_StartDiagnose(t *testing.T) { 36 | res := cluster.NewResources() 37 | res.Deployments = &appv1.DeploymentList{} 38 | res.ReplicaSets = &appv1.ReplicaSetList{} 39 | res.ReplicationControllers = &v1.ReplicationControllerList{} 40 | res.StatefulSets = &appv1.StatefulSetList{} 41 | res.DaemonSets = &appv1.DaemonSetList{} 42 | res.Pods = &v1.PodList{} 43 | 44 | kubeDNSlimit := make(map[v1.ResourceName]resource.Quantity) 45 | kubeDNSRequest := make(map[v1.ResourceName]resource.Quantity) 46 | 47 | kubeDNSlimit[v1.ResourceCPU] = resource.MustParse("100m") 48 | kubeDNSlimit[v1.ResourceMemory] = resource.MustParse("170M") 49 | kubeDNSRequest[v1.ResourceCPU] = resource.MustParse("100m") 50 | kubeDNSRequest[v1.ResourceMemory] = resource.MustParse("30M") 51 | 52 | pod := v1.Pod{} 53 | pod.Name = "pod1" 54 | pod.Namespace = "default" 55 | pod.UID = "pod1-uid" 56 | pod.Spec.Containers = []v1.Container{ 57 | { 58 | Name: "kubedns", 59 | Image: "1", 60 | Resources: v1.ResourceRequirements{ 61 | Limits: kubeDNSlimit, 62 | Requests: kubeDNSRequest, 63 | }, 64 | }, 65 | } 66 | res.Pods.Items = append(res.Pods.Items, pod) 67 | 68 | pod = v1.Pod{} 69 | pod.Name = "pod2" 70 | pod.Namespace = "default" 71 | pod.UID = "pod2-uid" 72 | pod.Spec.Containers = []v1.Container{ 73 | { 74 | Name: "kubedns", 75 | Image: "1", 76 | }, 77 | } 78 | res.Pods.Items = append(res.Pods.Items, pod) 79 | 80 | d := NewDiagnostic(&diagnose.MetaData{ 81 | MetaData: plugins.MetaData{ 82 | Translator: translate.NewFake(), 83 | }, 84 | }) 85 | 86 | if err := d.Complete(); err != nil { 87 | t.Fatalf(err.Error()) 88 | } 89 | 90 | result, _ := d.StartDiagnose(context.Background(), diagnose.StartDiagnoseParam{ 91 | Resources: res, 92 | }) 93 | 94 | total := 0 95 | for { 96 | s, ok := <-result 97 | if !ok { 98 | break 99 | } 100 | total++ 101 | 102 | t.Logf("%+v", *s) 103 | } 104 | if total != 1 { 105 | t.Fatalf("should return 1 result") 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /pkg/plugins/diagnose/resource/workload/status/README.md: -------------------------------------------------------------------------------- 1 | # workload-status diagnostic 2 | 3 | This diagnostic report the healthy of workload's status and conditions 4 | 5 | # config 6 | ```yaml 7 | diagnostics: 8 | - type: "workload-status" 9 | name: "workload replicas status" 10 | score: 100 11 | ``` 12 | # supported cloud providers 13 | * all -------------------------------------------------------------------------------- /pkg/plugins/diagnose/resource/workload/status/status_test.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | v12 "k8s.io/api/apps/v1" 8 | "k8s.io/utils/pointer" 9 | "tkestack.io/kube-jarvis/pkg/plugins" 10 | "tkestack.io/kube-jarvis/pkg/plugins/cluster" 11 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 12 | "tkestack.io/kube-jarvis/pkg/translate" 13 | ) 14 | 15 | func TestStatusDiagnostic_StartDiagnose(t *testing.T) { 16 | res := cluster.NewResources() 17 | res.Deployments = &v12.DeploymentList{} 18 | 19 | d := NewDiagnostic(&diagnose.MetaData{ 20 | MetaData: plugins.MetaData{ 21 | Translator: translate.NewFake(), 22 | }, 23 | }) 24 | 25 | res.StatefulSets = &v12.StatefulSetList{} 26 | res.StatefulSets.Items = make([]v12.StatefulSet, 1) 27 | sts := v12.StatefulSet{} 28 | sts.Name = "sts1" 29 | sts.Spec.Replicas = pointer.Int32Ptr(1) 30 | sts.Status.Replicas = 1 31 | sts.Status.ReadyReplicas = 0 32 | res.StatefulSets.Items[0] = sts 33 | 34 | res.Deployments = &v12.DeploymentList{} 35 | res.Deployments.Items = make([]v12.Deployment, 1) 36 | deploy := v12.Deployment{} 37 | deploy.Name = "deploy1" 38 | deploy.Spec.Replicas = pointer.Int32Ptr(1) 39 | deploy.Status.Replicas = 1 40 | deploy.Status.AvailableReplicas = 0 41 | res.Deployments.Items[0] = deploy 42 | 43 | res.DaemonSets = &v12.DaemonSetList{} 44 | res.DaemonSets.Items = make([]v12.DaemonSet, 1) 45 | ds := v12.DaemonSet{} 46 | ds.Name = "ds1" 47 | ds.Status.DesiredNumberScheduled = 1 48 | ds.Status.NumberReady = 0 49 | res.DaemonSets.Items[0] = ds 50 | 51 | if err := d.Complete(); err != nil { 52 | t.Fatalf(err.Error()) 53 | } 54 | 55 | result, _ := d.StartDiagnose(context.Background(), diagnose.StartDiagnoseParam{ 56 | Resources: res, 57 | }) 58 | 59 | total := 0 60 | for { 61 | s, ok := <-result 62 | if !ok { 63 | break 64 | } 65 | total++ 66 | 67 | t.Logf("%+v", *s) 68 | } 69 | if total != 3 { 70 | t.Fatalf("should return 1 result") 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /pkg/plugins/export/all/all.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package all 19 | 20 | import ( 21 | "tkestack.io/kube-jarvis/pkg/plugins/export" 22 | "tkestack.io/kube-jarvis/pkg/plugins/export/stdout" 23 | "tkestack.io/kube-jarvis/pkg/plugins/export/store" 24 | ) 25 | 26 | func init() { 27 | export.Add(stdout.ExporterType, export.Factory{ 28 | Creator: stdout.NewExporter, 29 | }) 30 | export.Add(store.ExporterType, export.Factory{ 31 | Creator: store.NewExporter, 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /pkg/plugins/export/export.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package export 19 | 20 | import ( 21 | "context" 22 | "tkestack.io/kube-jarvis/pkg/plugins" 23 | ) 24 | 25 | // MetaData contains core attributes of a Exporter 26 | type MetaData struct { 27 | plugins.MetaData 28 | } 29 | 30 | // Meta return core MetaData 31 | // this function can be use for struct implement Exporter interface 32 | func (m *MetaData) Meta() MetaData { 33 | return *m 34 | } 35 | 36 | // Exporter export all steps and results with special way or special format 37 | type Exporter interface { 38 | // Complete check and complete config items 39 | Complete() error 40 | // Meta return core attributes 41 | Meta() MetaData 42 | // Export export result 43 | Export(ctx context.Context, result *AllResult) error 44 | } 45 | 46 | // Factory create a new Exporter 47 | type Factory struct { 48 | // Creator is a factory function to create Exporter 49 | Creator func(d *MetaData) Exporter 50 | // SupportedClouds indicate what cloud providers will be supported of this exporter 51 | SupportedClouds []string 52 | } 53 | 54 | // Factories store all registered Exporter Creator 55 | var Factories = map[string]Factory{} 56 | 57 | // Add register a Exporter Factory 58 | func Add(typ string, f Factory) { 59 | Factories[typ] = f 60 | } 61 | -------------------------------------------------------------------------------- /pkg/plugins/export/result.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package export 19 | 20 | import ( 21 | "encoding/json" 22 | "time" 23 | 24 | "tkestack.io/kube-jarvis/pkg/translate" 25 | 26 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 27 | ) 28 | 29 | // DiagnosticResultItem collect one diagnostic and it's results 30 | type DiagnosticResultItem struct { 31 | StartTime time.Time 32 | EndTime time.Time 33 | Catalogue diagnose.Catalogue 34 | Type string 35 | Name string 36 | Desc translate.Message 37 | Results []*diagnose.Result 38 | Statistics map[diagnose.HealthyLevel]int 39 | } 40 | 41 | // NewDiagnosticResultItem return a new DiagnosticResultItem according to a Diagnostic 42 | func NewDiagnosticResultItem(dia diagnose.Diagnostic) *DiagnosticResultItem { 43 | return &DiagnosticResultItem{ 44 | StartTime: time.Now(), 45 | Catalogue: dia.Meta().Catalogue, 46 | Type: dia.Meta().Type, 47 | Name: dia.Meta().Name, 48 | Desc: dia.Meta().Desc, 49 | Results: []*diagnose.Result{}, 50 | Statistics: map[diagnose.HealthyLevel]int{}, 51 | } 52 | } 53 | 54 | func (d *DiagnosticResultItem) AddResult(r *diagnose.Result) { 55 | d.Results = append(d.Results, r) 56 | d.Statistics[r.Level]++ 57 | } 58 | 59 | // AllResult just collect diagnostic results and progress 60 | type AllResult struct { 61 | StartTime time.Time 62 | EndTime time.Time 63 | Statistics map[diagnose.HealthyLevel]int 64 | Diagnostics []*DiagnosticResultItem 65 | } 66 | 67 | // NewAllResult return a new AllResult 68 | func NewAllResult() *AllResult { 69 | return &AllResult{ 70 | StartTime: time.Now(), 71 | Diagnostics: []*DiagnosticResultItem{}, 72 | Statistics: map[diagnose.HealthyLevel]int{}, 73 | } 74 | } 75 | 76 | // AddDiagnosticResultItem add a diagnostic resultItem to AllResult 77 | func (r *AllResult) AddDiagnosticResultItem(d *DiagnosticResultItem) { 78 | r.Diagnostics = append(r.Diagnostics, d) 79 | for level, num := range d.Statistics { 80 | r.Statistics[level] += num 81 | } 82 | } 83 | 84 | // Marshal make AllResult become json 85 | func (r *AllResult) Marshal() ([]byte, error) { 86 | return json.Marshal(r) 87 | } 88 | 89 | // UnMarshal init AllResult from a json 90 | func (r *AllResult) UnMarshal(data []byte) error { 91 | return json.Unmarshal(data, r) 92 | } 93 | 94 | type HistoryItem struct { 95 | ID string 96 | Overview AllResult 97 | } 98 | 99 | type History struct { 100 | Records []*HistoryItem 101 | } 102 | 103 | // Marshal make History become json 104 | func (h *History) Marshal() ([]byte, error) { 105 | return json.Marshal(h) 106 | } 107 | 108 | // UnMarshal init History from a json 109 | func (h *History) UnMarshal(data []byte) error { 110 | return json.Unmarshal(data, h) 111 | } 112 | -------------------------------------------------------------------------------- /pkg/plugins/export/stdout/README.md: -------------------------------------------------------------------------------- 1 | # stdout exporter 2 | stdout exporter just print result to stdout with a simple format 3 | 4 | # config 5 | ```yaml 6 | exporters: 7 | - type: "stdout" 8 | name: "stdout" 9 | config: 10 | format: "fmt" # use "json" to print a json 11 | ``` 12 | 13 | # supported cluster type 14 | * all -------------------------------------------------------------------------------- /pkg/plugins/export/stdout/stdout_test.go: -------------------------------------------------------------------------------- 1 | package stdout 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | "tkestack.io/kube-jarvis/pkg/logger" 8 | "tkestack.io/kube-jarvis/pkg/plugins" 9 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 10 | "tkestack.io/kube-jarvis/pkg/plugins/export" 11 | "tkestack.io/kube-jarvis/pkg/translate" 12 | ) 13 | 14 | func TestExporter_Complete(t *testing.T) { 15 | s := Exporter{} 16 | if err := s.Complete(); err != nil { 17 | t.Fatalf(err.Error()) 18 | } 19 | 20 | if s.Format != "fmt" { 21 | t.Fatalf("Format default value should be 'fmt'") 22 | } 23 | 24 | if s.Level != diagnose.HealthyLevelGood { 25 | t.Fatalf("Level default value should be 'good'") 26 | } 27 | } 28 | 29 | func TestExporter_Export(t *testing.T) { 30 | 31 | s := NewExporter(&export.MetaData{ 32 | MetaData: plugins.MetaData{ 33 | Translator: translate.NewFake(), 34 | Logger: logger.NewLogger(), 35 | Type: ExporterType, 36 | }, 37 | }) 38 | 39 | if err := s.Complete(); err != nil { 40 | t.Fatalf(err.Error()) 41 | } 42 | 43 | if err := s.Export(context.Background(), &export.AllResult{ 44 | StartTime: time.Now(), 45 | EndTime: time.Now(), 46 | Statistics: map[diagnose.HealthyLevel]int{ 47 | diagnose.HealthyLevelGood: 1, 48 | diagnose.HealthyLevelWarn: 1, 49 | diagnose.HealthyLevelRisk: 1, 50 | diagnose.HealthyLevelSerious: 1, 51 | diagnose.HealthyLevelFailed: 1, 52 | }, 53 | Diagnostics: []*export.DiagnosticResultItem{ 54 | { 55 | Results: []*diagnose.Result{ 56 | { 57 | Level: diagnose.HealthyLevelGood, 58 | Title: "good", 59 | }, 60 | { 61 | Level: diagnose.HealthyLevelWarn, 62 | Title: "warn", 63 | }, { 64 | Level: diagnose.HealthyLevelRisk, 65 | Title: "risk", 66 | }, 67 | { 68 | Level: diagnose.HealthyLevelSerious, 69 | Title: "serious", 70 | }, 71 | { 72 | Level: diagnose.HealthyLevelFailed, 73 | Title: "failed", 74 | }, 75 | }, 76 | Statistics: map[diagnose.HealthyLevel]int{ 77 | diagnose.HealthyLevelGood: 1, 78 | diagnose.HealthyLevelWarn: 1, 79 | diagnose.HealthyLevelRisk: 1, 80 | diagnose.HealthyLevelSerious: 1, 81 | diagnose.HealthyLevelFailed: 1, 82 | }, 83 | }, 84 | }, 85 | }); err != nil { 86 | t.Fatalf(err.Error()) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /pkg/plugins/export/store/README.md: -------------------------------------------------------------------------------- 1 | # store exporter 2 | store exporter save result into global store and provides a simple query interface if config "server" is true 3 | 4 | # config 5 | 6 | ```yaml 7 | exporters: 8 | - type: "store" 9 | config: 10 | server: true 11 | ``` 12 | 13 | # supported cluster type 14 | * all 15 | 16 | # http api 17 | * POST "/exporter/store/history : "query history" 18 | 19 | request: 20 | ```json 21 | { 22 | "Offset":0, 23 | "Limit":0 24 | } 25 | ``` 26 | 27 | response: 28 | ```json 29 | { 30 | "Records": [ 31 | { 32 | "ID": "1578920254692112293", 33 | "Overview": { 34 | "StartTime": "2020-01-13T20:57:34.692112293+08:00", 35 | "EndTime": "2020-01-13T20:57:34.69260182+08:00", 36 | "Statistics": { 37 | "warn": 1 38 | }, 39 | "Diagnostics": null 40 | } 41 | } 42 | ] 43 | } 44 | ``` 45 | 46 | * POST "/exporter/store/query : query results 47 | 48 | request: 49 | ```json 50 | { 51 | "ID":123, 52 | "Type":"node-sys", 53 | "Name":"", 54 | "Level":"warn", 55 | "Offset":0, 56 | "Limit":0 57 | } 58 | ``` 59 | 60 | response: 61 | ```json 62 | { 63 | "StartTime": "2019-12-30T09:55:22.064492219+08:00", 64 | "EndTime": "2019-12-30T09:55:22.065226914+08:00", 65 | "Statistics": { 66 | "good": 4, 67 | "warn": 1 68 | }, 69 | "Diagnostics": [ 70 | { 71 | "StartTime": "2019-12-30T09:55:22.064973158+08:00", 72 | "EndTime": "0001-01-01T00:00:00Z", 73 | "Catalogue": [ 74 | "node" 75 | ], 76 | "Type": "node-sys", 77 | "Name": "", 78 | "Desc": "", 79 | "Results": [ 80 | { 81 | "Level": "warn", 82 | "ObjName": "10.0.2.4", 83 | "Title": "Kernel Parameters", 84 | "Desc": "Node 10.0.2.4 Parameters[ net.ipv4.tcp_tw_reuse=0 ] is not recommended", 85 | "Proposal": "Set net.ipv4.tcp_tw_reuse=1" 86 | } 87 | ], 88 | "Statistics": { 89 | "good": 4, 90 | "warn": 1 91 | } 92 | } 93 | ] 94 | } 95 | ``` 96 | 97 | -------------------------------------------------------------------------------- /pkg/plugins/export/store/exporter.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package store 19 | 20 | import ( 21 | "context" 22 | "encoding/json" 23 | "fmt" 24 | "sync" 25 | 26 | "tkestack.io/kube-jarvis/pkg/httpserver" 27 | "tkestack.io/kube-jarvis/pkg/plugins/export" 28 | ) 29 | 30 | const ( 31 | // ExporterType is type name of this Exporter 32 | ExporterType = "store" 33 | resultsStoreName = "results" 34 | ) 35 | 36 | // Exporter save result to file 37 | type Exporter struct { 38 | *export.MetaData 39 | // MaxRemain 40 | MaxRemain int 41 | Path string 42 | Server bool 43 | 44 | history *export.History 45 | hisLock sync.Mutex 46 | } 47 | 48 | // NewExporter return a file Exporter 49 | func NewExporter(m *export.MetaData) export.Exporter { 50 | e := &Exporter{ 51 | MetaData: m, 52 | history: &export.History{}, 53 | } 54 | return e 55 | } 56 | 57 | // Complete check and complete config items 58 | func (e *Exporter) Complete() error { 59 | if e.Path == "" { 60 | e.Path = "results" 61 | } 62 | 63 | if e.MaxRemain == 0 { 64 | e.MaxRemain = 7 65 | } 66 | 67 | if e.Server { 68 | httpserver.Default.HandleFunc(httpserver.StandardQueryPath, e.queryHandler) 69 | httpserver.Default.HandleFunc(httpserver.StandardHistoryPath, e.historyHandler) 70 | } 71 | 72 | if _, err := e.Store.CreateSpace(resultsStoreName); err != nil { 73 | return err 74 | } 75 | return e.reloadHistory() 76 | } 77 | 78 | func (e *Exporter) reloadHistory() error { 79 | e.hisLock.Lock() 80 | defer e.hisLock.Unlock() 81 | data, _, err := e.Store.Get(resultsStoreName, "history") 82 | if err != nil { 83 | return nil 84 | } 85 | 86 | if data != "" { 87 | return json.Unmarshal([]byte(data), e.history) 88 | } 89 | return nil 90 | } 91 | 92 | // Export export result 93 | func (e *Exporter) Export(ctx context.Context, result *export.AllResult) error { 94 | e.hisLock.Lock() 95 | defer e.hisLock.Unlock() 96 | 97 | ID := fmt.Sprint(result.StartTime.UnixNano()) 98 | if err := e.saveResult(ID, result); err != nil { 99 | return err 100 | } 101 | 102 | // create meta item 103 | e.history.Records = append(e.history.Records, &export.HistoryItem{ 104 | ID: ID, 105 | Overview: export.AllResult{ 106 | StartTime: result.StartTime, 107 | EndTime: result.EndTime, 108 | Statistics: result.Statistics, 109 | }, 110 | }) 111 | 112 | if e.MaxRemain >= len(e.history.Records) { 113 | return e.saveHistory() 114 | } 115 | 116 | // remove old items 117 | index := len(e.history.Records) - e.MaxRemain 118 | removeItems := e.history.Records[0:index] 119 | e.history.Records = e.history.Records[index:] 120 | if err := e.saveHistory(); err != nil { 121 | return err 122 | } 123 | 124 | for _, item := range removeItems { 125 | _ = e.removeResult(item.ID) 126 | } 127 | 128 | return nil 129 | } 130 | 131 | func (e *Exporter) saveResult(ID string, result *export.AllResult) error { 132 | data, err := result.Marshal() 133 | if err != nil { 134 | return err 135 | } 136 | 137 | return e.Store.Set(resultsStoreName, ID, string(data)) 138 | } 139 | 140 | func (e *Exporter) saveHistory() error { 141 | data, err := e.history.Marshal() 142 | if err != nil { 143 | return err 144 | } 145 | 146 | return e.Store.Set(resultsStoreName, "history", string(data)) 147 | } 148 | 149 | func (e *Exporter) removeResult(ID string) error { 150 | return e.Store.Delete(resultsStoreName, ID) 151 | } 152 | -------------------------------------------------------------------------------- /pkg/plugins/export/store/metahandler.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package store 19 | 20 | import ( 21 | "encoding/json" 22 | "io/ioutil" 23 | "math" 24 | "net/http" 25 | 26 | "tkestack.io/kube-jarvis/pkg/httpserver" 27 | ) 28 | 29 | func (e *Exporter) historyHandler(w http.ResponseWriter, r *http.Request) { 30 | e.hisLock.Lock() 31 | defer e.hisLock.Unlock() 32 | 33 | var err error 34 | var requestData []byte 35 | var respData []byte 36 | 37 | defer func() { 38 | e.Logger.Infof("handle meta request, err=%v, request=%s", err, string(requestData)) 39 | }() 40 | 41 | defer func() { _ = r.Body.Close() }() 42 | requestData, err = ioutil.ReadAll(r.Body) 43 | if err != nil { 44 | w.WriteHeader(http.StatusBadRequest) 45 | return 46 | } 47 | 48 | param := &httpserver.HistoryRequest{} 49 | if len(requestData) != 0 { 50 | if err = json.Unmarshal(requestData, param); err != nil { 51 | w.WriteHeader(http.StatusBadRequest) 52 | return 53 | } 54 | } 55 | 56 | if param.Limit == 0 { 57 | param.Limit = math.MaxInt32 58 | } 59 | 60 | history := httpserver.NewHistoryResponse() 61 | offset := param.Offset 62 | limit := param.Limit 63 | 64 | for i := len(e.history.Records) - 1; i >= 0; i-- { 65 | if offset != 0 { 66 | offset-- 67 | continue 68 | } 69 | 70 | if limit == 0 { 71 | break 72 | } 73 | limit-- 74 | history.Records = append(history.Records, e.history.Records[i]) 75 | } 76 | 77 | respData, err = json.Marshal(history) 78 | if err != nil { 79 | w.WriteHeader(http.StatusInternalServerError) 80 | return 81 | } 82 | 83 | total := 0 84 | n := 0 85 | for total < len(respData) { 86 | n, err = w.Write(respData[total:]) 87 | if err != nil { 88 | w.WriteHeader(http.StatusInternalServerError) 89 | return 90 | } 91 | total += n 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /pkg/plugins/export/store/queryhandler.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package store 19 | 20 | import ( 21 | "encoding/json" 22 | "fmt" 23 | "io/ioutil" 24 | "math" 25 | "net/http" 26 | 27 | "tkestack.io/kube-jarvis/pkg/httpserver" 28 | 29 | "tkestack.io/kube-jarvis/pkg/plugins/diagnose" 30 | "tkestack.io/kube-jarvis/pkg/plugins/export" 31 | ) 32 | 33 | func (e *Exporter) queryHandler(w http.ResponseWriter, r *http.Request) { 34 | var err error 35 | var requestData []byte 36 | var respData []byte 37 | 38 | defer func() { 39 | e.Logger.Infof("handle query request, err=%v, request=%s", err, string(requestData)) 40 | }() 41 | 42 | defer func() { _ = r.Body.Close() }() 43 | requestData, err = ioutil.ReadAll(r.Body) 44 | if err != nil { 45 | w.WriteHeader(http.StatusBadRequest) 46 | return 47 | } 48 | 49 | param := &httpserver.QueryRequest{} 50 | if err = json.Unmarshal(requestData, param); err != nil { 51 | w.WriteHeader(http.StatusBadRequest) 52 | return 53 | } 54 | 55 | if param.Level != "" && !param.Level.Verify() { 56 | err = fmt.Errorf("unknown 'Level'='%s'", param.Level) 57 | w.WriteHeader(http.StatusBadRequest) 58 | return 59 | } 60 | 61 | if param.Limit == 0 { 62 | param.Limit = math.MaxInt32 63 | } 64 | 65 | content, exist, err := e.Store.Get(resultsStoreName, param.ID) 66 | if err != nil { 67 | err = fmt.Errorf("get result failed: %v", err) 68 | w.WriteHeader(http.StatusBadRequest) 69 | return 70 | } 71 | 72 | if !exist { 73 | err = fmt.Errorf("result not exist") 74 | w.WriteHeader(http.StatusBadRequest) 75 | return 76 | } 77 | 78 | allResults := export.NewAllResult() 79 | if err = allResults.UnMarshal([]byte(content)); err != nil { 80 | return 81 | } 82 | 83 | respResult := httpserver.NewQueryResponse() 84 | respResult.StartTime = allResults.StartTime 85 | respResult.EndTime = allResults.EndTime 86 | respResult.Statistics = allResults.Statistics 87 | 88 | for _, dia := range allResults.Diagnostics { 89 | if param.Type != "" && param.Type != dia.Type { 90 | continue 91 | } 92 | 93 | if param.Name != "" && param.Name != dia.Name { 94 | continue 95 | } 96 | 97 | newDia := &export.DiagnosticResultItem{ 98 | StartTime: dia.StartTime, 99 | EndTime: dia.EndTime, 100 | Catalogue: dia.Catalogue, 101 | Type: dia.Type, 102 | Name: dia.Name, 103 | Desc: dia.Desc, 104 | Results: []*diagnose.Result{}, 105 | Statistics: dia.Statistics, 106 | } 107 | 108 | offset := param.Offset 109 | limit := param.Limit 110 | for _, item := range dia.Results { 111 | if param.Level != "" && item.Level.Compare(param.Level) > 0 { 112 | continue 113 | } 114 | 115 | if offset > 0 { 116 | offset-- 117 | continue 118 | } 119 | 120 | if limit == 0 { 121 | break 122 | } 123 | 124 | newDia.Results = append(newDia.Results, item) 125 | } 126 | respResult.Diagnostics = append(respResult.Diagnostics, newDia) 127 | } 128 | 129 | respData, err = json.Marshal(respResult) 130 | if err != nil { 131 | w.WriteHeader(http.StatusInternalServerError) 132 | return 133 | } 134 | 135 | total := 0 136 | n := 0 137 | for total < len(respData) { 138 | n, err = w.Write(respData[total:]) 139 | if err != nil { 140 | w.WriteHeader(http.StatusInternalServerError) 141 | return 142 | } 143 | total += n 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /pkg/plugins/meta.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package plugins 19 | 20 | import ( 21 | "tkestack.io/kube-jarvis/pkg/logger" 22 | "tkestack.io/kube-jarvis/pkg/store" 23 | "tkestack.io/kube-jarvis/pkg/translate" 24 | ) 25 | 26 | // MetaData is the common attributes of a plugins 27 | type MetaData struct { 28 | // Store is the global storage 29 | Store store.Store 30 | // Translator is a translator with plugins module context 31 | Translator translate.Translator 32 | // Logger is a logger with plugins module context 33 | Logger logger.Logger 34 | // Type is the type of plugins 35 | Type string 36 | // Title is the custom name of plugins 37 | Name string 38 | // Desc is the description of this plugin, 39 | // Desc should be set by plugin 40 | Desc translate.Message 41 | } 42 | 43 | // IsSupportedCloud return true if cloud type is supported 44 | func IsSupportedCloud(supported []string, cloud string) bool { 45 | if len(supported) == 0 { 46 | return true 47 | } 48 | 49 | for _, c := range supported { 50 | if c == cloud { 51 | return true 52 | } 53 | } 54 | return false 55 | } 56 | -------------------------------------------------------------------------------- /pkg/plugins/meta_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package plugins 19 | 20 | import ( 21 | "fmt" 22 | "testing" 23 | ) 24 | 25 | func TestIsSupportedCloud(t *testing.T) { 26 | var cases = []struct { 27 | supported bool 28 | clouds []string 29 | cloud string 30 | }{ 31 | { 32 | supported: true, 33 | clouds: []string{}, 34 | cloud: "123", 35 | }, 36 | { 37 | supported: true, 38 | clouds: []string{ 39 | "123", 40 | }, 41 | cloud: "123", 42 | }, 43 | { 44 | supported: false, 45 | clouds: []string{ 46 | "321", 47 | }, 48 | cloud: "123", 49 | }, 50 | } 51 | 52 | for _, cs := range cases { 53 | t.Run(fmt.Sprintf("%+v", cs), func(t *testing.T) { 54 | if IsSupportedCloud(cs.clouds, cs.cloud) != cs.supported { 55 | t.Fatalf("shoud %v", cs.supported) 56 | } 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pkg/plugins/progress.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package plugins 19 | 20 | import ( 21 | "sync" 22 | ) 23 | 24 | // Progress show the progress of cluster Initialization 25 | type Progress struct { 26 | sync.Mutex 27 | // IsDone if true if will Step done 28 | IsDone bool 29 | // Steps shows the sub progress of every step 30 | Steps map[string]*ProgressStep 31 | // CurStep is the name of current ProgressStep in Steps 32 | CurStep string 33 | // Total is the max step value of Progress 34 | Total int 35 | // Current is finished step value of Progress 36 | Current int 37 | // Percent is the percentage of overall progress 38 | Percent float64 39 | watchers []func(p *Progress) 40 | } 41 | 42 | // ProgressStep is one step of Initialization 43 | type ProgressStep struct { 44 | // Title is the short describe of this step 45 | Title string 46 | // Total is the total value of this step 47 | Total int 48 | // Percent is the percentage of overall this step 49 | Percent float64 50 | // Current is current value of this step 51 | Current int 52 | } 53 | 54 | // NewProgress return a new Progress 55 | func NewProgress() *Progress { 56 | return &Progress{ 57 | Steps: make(map[string]*ProgressStep), 58 | } 59 | } 60 | 61 | // Clone return a new Progress with the same value of origin 62 | func (p *Progress) Clone() *Progress { 63 | progress := &Progress{ 64 | IsDone: p.IsDone, 65 | CurStep: p.CurStep, 66 | Total: p.Total, 67 | Current: p.Current, 68 | Steps: map[string]*ProgressStep{}, 69 | } 70 | 71 | for name, step := range p.Steps { 72 | s := &ProgressStep{ 73 | Title: step.Title, 74 | Total: step.Total, 75 | Current: step.Current, 76 | } 77 | 78 | progress.Steps[name] = s 79 | } 80 | return progress 81 | } 82 | 83 | // CreateStep create and return an new InitialProgressStep from a InitialProgress 84 | func (p *Progress) CreateStep(name string, title string, total int) { 85 | p.update(func() { 86 | if _, exist := p.Steps[name]; exist { 87 | return 88 | } 89 | 90 | step := &ProgressStep{ 91 | Title: title, 92 | Total: total, 93 | } 94 | p.Total += total 95 | p.Steps[name] = step 96 | }) 97 | } 98 | 99 | // SetCurStep change current step of Progress 100 | func (p *Progress) SetCurStep(name string) { 101 | p.update(func() { 102 | p.CurStep = name 103 | }) 104 | } 105 | 106 | // AddPercent add current progress percent 107 | func (p *Progress) AddStepPercent(name string, n int) { 108 | p.update(func() { 109 | step := p.Steps[name] 110 | step.Current += n 111 | step.Percent = float64(step.Current) / float64(step.Total) * 100 112 | p.Current += n 113 | p.Percent = float64(p.Current) / float64(p.Total) * 100 114 | }) 115 | } 116 | 117 | // Done 118 | func (p *Progress) Done() { 119 | p.update(func() { 120 | p.IsDone = true 121 | }) 122 | } 123 | 124 | // AddProgressUpdatedWatcher add a watcher that will be called once progress updated 125 | func (p *Progress) AddProgressUpdatedWatcher(f func(p *Progress)) { 126 | p.watchers = append(p.watchers, f) 127 | } 128 | 129 | func (p *Progress) updated() { 130 | for _, f := range p.watchers { 131 | f(p) 132 | } 133 | } 134 | 135 | func (p *Progress) update(f func()) { 136 | p.Lock() 137 | defer p.Unlock() 138 | f() 139 | p.updated() 140 | } 141 | -------------------------------------------------------------------------------- /pkg/store/file.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package store 19 | 20 | import ( 21 | "fmt" 22 | "io/ioutil" 23 | "os" 24 | ) 25 | 26 | // File save data in files, the data of different space saved in different file 27 | type File struct { 28 | // Dir is the directory all data files in 29 | Dir string 30 | } 31 | 32 | func init() { 33 | registerStore("file", func(string) Store { 34 | return &File{} 35 | }) 36 | } 37 | 38 | func (f *File) spacePath(name string) string { 39 | return fmt.Sprintf("%s/%s", f.Dir, name) 40 | } 41 | 42 | func (f *File) dataPath(space, key string) string { 43 | return fmt.Sprintf("%s/%s/%s", f.Dir, space, key) 44 | } 45 | 46 | // Complete do Initialize 47 | func (f *File) Complete() error { 48 | if f.Dir == "" { 49 | f.Dir = "data" 50 | } 51 | _ = os.MkdirAll(f.Dir, 0755) 52 | return nil 53 | } 54 | 55 | // CreateSpace create a new namespace for specific data set 56 | func (f *File) CreateSpace(name string) (created bool, err error) { 57 | if exists(f.spacePath(name)) { 58 | return false, nil 59 | } 60 | 61 | if err := os.MkdirAll(f.spacePath(name), 0755); err != nil { 62 | return false, err 63 | } 64 | 65 | return true, nil 66 | } 67 | 68 | // Set update a value of key 69 | func (f *File) Set(space string, key, value string) error { 70 | if !exists(f.spacePath(space)) { 71 | return SpaceNotFound 72 | } 73 | 74 | if err := ioutil.WriteFile(f.dataPath(space, key), []byte(value), 0644); err != nil { 75 | return err 76 | } 77 | 78 | return nil 79 | } 80 | 81 | // Get return target value of key 82 | func (f *File) Get(space string, key string) (value string, exist bool, err error) { 83 | if !exists(f.spacePath(space)) { 84 | return "", false, SpaceNotFound 85 | } 86 | 87 | if !exists(f.dataPath(space, key)) { 88 | return "", false, nil 89 | } 90 | 91 | data, err := ioutil.ReadFile(f.dataPath(space, key)) 92 | if err != nil { 93 | return "", false, err 94 | } 95 | 96 | return string(data), true, nil 97 | } 98 | 99 | // Delete delete target key 100 | func (f *File) Delete(space string, key string) error { 101 | if !exists(f.spacePath(space)) { 102 | return SpaceNotFound 103 | } 104 | 105 | if !exists(f.dataPath(space, key)) { 106 | return nil 107 | } 108 | 109 | return os.Remove(f.dataPath(space, key)) 110 | } 111 | 112 | // DeleteSpace Delete whole namespace 113 | func (f *File) DeleteSpace(name string) error { 114 | if !exists(f.spacePath(name)) { 115 | return SpaceNotFound 116 | } 117 | return os.Remove(f.spacePath(name)) 118 | } 119 | 120 | func exists(path string) bool { 121 | _, err := os.Stat(path) 122 | if err != nil { 123 | if os.IsExist(err) { 124 | return true 125 | } 126 | return false 127 | } 128 | return true 129 | } 130 | -------------------------------------------------------------------------------- /pkg/store/mem.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package store 19 | 20 | // Mem save data in maps 21 | type Mem struct { 22 | data map[string]map[string]string 23 | } 24 | 25 | func init() { 26 | registerStore("mem", func(string) Store { 27 | return &Mem{ 28 | data: map[string]map[string]string{}, 29 | } 30 | }) 31 | } 32 | 33 | // Complete do Initialize 34 | func (m *Mem) Complete() error { 35 | return nil 36 | } 37 | 38 | // CreateSpace create a new namespace for specific data set 39 | func (m *Mem) CreateSpace(name string) (created bool, err error) { 40 | _, e := m.data[name] 41 | if e { 42 | return false, nil 43 | } 44 | 45 | m.data[name] = map[string]string{} 46 | return true, nil 47 | } 48 | 49 | // Set update a value of key 50 | func (m *Mem) Set(space string, key, value string) error { 51 | d, e := m.data[space] 52 | if e { 53 | return SpaceNotFound 54 | } 55 | 56 | d[key] = value 57 | return nil 58 | } 59 | 60 | // Get return target value of key 61 | func (m *Mem) Get(space string, key string) (value string, exist bool, err error) { 62 | d, e := m.data[space] 63 | if e { 64 | return "", false, SpaceNotFound 65 | } 66 | 67 | v, exist := d[key] 68 | return v, true, nil 69 | } 70 | 71 | // Delete delete target key 72 | func (m *Mem) Delete(space string, key string) error { 73 | d, e := m.data[space] 74 | if e { 75 | return SpaceNotFound 76 | } 77 | delete(d, key) 78 | return nil 79 | } 80 | 81 | // DeleteSpace Delete whole namespace 82 | func (m *Mem) DeleteSpace(name string) error { 83 | _, e := m.data[name] 84 | if e { 85 | return SpaceNotFound 86 | } 87 | delete(m.data, name) 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /pkg/store/store.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package store 19 | 20 | import "fmt" 21 | 22 | // SpaceNotFound will be returned if target space not found 23 | var SpaceNotFound = fmt.Errorf("space not found") 24 | 25 | // Store provider a default k/v storage for all plugins 26 | type Store interface { 27 | // Complete do Initialize 28 | Complete() error 29 | // CreateSpace create a new namespace for specific data set 30 | CreateSpace(name string) (created bool, err error) 31 | // Set update a value of key 32 | Set(space string, key, value string) error 33 | // Get return target value of key 34 | Get(space string, key string) (value string, exist bool, err error) 35 | // Delete delete target key 36 | Delete(space string, key string) error 37 | // DeleteSpace Delete whole namespace 38 | DeleteSpace(name string) error 39 | } 40 | 41 | var factories = map[string]func(string) Store{} 42 | 43 | func registerStore(typ string, creator func(string) Store) { 44 | factories[typ] = creator 45 | } 46 | 47 | // GetStore return a store according to typ 48 | func GetStore(typ string, clsName string) Store { 49 | f, exist := factories[typ] 50 | if !exist { 51 | panic(fmt.Sprintf("cant not found store type %s", typ)) 52 | } 53 | return f(clsName) 54 | } 55 | -------------------------------------------------------------------------------- /pkg/translate/default.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package translate 19 | 20 | import ( 21 | "fmt" 22 | "io/ioutil" 23 | "os" 24 | "path/filepath" 25 | "strings" 26 | 27 | "github.com/nicksnyder/go-i18n/v2/i18n" 28 | "golang.org/x/text/language" 29 | "gopkg.in/yaml.v2" 30 | ) 31 | 32 | // Default translate string to target language 33 | type Default struct { 34 | module string 35 | bundle *i18n.Bundle 36 | localize *i18n.Localizer 37 | } 38 | 39 | // NewDefault create a new default Translator 40 | // Translator will read translation message from "dir/defLang" and "dir/targetLang" 41 | func NewDefault(dir string, defLang string, targetLang string) (Translator, error) { 42 | t := &Default{} 43 | defTag, err := language.Parse(defLang) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | targetTag := language.Make(targetLang) 49 | t.bundle = i18n.NewBundle(defTag) 50 | t.bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal) 51 | t.localize = i18n.NewLocalizer(t.bundle, targetLang) 52 | 53 | // load default message 54 | if err := t.addMessage(dir, defTag); err != nil { 55 | return nil, err 56 | } 57 | 58 | // load target message 59 | return t, t.addMessage(dir, targetTag) 60 | } 61 | 62 | func (d *Default) addMessage(dir string, tag language.Tag) error { 63 | return filepath.Walk(fmt.Sprintf("%s/%s", dir, tag.String()), 64 | func(path string, info os.FileInfo, err error) error { 65 | if err != nil { 66 | return err 67 | } 68 | 69 | if info.Mode().IsRegular() && strings.HasSuffix(info.Name(), ".yaml") { 70 | buf, err := ioutil.ReadFile(path) 71 | if err != nil { 72 | return err 73 | } 74 | mes, err := i18n.ParseMessageFileBytes(buf, path, map[string]i18n.UnmarshalFunc{ 75 | "yaml": yaml.Unmarshal, 76 | }) 77 | if err != nil { 78 | return fmt.Errorf("load message file %s failed : %s", path, err.Error()) 79 | } 80 | 81 | for _, m := range mes.Messages { 82 | m.ID = fmt.Sprintf("%s.%s.%s", filepath.Base(filepath.Dir(path)), 83 | strings.TrimSuffix(info.Name(), ".yaml"), m.ID) 84 | } 85 | 86 | if err := d.bundle.AddMessages(tag, mes.Messages...); err != nil { 87 | return fmt.Errorf("add message failed : %s", err.Error()) 88 | } 89 | } 90 | return nil 91 | }) 92 | } 93 | 94 | // WithModule attach a module label to a Translator 95 | // module will be add before ID when you call Translator.Message 96 | func (d *Default) WithModule(module string) Translator { 97 | return &Default{ 98 | module: module, 99 | bundle: d.bundle, 100 | localize: d.localize, 101 | } 102 | } 103 | 104 | // Message get translated message from Translator 105 | // t.module will be add before ID 106 | // example: 107 | // ID = "message" and module = "diagnostics.example" 108 | // then real ID will be "diagnostics.example.message" 109 | func (d *Default) Message(ID string, templateData map[string]interface{}) Message { 110 | mes, _ := d.localize.Localize(&i18n.LocalizeConfig{ 111 | DefaultMessage: &i18n.Message{ 112 | ID: fmt.Sprintf("%s.%s", d.module, ID), 113 | }, 114 | TemplateData: templateData, 115 | }) 116 | return Message(mes) 117 | } 118 | -------------------------------------------------------------------------------- /pkg/translate/default_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package translate 19 | 20 | import "testing" 21 | 22 | func TestTranslator_Message(t *testing.T) { 23 | tr, err := NewDefault("../../translation", "en", "zh") 24 | if err != nil { 25 | t.Fatalf(err.Error()) 26 | } 27 | 28 | tr = tr.WithModule("diagnostics.example") 29 | t.Log(tr.Message("message", map[string]interface{}{ 30 | "Mes": "test", 31 | })) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/translate/fake.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package translate 19 | 20 | // Fake is a Translator that just return ID as message directly 21 | type Fake struct { 22 | } 23 | 24 | // NewFake return a Fake Translator 25 | func NewFake() Translator { 26 | return &Fake{} 27 | } 28 | 29 | // Message get translated message from Translator 30 | // t.module will be add before ID 31 | // example: 32 | // ID = "message" and module = "diagnostics.example" 33 | // then real ID will be "diagnostics.example.message" 34 | func (f *Fake) Message(ID string, templateData map[string]interface{}) Message { 35 | return Message(ID) 36 | } 37 | 38 | // WithModule attach a module label to a Translator 39 | // module will be add before ID when you call Translator.Message 40 | func (f *Fake) WithModule(module string) Translator { 41 | return f 42 | } 43 | -------------------------------------------------------------------------------- /pkg/translate/fake_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package translate 19 | 20 | import "testing" 21 | 22 | func TestNewFake(t *testing.T) { 23 | f := NewFake() 24 | f.WithModule("123") 25 | ms := f.Message("123", nil) 26 | if ms != "123" { 27 | t.Fatalf("return msg should be 123") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pkg/translate/translator.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package translate 19 | 20 | // Message is a translated string 21 | type Message string 22 | 23 | // Translator translate string to target language 24 | type Translator interface { 25 | // WithModule attach a module label to a Translator 26 | // module will be add before ID when you call Translator.Message 27 | Message(ID string, templateData map[string]interface{}) Message 28 | // Message get translated message from Translator 29 | // t.module will be add before ID 30 | // example: 31 | // ID = "message" and module = "diagnostics.example" 32 | // then real ID will be "diagnostics.example.message" 33 | WithModule(module string) Translator 34 | } 35 | -------------------------------------------------------------------------------- /pkg/util/goroutine.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package util 19 | 20 | import ( 21 | "fmt" 22 | "math" 23 | "time" 24 | ) 25 | 26 | // RetryAbleErr should be returned if you want RetryUntilTimeout to retry 27 | var RetryAbleErr = fmt.Errorf("retry") 28 | 29 | // RetryUntilTimeout retry target function "do" until timeout 30 | func RetryUntilTimeout(interval time.Duration, timeout time.Duration, do func() error) error { 31 | err := do() 32 | if err == nil { 33 | return nil 34 | } 35 | 36 | if err != RetryAbleErr { 37 | return err 38 | } 39 | 40 | if timeout == 0 { 41 | timeout = time.Duration(math.MaxInt64) 42 | } 43 | 44 | t := time.NewTimer(timeout) 45 | for { 46 | select { 47 | case <-t.C: 48 | return fmt.Errorf("timeout") 49 | case <-time.After(interval): 50 | err := do() 51 | if err == nil { 52 | return nil 53 | } 54 | 55 | if err != RetryAbleErr { 56 | return err 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pkg/util/goroutine_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package util 19 | 20 | import ( 21 | "testing" 22 | "time" 23 | ) 24 | 25 | func TestRetryUntilTimeout(t *testing.T) { 26 | // direct return 27 | done := false 28 | go func() { 29 | <-time.After(time.Second * 3) 30 | if !done { 31 | t.Fatalf("not done") 32 | } 33 | }() 34 | if err := RetryUntilTimeout(time.Hour, time.Hour, func() error { 35 | done = true 36 | return nil 37 | }); err != nil { 38 | t.Fatalf(err.Error()) 39 | } 40 | 41 | // check retry 42 | count := 0 43 | if err := RetryUntilTimeout(0, 0, func() error { 44 | count++ 45 | if count == 3 { 46 | return nil 47 | } 48 | return RetryAbleErr 49 | }); err != nil { 50 | t.Fatalf(err.Error()) 51 | } 52 | 53 | // check timeout 54 | if err := RetryUntilTimeout(time.Second, time.Second*2, func() error { 55 | return RetryAbleErr 56 | }); err == nil { 57 | t.Fatalf("should return an error") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pkg/util/k8s.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package util 19 | 20 | import ( 21 | "fmt" 22 | 23 | "k8s.io/apimachinery/pkg/api/resource" 24 | ) 25 | 26 | // MemQuantityStr returns a string in GB 27 | func MemQuantityStr(q *resource.Quantity) string { 28 | return fmt.Sprintf("%.2f", float64(q.Value())/1024/1024/1024) + "GB" 29 | } 30 | 31 | // CpuQuantityStr returns a string in Core 32 | func CpuQuantityStr(q *resource.Quantity) string { 33 | return fmt.Sprintf("%.2f", float64(q.MilliValue())/1000) + "Core" 34 | } 35 | -------------------------------------------------------------------------------- /pkg/util/k8s_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package util 19 | 20 | import ( 21 | "testing" 22 | 23 | "k8s.io/apimachinery/pkg/api/resource" 24 | ) 25 | 26 | func Test_Quantity(t *testing.T) { 27 | a := resource.MustParse("7.63Gi") 28 | if MemQuantityStr(&a) != "7.63GB" { 29 | t.Fatalf("shoud be 7.63GB") 30 | } 31 | 32 | a = resource.MustParse("1100m") 33 | t.Log(CpuQuantityStr(&a)) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/util/obj.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package util 19 | 20 | import "gopkg.in/yaml.v2" 21 | 22 | // InitObjViaYaml marshal "config" to yaml data, then unMarshal data to "obj" 23 | func InitObjViaYaml(obj interface{}, config interface{}) error { 24 | if obj == nil || config == nil { 25 | return nil 26 | } 27 | 28 | data, err := yaml.Marshal(config) 29 | if err != nil { 30 | return err 31 | } 32 | return yaml.Unmarshal(data, obj) 33 | } 34 | -------------------------------------------------------------------------------- /pkg/util/obj_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack 3 | * available. 4 | * 5 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the “License”); you may not use 8 | * this file except in compliance with the License. You may obtain a copy of the 9 | * License at 10 | * 11 | * https://opensource.org/licenses/Apache-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 15 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations under the License. 17 | */ 18 | package util 19 | 20 | import ( 21 | "testing" 22 | ) 23 | 24 | func TestInitObjViaYaml(t *testing.T) { 25 | type E struct { 26 | A string 27 | } 28 | type T struct { 29 | E 30 | } 31 | var a T 32 | var b T 33 | a.A = "123" 34 | 35 | if err := InitObjViaYaml(&b, nil); err != nil { 36 | t.Fatalf(err.Error()) 37 | } 38 | if b.A != "" { 39 | t.Fatalf("b.A want empty") 40 | } 41 | 42 | if err := InitObjViaYaml(&b, &a); err != nil { 43 | t.Fatalf(err.Error()) 44 | } 45 | 46 | if b.A != a.A { 47 | t.Fatalf("b.A != a.A") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Download kube-jarvis...." 3 | wget https://kube-jarvis-1251707795.cos.ap-guangzhou.myqcloud.com/kube-jarvis.tar.gz 4 | tar xf kube-jarvis.tar.gz 5 | cd kube-jarvis 6 | echo "Creating Namespace kube-jarvis..." 7 | kubectl apply -f manifests/common/namespace.yaml 8 | echo "Creating Daemonset kube-jarvis-agent..." 9 | kubectl apply -f manifests/common/agent-ds.yaml 10 | while true 11 | do 12 | echo "Waiting all kube-jarvis-agent to running..." 13 | check=`kubectl get po -n kube-jarvis | grep -v NAME | grep -v Running | grep -v grep` 14 | if [ "$check" == "" ] 15 | then 16 | echo "Done" 17 | break 18 | fi 19 | sleep 3 20 | done 21 | ./kube-jarvis 22 | -------------------------------------------------------------------------------- /translation/en/diagnostics/affinity.yaml: -------------------------------------------------------------------------------- 1 | title: "Affinity Check" 2 | desc: "{{.Kind}} {{.Namespace}}:{{.Name}} lack of affinity configure" 3 | proposal: "set right affinity can get better disaster toleration" 4 | -------------------------------------------------------------------------------- /translation/en/diagnostics/batch.yaml: -------------------------------------------------------------------------------- 1 | job-backofflimit-title: "Job BackOffLimit Check" 2 | job-backofflimit-desc: "Job {{.Namespace}}:{{.Name}} BackOffLimit is too large" 3 | job-backofflimit-proposal: "Job BackOffLimit less than {{.RecommendedValue}} is recommended" 4 | 5 | cronjob-failedjobhistorylimit-title: "CronJob FailedJobsHistoryLimit Check" 6 | cronjob-failedjobhistorylimit-desc: "CronJob {{.Namespace}}:{{.Name}} FailedJobsHistoryLimit is too large" 7 | cronjob-failedjobhistorylimit-proposal: "CronJob FailedJobsHistoryLimit less than {{.RecommendedValue}} is recommended" 8 | 9 | cronjob-successfuljobshistorylimit-title: "CronJob SuccessfulJobsHistoryLimit Check" 10 | cronjob-successfuljobshistorylimit-desc: "CronJob {{.Namespace}}:{{.Name}} SuccessfulJobsHistoryLimit is too large" 11 | cronjob-successfuljobshistorylimit-proposal: "CronJob SuccessfulJobsHistoryLimit less than {{.RecommendedValue}} is recommended" 12 | 13 | cronjob-concurrencypolicy-title: "CronJob ConcurrencyPolicy Check" 14 | cronjob-concurrencypolicy-desc: "CronJob {{.Namespace}}:{{.Name}} ConcurrencyPolicy is {{.CurrentConcurrencyPolicy}}" 15 | cronjob-concurrencypolicy-proposal: "CronJob ConcurrencyPolicy {{.RecommendedConcurrencyPolicy}} is recommended" 16 | -------------------------------------------------------------------------------- /translation/en/diagnostics/etcd-args.yaml: -------------------------------------------------------------------------------- 1 | quota-backend-bytes-title: "Number of bytes the backend Size" 2 | quota-backend-bytes-desc: "The arg --{{.Arg}} current value {{.CurVal}} is too small" 3 | quota-backend-bytes-proposal: "--{{.Arg}}={{.TargetVal}} is recommended" 4 | 5 | good-desc: "The arg --{{.Arg}} current value {{.CurVal}} is good" -------------------------------------------------------------------------------- /translation/en/diagnostics/example.yaml: -------------------------------------------------------------------------------- 1 | message: "This is a example diagnostic" 2 | proposal: "This is a example proposal" -------------------------------------------------------------------------------- /translation/en/diagnostics/health-check.yaml: -------------------------------------------------------------------------------- 1 | title: "Health Check" 2 | desc: "{{.Kind}} {{.Namespace}}:{{.Name}} has container lack readiness or liveness probe" 3 | proposal: "always set container readiness and liveness probe for {{.Kind}}" 4 | -------------------------------------------------------------------------------- /translation/en/diagnostics/hpa-ip.yaml: -------------------------------------------------------------------------------- 1 | hpa-ip-title: "HPA ip used report" 2 | hpa-ip-desc: "Cluster ip count {{.ClusterIPCount}}, pod already used {{.CurrentIPCount}}, hpa would be used {{.HPAMaxIPCount}} at most." -------------------------------------------------------------------------------- /translation/en/diagnostics/kube-apiserver-args.yaml: -------------------------------------------------------------------------------- 1 | max-requests-inflight-title: "The maximum number of non-mutating requests in flight at a given time" 2 | max-requests-inflight-desc: "For the current master node configuration,the arg --{{.Arg}} current value {{.CurVal}} is too small" 3 | max-requests-inflight-proposal: "--{{.Arg}}={{.TargetVal}} is recommended" 4 | 5 | max-mutating-requests-inflight-title: "The maximum number of mutating requests in flight at a given time" 6 | max-mutating-requests-inflight-desc: "For the current master node configuration,the arg --{{.Arg}} current value {{.CurVal}} is too small" 7 | max-mutating-requests-inflight-proposal: "--{{.Arg}}={{.TargetVal}} is recommended" 8 | 9 | good-desc: "For the current master node configuration,the arg --{{.Arg}} current value {{.CurVal}} is good" -------------------------------------------------------------------------------- /translation/en/diagnostics/kube-controller-manager-args.yaml: -------------------------------------------------------------------------------- 1 | kube-api-qps-title: "QPS to use while talking with kubernetes apiserver" 2 | kube-api-qps-desc: "For the current master node configuration,the arg --{{.Arg}} current value {{.CurVal}} is too small" 3 | kube-api-qps-proposal: "--{{.Arg}}={{.TargetVal}} is recommended" 4 | 5 | kube-api-burst-title: "Burst to use while talking with kubernetes apiserver" 6 | kube-api-burst-desc: "For the current master node configuration, the arg --{{.Arg}} current value {{.CurVal}} is too small" 7 | kube-api-burst-proposal: "--{{.Arg}}={{.TargetVal}} is recommended" 8 | 9 | good-desc: "For the current master node configuration,the arg --{{.Arg}} current value {{.CurVal}} is good" -------------------------------------------------------------------------------- /translation/en/diagnostics/kube-scheduler-args.yaml: -------------------------------------------------------------------------------- 1 | kube-api-qps-title: "QPS to use while talking with kubernetes apiserver" 2 | kube-api-qps-desc: "For the current master node configuration, the arg --{{.Arg}} current value {{.CurVal}} is too small" 3 | kube-api-qps-proposal: "--{{.Arg}}={{.TargetVal}} is recommended" 4 | 5 | kube-api-burst-title: "Burst to use while talking with kubernetes apiserver" 6 | kube-api-burst-desc: "For the current master node configuration, the arg --{{.Arg}} current value {{.CurVal}} is too small" 7 | kube-api-burst-proposal: "--{{.Arg}}={{.TargetVal}} is recommended" 8 | 9 | good-desc: "For the current master node configuration,the arg --{{.Arg}} current value {{.CurVal}} is good" -------------------------------------------------------------------------------- /translation/en/diagnostics/master-capacity.yaml: -------------------------------------------------------------------------------- 1 | title: "Master node {{.Resource}} capacity" 2 | desc: "Master({{.NodeName}}) with {{.CurValue}} {{.Resource}} is insufficient for cluster with {{.NodeTotal}} nodes" 3 | proposal: "{{.TargetValue}} {{.Resource}} needed for cluster with {{.NodeTotal}} nodes" 4 | good-desc: "Master({{.NodeName}}) with {{.CurValue}} {{.Resource}} is sufficient for cluster with {{.NodeTotal}} nodes" -------------------------------------------------------------------------------- /translation/en/diagnostics/master-components.yaml: -------------------------------------------------------------------------------- 1 | err-title: "Error occur" 2 | err-desc: "There is unexpected error" 3 | err-proposal: "See running logs" 4 | 5 | not-run-title: "Component is not running" 6 | not-run-desc: "{{.Name}} on {{.Node}} is not running, it may crashed" 7 | not-run-proposal: "Check your {{.Name}} on {{.Node}}" 8 | 9 | restart-title: "Component restarted" 10 | restart-desc: "{{.Name}} run as pod, and restart count is {{.Count}}, last restartTime is {{.LastTime}}" 11 | restart-proposal: "Check your {{.Name}} pod status and event know the restart reason" 12 | 13 | good-title: "Component status is good" 14 | good-desc: "{{.Name}} on {{.Node}} is running and never restart recently" 15 | good-proposal: "" -------------------------------------------------------------------------------- /translation/en/diagnostics/master-status.yaml: -------------------------------------------------------------------------------- 1 | master-status-title: "Master node status" 2 | master-status-desc: "Master node {{.Node}} {{.Resource}} status exception" 3 | master-status-proposal: "Recover Master node's {{.Resource}} status,or you can submit a order" 4 | 5 | master-status-good-desc: "Master node {{.Node}} status normal" 6 | master-status-good-proposal: "" -------------------------------------------------------------------------------- /translation/en/diagnostics/node-ha.yaml: -------------------------------------------------------------------------------- 1 | node-num-title: "Node Num" 2 | node-num-good-desc: "The cluster node has no single point of failure,current node num: {{.CurTotalNode}}" 3 | node-num-bad-desc: "The cluster node has a single point of failure,only one node" 4 | node-num-proposal: "scale cluster nodes, make sure the num of cluster nodes is greater than 1" 5 | 6 | node-zone-title: "Node Zone" 7 | node-zone-good-desc: "Nodes are distributed in multiple zone,total zone num: {{.CurTotalZoneNum}}" 8 | node-zone-bad-desc: "Nodes are not evenly distributed in multiple zone,total zone num: {{.CurTotalZoneNum}},other zones may be short of {{.ResourceName}} resource when zone {{.ZoneName}} failed" 9 | node-zone-proposal: "It is suggested that all zone nodes are evenly distributed and the total of zone num >= 2" 10 | -------------------------------------------------------------------------------- /translation/en/diagnostics/node-iptables.yaml: -------------------------------------------------------------------------------- 1 | iptables-count-title: "IPTables Count" 2 | iptables-count-desc: "Node {{.Node}} iptables current count is {{.CurCount}}, more than {{.SuggestedCount}} is not recommended" 3 | iptables-count-proposal: "Use headless services or replace proxier iptables with ipvs" 4 | 5 | iptables-forward-policy-title: "IPTables Forward Policy" 6 | iptables-forward-policy-desc: "Node {{.Node}} iptables chain forward policy {{.CurPolicy}} is not recommended" 7 | iptables-forward-policy-good-desc: "Node {{.Node}} iptables chain forward policy {{.CurPolicy}} is recommended" 8 | iptables-forward-policy-proposal: "Set {{.Name}}={{.SuggestedPolicy}}" -------------------------------------------------------------------------------- /translation/en/diagnostics/node-status.yaml: -------------------------------------------------------------------------------- 1 | node-status-title: "Worker node status" 2 | node-status-desc: "Worker node {{.Node}} {{.Resource}} status exception" 3 | node-status-proposal: "Recover Worker node's {{.Resource}} status,or you can submit a work order" 4 | 5 | node-status-good-desc: "Worker node {{.Node}} status normal" 6 | node-status-good-proposal: "" -------------------------------------------------------------------------------- /translation/en/diagnostics/node-sys.yaml: -------------------------------------------------------------------------------- 1 | kernel-para-title: "Kernel Parameters" 2 | kernel-para-desc: "Node {{.Node}} Parameters[ {{.Name}}={{.CurVal}} ] is not recommended" 3 | kernel-para-proposal: "Set {{.Name}}={{.TargetVal}}" 4 | kernel-para-good-desc: "Node {{.Node}} Parameters[ {{.Name}}={{.CurVal}} ] is recommended" 5 | -------------------------------------------------------------------------------- /translation/en/diagnostics/pdb.yaml: -------------------------------------------------------------------------------- 1 | title: "PDB Check" 2 | desc: "{{.Kind}} {{.Namespace}}:{{.Name}} lack of pdb" 3 | proposal: "set right pdb can protect your application when evicted occurs" 4 | -------------------------------------------------------------------------------- /translation/en/diagnostics/requests-limits.yaml: -------------------------------------------------------------------------------- 1 | title: "Requests Limits" 2 | desc: "{{.Kind}} resources no requests or no limits" 3 | proposal: "always set {{.Kind}} requests and limits" 4 | -------------------------------------------------------------------------------- /translation/en/diagnostics/workload-ha.yaml: -------------------------------------------------------------------------------- 1 | title: "Workload HA Check" 2 | desc: "{{.Kind}} {{.Namespace}}:{{.Name}} all pods running on the same node" 3 | proposal: "recreate pod to schedule it to other node and set right affinity can get better disaster toleration" 4 | -------------------------------------------------------------------------------- /translation/en/diagnostics/workload-status.yaml: -------------------------------------------------------------------------------- 1 | workload-status-title: "Workload status" 2 | workload-status-desc: "{{.Workload}}[{{.Namespace}}] {{.Name}} replicas {{.Available}} is not as expected {{.Replicas}} " 3 | workload-status-proposal: "Recover {{.Workload}}[{{.Namespace}}] {{.Name}} status, or you can submit a work order" 4 | 5 | workload-status-good-desc: "{{.Workload}}[{{.Namespace}}] {{.Name}} replicas {{.Available}} is as expected {{.Replicas}} " 6 | workload-status-good-proposal: "" -------------------------------------------------------------------------------- /translation/zh/diagnostics/affinity.yaml: -------------------------------------------------------------------------------- 1 | title: "亲和性检查" 2 | desc: "{{.Kind}} {{.Namespace}}:{{.Name}} 没有配置亲和性或反亲和性" 3 | proposal: "配置合适的亲和性和反亲和性可以使应用获得更好的容灾" 4 | -------------------------------------------------------------------------------- /translation/zh/diagnostics/batch.yaml: -------------------------------------------------------------------------------- 1 | job-backofflimit-title: "Job BackOffLimit 检查" 2 | job-backofflimit-desc: "Job {{.Namespace}}:{{.Name}} BackOffLimit 太大" 3 | job-backofflimit-proposal: "Job BackOffLimit 建议小于 {{.RecommendedValue}}" 4 | 5 | cronjob-failedjobhistorylimit-title: "CronJob FailedJobsHistoryLimit 检查" 6 | cronjob-failedjobhistorylimit-desc: "CronJob {{.Namespace}}:{{.Name}} FailedJobsHistoryLimit 太大" 7 | cronjob-failedjobhistorylimit-proposal: "CronJob FailedJobsHistoryLimit 建议小于 {{.RecommendedValue}}" 8 | 9 | cronjob-successfuljobshistorylimit-title: "CronJob SuccessfulJobsHistoryLimit 检查" 10 | cronjob-successfuljobshistorylimit-desc: "CronJob {{.Namespace}}:{{.Name}} SuccessfulJobsHistoryLimit 太大" 11 | cronjob-successfuljobshistorylimit-proposal: "CronJob SuccessfulJobsHistoryLimit 建议小于 {{.RecommendedValue}}" 12 | 13 | cronjob-concurrencypolicy-title: "CronJob ConcurrencyPolicy 检查" 14 | cronjob-concurrencypolicy-desc: "CronJob {{.Namespace}}:{{.Name}} 当前的ConcurrencyPolicy为 {{.CurrentConcurrencyPolicy}}" 15 | cronjob-concurrencypolicy-proposal: "CronJob ConcurrencyPolicy 建议设置为 {{.RecommendedConcurrencyPolicy}}" 16 | -------------------------------------------------------------------------------- /translation/zh/diagnostics/etcd-args.yaml: -------------------------------------------------------------------------------- 1 | quota-backend-bytes-title: "存储大小" 2 | quota-backend-bytes-desc: "参数 --{{.Arg}} 当前值 {{.CurVal}} 过小" 3 | quota-backend-bytes-proposal: "推荐设置参数为--{{.Arg}}={{.TargetVal}}" 4 | 5 | good-desc: "参数 --{{.Arg}} 当前值 {{.CurVal}} 是合理的" -------------------------------------------------------------------------------- /translation/zh/diagnostics/example.yaml: -------------------------------------------------------------------------------- 1 | message: "这是一个测试用的诊断器" 2 | proposal: "这是一个建议" -------------------------------------------------------------------------------- /translation/zh/diagnostics/health-check.yaml: -------------------------------------------------------------------------------- 1 | title: "健康检查" 2 | desc: "{{.Kind}} {{.Namespace}}:{{.Name}} 包含未设置存活检查或就绪检查的容器" 3 | proposal: "应该始终为{{.Kind}}的容器设置存活检查和就绪检查" -------------------------------------------------------------------------------- /translation/zh/diagnostics/hpa-ip.yaml: -------------------------------------------------------------------------------- 1 | hpa-ip-title: "HPA ip 使用报告" 2 | hpa-ip-desc: "集群 ip 总数 {{.ClusterIPCount}}, pod 已经使用数目 {{.CurrentIPCount}}, 使用 hpa 后最多使用 {{.HPAMaxIPCount}}" -------------------------------------------------------------------------------- /translation/zh/diagnostics/kube-apiserver-args.yaml: -------------------------------------------------------------------------------- 1 | max-requests-inflight-title: "给定时间内进行中不可变请求的最大数量" 2 | max-requests-inflight-desc: "当前集群master节点的性能下,参数 --{{.Arg}} 当前值 {{.CurVal}} 过小" 3 | max-requests-inflight-proposal: "当前集群master节点的性能下,推荐设置参数为 --{{.Arg}}={{.TargetVal}}" 4 | 5 | max-mutating-requests-inflight-title: "在给定时间内进行中可变请求的最大数量" 6 | max-mutating-requests-inflight-desc: "当前集群master节点的性能下,参数 --{{.Arg}} 当前值 {{.CurVal}} 过小" 7 | max-mutating-requests-inflight-proposal: "当前集群master节点的性能下,推荐设置参数为 --{{.Arg}}={{.TargetVal}}" 8 | 9 | good-desc: "当前集群master节点的性能下,参数 --{{.Arg}} 当前值 {{.CurVal}} 是合理的" -------------------------------------------------------------------------------- /translation/zh/diagnostics/kube-controller-manager-args.yaml: -------------------------------------------------------------------------------- 1 | kube-api-qps-title: "请求kube-apiserver使用的QPS" 2 | kube-api-qps-desc: "当前集群master节点的性能下,参数 --{{.Arg}} 当前值 {{.CurVal}} 过小" 3 | kube-api-qps-proposal: "当前集群master节点的性能下,推荐设置参数为 --{{.Arg}}={{.TargetVal}}" 4 | 5 | kube-api-burst-title: "和kube-apiserver通信的时候最大burst 值" 6 | kube-api-burst-desc: "当前集群master节点的性能下,参数 --{{.Arg}} 当前值 {{.CurVal}} 过小" 7 | kube-api-burst-proposal: "当前集群master节点的性能下,推荐设置参数为 --{{.Arg}}={{.TargetVal}}" 8 | 9 | good-desc: "当前集群master节点的性能下,参数 --{{.Arg}} 当前值 {{.CurVal}} 是合理的" -------------------------------------------------------------------------------- /translation/zh/diagnostics/kube-scheduler-args.yaml: -------------------------------------------------------------------------------- 1 | kube-api-qps-title: "请求kube-apiserver使用的QPS" 2 | kube-api-qps-desc: "当前集群master节点的性能下,参数 --{{.Arg}} 当前值 {{.CurVal}} 过小" 3 | kube-api-qps-proposal: "当前集群master节点的性能下,推荐设置参数为 --{{.Arg}}={{.TargetVal}}" 4 | 5 | kube-api-burst-title: "和kube-apiserver通信的时候最大burst 值" 6 | kube-api-burst-desc: "当前集群master节点的性能下,参数 --{{.Arg}} 当前值 {{.CurVal}} 过小" 7 | kube-api-burst-proposal: "当前集群master节点的性能下,推荐设置参数为 --{{.Arg}}={{.TargetVal}}" 8 | 9 | good-desc: "当前集群master节点的性能下,参数 --{{.Arg}} 当前值 {{.CurVal}} 是合理的" -------------------------------------------------------------------------------- /translation/zh/diagnostics/master-capacity.yaml: -------------------------------------------------------------------------------- 1 | title: "Master 节点 {{.Resource}} 配置" 2 | desc: "Master({{.NodeName}}) {{.CurValue}} {{.Resource}} 不足以支持 {{.NodeTotal}} 节点的集群" 3 | proposal: "拥有 {{.NodeTotal}} 节点的集群至少需要 {{.TargetValue}} {{.Resource}}" 4 | good-desc: "Master({{.NodeName}}) {{.CurValue}} {{.Resource}} 足以支持 {{.NodeTotal}} 节点的集群" -------------------------------------------------------------------------------- /translation/zh/diagnostics/master-status.yaml: -------------------------------------------------------------------------------- 1 | master-status-title: "Master节点状态" 2 | master-status-desc: "Master节点 {{.Node}} {{.Resource}} 状态异常" 3 | master-status-proposal: "建议恢复Master节点 {{.Resource}} 状态,或提交工单" 4 | 5 | master-status-good-desc: "Master节点 {{.Node}} 状态正常" 6 | master-status-good-proposal: "" -------------------------------------------------------------------------------- /translation/zh/diagnostics/node-ha.yaml: -------------------------------------------------------------------------------- 1 | node-num-title: "集群节点数" 2 | node-num-good-desc: "集群节点数大于1,没有单点故障,当前集群节点数: {{.CurTotalNode}}" 3 | node-num-bad-desc: "集群节点只有1个,存在高风险单点故障" 4 | node-num-proposal: "扩容集群节点数,确保集群节点数大于1" 5 | 6 | node-zone-title: "Node跨可用区容灾" 7 | node-zone-good-desc: "Node均匀分布在各个可用区,总可用区数: {{.CurTotalZoneNum}}" 8 | node-zone-bad-desc: "Node未均匀分布在各个可用区,总可用区数: {{.CurTotalZoneNum}},当可用区{{.ZoneName}}出现故障时,其他可用区可能缺少{{.ResourceName}}资源" 9 | node-zone-proposal: "建议Node均匀分布在各个可用区,总可用区数大于1" 10 | -------------------------------------------------------------------------------- /translation/zh/diagnostics/node-iptables.yaml: -------------------------------------------------------------------------------- 1 | iptables-count-title: "IPTables 数量" 2 | iptables-count-desc: "节点 {{.Node}} iptables 当前数量为 {{.CurCount}}, 超出 {{.SuggestedCount}} 不是推荐设置" 3 | iptables-count-proposal: "使用无头服务或者使用 ipvs 模式替换 iptables" 4 | 5 | iptables-forward-policy-title: "IPTables 转发默认策略" 6 | iptables-forward-policy-desc: "节点 {{.Node}} iptables 转发默认策略 {{.CurPolicy}} 不是推荐设置" 7 | iptables-forward-policy-good-desc: "节点 {{.Node}} iptables 转发默认策略 {{.CurPolicy}} 是推荐设置" 8 | iptables-forward-policy-proposal: "将 iptables 转发默认策略设置 {{.Name}}={{.SuggestedPolicy}}" -------------------------------------------------------------------------------- /translation/zh/diagnostics/node-status.yaml: -------------------------------------------------------------------------------- 1 | node-status-title: "Worker节点状态" 2 | node-status-desc: "Worker节点 {{.Node}} {{.Status}} 状态异常" 3 | node-status-proposal: "建议恢复Worker节点 {{.Resource}} 状态,或提交工单" 4 | 5 | node-status-good-desc: "Worker节点 {{.Node}} 状态正常" 6 | node-status-good-proposal: "" -------------------------------------------------------------------------------- /translation/zh/diagnostics/node-sys.yaml: -------------------------------------------------------------------------------- 1 | kernel-para-title: "内核参数" 2 | kernel-para-desc: "节点 {{.Node}} 参数[ {{.Name}}={{.CurVal}} ] 不是推荐设置" 3 | kernel-para-good-desc: "节点 {{.Node}} 参数[ {{.Name}}={{.CurVal}} ] 是推荐设置" 4 | kernel-para-proposal: "推荐设置为 {{.Name}}={{.TargetVal}}" -------------------------------------------------------------------------------- /translation/zh/diagnostics/pdb.yaml: -------------------------------------------------------------------------------- 1 | title: "PDB检查" 2 | desc: "{{.Kind}} {{.Namespace}}:{{.Name}} 缺少PDB配置" 3 | proposal: "配置合理的PDB可以在驱逐发生的时候更好的保护您的应用" 4 | -------------------------------------------------------------------------------- /translation/zh/diagnostics/requests-limits.yaml: -------------------------------------------------------------------------------- 1 | title: "Requests Limits" 2 | desc: "{{.Kind}} {{.Namespace}}:{{.Name}} 有未设置的requests或者limits" 3 | proposal: "{{.Kind}} 应该始终设置正确的requests和limits" -------------------------------------------------------------------------------- /translation/zh/diagnostics/workload-status.yaml: -------------------------------------------------------------------------------- 1 | workload-status-title: "Workload状态" 2 | workload-status-desc: "{{.Workload}}[{{.Namespace}}] {{.Name}} 副本数 {{.Available}} 不符合预期数 {{.Replicas}} " 3 | workload-status-proposal: "尽量恢复该资源状态,或提交工单" 4 | 5 | workload-status-good-desc: "{{.Workload}}[{{.Namespace}}] {{.Name}} 副本数 符合预期数 {{.Replicas}} " 6 | workload-status-good-proposal: "" 7 | --------------------------------------------------------------------------------