├── .dockerignore ├── .github └── workflows │ ├── ci.yaml │ └── stale.yaml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── charts └── tikv-operator │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── hpa.yaml │ ├── rbac.yaml │ └── serviceaccount.yaml │ └── values.yaml ├── cmd ├── pd-discovery │ └── main.go └── tikv-controller-manager │ ├── app │ └── app.go │ └── main.go ├── docs ├── development.md └── getting-started.md ├── examples ├── basic-cn │ └── tikv-cluster.yaml └── basic │ └── tikv-cluster.yaml ├── go.mod ├── go.sum ├── hack ├── .notableofcontents ├── boilerplate │ ├── boilerplate.Dockerfile.txt │ ├── boilerplate.Makefile.txt │ ├── boilerplate.generatego.txt │ ├── boilerplate.go.txt │ ├── boilerplate.py │ ├── boilerplate.py.txt │ └── boilerplate.sh.txt ├── e2e-examples.sh ├── jenkins │ └── release.groovy ├── lib.sh ├── local-up-operator.sh ├── pin-k8s-deps.sh ├── publish-charts.sh ├── run-in-container.sh ├── update-all.sh ├── update-codegen.sh ├── update-toc.sh ├── upload-qiniu.py ├── verify-all.sh ├── verify-boilerplate.sh ├── verify-codegen.sh ├── verify-crd-groups.sh ├── verify-toc.sh └── version.sh ├── manifests └── crd.v1beta1.yaml ├── pkg ├── apis │ └── tikv │ │ └── v1alpha1 │ │ ├── component.go │ │ ├── defaulting │ │ └── tidbcluster.go │ │ ├── doc.go │ │ ├── helper.go │ │ ├── pd_config.go │ │ ├── register.go │ │ ├── tikv_config.go │ │ ├── tikv_config_test.go │ │ ├── types.go │ │ ├── validation │ │ ├── validation.go │ │ └── validation_test.go │ │ └── zz_generated.deepcopy.go ├── client │ ├── clientset │ │ └── versioned │ │ │ ├── clientset.go │ │ │ ├── doc.go │ │ │ ├── fake │ │ │ ├── clientset_generated.go │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ ├── scheme │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ └── typed │ │ │ └── tikv │ │ │ └── v1alpha1 │ │ │ ├── doc.go │ │ │ ├── fake │ │ │ ├── doc.go │ │ │ ├── fake_tikv_client.go │ │ │ └── fake_tikvcluster.go │ │ │ ├── generated_expansion.go │ │ │ ├── tikv_client.go │ │ │ └── tikvcluster.go │ ├── informers │ │ └── externalversions │ │ │ ├── factory.go │ │ │ ├── generic.go │ │ │ ├── internalinterfaces │ │ │ └── factory_interfaces.go │ │ │ └── tikv │ │ │ ├── interface.go │ │ │ └── v1alpha1 │ │ │ ├── interface.go │ │ │ └── tikvcluster.go │ └── listers │ │ └── tikv │ │ └── v1alpha1 │ │ ├── expansion_generated.go │ │ └── tikvcluster.go ├── controller │ ├── configmap_control.go │ ├── configmap_control_test.go │ ├── equality.go │ ├── general_pvc_control.go │ ├── generic_control.go │ ├── generic_control_test.go │ ├── pd_control.go │ ├── pod_control.go │ ├── pod_control_test.go │ ├── pv_control.go │ ├── pv_control_test.go │ ├── pvc_control.go │ ├── pvc_control_test.go │ ├── secret_control.go │ ├── service_control.go │ ├── service_control_test.go │ ├── stateful_set_control.go │ ├── stateful_set_control_test.go │ ├── tikvcluster │ │ ├── tikv_cluster_condition_updater.go │ │ ├── tikv_cluster_condition_updater_test.go │ │ ├── tikv_cluster_control.go │ │ ├── tikv_cluster_control_test.go │ │ └── tikv_cluster_controller.go │ ├── tikvcluster_control.go │ ├── tikvcluster_control_test.go │ ├── util.go │ └── util_test.go ├── discovery │ ├── discovery.go │ ├── discovery_test.go │ └── server │ │ └── mux.go ├── features │ └── features.go ├── httputil │ └── httputil.go ├── label │ └── label.go ├── manager │ ├── manager.go │ ├── member │ │ ├── failover.go │ │ ├── orphan_pods_cleaner.go │ │ ├── orphan_pods_cleaner_test.go │ │ ├── pd_discovery_manager.go │ │ ├── pd_discovery_manager_test.go │ │ ├── pd_failover.go │ │ ├── pd_failover_test.go │ │ ├── pd_member_manager.go │ │ ├── pd_member_manager_test.go │ │ ├── pd_scaler.go │ │ ├── pd_scaler_test.go │ │ ├── pd_upgrader.go │ │ ├── pd_upgrader_test.go │ │ ├── scaler.go │ │ ├── scaler_test.go │ │ ├── template.go │ │ ├── tikv_failover.go │ │ ├── tikv_failover_test.go │ │ ├── tikv_member_manager.go │ │ ├── tikv_member_manager_test.go │ │ ├── tikv_scaler.go │ │ ├── tikv_scaler_test.go │ │ ├── tikv_upgrader.go │ │ ├── tikv_upgrader_test.go │ │ ├── upgrader.go │ │ ├── utils.go │ │ └── utils_test.go │ └── meta │ │ ├── meta_manager.go │ │ └── meta_manager_test.go ├── pdapi │ ├── pd_config.go │ ├── pdapi.go │ ├── pdapi_test.go │ └── pdetcd.go ├── registry │ ├── registry.go │ ├── strategy.go │ └── tikvcluster_strategy.go ├── scheme │ └── scheme.go ├── util │ ├── config │ │ ├── config.go │ │ └── config_test.go │ ├── crypto │ │ └── certs.go │ ├── discovery │ │ ├── discovery.go │ │ └── discovery_test.go │ ├── tikvcluster │ │ └── tidbcluster.go │ ├── util.go │ └── utils_test.go └── verflag │ └── verflag.go ├── test └── examples │ ├── 001-basic.sh │ └── t.sh └── tools └── tools.go /.dockerignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /charts/ 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | - release-* 8 | 9 | jobs: 10 | 11 | pull: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | job: 17 | - verify 18 | - build 19 | - test 20 | - e2e-examples 21 | steps: 22 | - name: Set up Go 1.14 23 | uses: actions/setup-go@v1 24 | with: 25 | go-version: 1.14 26 | id: go 27 | 28 | - name: Check out code into the Go module directory 29 | uses: actions/checkout@v2 30 | with: 31 | ref: ${{ github.event.pull_request.head.sha }} 32 | 33 | - name: ${{ matrix.job }} 34 | run: | 35 | # workaround for https://github.com/actions/setup-go/issues/14 36 | export GOPATH=${GITHUB_WORKSPACE}/go 37 | export PATH=$PATH:$GOPATH/bin 38 | make $job 39 | env: 40 | job: ${{ matrix.job }} 41 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues/prs" 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v1.1.0 11 | with: 12 | repo-token: ${{ secrets.GITHUB_TOKEN }} 13 | stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 15 days' 14 | stale-pr-message: 'This pr is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 15 days' 15 | days-before-stale: 60 16 | days-before-close: 15 17 | stale-issue-label: 'lifecycle/stale' 18 | stale-pr-label: 'lifecycle/stale' 19 | exempt-issue-label: 'lifecycle/frozen' 20 | exempt-pr-label: 'lifecycle/frozen' 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /output/ 2 | /vendor/ 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 PingCAP, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | FROM alpine:3.12 15 | 16 | RUN apk add tzdata --no-cache 17 | 18 | ADD output/bin/linux/amd64/cmd/pd-discovery /usr/local/bin/pd-discovery 19 | ADD output/bin/linux/amd64/cmd/tikv-controller-manager /usr/local/bin/tikv-controller-manager 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2020 PingCAP, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | GO := go 15 | 16 | ARCH ?= $(shell ${GO} env GOARCH) 17 | OS ?= $(shell ${GO} env GOOS) 18 | IMAGE_REPO ?= localhost:5000/tikv 19 | IMAGE_TAG ?= latest 20 | 21 | ALL_TARGETS := cmd/tikv-controller-manager cmd/pd-discovery 22 | GIT_VERSION = $(shell ./hack/version.sh | awk -F': ' '/^GIT_VERSION:/ {print $$2}') 23 | 24 | ifneq ($(VERSION),) 25 | LDFLAGS += -X k8s.io/component-base/version.gitVersion=${VERSION} 26 | else 27 | LDFLAGS += -X k8s.io/component-base/version.gitVersion=${GIT_VERSION} 28 | endif 29 | 30 | all: build 31 | .PHONY: all 32 | 33 | verify: 34 | ./hack/verify-all.sh 35 | .PHONY: verify 36 | 37 | build: $(ALL_TARGETS) 38 | .PHONY: all 39 | 40 | $(ALL_TARGETS): GOOS = $(OS) 41 | $(ALL_TARGETS): GOARCH = $(ARCH) 42 | $(ALL_TARGETS): 43 | CGO_ENABLED=0 $(GO) build -ldflags "${LDFLAGS}" -o output/bin/$(GOOS)/$(GOARCH)/$@ ./$@ 44 | .PHONY: $(ALL_TARGETS) 45 | 46 | test: 47 | ${GO} test ./cmd/... ./pkg/... 48 | .PHONY: test 49 | 50 | # OS/ARCH for binary in image is hardcoded to linux/amd64 51 | image: GOOS = linux 52 | image: GOARCH = amd64 53 | image: build 54 | docker build -t "${IMAGE_REPO}/tikv-operator:${IMAGE_TAG}" . 55 | .PHONY: image 56 | 57 | e2e-examples: 58 | hack/e2e-examples.sh 59 | .PHONY: e2e-examples 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TiKV Operator 2 | 3 | TiKV Operator is no longer maintained. Please use [TiDB Operator](https://github.com/pingcap/tidb-operator) instead. 4 | 5 | ## License 6 | 7 | TiKV Operator is under the Apache 2.0 license. See the [LICENSE](./LICENSE) file for 8 | details. 9 | -------------------------------------------------------------------------------- /charts/tikv-operator/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/tikv-operator/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: tikv-operator 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | appVersion: 0.1.0 24 | -------------------------------------------------------------------------------- /charts/tikv-operator/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "tikv-operator.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 7 | {{- end }} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "tikv-operator.fullname" -}} 15 | {{- if .Values.fullnameOverride }} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 17 | {{- else }} 18 | {{- $name := default .Chart.Name .Values.nameOverride }} 19 | {{- if contains $name .Release.Name }} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 21 | {{- else }} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 23 | {{- end }} 24 | {{- end }} 25 | {{- end }} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "tikv-operator.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 32 | {{- end }} 33 | 34 | {{/* 35 | Common labels 36 | */}} 37 | {{- define "tikv-operator.labels" -}} 38 | helm.sh/chart: {{ include "tikv-operator.chart" . }} 39 | {{ include "tikv-operator.selectorLabels" . }} 40 | {{- if .Chart.AppVersion }} 41 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 42 | {{- end }} 43 | app.kubernetes.io/managed-by: {{ .Release.Service }} 44 | {{- end }} 45 | 46 | {{/* 47 | Selector labels 48 | */}} 49 | {{- define "tikv-operator.selectorLabels" -}} 50 | app.kubernetes.io/name: {{ include "tikv-operator.name" . }} 51 | app.kubernetes.io/instance: {{ .Release.Name }} 52 | {{- end }} 53 | 54 | {{/* 55 | Create the name of the service account to use 56 | */}} 57 | {{- define "tikv-operator.serviceAccountName" -}} 58 | {{- if .Values.serviceAccount.create }} 59 | {{- default (include "tikv-operator.fullname" .) .Values.serviceAccount.name }} 60 | {{- else }} 61 | {{- default "default" .Values.serviceAccount.name }} 62 | {{- end }} 63 | {{- end }} 64 | -------------------------------------------------------------------------------- /charts/tikv-operator/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "tikv-operator.fullname" . }} 5 | labels: 6 | {{- include "tikv-operator.labels" . | nindent 4 }} 7 | spec: 8 | {{- if not .Values.autoscaling.enabled }} 9 | replicas: {{ .Values.replicaCount }} 10 | {{- end }} 11 | selector: 12 | matchLabels: 13 | {{- include "tikv-operator.selectorLabels" . | nindent 6 }} 14 | template: 15 | metadata: 16 | {{- with .Values.podAnnotations }} 17 | annotations: 18 | {{- toYaml . | nindent 8 }} 19 | {{- end }} 20 | labels: 21 | {{- include "tikv-operator.selectorLabels" . | nindent 8 }} 22 | spec: 23 | {{- with .Values.imagePullSecrets }} 24 | imagePullSecrets: 25 | {{- toYaml . | nindent 8 }} 26 | {{- end }} 27 | serviceAccountName: {{ include "tikv-operator.serviceAccountName" . }} 28 | securityContext: 29 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 30 | containers: 31 | - name: {{ .Chart.Name }} 32 | securityContext: 33 | {{- toYaml .Values.securityContext | nindent 12 }} 34 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 35 | imagePullPolicy: {{ .Values.image.pullPolicy }} 36 | command: 37 | - /usr/local/bin/tikv-controller-manager 38 | {{- if .Values.image.args }} 39 | args: 40 | - "--pd-discovery-image={{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 41 | {{- toYaml .Values.image.args | nindent 12 }} 42 | {{- end }} 43 | ports: 44 | - name: http 45 | containerPort: 6060 46 | protocol: TCP 47 | livenessProbe: 48 | httpGet: 49 | path: /healthz 50 | port: http 51 | env: 52 | - name: NAMESPACE 53 | valueFrom: 54 | fieldRef: 55 | fieldPath: metadata.namespace 56 | resources: 57 | {{- toYaml .Values.resources | nindent 12 }} 58 | {{- with .Values.nodeSelector }} 59 | nodeSelector: 60 | {{- toYaml . | nindent 8 }} 61 | {{- end }} 62 | {{- with .Values.affinity }} 63 | affinity: 64 | {{- toYaml . | nindent 8 }} 65 | {{- end }} 66 | {{- with .Values.tolerations }} 67 | tolerations: 68 | {{- toYaml . | nindent 8 }} 69 | {{- end }} 70 | -------------------------------------------------------------------------------- /charts/tikv-operator/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "tikv-operator.fullname" . }} 6 | labels: 7 | {{- include "tikv-operator.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "tikv-operator.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/tikv-operator/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: tikv-operator-controller-manager 6 | rules: 7 | - apiGroups: 8 | - tikv.org 9 | resources: 10 | - '*' 11 | verbs: 12 | - '*' 13 | - apiGroups: 14 | - 'apps' 15 | resources: 16 | - 'statefulsets' 17 | - 'deployments' 18 | verbs: 19 | - '*' 20 | - apiGroups: 21 | - '' 22 | resources: 23 | - 'events' 24 | - 'pods' 25 | - 'persistentvolumeclaims' 26 | - 'persistentvolumes' 27 | - 'services' 28 | - 'endpoints' 29 | - 'nodes' 30 | - 'configmaps' 31 | - 'serviceaccounts' 32 | verbs: 33 | - '*' 34 | - apiGroups: 35 | - 'rbac.authorization.k8s.io' 36 | resources: 37 | - 'roles' 38 | - 'rolebindings' 39 | verbs: 40 | - '*' 41 | --- 42 | apiVersion: rbac.authorization.k8s.io/v1 43 | kind: ClusterRoleBinding 44 | metadata: 45 | name: tikv-operator-controller-manager 46 | roleRef: 47 | apiGroup: rbac.authorization.k8s.io 48 | kind: ClusterRole 49 | name: tikv-operator-controller-manager 50 | subjects: 51 | - kind: ServiceAccount 52 | name: {{ include "tikv-operator.serviceAccountName" . }} 53 | namespace: {{ .Release.Namespace }} 54 | --- 55 | apiVersion: rbac.authorization.k8s.io/v1 56 | kind: Role 57 | metadata: 58 | name: tikv-operator-controller-manager-leaderelection 59 | rules: 60 | - apiGroups: 61 | - '' 62 | resources: 63 | - 'endpoints' 64 | verbs: 65 | - '*' 66 | --- 67 | apiVersion: rbac.authorization.k8s.io/v1 68 | kind: RoleBinding 69 | metadata: 70 | name: tikv-operator-controller-manager-leaderelection 71 | roleRef: 72 | apiGroup: rbac.authorization.k8s.io 73 | kind: Role 74 | name: tikv-operator-controller-manager-leaderelection 75 | subjects: 76 | - kind: ServiceAccount 77 | name: {{ include "tikv-operator.serviceAccountName" . }} 78 | namespace: {{ .Release.Namespace }} 79 | {{- end }} 80 | -------------------------------------------------------------------------------- /charts/tikv-operator/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "tikv-operator.serviceAccountName" . }} 6 | labels: 7 | {{- include "tikv-operator.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/tikv-operator/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for tikv-operator. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: pingcap/tikv-operator 9 | pullPolicy: IfNotPresent 10 | # Overrides the image tag whose default is the chart appVersion. 11 | # tag: latest 12 | args: 13 | - -v=2 14 | 15 | imagePullSecrets: [] 16 | nameOverride: "" 17 | fullnameOverride: "" 18 | 19 | serviceAccount: 20 | # Specifies whether a service account should be created 21 | create: true 22 | # Annotations to add to the service account 23 | annotations: {} 24 | # The name of the service account to use. 25 | # If not set and create is true, a name is generated using the fullname template 26 | name: "" 27 | 28 | podAnnotations: {} 29 | 30 | podSecurityContext: {} 31 | # fsGroup: 2000 32 | 33 | securityContext: {} 34 | # capabilities: 35 | # drop: 36 | # - ALL 37 | # readOnlyRootFilesystem: true 38 | # runAsNonRoot: true 39 | # runAsUser: 1000 40 | 41 | resources: {} 42 | # We usually recommend not to specify default resources and to leave this as a conscious 43 | # choice for the user. This also increases chances charts run on environments with little 44 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 45 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 46 | # limits: 47 | # cpu: 100m 48 | # memory: 128Mi 49 | # requests: 50 | # cpu: 100m 51 | # memory: 128Mi 52 | 53 | autoscaling: 54 | enabled: false 55 | minReplicas: 1 56 | maxReplicas: 100 57 | targetCPUUtilizationPercentage: 80 58 | # targetMemoryUtilizationPercentage: 80 59 | 60 | nodeSelector: {} 61 | 62 | tolerations: [] 63 | 64 | affinity: {} 65 | -------------------------------------------------------------------------------- /cmd/pd-discovery/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package main 15 | 16 | import ( 17 | "flag" 18 | "net/http" 19 | _ "net/http/pprof" 20 | "time" 21 | 22 | "github.com/tikv/tikv-operator/pkg/client/clientset/versioned" 23 | "github.com/tikv/tikv-operator/pkg/discovery/server" 24 | "github.com/tikv/tikv-operator/pkg/verflag" 25 | "k8s.io/apimachinery/pkg/util/wait" 26 | "k8s.io/client-go/kubernetes" 27 | "k8s.io/client-go/rest" 28 | "k8s.io/component-base/logs" 29 | "k8s.io/component-base/version" 30 | "k8s.io/klog" 31 | ) 32 | 33 | var ( 34 | printVersion bool 35 | port int 36 | ) 37 | 38 | func init() { 39 | flag.BoolVar(&printVersion, "version", false, "Show version and quit") 40 | flag.IntVar(&port, "port", 10261, "The port that the tidb discovery's http service runs on (default 10261)") 41 | flag.Parse() 42 | } 43 | 44 | func main() { 45 | verflag.PrintAndExitIfRequested() 46 | klog.Infof("PD Discovery: %s", version.Get()) 47 | 48 | logs.InitLogs() 49 | defer logs.FlushLogs() 50 | 51 | flag.CommandLine.VisitAll(func(flag *flag.Flag) { 52 | klog.V(1).Infof("FLAG: --%s=%q", flag.Name, flag.Value) 53 | }) 54 | 55 | cfg, err := rest.InClusterConfig() 56 | if err != nil { 57 | klog.Fatalf("failed to get config: %v", err) 58 | } 59 | cli, err := versioned.NewForConfig(cfg) 60 | if err != nil { 61 | klog.Fatalf("failed to create Clientset: %v", err) 62 | } 63 | kubeCli, err := kubernetes.NewForConfig(cfg) 64 | if err != nil { 65 | klog.Fatalf("failed to get kubernetes Clientset: %v", err) 66 | } 67 | 68 | go wait.Forever(func() { 69 | server.StartServer(cli, kubeCli, port) 70 | }, 5*time.Second) 71 | klog.Fatal(http.ListenAndServe(":6060", nil)) 72 | } 73 | -------------------------------------------------------------------------------- /cmd/tikv-controller-manager/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package main 15 | 16 | import ( 17 | "math/rand" 18 | _ "net/http/pprof" 19 | "os" 20 | "time" 21 | 22 | "github.com/tikv/tikv-operator/cmd/tikv-controller-manager/app" 23 | "k8s.io/component-base/logs" 24 | ) 25 | 26 | func main() { 27 | rand.Seed(time.Now().UnixNano()) 28 | 29 | command := app.NewControllerManagerCommand() 30 | 31 | logs.InitLogs() 32 | defer logs.FlushLogs() 33 | 34 | if err := command.Execute(); err != nil { 35 | os.Exit(1) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docs/development.md: -------------------------------------------------------------------------------- 1 | # Development Guide 2 | 3 | This document walks you through the process for TiKV Operator development. 4 | 5 | 6 | - [Prerequisites](#prerequisites) 7 | - [Verify code changes](#verify-code-changes) 8 | - [Run unit tests](#run-unit-tests) 9 | - [Run tikv-operator locally](#run-tikv-operator-locally) 10 | 11 | 12 | ## Prerequisites 13 | 14 | * [golang](https://golang.org): version >= 1.13 15 | * [Docker](https://docs.docker.com/get-started/): the latest version is recommended 16 | 17 | ## Verify code changes 18 | 19 | Run the following commands to verify your code changes: 20 | 21 | ```shell 22 | $ make verify 23 | ``` 24 | 25 | This will show errors if your code changes fail to pass checks (e.g. fmt, lint). If there is any error, fix them before submitting the PR. 26 | 27 | ## Run unit tests 28 | 29 | Before running your code in a real Kubernetes cluster, make sure it passes all unit tests: 30 | 31 | ```shell 32 | $ make test 33 | ``` 34 | 35 | ## Run tikv-operator locally 36 | 37 | The following steps use [kind](https://kind.sigs.k8s.io) to start a Kubernetes cluster locally. 38 | 39 | 1. Install kind and kubectl: 40 | 41 | You can refer to [the official installation steps](https://kind.sigs.k8s.io/docs/user/quick-start/#installation) to install them on your machine, or run the following command to install them into the local binary directory `output/bin`: 42 | 43 | ```shell 44 | $ hack/local-up-operator.sh -i 45 | $ export PATH=$(pwd)/output/bin:$PATH 46 | ``` 47 | 48 | 2. Make sure they are installed correctly: 49 | 50 | ``` 51 | $ kind --version 52 | ... 53 | $ kubectl version --client 54 | ... 55 | ``` 56 | 57 | 3. Create a Kubernetes cluster: 58 | 59 | ```shell 60 | $ kind create cluster 61 | ``` 62 | 63 | 4. Build and run tidb-operator: 64 | 65 | ```shell 66 | $ ./hack/local-up-operator.sh 67 | ``` 68 | 69 | 5. Start a basic TiKV cluster: 70 | 71 | ```shell 72 | $ kubectl apply -f examples/basic/tikv-cluster.yaml 73 | ``` 74 | -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | 3 | This document explains how to create a simple Kubernetes cluster and use it to do a basic test deployment of TiKV Cluster using TiKV Operator. 4 | 5 | 6 | - [Step 1: Create a Kubernetes cluster](#step-1-create-a-kubernetes-cluster) 7 | - [Step 2: Deploy TiKV Operator](#step-2-deploy-tikv-operator) 8 | - [Step 3: Deploy TiKV Cluster](#step-3-deploy-tikv-cluster) 9 | - [Step 4: Access the PD endpoint](#step-4-access-the-pd-endpoint) 10 | 11 | 12 | ## Step 1: Create a Kubernetes cluster 13 | 14 | If you have already created a Kubernetes cluster, skip to [Step 2: Deploy TiKV Operator](#step-2-deploy-tikv-operator). 15 | 16 | This section covers 2 different ways to create a simple Kubernetes cluster that 17 | can be used to test TiKV Cluster locally. Choose whichever best matches your 18 | environment or experience level. 19 | 20 | - Using [kind](https://kind.sigs.k8s.io/docs/user/quick-start/) (Kubernetes in Docker) 21 | - Using [minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) (Kubernetes running locally in a VM) 22 | 23 | You can refer to their official documents to prepare a Kubernetes cluster. 24 | 25 | The following shows a simple way to create a Kubernetes cluster using kind. Make sure Docker is up and running before proceeding. 26 | 27 | On macOS / Linux: 28 | 29 | ```shell 30 | curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.8.1/kind-$(uname)-amd64 31 | chmod +x ./kind 32 | ./kind create cluster 33 | ``` 34 | 35 | On Windows: 36 | 37 | ```shell 38 | curl.exe -Lo kind-windows-amd64.exe https://kind.sigs.k8s.io/dl/v0.8.1/kind-windows-amd64 39 | .\kind-windows-amd64.exe create cluster 40 | ``` 41 | 42 | ## Step 2: Deploy TiKV Operator 43 | 44 | Before deployment, make sure the following requirements are satisfied: 45 | 46 | - A running Kubernetes Cluster that kubectl can connect to 47 | - Helm 3 48 | 49 | 1. Install helm 50 | 51 | ```shell 52 | curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash 53 | ``` 54 | 55 | Refer to [Helm Documentation](https://helm.sh/docs/intro/install/) for more installation alternatives. 56 | 57 | 2. Install CRD 58 | 59 | ```shell 60 | kubectl apply -f https://raw.githubusercontent.com/tikv/tikv-operator/master/manifests/crd.v1beta1.yaml 61 | ``` 62 | 63 | 3. Install tikv-operator 64 | 65 | 1. Add the PingCAP Repository: 66 | 67 | ```shell 68 | helm repo add pingcap https://charts.pingcap.org/ 69 | ``` 70 | 71 | 2. Create a namespace for TiKV Operator: 72 | 73 | ```shell 74 | kubectl create ns tikv-operator 75 | helm install --namespace tikv-operator tikv-operator pingcap/tikv-operator --version v0.1.0 76 | ``` 77 | 78 | 3. Install TiKV Operator: 79 | 80 | ```shell 81 | helm install --namespace tikv-operator tikv-operator pingcap/tikv-operator --version v0.1.0 82 | ``` 83 | 84 | 4. Confirm that the TiKV Operator components are running: 85 | 86 | ```shell 87 | kubectl --namespace tikv-operator get pods 88 | ``` 89 | 90 | ## Step 3: Deploy TiKV Cluster 91 | 92 | 1. Deploy the TiKV Cluster: 93 | 94 | ```shell 95 | curl -LO https://raw.githubusercontent.com/tikv/tikv-operator/master/examples/basic/tikv-cluster.yaml 96 | kubectl apply -f tikv-cluster.yaml 97 | ``` 98 | 99 | Expected output: 100 | 101 | ``` 102 | tikvcluster.tikv.org/basic created 103 | ``` 104 | 105 | 2. Wait for it to be ready: 106 | 107 | ```shell 108 | kubectl wait --for=condition=Ready --timeout 10m tikvcluster/basic 109 | ``` 110 | 111 | It may takes several minutes as it needs to pull images from Docker Hub. 112 | 113 | 3. Check the progress with the following command: 114 | 115 | ```shell 116 | kubect get pods -o wide 117 | ``` 118 | 119 | If the network connection to the Docker Hub is slow, you can try this example which uses images hosted in Alibaba Cloud: 120 | 121 | ```shell 122 | curl -LO https://raw.githubusercontent.com/tikv/tikv-operator/master/examples/basic-cn/tikv-cluster.yaml 123 | kubectl apply -f tikv-cluster.yaml 124 | ``` 125 | 126 | ## Step 4: Access the PD endpoint 127 | 128 | Open a new terminal tab and run this command: 129 | 130 | ```shell 131 | kubectl port-forward svc/basic-pd 2379:2379 132 | ``` 133 | 134 | This will forward local port `2379` to PD service `basic-pd`. 135 | 136 | Now, you can access the PD endpoint with `pd-ctl` or any other PD client: 137 | 138 | ```shell 139 | $ pd-ctl cluster 140 | { 141 | "id": 6841476120821315702, 142 | "max_peer_count": 3 143 | } 144 | ``` 145 | -------------------------------------------------------------------------------- /examples/basic-cn/tikv-cluster.yaml: -------------------------------------------------------------------------------- 1 | # IT IS NOT SUITABLE FOR PRODUCTION USE. 2 | # This YAML describes a basic TiDB cluster with minimum resource requirements, 3 | # which should be able to run in any Kubernetes cluster with storage support. 4 | apiVersion: tikv.org/v1alpha1 5 | kind: TikvCluster 6 | metadata: 7 | name: basic 8 | spec: 9 | version: v4.0.0 10 | pd: 11 | baseImage: registry.cn-beijing.aliyuncs.com/tidb/pd 12 | replicas: 1 13 | # if storageClassName is not set, the default Storage Class of the Kubernetes cluster will be used 14 | # storageClassName: local-storage 15 | requests: 16 | storage: "1Gi" 17 | config: {} 18 | tikv: 19 | baseImage: registry.cn-beijing.aliyuncs.com/tidb/tikv 20 | replicas: 1 21 | # if storageClassName is not set, the default Storage Class of the Kubernetes cluster will be used 22 | # storageClassName: local-storage 23 | requests: 24 | storage: "1Gi" 25 | config: {} 26 | -------------------------------------------------------------------------------- /examples/basic/tikv-cluster.yaml: -------------------------------------------------------------------------------- 1 | # IT IS NOT SUITABLE FOR PRODUCTION USE. 2 | # This YAML describes a basic TiDB cluster with minimum resource requirements, 3 | # which should be able to run in any Kubernetes cluster with storage support. 4 | apiVersion: tikv.org/v1alpha1 5 | kind: TikvCluster 6 | metadata: 7 | name: basic 8 | spec: 9 | version: v4.0.0 10 | pd: 11 | baseImage: pingcap/pd 12 | replicas: 1 13 | # if storageClassName is not set, the default Storage Class of the Kubernetes cluster will be used 14 | # storageClassName: local-storage 15 | requests: 16 | storage: "1Gi" 17 | config: {} 18 | tikv: 19 | baseImage: pingcap/tikv 20 | replicas: 1 21 | # if storageClassName is not set, the default Storage Class of the Kubernetes cluster will be used 22 | # storageClassName: local-storage 23 | requests: 24 | storage: "1Gi" 25 | config: {} 26 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tikv/tikv-operator 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/BurntSushi/toml v0.3.1 7 | github.com/Masterminds/semver v1.4.2 8 | github.com/coreos/etcd v3.3.15+incompatible 9 | github.com/dustin/go-humanize v1.0.0 10 | github.com/emicklei/go-restful v2.9.5+incompatible 11 | github.com/go-openapi/spec v0.19.3 // indirect 12 | github.com/gogo/protobuf v1.3.1 // indirect 13 | github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff // indirect 14 | github.com/google/go-cmp v0.3.1 15 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 // indirect 16 | github.com/grpc-ecosystem/grpc-gateway v1.13.0 // indirect 17 | github.com/imdario/mergo v0.3.7 // indirect 18 | github.com/json-iterator/go v1.1.9 // indirect 19 | github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb 20 | github.com/onsi/ginkgo v1.10.3 // indirect 21 | github.com/onsi/gomega v1.5.0 22 | github.com/pingcap/advanced-statefulset/client v1.16.0 23 | github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8 // indirect 24 | github.com/pingcap/errors v0.11.0 25 | github.com/pingcap/kvproto v0.0.0-20191217072959-393e6c0fd4b7 26 | github.com/pingcap/pd v2.1.17+incompatible 27 | github.com/pkg/errors v0.9.1 // indirect 28 | github.com/sirupsen/logrus v1.5.0 // indirect 29 | github.com/soheilhy/cmux v0.1.4 // indirect 30 | github.com/spf13/cobra v0.0.5 31 | github.com/spf13/pflag v1.0.3 32 | github.com/stretchr/testify v1.5.1 // indirect 33 | github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect 34 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect 35 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect 36 | golang.org/x/sys v0.0.0-20190620070143-6f217b454f45 // indirect 37 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c // indirect 38 | google.golang.org/appengine v1.6.1 // indirect 39 | gopkg.in/yaml.v2 v2.2.4 // indirect 40 | k8s.io/api v0.0.0 41 | k8s.io/apimachinery v0.0.0 42 | k8s.io/apiserver v0.0.0 43 | k8s.io/client-go v0.0.0 44 | k8s.io/code-generator v0.0.0 45 | k8s.io/component-base v0.0.0 46 | k8s.io/gengo v0.0.0-20190907103519-ebc107f98eab // indirect 47 | k8s.io/klog v1.0.0 48 | k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d // indirect 49 | k8s.io/kubernetes v1.16.0 50 | k8s.io/utils v0.0.0-20190801114015-581e00157fb1 51 | sigs.k8s.io/controller-runtime v0.4.0 52 | ) 53 | 54 | replace github.com/renstrom/dedent => github.com/lithammer/dedent v1.1.0 55 | 56 | replace k8s.io/api => k8s.io/api v0.0.0-20190918155943-95b840bb6a1f 57 | 58 | replace k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783 59 | 60 | replace k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655 61 | 62 | replace k8s.io/apiserver => k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad 63 | 64 | replace k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20190918162238-f783a3654da8 65 | 66 | replace k8s.io/client-go => k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90 67 | 68 | replace k8s.io/code-generator => k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269 69 | 70 | replace k8s.io/csi-api => k8s.io/csi-api v0.0.0-20190118125032-c4557c74373f 71 | 72 | replace k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.0.0-20190918161219-8c8f079fddc3 73 | 74 | replace k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.0.0-20190918162944-7a93a0ddadd8 75 | 76 | replace k8s.io/kube-proxy => k8s.io/kube-proxy v0.0.0-20190918162534-de037b596c1e 77 | 78 | replace k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.0.0-20190918162820-3b5c1246eb18 79 | 80 | replace k8s.io/kubelet => k8s.io/kubelet v0.0.0-20190918162654-250a1838aa2c 81 | 82 | replace k8s.io/metrics => k8s.io/metrics v0.0.0-20190918162108-227c654b2546 83 | 84 | replace k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.0.0-20190918161442-d4c9c65c82af 85 | 86 | replace k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.0.0-20190918162410-e45c26d066f2 87 | 88 | replace k8s.io/sample-controller => k8s.io/sample-controller v0.0.0-20190918161628-92eb3cb7496c 89 | 90 | replace k8s.io/cloud-provider => k8s.io/cloud-provider v0.0.0-20190918163234-a9c1f33e9fb9 91 | 92 | replace k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.0.0-20190918163108-da9fdfce26bb 93 | 94 | replace k8s.io/component-base => k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090 95 | 96 | replace k8s.io/cri-api => k8s.io/cri-api v0.0.0-20190828162817-608eb1dad4ac 97 | 98 | replace k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.0.0-20190918163402-db86a8c7bb21 99 | 100 | replace k8s.io/kubectl => k8s.io/kubectl v0.0.0-20190918164019-21692a0861df 101 | 102 | replace k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.0.0-20190918163543-cfa506e53441 103 | 104 | replace k8s.io/node-api => k8s.io/node-api v0.0.0-20190918163711-2299658ad911 105 | 106 | replace github.com/uber-go/atomic => go.uber.org/atomic v1.5.0 107 | 108 | replace github.com/Azure/go-autorest => github.com/Azure/go-autorest v12.2.0+incompatible 109 | -------------------------------------------------------------------------------- /hack/.notableofcontents: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikv/tikv-operator/9f4a6c9b1b7f373270a1fdd2460af8cfeaff22d2/hack/.notableofcontents -------------------------------------------------------------------------------- /hack/boilerplate/boilerplate.Dockerfile.txt: -------------------------------------------------------------------------------- 1 | # Copyright YEAR TiKV Project Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | -------------------------------------------------------------------------------- /hack/boilerplate/boilerplate.Makefile.txt: -------------------------------------------------------------------------------- 1 | # Copyright YEAR TiKV Project Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | -------------------------------------------------------------------------------- /hack/boilerplate/boilerplate.generatego.txt: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | -------------------------------------------------------------------------------- /hack/boilerplate/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | // Copyright YEAR TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | -------------------------------------------------------------------------------- /hack/boilerplate/boilerplate.py.txt: -------------------------------------------------------------------------------- 1 | # Copyright YEAR TiKV Project Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | -------------------------------------------------------------------------------- /hack/boilerplate/boilerplate.sh.txt: -------------------------------------------------------------------------------- 1 | # Copyright YEAR TiKV Project Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | -------------------------------------------------------------------------------- /hack/e2e-examples.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # 17 | # E2E entrypoint script for examples. 18 | # 19 | 20 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) 21 | cd $ROOT 22 | 23 | source "${ROOT}/hack/lib.sh" 24 | 25 | hack::ensure_kind 26 | 27 | echo "info: create a Kubernetes cluster" 28 | $KIND_BIN create cluster 29 | 30 | echo "info: start tidb-operator" 31 | hack/local-up-operator.sh 32 | 33 | echo "info: testing examples" 34 | export PATH=$PATH:$OUTPUT_BIN 35 | hack::ensure_kubectl 36 | 37 | cnt=0 38 | for t in $(find test/examples/ -regextype sed -regex '.*/[0-9]\{3\}-.*\.sh'); do 39 | echo "info: testing $t" 40 | $t 41 | if [ $? -eq 0 ]; then 42 | echo "info: test $t passed" 43 | else 44 | echo "error: test $t failed" 45 | ((cnt++)) 46 | fi 47 | done 48 | if [ $cnt -gt 0 ]; then 49 | echo "fatal: $cnt tests failed" 50 | exit 1 51 | fi 52 | -------------------------------------------------------------------------------- /hack/jenkins/release.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Jenkins pipeline script for release job. 3 | // 4 | // It accepts two parameters: 5 | // 6 | // - BUILD_REF (string): git ref to build 7 | // - RELEASE_TAG (string): image tag to build and chart version to publish 8 | // 9 | // This script requires the following credential IDs to be set in environments: 10 | // 11 | // - QN_ACCESS_KEY_ID: the ID of credential that stores the access key of Qiniu 12 | // - QN_SECRET_KEY_ID: the ID of credential that stores the secret key of Qiniu 13 | // 14 | 15 | def REPO_GIT_URL = "https://github.com/tikv/tikv-operator.git" 16 | 17 | properties([ 18 | parameters([ 19 | string(name: "BUILD_REF", defaultValue: "master", description: "git ref to build"), 20 | string(name: "RELEASE_TAG", defaultValue: "latest", description: "tag used in image and published chart, etc."), 21 | ]) 22 | ]) 23 | 24 | try { 25 | node('build_go1130_memvolume') { 26 | container("golang") { 27 | stage('Checkout') { 28 | checkout scm: [ 29 | $class: 'GitSCM', 30 | branches: [[name: "${BUILD_REF}"]], 31 | userRemoteConfigs: [[ 32 | refspec: '+refs/heads/*:refs/remotes/origin/* +refs/pull/*:refs/remotes/origin/pull/*', 33 | url: "${REPO_GIT_URL}", 34 | ]] 35 | ] 36 | } 37 | 38 | stage('build') { 39 | sh """ 40 | make 41 | """ 42 | } 43 | 44 | stash excludes: "vendor/**,test/**", name: "tikv-operator" 45 | } 46 | } 47 | 48 | node('delivery') { 49 | container("delivery") { 50 | deleteDir() 51 | unstash 'tikv-operator' 52 | 53 | stage('Build and push docker image') { 54 | withDockerServer([uri: "tcp://localhost:2375"]) { 55 | def image = docker.build("pingcap/tikv-operator:${RELEASE_TAG}") 56 | image.push() 57 | withDockerRegistry([url: "https://registry.cn-beijing.aliyuncs.com", credentialsId: "ACR_TIDB_ACCOUNT"]) { 58 | sh """ 59 | docker tag pingcap/tikv-operator:${RELEASE_TAG} registry.cn-beijing.aliyuncs.com/tidb/tikv-operator:${RELEASE_TAG} 60 | docker push registry.cn-beijing.aliyuncs.com/tidb/tikv-operator:${RELEASE_TAG} 61 | """ 62 | } 63 | } 64 | } 65 | 66 | stage('Publish chart') { 67 | withCredentials([string(credentialsId: "${env.QN_ACCESS_KEY_ID}", variable: "QINIU_ACCESS_KEY"), string(credentialsId: "${env.QN_SECRET_KEY_ID}", variable: 'QINIU_SECRET_KEY')]) { 68 | sh """#!/bin/bash 69 | hack/publish-charts.sh 70 | """ 71 | } 72 | } 73 | } 74 | } 75 | 76 | currentBuild.result = "SUCCESS" 77 | } catch (err) { 78 | println("fatal: " + err) 79 | currentBuild.result = 'FAILURE' 80 | } 81 | 82 | // vim: et 83 | -------------------------------------------------------------------------------- /hack/pin-k8s-deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Pin all k8s.io dependencies to a specified version. 17 | # 18 | # Kubernetes staging repos are hosted in code repo (k8s.io/kubernetes). If we 19 | # use pkgs from k8s.io/kubernetes, `replace` directive must be used to override 20 | # version constraints derived from k8s.io/kubernetes. 21 | 22 | VERSION=${VERSION:-${1:-}} 23 | 24 | if [ -z "$VERSION" ]; then 25 | echo "VERSION is required, e.g. VERSION=x.y.z $0 or $0 x.y.z" 26 | exit 1 27 | fi 28 | 29 | echo "VERSION: $VERSION" 30 | 31 | # Explicitly opt into go modules, even though we're inside a GOPATH directory 32 | export GO111MODULE=on 33 | 34 | go mod edit -require k8s.io/kubernetes@v$VERSION 35 | 36 | # 37 | # Return true if "$v2" is greater or equal to "$v1". 38 | # 39 | # Usage: version_ge "$v1" "$v2" 40 | # 41 | function version_ge() { 42 | local a="$1" 43 | local b="$2" 44 | [[ "${a}" == $(echo -e "${a}\n${b}" | sort -s -t. -k 1,1n -k 2,2n -k3,3n | head -n1) ]] 45 | } 46 | 47 | if version_ge "1.15.0" $VERSION; then 48 | STAGING_REPOS=($(curl -sS https://raw.githubusercontent.com/kubernetes/kubernetes/v${VERSION}/go.mod | sed -n 's|.*k8s.io/\(.*\) => ./staging/src/k8s.io/.*|k8s.io/\1|p')) 49 | else 50 | STAGING_REPOS=($(curl -sS https://raw.githubusercontent.com/kubernetes/kubernetes/v${VERSION}/staging/README.md | sed -n 's|.*\[`\(k8s.io/[^`]*\)`\].*|\1|p')) 51 | fi 52 | 53 | edit_args=( 54 | -fmt 55 | ) 56 | for repo in ${STAGING_REPOS[@]}; do 57 | if version_ge "1.17.0" $VERSION; then 58 | staging_v=${VERSION/#1/0} 59 | edit_args+=(-replace $repo=$repo@v$staging_v) 60 | else 61 | edit_args+=(-replace $repo=$repo@kubernetes-$VERSION) 62 | fi 63 | done 64 | 65 | go mod edit ${edit_args[@]} 66 | # workaround for https://github.com/golang/go/issues/33008 67 | # go mod tidy does not always remove unncessary lines from go.sum. For now we 68 | # can remove it first and populate again. 69 | rm go.sum 70 | go mod tidy 71 | -------------------------------------------------------------------------------- /hack/publish-charts.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -o errexit 17 | set -o nounset 18 | set -o pipefail 19 | 20 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) 21 | cd $ROOT 22 | 23 | source "${ROOT}/hack/lib.sh" 24 | 25 | export QINIU_ACCESS_KEY=${QINIU_ACCESS_KEY:-} 26 | export QINIU_SECRET_KEY=${QINIU_SECRET_KEY:-} 27 | export QINIU_BUCKET_NAME=${QINIU_BUCKET_NAME:-charts} 28 | export RELEASE_TAG=${RELEASE_TAG:-} 29 | export DRY_RUN=${DRY_RUN:-} 30 | 31 | if [ -z "$QINIU_ACCESS_KEY" ]; then 32 | echo "error: QINIU_ACCESS_KEY is required" 33 | exit 1 34 | fi 35 | 36 | if [ -z "$QINIU_SECRET_KEY" ]; then 37 | echo "error: QINIU_SECRET_KEY is required" 38 | exit 1 39 | fi 40 | 41 | if [ -z "$RELEASE_TAG" ]; then 42 | echo "error: RELEASE_TAG is required" 43 | exit 1 44 | fi 45 | 46 | tmpdir=$(mktemp -d) 47 | trap "rm -rf $tmpdir" EXIT 48 | 49 | echo "info: temporary directory is $tmpdir" 50 | 51 | cd $tmpdir 52 | 53 | cp -r $ROOT/charts/ . 54 | for chart in tikv-operator; do 55 | echo "info: publish chart $chart" 56 | sed -i "s/version:.*/version: ${RELEASE_TAG}/g" charts/$chart/Chart.yaml 57 | sed -i "s/appVersion:.*/appVersion: ${RELEASE_TAG}/g" charts/$chart/Chart.yaml 58 | chartPrefixName=$chart-${RELEASE_TAG} 59 | tar -zcf ${chartPrefixName}.tgz -C charts $chart 60 | sha256sum ${chartPrefixName}.tgz > ${chartPrefixName}.sha256 61 | if [ -n "$DRY_RUN" ]; then 62 | echo "info: DRY_RUN is set, skipping uploading charts" 63 | else 64 | $ROOT/hack/upload-qiniu.py ${chartPrefixName}.tgz ${chartPrefixName}.tgz 65 | $ROOT/hack/upload-qiniu.py ${chartPrefixName}.sha256 ${chartPrefixName}.sha256 66 | fi 67 | done 68 | 69 | # TODO check if it's semantic version 70 | if [ "${RELEASE_TAG}" != "latest" ]; then 71 | echo "info: updating the index.yaml" 72 | hack::ensure_helm 73 | curl http://charts.pingcap.org/index.yaml -o index-old.yaml 74 | $HELM_BIN repo index . --url http://charts.pingcap.org/ --merge index-old.yaml 75 | echo "info: the diff of index.yaml" 76 | diff -u index-old.yaml index.yaml || true 77 | if [ -n "$DRY_RUN" ]; then 78 | echo "info: DRY_RUN is set, skipping uploading index.yaml" 79 | else 80 | $ROOT/hack/upload-qiniu.py index.yaml index.yaml 81 | fi 82 | else 83 | echo "info: RELEASE_TAG is ${RELEASE_TAG}, skip adding it into chart index file" 84 | fi 85 | -------------------------------------------------------------------------------- /hack/run-in-container.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2019 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # 17 | # Isolated container environment for development. 18 | # 19 | 20 | set -o errexit 21 | set -o nounset 22 | set -o pipefail 23 | 24 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) 25 | cd $ROOT 26 | 27 | CLEANUP=${CLEANUP:-} # if set, cleaning up local volumes 28 | DOCKER_LIB_VOLUME=${DOCKER_LIB_VOLUME:-tidb-operator-docker-lib} 29 | DOCKER_GRAPH_VOLUME=${DOCKER_GRAPH_VOLUME:-tidb-operator-docker-graph} 30 | DOCKER_GO_VOLUME=${DOCKER_GO_VOLUME:-tidb-operator-go} 31 | NAME=${NAME:-tidb-operator-dev} 32 | 33 | function usage() { 34 | cat <<'EOF' 35 | This script is entrypoint to start a isolated container environment for development. 36 | 37 | Usage: hack/run-in-container.sh [-h] [command] 38 | 39 | -h show this message and exit 40 | 41 | Environments: 42 | 43 | CLEANUP if passed, clean up local caches 44 | DOCKER_LIB_VOLUME the name of docker lib volume, defaults: tidb-operator-docker-lib 45 | DOCKER_GRAPH_VOLUME the name of docker graph volume, defaults: tidb-operator-docker-graph 46 | DOCKER_GO_VOLUME the name of go cache volume, defaults: tidb-operator-go 47 | NAME the name of container 48 | 49 | Examples: 50 | 51 | 0) view help 52 | 53 | ./hack/run-in-container.sh -h 54 | 55 | 1) start an interactive shell 56 | 57 | ./hack/run-in-container.sh 58 | 59 | You can start more than one terminals and run `./hack/run-in-container.sh` to 60 | enter into the same container for debugging. 61 | 62 | 2) run command and exit 63 | 64 | ./hack/run-in-container.sh make test 65 | ./hack/run-in-container.sh ./hack/e2e.sh -- --ginkgo.focus='aggregated' 66 | 67 | 3) clean docker and go caches local volumes 68 | 69 | CLEANUP=y ./hack/run-in-container.sh 70 | 71 | EOF 72 | } 73 | 74 | if [ "${1:-}" == "-h" ]; then 75 | usage 76 | exit 0 77 | fi 78 | 79 | args=(bash) 80 | if [ $# -gt 0 ]; then 81 | args=("$@") 82 | fi 83 | 84 | docker_args=( 85 | --rm 86 | -h $NAME 87 | --name $NAME 88 | ) 89 | 90 | if [ -t 1 ]; then 91 | # Allocate a pseudo-TTY when the STDIN is a terminal 92 | docker_args+=(-it) 93 | fi 94 | 95 | # required by dind 96 | docker_args+=( 97 | --privileged 98 | -e DOCKER_IN_DOCKER_ENABLED=true 99 | # Docker in Docker expects it to be a volume 100 | -v $DOCKER_LIB_VOLUME:/var/lib/docker 101 | -v $DOCKER_GRAPH_VOLUME:/docker-graph # legacy path for gcr.io/k8s-testimages/kubekins-e2e 102 | # golang cache 103 | -v $DOCKER_GO_VOLUME:/go 104 | # golang xdg cache directory 105 | -e XDG_CACHE_HOME=/go/cache 106 | ) 107 | 108 | # required by kind 109 | docker_args+=( 110 | -v /lib/modules:/lib/modules 111 | -v /sys/fs/cgroup:/sys/fs/cgroup 112 | ) 113 | 114 | function cleanup() { 115 | local volumes=( 116 | $DOCKER_LIB_VOLUME 117 | $DOCKER_GRAPH_VOLUME 118 | $DOCKER_GO_VOLUME 119 | ) 120 | for v in "${volumes[@]}"; do 121 | echo "info: cleaning up volume $v" 122 | docker volume rm $v || true 123 | done 124 | } 125 | 126 | if [ -n "$CLEANUP" ]; then 127 | cleanup 128 | exit 129 | fi 130 | 131 | ret=0 132 | sts=$(docker inspect ${NAME} -f '{{.State.Status}}' 2>/dev/null) || ret=$? 133 | if [ $ret -eq 0 ]; then 134 | if [[ "$sts" == "running" ]]; then 135 | echo "info: found a running container named '${NAME}', trying to exec into it" >&2 136 | exec docker exec -it ${NAME} "${args[@]}" 137 | else 138 | echo "info: found a non-running ($sts) container named '${NAME}', removing it first" >&2 139 | docker rm ${NAME} 140 | fi 141 | fi 142 | 143 | docker run ${docker_args[@]} \ 144 | -v $ROOT:/go/src/github.com/pingcap/tidb-operator \ 145 | -w /go/src/github.com/pingcap/tidb-operator \ 146 | --entrypoint /usr/local/bin/runner.sh \ 147 | gcr.io/k8s-testimages/kubekins-e2e:v20200311-1e25827-master \ 148 | "${args[@]}" 149 | -------------------------------------------------------------------------------- /hack/update-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -o errexit 17 | set -o nounset 18 | set -o pipefail 19 | 20 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) 21 | cd $ROOT 22 | 23 | hack/update-codegen.sh 24 | hack/update-toc.sh 25 | -------------------------------------------------------------------------------- /hack/update-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2017 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -o errexit 17 | set -o nounset 18 | set -o pipefail 19 | 20 | SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. 21 | cd $SCRIPT_ROOT 22 | 23 | export GO111MODULE=on 24 | 25 | go mod vendor 26 | 27 | CODEGEN_PKG=${CODEGEN_PKG:-$(ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)} 28 | 29 | bash "${CODEGEN_PKG}"/generate-groups.sh "deepcopy,client,informer,lister" \ 30 | github.com/tikv/tikv-operator/pkg/client \ 31 | github.com/tikv/tikv-operator/pkg/apis \ 32 | tikv:v1alpha1 \ 33 | --go-header-file ./hack/boilerplate/boilerplate.generatego.txt 34 | -------------------------------------------------------------------------------- /hack/update-toc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # This script is modified from https://github.com/kubernetes/enhancements/blob/master/hack/update-toc.sh. 17 | 18 | set -o errexit 19 | set -o nounset 20 | set -o pipefail 21 | 22 | # keep in sync with hack/verify-toc.sh 23 | TOOL_VERSION=ee652eb78c047a7b6c7417d9324a97bb05689563 24 | 25 | # cd to the root path 26 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" 27 | cd "${ROOT}" 28 | 29 | # create a temporary directory 30 | TMP_DIR=$(mktemp -d) 31 | 32 | # cleanup 33 | exitHandler() ( 34 | echo "Cleaning up..." 35 | rm -rf "${TMP_DIR}" 36 | ) 37 | trap exitHandler EXIT 38 | 39 | # perform go get in a temp dir as we are not tracking this version in a go module 40 | # if we do the go get in the repo, it will create / update a go.mod and go.sum 41 | cd "${TMP_DIR}" 42 | GO111MODULE=on GOBIN="${TMP_DIR}" go get "github.com/tallclair/mdtoc@${TOOL_VERSION}" 43 | export PATH="${TMP_DIR}:${PATH}" 44 | cd "${ROOT}" 45 | 46 | # Update tables of contents if necessary. 47 | find docs -name '*.md' | grep -Fxvf hack/.notableofcontents | xargs mdtoc --inplace 48 | -------------------------------------------------------------------------------- /hack/upload-qiniu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import sys 17 | import os 18 | from qiniu import Auth, put_file, etag, urlsafe_base64_encode 19 | import qiniu.config 20 | from qiniu.compat import is_py2, is_py3 21 | 22 | 23 | ACCESS_KEY = os.getenv('QINIU_ACCESS_KEY') 24 | SECRET_KEY = os.getenv('QINIU_SECRET_KEY') 25 | BUCKET_NAME = os.getenv('QINIU_BUCKET_NAME') 26 | 27 | assert(ACCESS_KEY and SECRET_KEY and BUCKET_NAME) 28 | 29 | def progress_handler(progress, total): 30 | print("{}/{} {:.2f}".format(progress, total, progress/total*100)) 31 | 32 | def upload(local_file, remote_name, ttl=3600): 33 | print(local_file, remote_name, ttl) 34 | q = Auth(ACCESS_KEY, SECRET_KEY) 35 | 36 | token = q.upload_token(BUCKET_NAME, remote_name, ttl) 37 | 38 | ret, info = put_file(token, remote_name, local_file) 39 | print("ret", ret) 40 | print("info", info) 41 | # assert ret['key'] == remote_name 42 | if is_py2: 43 | assert ret['key'].encode('utf-8') == remote_name 44 | elif is_py3: 45 | assert ret['key'] == remote_name 46 | 47 | assert ret['hash'] == etag(local_file) 48 | 49 | if __name__ == "__main__": 50 | local_file = sys.argv[1] 51 | remote_name = sys.argv[2] 52 | upload(local_file, remote_name) 53 | 54 | print("https://charts.pingcap.org/{}".format(remote_name)) 55 | -------------------------------------------------------------------------------- /hack/verify-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -o errexit 17 | set -o nounset 18 | set -o pipefail 19 | 20 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) 21 | cd $ROOT 22 | 23 | hack/verify-boilerplate.sh 24 | hack/verify-codegen.sh 25 | hack/verify-toc.sh 26 | -------------------------------------------------------------------------------- /hack/verify-boilerplate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2017 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -o errexit 17 | set -o nounset 18 | set -o pipefail 19 | 20 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) 21 | cd $ROOT 22 | 23 | boiler="${ROOT}/hack/boilerplate/boilerplate.py" 24 | 25 | # ignored files is a list of files we should ignore, e.g. k8s script. 26 | # one file per line 27 | ignored_files='./hack/cherry_pick_pull.sh 28 | hack/generate-internal-groups.sh' 29 | 30 | # 31 | # TODO update license information for following files 32 | # 33 | # - ./deploy/* 34 | # - */Makefile 35 | # - */Dockerfile 36 | # - */*.register.go # files geneated by apiregister-gen 37 | # 38 | files=($(find . -type f -not \( \ 39 | -path './hack/boilerplate/*' \ 40 | -o -path './_tools/*' \ 41 | -o -path './.git/*' \ 42 | -o -path './.*/*' \ 43 | -o -path './vendor/*' \ 44 | -o -path './pkg/client/*' \ 45 | -o -path './*/.terraform/*' \ 46 | -o -path './tests/images/*/*' \ 47 | -o -path './deploy/*' \ 48 | -o -path '*/Makefile' \ 49 | -o -path '*/Dockerfile' \ 50 | -o -path '*/*.register.go' \ 51 | \) | grep -v -F "$ignored_files" 52 | )) 53 | 54 | files_need_boilerplate=() 55 | while IFS=$'\n' read -r line; do 56 | files_need_boilerplate+=( "$line" ) 57 | done < <("${boiler}" "${files[@]}") 58 | 59 | # Run boilerplate check 60 | if [[ ${#files_need_boilerplate[@]} -gt 0 ]]; then 61 | for file in "${files_need_boilerplate[@]}"; do 62 | echo "Boilerplate header is wrong for: ${file}" >&2 63 | done 64 | exit 1 65 | fi 66 | -------------------------------------------------------------------------------- /hack/verify-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -o errexit 17 | set -o nounset 18 | set -o pipefail 19 | 20 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) 21 | 22 | DIFFROOT="${ROOT}/pkg" 23 | TMP_DIFFROOT="${ROOT}/_tmp/pkg" 24 | _tmp="${ROOT}/_tmp" 25 | 26 | cleanup() { 27 | rm -rf "${_tmp}" 28 | } 29 | trap "cleanup" EXIT SIGINT 30 | 31 | cleanup 32 | 33 | mkdir -p "${TMP_DIFFROOT}" 34 | cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" 35 | 36 | "${ROOT}/hack/update-codegen.sh" 37 | echo "diffing ${DIFFROOT} against freshly generated codegen" 38 | ret=0 39 | diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? 40 | cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}" 41 | if [[ $ret -eq 0 ]]; then 42 | echo "${DIFFROOT} up to date." 43 | else 44 | echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh" 45 | exit 1 46 | fi 47 | -------------------------------------------------------------------------------- /hack/verify-crd-groups.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -o errexit 17 | set -o nounset 18 | set -o pipefail 19 | 20 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) 21 | cd $ROOT 22 | 23 | target="manifests/crd.yaml" 24 | verify_tmp=$(mktemp) 25 | trap "rm -f $verify_tmp" EXIT 26 | 27 | targetDocs="$ROOT/docs/api-references/docs.md" 28 | verifyDocs_tmp=$(mktemp) 29 | trap "rm -f $verifyDocs_tmp" EXIT 30 | 31 | cp "$target" "${verify_tmp}" 32 | cp "$targetDocs" "${verifyDocs_tmp}" 33 | 34 | hack/update-crd-groups.sh 35 | 36 | echo "diffing $target with $verify_tmp" >&2 37 | diff=$(diff "$target" "$verify_tmp") || true 38 | if [[ -n "${diff}" ]]; then 39 | echo "${diff}" >&2 40 | echo >&2 41 | echo "Run ./hack/update-crd-groups.sh" >&2 42 | exit 1 43 | fi 44 | 45 | echo "diffing $targetDocs with $verifyDocs_tmp" >&2 46 | diff=$(diff "$targetDocs" "$verifyDocs_tmp") || true 47 | if [[ -n "${diff}" ]]; then 48 | echo "${diff}" >&2 49 | echo >&2 50 | echo "Run ./hack/update-crd-groups.sh" >&2 51 | exit 1 52 | fi 53 | -------------------------------------------------------------------------------- /hack/verify-toc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # This script is modified from https://github.com/kubernetes/enhancements/blob/master/hack/verify-toc.sh. 17 | 18 | set -o errexit 19 | set -o nounset 20 | set -o pipefail 21 | 22 | # keep in sync with hack/update-toc.sh 23 | TOOL_VERSION=ee652eb78c047a7b6c7417d9324a97bb05689563 24 | 25 | # cd to the root path 26 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" 27 | cd "${ROOT}" 28 | 29 | # create a temporary directory 30 | TMP_DIR=$(mktemp -d) 31 | 32 | # cleanup 33 | exitHandler() ( 34 | echo "Cleaning up..." 35 | rm -rf "${TMP_DIR}" 36 | ) 37 | trap exitHandler EXIT 38 | 39 | # perform go get in a temp dir as we are not tracking this version in a go module 40 | # if we do the go get in the repo, it will create / update a go.mod and go.sum 41 | cd "${TMP_DIR}" 42 | GO111MODULE=on GOBIN="${TMP_DIR}" go get "github.com/tallclair/mdtoc@${TOOL_VERSION}" 43 | export PATH="${TMP_DIR}:${PATH}" 44 | cd "${ROOT}" 45 | 46 | echo "Checking table of contents are up to date..." 47 | # Verify tables of contents are up-to-date 48 | find docs -name '*.md' | grep -Fxvf hack/.notableofcontents | xargs mdtoc --inplace --dryrun 49 | -------------------------------------------------------------------------------- /hack/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # This script is used to generate version informations from git repo automatically. 17 | # 18 | # Output format: 19 | # 20 | # : 21 | # : 22 | # 23 | # You can simply use this command to extrac the value by key: 24 | # 25 | # ./hack/version.sh | awk -F' ' '/^GIT_VERSION:/ {print $2}' 26 | # 27 | 28 | set -o errexit 29 | set -o nounset 30 | set -o pipefail 31 | 32 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) 33 | cd $ROOT 34 | 35 | GIT_EXACT_TAG=$(git describe --tags --abbrev=0 --exact-match 2>/dev/null || true) 36 | GIT_RECENT_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true) 37 | GIT_SHA_SHORT=$(git rev-parse --short HEAD) 38 | GIT_DIRTY=$(test -n "`git status --porcelain`" && echo "dirty" || echo "clean") 39 | 40 | echo "GIT_EXACT_TAG: $GIT_EXACT_TAG" 41 | echo "GIT_RECENT_TAG: $GIT_RECENT_TAG" 42 | echo "GIT_DIRTY: $GIT_DIRTY" 43 | echo "GIT_SHA_SHORT: $GIT_SHA_SHORT" 44 | 45 | # Modifed from k8s.io/kubernetes/hack/lib/version.sh. 46 | if [[ -n ${GIT_VERSION-} ]] || GIT_VERSION=$(git describe --tags --abbrev=7 HEAD 2>/dev/null); then 47 | # This translates the "git describe" to an actual semver.org 48 | # compatible semantic version that looks something like this: 49 | # v1.1.0-alpha.0.6+84c76d1 50 | DASHES_IN_GIT_VERSION=$(echo "${GIT_VERSION}" | sed "s/[^-]//g") 51 | if [[ "${DASHES_IN_GIT_VERSION}" == "---" ]] ; then 52 | # We have distance to subversion (v1.1.0-subversion-1-gCommitHash) 53 | GIT_VERSION=$(echo "${GIT_VERSION}" | sed "s/-\([0-9]\{1,\}\)-g\([0-9a-f]\{7\}\)$/.\1\+\2/") 54 | elif [[ "${DASHES_IN_GIT_VERSION}" == "--" ]] ; then 55 | # We have distance to base tag (v1.1.0-1-gCommitHash) 56 | GIT_VERSION=$(echo "${GIT_VERSION}" | sed "s/-g\([0-9a-f]\{7\}\)$/+\1/") 57 | fi 58 | fi 59 | if [[ -z ${GIT_VERSION} ]]; then 60 | GIT_VERSION="v0.0.0" 61 | fi 62 | if [[ "$GIT_DIRTY" == "dirty" ]]; then 63 | # git describe --dirty only considers changes to existing files, but 64 | # that is problematic since new untracked .go files affect the build, 65 | # so use our idea of "dirty" from git status instead. 66 | GIT_VERSION+="-dirty" 67 | fi 68 | echo "GIT_VERSION: $GIT_VERSION" 69 | -------------------------------------------------------------------------------- /manifests/crd.v1beta1.yaml: -------------------------------------------------------------------------------- 1 | # For Kubernetes before 1.16. 2 | apiVersion: apiextensions.k8s.io/v1beta1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: tikvclusters.tikv.org 6 | spec: 7 | group: tikv.org 8 | scope: Namespaced 9 | names: 10 | plural: tikvclusters 11 | singular: tikvcluster 12 | kind: TikvCluster 13 | versions: 14 | - name: v1alpha1 15 | served: true 16 | storage: true 17 | validation: 18 | openAPIV3Schema: 19 | type: object 20 | additionalPrinterColumns: 21 | - JSONPath: .status.conditions[?(@.type=="Ready")].status 22 | name: Ready 23 | type: string 24 | - JSONPath: .status.pd.image 25 | description: The image for PD cluster 26 | name: PD 27 | type: string 28 | - JSONPath: .spec.pd.replicas 29 | description: The desired replicas number of PD cluster 30 | name: Desire 31 | type: integer 32 | - JSONPath: .status.pd.statefulSet.readyReplicas 33 | description: The current replicas number of PD cluster 34 | name: Current 35 | type: integer 36 | - JSONPath: .status.tikv.image 37 | description: The image for TiKV cluster 38 | name: TiKV 39 | type: string 40 | - JSONPath: .spec.tikv.replicas 41 | description: The desired replicas number of TiKV cluster 42 | name: Desire 43 | type: integer 44 | - JSONPath: .status.tikv.statefulSet.readyReplicas 45 | description: The current replicas number of TiKV cluster 46 | name: Current 47 | type: integer 48 | - name: Age 49 | type: date 50 | JSONPath: .metadata.creationTimestamp 51 | - JSONPath: .status.conditions[?(@.type=="Ready")].message 52 | name: Status 53 | priority: 1 54 | type: string 55 | -------------------------------------------------------------------------------- /pkg/apis/tikv/v1alpha1/component.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package v1alpha1 15 | 16 | import ( 17 | corev1 "k8s.io/api/core/v1" 18 | ) 19 | 20 | const ( 21 | defaultHostNetwork = false 22 | ) 23 | 24 | // ComponentAccessor is the interface to access component details, which respects the cluster-level properties 25 | // and component-level overrides 26 | type ComponentAccessor interface { 27 | ImagePullPolicy() corev1.PullPolicy 28 | HostNetwork() bool 29 | Affinity() *corev1.Affinity 30 | PriorityClassName() *string 31 | NodeSelector() map[string]string 32 | Annotations() map[string]string 33 | Tolerations() []corev1.Toleration 34 | PodSecurityContext() *corev1.PodSecurityContext 35 | SchedulerName() string 36 | DnsPolicy() corev1.DNSPolicy 37 | ConfigUpdateStrategy() ConfigUpdateStrategy 38 | BuildPodSpec() corev1.PodSpec 39 | Env() []corev1.EnvVar 40 | } 41 | 42 | type componentAccessorImpl struct { 43 | // Cluster is the TikvCluster Spec 44 | ClusterSpec *TikvClusterSpec 45 | 46 | // Cluster is the Component Spec 47 | ComponentSpec *ComponentSpec 48 | } 49 | 50 | func (a *componentAccessorImpl) PodSecurityContext() *corev1.PodSecurityContext { 51 | return a.ComponentSpec.PodSecurityContext 52 | } 53 | 54 | func (a *componentAccessorImpl) ImagePullPolicy() corev1.PullPolicy { 55 | pp := a.ComponentSpec.ImagePullPolicy 56 | if pp == nil { 57 | return a.ClusterSpec.ImagePullPolicy 58 | } 59 | return *pp 60 | } 61 | 62 | func (a *componentAccessorImpl) HostNetwork() bool { 63 | hostNetwork := a.ComponentSpec.HostNetwork 64 | if hostNetwork == nil { 65 | hostNetwork = a.ClusterSpec.HostNetwork 66 | } 67 | if hostNetwork == nil { 68 | return defaultHostNetwork 69 | } 70 | return *hostNetwork 71 | } 72 | 73 | func (a *componentAccessorImpl) Affinity() *corev1.Affinity { 74 | affi := a.ComponentSpec.Affinity 75 | if affi == nil { 76 | affi = a.ClusterSpec.Affinity 77 | } 78 | return affi 79 | } 80 | 81 | func (a *componentAccessorImpl) PriorityClassName() *string { 82 | pcn := a.ComponentSpec.PriorityClassName 83 | if pcn == nil { 84 | pcn = a.ClusterSpec.PriorityClassName 85 | } 86 | return pcn 87 | } 88 | 89 | func (a *componentAccessorImpl) SchedulerName() string { 90 | pcn := a.ComponentSpec.SchedulerName 91 | if pcn == nil { 92 | pcn = &a.ClusterSpec.SchedulerName 93 | } 94 | return *pcn 95 | } 96 | 97 | func (a *componentAccessorImpl) NodeSelector() map[string]string { 98 | sel := map[string]string{} 99 | for k, v := range a.ClusterSpec.NodeSelector { 100 | sel[k] = v 101 | } 102 | for k, v := range a.ComponentSpec.NodeSelector { 103 | sel[k] = v 104 | } 105 | return sel 106 | } 107 | 108 | func (a *componentAccessorImpl) Annotations() map[string]string { 109 | anno := map[string]string{} 110 | for k, v := range a.ClusterSpec.Annotations { 111 | anno[k] = v 112 | } 113 | for k, v := range a.ComponentSpec.Annotations { 114 | anno[k] = v 115 | } 116 | return anno 117 | } 118 | 119 | func (a *componentAccessorImpl) Tolerations() []corev1.Toleration { 120 | tols := a.ComponentSpec.Tolerations 121 | if len(tols) == 0 { 122 | tols = a.ClusterSpec.Tolerations 123 | } 124 | return tols 125 | } 126 | 127 | func (a *componentAccessorImpl) DnsPolicy() corev1.DNSPolicy { 128 | dnsPolicy := corev1.DNSClusterFirst // same as kubernetes default 129 | if a.HostNetwork() { 130 | dnsPolicy = corev1.DNSClusterFirstWithHostNet 131 | } 132 | return dnsPolicy 133 | } 134 | 135 | func (a *componentAccessorImpl) ConfigUpdateStrategy() ConfigUpdateStrategy { 136 | strategy := a.ComponentSpec.ConfigUpdateStrategy 137 | if strategy == nil { 138 | strategy = &a.ClusterSpec.ConfigUpdateStrategy 139 | } 140 | // defaulting logic will set a default value for configUpdateStrategy field, but if the 141 | // object is created in early version without this field being set, we should set a safe default 142 | if string(*strategy) == "" { 143 | return ConfigUpdateStrategyInPlace 144 | } 145 | return *strategy 146 | } 147 | 148 | func (a *componentAccessorImpl) BuildPodSpec() corev1.PodSpec { 149 | spec := corev1.PodSpec{ 150 | SchedulerName: a.SchedulerName(), 151 | Affinity: a.Affinity(), 152 | NodeSelector: a.NodeSelector(), 153 | HostNetwork: a.HostNetwork(), 154 | RestartPolicy: corev1.RestartPolicyAlways, 155 | Tolerations: a.Tolerations(), 156 | SecurityContext: a.PodSecurityContext(), 157 | } 158 | if a.PriorityClassName() != nil { 159 | spec.PriorityClassName = *a.PriorityClassName() 160 | } 161 | return spec 162 | } 163 | 164 | func (a *componentAccessorImpl) Env() []corev1.EnvVar { 165 | return a.ComponentSpec.Env 166 | } 167 | 168 | // BaseTiKVSpec returns the base spec of TiKV servers 169 | func (tc *TikvCluster) BaseTiKVSpec() ComponentAccessor { 170 | return &componentAccessorImpl{&tc.Spec, &tc.Spec.TiKV.ComponentSpec} 171 | } 172 | 173 | // BasePDSpec returns the base spec of PD servers 174 | func (tc *TikvCluster) BasePDSpec() ComponentAccessor { 175 | return &componentAccessorImpl{&tc.Spec, &tc.Spec.PD.ComponentSpec} 176 | } 177 | -------------------------------------------------------------------------------- /pkg/apis/tikv/v1alpha1/defaulting/tidbcluster.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package defaulting 15 | 16 | import ( 17 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 18 | corev1 "k8s.io/api/core/v1" 19 | "k8s.io/utils/pointer" 20 | ) 21 | 22 | const ( 23 | defaultTiKVImage = "pingcap/tikv" 24 | defaultPDImage = "pingcap/pd" 25 | ) 26 | 27 | func SetTikvClusterDefault(tc *v1alpha1.TikvCluster) { 28 | setTikvClusterSpecDefault(tc) 29 | setPdSpecDefault(tc) 30 | setTikvSpecDefault(tc) 31 | } 32 | 33 | // setTikvClusterSpecDefault is only managed the property under Spec 34 | func setTikvClusterSpecDefault(tc *v1alpha1.TikvCluster) { 35 | if string(tc.Spec.ImagePullPolicy) == "" { 36 | tc.Spec.ImagePullPolicy = corev1.PullIfNotPresent 37 | } 38 | } 39 | 40 | func setTikvSpecDefault(tc *v1alpha1.TikvCluster) { 41 | if len(tc.Spec.Version) > 0 || tc.Spec.TiKV.Version != nil { 42 | if tc.Spec.TiKV.BaseImage == "" { 43 | tc.Spec.TiKV.BaseImage = defaultTiKVImage 44 | } 45 | } 46 | if tc.Spec.TiKV.MaxFailoverCount == nil { 47 | tc.Spec.TiKV.MaxFailoverCount = pointer.Int32Ptr(3) 48 | } 49 | } 50 | 51 | func setPdSpecDefault(tc *v1alpha1.TikvCluster) { 52 | if len(tc.Spec.Version) > 0 || tc.Spec.PD.Version != nil { 53 | if tc.Spec.PD.BaseImage == "" { 54 | tc.Spec.PD.BaseImage = defaultPDImage 55 | } 56 | } 57 | if tc.Spec.PD.MaxFailoverCount == nil { 58 | tc.Spec.PD.MaxFailoverCount = pointer.Int32Ptr(3) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pkg/apis/tikv/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // +k8s:deepcopy-gen=package,register 15 | 16 | // Package v1alpha1 is the v1alpha1 version of the API. 17 | // +groupName=tikv.org 18 | package v1alpha1 19 | -------------------------------------------------------------------------------- /pkg/apis/tikv/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package v1alpha1 15 | 16 | import ( 17 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 | "k8s.io/apimachinery/pkg/runtime" 19 | "k8s.io/apimachinery/pkg/runtime/schema" 20 | ) 21 | 22 | const ( 23 | // GroupName is the group name use in this package 24 | GroupName = "tikv.org" 25 | ) 26 | 27 | var ( 28 | // SchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. 29 | SchemeBuilder runtime.SchemeBuilder 30 | localSchemeBuilder = &SchemeBuilder 31 | // AddToScheme applies all the stored functions to the scheme. 32 | AddToScheme = localSchemeBuilder.AddToScheme 33 | // Scheme is the scheme instance of operator 34 | Scheme *runtime.Scheme 35 | 36 | groupName = "tikv.org" 37 | ) 38 | 39 | // SchemeGroupVersion is group version used to register these objects 40 | var SchemeGroupVersion = schema.GroupVersion{Group: groupName, Version: "v1alpha1"} 41 | 42 | func init() { 43 | // We only register manually written functions here. The registration of the 44 | // generated functions takes place in the generated files. The separation 45 | // makes the code compile even when the generated files are missing. 46 | localSchemeBuilder.Register(addKnownTypes) 47 | } 48 | 49 | // Resource takes an unqualified resource and returns back a Group qualified GroupResource 50 | func Resource(resource string) schema.GroupResource { 51 | return SchemeGroupVersion.WithResource(resource).GroupResource() 52 | } 53 | 54 | // Adds the list of known types to api.Scheme. 55 | func addKnownTypes(scheme *runtime.Scheme) error { 56 | Scheme = scheme 57 | scheme.AddKnownTypes(SchemeGroupVersion, 58 | &TikvCluster{}, 59 | &TikvClusterList{}, 60 | ) 61 | 62 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /pkg/apis/tikv/v1alpha1/tikv_config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package v1alpha1 15 | 16 | import ( 17 | "bytes" 18 | "encoding/json" 19 | "testing" 20 | 21 | "github.com/BurntSushi/toml" 22 | . "github.com/onsi/gomega" 23 | "k8s.io/utils/pointer" 24 | ) 25 | 26 | func TestTiKVConfig(t *testing.T) { 27 | g := NewGomegaWithT(t) 28 | c := &TiKVConfig{ 29 | ReadPool: &TiKVReadPoolConfig{ 30 | Storage: &TiKVStorageReadPoolConfig{ 31 | HighConcurrency: pointer.Int64Ptr(4), 32 | }, 33 | Coprocessor: &TiKVCoprocessorReadPoolConfig{ 34 | HighConcurrency: pointer.Int64Ptr(8), 35 | }, 36 | }, 37 | Storage: &TiKVStorageConfig{ 38 | BlockCache: &TiKVBlockCacheConfig{ 39 | Shared: pointer.BoolPtr(true), 40 | }, 41 | }, 42 | } 43 | jsonStr, err := json.Marshal(c) 44 | g.Expect(err).To(Succeed()) 45 | g.Expect(jsonStr).NotTo(ContainSubstring("port"), "Expected empty fields to be omitted") 46 | var jsonUnmarshaled TiKVConfig 47 | err = json.Unmarshal(jsonStr, &jsonUnmarshaled) 48 | g.Expect(err).To(Succeed()) 49 | g.Expect(&jsonUnmarshaled).To(Equal(c)) 50 | 51 | buff := new(bytes.Buffer) 52 | encoder := toml.NewEncoder(buff) 53 | err = encoder.Encode(c) 54 | g.Expect(err).To(Succeed()) 55 | tStr := buff.String() 56 | g.Expect(tStr).To((Equal(`[storage] 57 | [storage.block-cache] 58 | shared = true 59 | 60 | [readpool] 61 | [readpool.coprocessor] 62 | high-concurrency = 8 63 | [readpool.storage] 64 | high-concurrency = 4 65 | `))) 66 | 67 | var tUnmarshaled TiKVConfig 68 | err = toml.Unmarshal([]byte(tStr), &tUnmarshaled) 69 | g.Expect(err).To(Succeed()) 70 | g.Expect(&tUnmarshaled).To(Equal(c)) 71 | } 72 | -------------------------------------------------------------------------------- /pkg/apis/tikv/v1alpha1/validation/validation_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package validation 15 | 16 | import ( 17 | "testing" 18 | 19 | . "github.com/onsi/gomega" 20 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 21 | corev1 "k8s.io/api/core/v1" 22 | "k8s.io/apimachinery/pkg/api/resource" 23 | ) 24 | 25 | func TestValidateRequestsStorage(t *testing.T) { 26 | g := NewGomegaWithT(t) 27 | tests := []struct { 28 | name string 29 | haveRequest bool 30 | resourceRequirements corev1.ResourceRequirements 31 | expectedErrors int 32 | }{ 33 | { 34 | name: "has request storage", 35 | haveRequest: true, 36 | resourceRequirements: corev1.ResourceRequirements{ 37 | Requests: corev1.ResourceList{ 38 | corev1.ResourceStorage: resource.MustParse("10G"), 39 | }, 40 | }, 41 | expectedErrors: 0, 42 | }, 43 | { 44 | name: "Empty request storage", 45 | haveRequest: false, 46 | resourceRequirements: corev1.ResourceRequirements{ 47 | Requests: corev1.ResourceList{}, 48 | }, 49 | expectedErrors: 2, 50 | }, 51 | } 52 | for _, tt := range tests { 53 | t.Run(tt.name, func(t *testing.T) { 54 | tc := newTikvCluster() 55 | if tt.haveRequest { 56 | tc.Spec.PD.ResourceRequirements = tt.resourceRequirements 57 | tc.Spec.TiKV.ResourceRequirements = tt.resourceRequirements 58 | } 59 | err := ValidateTikvCluster(tc) 60 | r := len(err) 61 | g.Expect(r).Should(Equal(tt.expectedErrors)) 62 | }) 63 | } 64 | } 65 | 66 | func newTikvCluster() *v1alpha1.TikvCluster { 67 | tc := &v1alpha1.TikvCluster{} 68 | tc.Name = "test-validate-requests-storage" 69 | tc.Namespace = "default" 70 | return tc 71 | } 72 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/clientset.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by client-gen. DO NOT EDIT. 15 | 16 | package versioned 17 | 18 | import ( 19 | "fmt" 20 | 21 | tikvv1alpha1 "github.com/tikv/tikv-operator/pkg/client/clientset/versioned/typed/tikv/v1alpha1" 22 | discovery "k8s.io/client-go/discovery" 23 | rest "k8s.io/client-go/rest" 24 | flowcontrol "k8s.io/client-go/util/flowcontrol" 25 | ) 26 | 27 | type Interface interface { 28 | Discovery() discovery.DiscoveryInterface 29 | TikvV1alpha1() tikvv1alpha1.TikvV1alpha1Interface 30 | } 31 | 32 | // Clientset contains the clients for groups. Each group has exactly one 33 | // version included in a Clientset. 34 | type Clientset struct { 35 | *discovery.DiscoveryClient 36 | tikvV1alpha1 *tikvv1alpha1.TikvV1alpha1Client 37 | } 38 | 39 | // TikvV1alpha1 retrieves the TikvV1alpha1Client 40 | func (c *Clientset) TikvV1alpha1() tikvv1alpha1.TikvV1alpha1Interface { 41 | return c.tikvV1alpha1 42 | } 43 | 44 | // Discovery retrieves the DiscoveryClient 45 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 46 | if c == nil { 47 | return nil 48 | } 49 | return c.DiscoveryClient 50 | } 51 | 52 | // NewForConfig creates a new Clientset for the given config. 53 | // If config's RateLimiter is not set and QPS and Burst are acceptable, 54 | // NewForConfig will generate a rate-limiter in configShallowCopy. 55 | func NewForConfig(c *rest.Config) (*Clientset, error) { 56 | configShallowCopy := *c 57 | if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { 58 | if configShallowCopy.Burst <= 0 { 59 | return nil, fmt.Errorf("Burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") 60 | } 61 | configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) 62 | } 63 | var cs Clientset 64 | var err error 65 | cs.tikvV1alpha1, err = tikvv1alpha1.NewForConfig(&configShallowCopy) 66 | if err != nil { 67 | return nil, err 68 | } 69 | 70 | cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) 71 | if err != nil { 72 | return nil, err 73 | } 74 | return &cs, nil 75 | } 76 | 77 | // NewForConfigOrDie creates a new Clientset for the given config and 78 | // panics if there is an error in the config. 79 | func NewForConfigOrDie(c *rest.Config) *Clientset { 80 | var cs Clientset 81 | cs.tikvV1alpha1 = tikvv1alpha1.NewForConfigOrDie(c) 82 | 83 | cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) 84 | return &cs 85 | } 86 | 87 | // New creates a new Clientset for the given RESTClient. 88 | func New(c rest.Interface) *Clientset { 89 | var cs Clientset 90 | cs.tikvV1alpha1 = tikvv1alpha1.New(c) 91 | 92 | cs.DiscoveryClient = discovery.NewDiscoveryClient(c) 93 | return &cs 94 | } 95 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by client-gen. DO NOT EDIT. 15 | 16 | // This package has the automatically generated clientset. 17 | package versioned 18 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/fake/clientset_generated.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by client-gen. DO NOT EDIT. 15 | 16 | package fake 17 | 18 | import ( 19 | clientset "github.com/tikv/tikv-operator/pkg/client/clientset/versioned" 20 | tikvv1alpha1 "github.com/tikv/tikv-operator/pkg/client/clientset/versioned/typed/tikv/v1alpha1" 21 | faketikvv1alpha1 "github.com/tikv/tikv-operator/pkg/client/clientset/versioned/typed/tikv/v1alpha1/fake" 22 | "k8s.io/apimachinery/pkg/runtime" 23 | "k8s.io/apimachinery/pkg/watch" 24 | "k8s.io/client-go/discovery" 25 | fakediscovery "k8s.io/client-go/discovery/fake" 26 | "k8s.io/client-go/testing" 27 | ) 28 | 29 | // NewSimpleClientset returns a clientset that will respond with the provided objects. 30 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 31 | // without applying any validations and/or defaults. It shouldn't be considered a replacement 32 | // for a real clientset and is mostly useful in simple unit tests. 33 | func NewSimpleClientset(objects ...runtime.Object) *Clientset { 34 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 35 | for _, obj := range objects { 36 | if err := o.Add(obj); err != nil { 37 | panic(err) 38 | } 39 | } 40 | 41 | cs := &Clientset{tracker: o} 42 | cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} 43 | cs.AddReactor("*", "*", testing.ObjectReaction(o)) 44 | cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { 45 | gvr := action.GetResource() 46 | ns := action.GetNamespace() 47 | watch, err := o.Watch(gvr, ns) 48 | if err != nil { 49 | return false, nil, err 50 | } 51 | return true, watch, nil 52 | }) 53 | 54 | return cs 55 | } 56 | 57 | // Clientset implements clientset.Interface. Meant to be embedded into a 58 | // struct to get a default implementation. This makes faking out just the method 59 | // you want to test easier. 60 | type Clientset struct { 61 | testing.Fake 62 | discovery *fakediscovery.FakeDiscovery 63 | tracker testing.ObjectTracker 64 | } 65 | 66 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 67 | return c.discovery 68 | } 69 | 70 | func (c *Clientset) Tracker() testing.ObjectTracker { 71 | return c.tracker 72 | } 73 | 74 | var _ clientset.Interface = &Clientset{} 75 | 76 | // TikvV1alpha1 retrieves the TikvV1alpha1Client 77 | func (c *Clientset) TikvV1alpha1() tikvv1alpha1.TikvV1alpha1Interface { 78 | return &faketikvv1alpha1.FakeTikvV1alpha1{Fake: &c.Fake} 79 | } 80 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by client-gen. DO NOT EDIT. 15 | 16 | // This package has the automatically generated fake clientset. 17 | package fake 18 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by client-gen. DO NOT EDIT. 15 | 16 | package fake 17 | 18 | import ( 19 | tikvv1alpha1 "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 20 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | runtime "k8s.io/apimachinery/pkg/runtime" 22 | schema "k8s.io/apimachinery/pkg/runtime/schema" 23 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 24 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 25 | ) 26 | 27 | var scheme = runtime.NewScheme() 28 | var codecs = serializer.NewCodecFactory(scheme) 29 | var parameterCodec = runtime.NewParameterCodec(scheme) 30 | var localSchemeBuilder = runtime.SchemeBuilder{ 31 | tikvv1alpha1.AddToScheme, 32 | } 33 | 34 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 35 | // of clientsets, like in: 36 | // 37 | // import ( 38 | // "k8s.io/client-go/kubernetes" 39 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 40 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 41 | // ) 42 | // 43 | // kclientset, _ := kubernetes.NewForConfig(c) 44 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 45 | // 46 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 47 | // correctly. 48 | var AddToScheme = localSchemeBuilder.AddToScheme 49 | 50 | func init() { 51 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 52 | utilruntime.Must(AddToScheme(scheme)) 53 | } 54 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by client-gen. DO NOT EDIT. 15 | 16 | // This package contains the scheme of the automatically generated clientset. 17 | package scheme 18 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by client-gen. DO NOT EDIT. 15 | 16 | package scheme 17 | 18 | import ( 19 | tikvv1alpha1 "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 20 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | runtime "k8s.io/apimachinery/pkg/runtime" 22 | schema "k8s.io/apimachinery/pkg/runtime/schema" 23 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 24 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 25 | ) 26 | 27 | var Scheme = runtime.NewScheme() 28 | var Codecs = serializer.NewCodecFactory(Scheme) 29 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 30 | var localSchemeBuilder = runtime.SchemeBuilder{ 31 | tikvv1alpha1.AddToScheme, 32 | } 33 | 34 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 35 | // of clientsets, like in: 36 | // 37 | // import ( 38 | // "k8s.io/client-go/kubernetes" 39 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 40 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 41 | // ) 42 | // 43 | // kclientset, _ := kubernetes.NewForConfig(c) 44 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 45 | // 46 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 47 | // correctly. 48 | var AddToScheme = localSchemeBuilder.AddToScheme 49 | 50 | func init() { 51 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 52 | utilruntime.Must(AddToScheme(Scheme)) 53 | } 54 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/tikv/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by client-gen. DO NOT EDIT. 15 | 16 | // This package has the automatically generated typed clients. 17 | package v1alpha1 18 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/tikv/v1alpha1/fake/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by client-gen. DO NOT EDIT. 15 | 16 | // Package fake has the automatically generated clients. 17 | package fake 18 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/tikv/v1alpha1/fake/fake_tikv_client.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by client-gen. DO NOT EDIT. 15 | 16 | package fake 17 | 18 | import ( 19 | v1alpha1 "github.com/tikv/tikv-operator/pkg/client/clientset/versioned/typed/tikv/v1alpha1" 20 | rest "k8s.io/client-go/rest" 21 | testing "k8s.io/client-go/testing" 22 | ) 23 | 24 | type FakeTikvV1alpha1 struct { 25 | *testing.Fake 26 | } 27 | 28 | func (c *FakeTikvV1alpha1) TikvClusters(namespace string) v1alpha1.TikvClusterInterface { 29 | return &FakeTikvClusters{c, namespace} 30 | } 31 | 32 | // RESTClient returns a RESTClient that is used to communicate 33 | // with API server by this client implementation. 34 | func (c *FakeTikvV1alpha1) RESTClient() rest.Interface { 35 | var ret *rest.RESTClient 36 | return ret 37 | } 38 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/tikv/v1alpha1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by client-gen. DO NOT EDIT. 15 | 16 | package v1alpha1 17 | 18 | type TikvClusterExpansion interface{} 19 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/tikv/v1alpha1/tikv_client.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by client-gen. DO NOT EDIT. 15 | 16 | package v1alpha1 17 | 18 | import ( 19 | v1alpha1 "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 20 | "github.com/tikv/tikv-operator/pkg/client/clientset/versioned/scheme" 21 | rest "k8s.io/client-go/rest" 22 | ) 23 | 24 | type TikvV1alpha1Interface interface { 25 | RESTClient() rest.Interface 26 | TikvClustersGetter 27 | } 28 | 29 | // TikvV1alpha1Client is used to interact with features provided by the tikv.org group. 30 | type TikvV1alpha1Client struct { 31 | restClient rest.Interface 32 | } 33 | 34 | func (c *TikvV1alpha1Client) TikvClusters(namespace string) TikvClusterInterface { 35 | return newTikvClusters(c, namespace) 36 | } 37 | 38 | // NewForConfig creates a new TikvV1alpha1Client for the given config. 39 | func NewForConfig(c *rest.Config) (*TikvV1alpha1Client, error) { 40 | config := *c 41 | if err := setConfigDefaults(&config); err != nil { 42 | return nil, err 43 | } 44 | client, err := rest.RESTClientFor(&config) 45 | if err != nil { 46 | return nil, err 47 | } 48 | return &TikvV1alpha1Client{client}, nil 49 | } 50 | 51 | // NewForConfigOrDie creates a new TikvV1alpha1Client for the given config and 52 | // panics if there is an error in the config. 53 | func NewForConfigOrDie(c *rest.Config) *TikvV1alpha1Client { 54 | client, err := NewForConfig(c) 55 | if err != nil { 56 | panic(err) 57 | } 58 | return client 59 | } 60 | 61 | // New creates a new TikvV1alpha1Client for the given RESTClient. 62 | func New(c rest.Interface) *TikvV1alpha1Client { 63 | return &TikvV1alpha1Client{c} 64 | } 65 | 66 | func setConfigDefaults(config *rest.Config) error { 67 | gv := v1alpha1.SchemeGroupVersion 68 | config.GroupVersion = &gv 69 | config.APIPath = "/apis" 70 | config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() 71 | 72 | if config.UserAgent == "" { 73 | config.UserAgent = rest.DefaultKubernetesUserAgent() 74 | } 75 | 76 | return nil 77 | } 78 | 79 | // RESTClient returns a RESTClient that is used to communicate 80 | // with API server by this client implementation. 81 | func (c *TikvV1alpha1Client) RESTClient() rest.Interface { 82 | if c == nil { 83 | return nil 84 | } 85 | return c.restClient 86 | } 87 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by informer-gen. DO NOT EDIT. 15 | 16 | package externalversions 17 | 18 | import ( 19 | "fmt" 20 | 21 | v1alpha1 "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 22 | schema "k8s.io/apimachinery/pkg/runtime/schema" 23 | cache "k8s.io/client-go/tools/cache" 24 | ) 25 | 26 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other 27 | // sharedInformers based on type 28 | type GenericInformer interface { 29 | Informer() cache.SharedIndexInformer 30 | Lister() cache.GenericLister 31 | } 32 | 33 | type genericInformer struct { 34 | informer cache.SharedIndexInformer 35 | resource schema.GroupResource 36 | } 37 | 38 | // Informer returns the SharedIndexInformer. 39 | func (f *genericInformer) Informer() cache.SharedIndexInformer { 40 | return f.informer 41 | } 42 | 43 | // Lister returns the GenericLister. 44 | func (f *genericInformer) Lister() cache.GenericLister { 45 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) 46 | } 47 | 48 | // ForResource gives generic access to a shared informer of the matching type 49 | // TODO extend this to unknown resources with a client pool 50 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { 51 | switch resource { 52 | // Group=tikv.org, Version=v1alpha1 53 | case v1alpha1.SchemeGroupVersion.WithResource("tikvclusters"): 54 | return &genericInformer{resource: resource.GroupResource(), informer: f.Tikv().V1alpha1().TikvClusters().Informer()}, nil 55 | 56 | } 57 | 58 | return nil, fmt.Errorf("no informer found for %v", resource) 59 | } 60 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by informer-gen. DO NOT EDIT. 15 | 16 | package internalinterfaces 17 | 18 | import ( 19 | time "time" 20 | 21 | versioned "github.com/tikv/tikv-operator/pkg/client/clientset/versioned" 22 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | runtime "k8s.io/apimachinery/pkg/runtime" 24 | cache "k8s.io/client-go/tools/cache" 25 | ) 26 | 27 | // NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. 28 | type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer 29 | 30 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 31 | type SharedInformerFactory interface { 32 | Start(stopCh <-chan struct{}) 33 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 34 | } 35 | 36 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions. 37 | type TweakListOptionsFunc func(*v1.ListOptions) 38 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/tikv/interface.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by informer-gen. DO NOT EDIT. 15 | 16 | package tikv 17 | 18 | import ( 19 | internalinterfaces "github.com/tikv/tikv-operator/pkg/client/informers/externalversions/internalinterfaces" 20 | v1alpha1 "github.com/tikv/tikv-operator/pkg/client/informers/externalversions/tikv/v1alpha1" 21 | ) 22 | 23 | // Interface provides access to each of this group's versions. 24 | type Interface interface { 25 | // V1alpha1 provides access to shared informers for resources in V1alpha1. 26 | V1alpha1() v1alpha1.Interface 27 | } 28 | 29 | type group struct { 30 | factory internalinterfaces.SharedInformerFactory 31 | namespace string 32 | tweakListOptions internalinterfaces.TweakListOptionsFunc 33 | } 34 | 35 | // New returns a new Interface. 36 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 37 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 38 | } 39 | 40 | // V1alpha1 returns a new v1alpha1.Interface. 41 | func (g *group) V1alpha1() v1alpha1.Interface { 42 | return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) 43 | } 44 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/tikv/v1alpha1/interface.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by informer-gen. DO NOT EDIT. 15 | 16 | package v1alpha1 17 | 18 | import ( 19 | internalinterfaces "github.com/tikv/tikv-operator/pkg/client/informers/externalversions/internalinterfaces" 20 | ) 21 | 22 | // Interface provides access to all the informers in this group version. 23 | type Interface interface { 24 | // TikvClusters returns a TikvClusterInformer. 25 | TikvClusters() TikvClusterInformer 26 | } 27 | 28 | type version struct { 29 | factory internalinterfaces.SharedInformerFactory 30 | namespace string 31 | tweakListOptions internalinterfaces.TweakListOptionsFunc 32 | } 33 | 34 | // New returns a new Interface. 35 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 36 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 37 | } 38 | 39 | // TikvClusters returns a TikvClusterInformer. 40 | func (v *version) TikvClusters() TikvClusterInformer { 41 | return &tikvClusterInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 42 | } 43 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/tikv/v1alpha1/tikvcluster.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by informer-gen. DO NOT EDIT. 15 | 16 | package v1alpha1 17 | 18 | import ( 19 | time "time" 20 | 21 | tikvv1alpha1 "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 22 | versioned "github.com/tikv/tikv-operator/pkg/client/clientset/versioned" 23 | internalinterfaces "github.com/tikv/tikv-operator/pkg/client/informers/externalversions/internalinterfaces" 24 | v1alpha1 "github.com/tikv/tikv-operator/pkg/client/listers/tikv/v1alpha1" 25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | runtime "k8s.io/apimachinery/pkg/runtime" 27 | watch "k8s.io/apimachinery/pkg/watch" 28 | cache "k8s.io/client-go/tools/cache" 29 | ) 30 | 31 | // TikvClusterInformer provides access to a shared informer and lister for 32 | // TikvClusters. 33 | type TikvClusterInformer interface { 34 | Informer() cache.SharedIndexInformer 35 | Lister() v1alpha1.TikvClusterLister 36 | } 37 | 38 | type tikvClusterInformer struct { 39 | factory internalinterfaces.SharedInformerFactory 40 | tweakListOptions internalinterfaces.TweakListOptionsFunc 41 | namespace string 42 | } 43 | 44 | // NewTikvClusterInformer constructs a new informer for TikvCluster type. 45 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 46 | // one. This reduces memory footprint and number of connections to the server. 47 | func NewTikvClusterInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { 48 | return NewFilteredTikvClusterInformer(client, namespace, resyncPeriod, indexers, nil) 49 | } 50 | 51 | // NewFilteredTikvClusterInformer constructs a new informer for TikvCluster type. 52 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 53 | // one. This reduces memory footprint and number of connections to the server. 54 | func NewFilteredTikvClusterInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { 55 | return cache.NewSharedIndexInformer( 56 | &cache.ListWatch{ 57 | ListFunc: func(options v1.ListOptions) (runtime.Object, error) { 58 | if tweakListOptions != nil { 59 | tweakListOptions(&options) 60 | } 61 | return client.TikvV1alpha1().TikvClusters(namespace).List(options) 62 | }, 63 | WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { 64 | if tweakListOptions != nil { 65 | tweakListOptions(&options) 66 | } 67 | return client.TikvV1alpha1().TikvClusters(namespace).Watch(options) 68 | }, 69 | }, 70 | &tikvv1alpha1.TikvCluster{}, 71 | resyncPeriod, 72 | indexers, 73 | ) 74 | } 75 | 76 | func (f *tikvClusterInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { 77 | return NewFilteredTikvClusterInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) 78 | } 79 | 80 | func (f *tikvClusterInformer) Informer() cache.SharedIndexInformer { 81 | return f.factory.InformerFor(&tikvv1alpha1.TikvCluster{}, f.defaultInformer) 82 | } 83 | 84 | func (f *tikvClusterInformer) Lister() v1alpha1.TikvClusterLister { 85 | return v1alpha1.NewTikvClusterLister(f.Informer().GetIndexer()) 86 | } 87 | -------------------------------------------------------------------------------- /pkg/client/listers/tikv/v1alpha1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by lister-gen. DO NOT EDIT. 15 | 16 | package v1alpha1 17 | 18 | // TikvClusterListerExpansion allows custom methods to be added to 19 | // TikvClusterLister. 20 | type TikvClusterListerExpansion interface{} 21 | 22 | // TikvClusterNamespaceListerExpansion allows custom methods to be added to 23 | // TikvClusterNamespaceLister. 24 | type TikvClusterNamespaceListerExpansion interface{} 25 | -------------------------------------------------------------------------------- /pkg/client/listers/tikv/v1alpha1/tikvcluster.go: -------------------------------------------------------------------------------- 1 | // Copyright TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Code generated by lister-gen. DO NOT EDIT. 15 | 16 | package v1alpha1 17 | 18 | import ( 19 | v1alpha1 "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 20 | "k8s.io/apimachinery/pkg/api/errors" 21 | "k8s.io/apimachinery/pkg/labels" 22 | "k8s.io/client-go/tools/cache" 23 | ) 24 | 25 | // TikvClusterLister helps list TikvClusters. 26 | type TikvClusterLister interface { 27 | // List lists all TikvClusters in the indexer. 28 | List(selector labels.Selector) (ret []*v1alpha1.TikvCluster, err error) 29 | // TikvClusters returns an object that can list and get TikvClusters. 30 | TikvClusters(namespace string) TikvClusterNamespaceLister 31 | TikvClusterListerExpansion 32 | } 33 | 34 | // tikvClusterLister implements the TikvClusterLister interface. 35 | type tikvClusterLister struct { 36 | indexer cache.Indexer 37 | } 38 | 39 | // NewTikvClusterLister returns a new TikvClusterLister. 40 | func NewTikvClusterLister(indexer cache.Indexer) TikvClusterLister { 41 | return &tikvClusterLister{indexer: indexer} 42 | } 43 | 44 | // List lists all TikvClusters in the indexer. 45 | func (s *tikvClusterLister) List(selector labels.Selector) (ret []*v1alpha1.TikvCluster, err error) { 46 | err = cache.ListAll(s.indexer, selector, func(m interface{}) { 47 | ret = append(ret, m.(*v1alpha1.TikvCluster)) 48 | }) 49 | return ret, err 50 | } 51 | 52 | // TikvClusters returns an object that can list and get TikvClusters. 53 | func (s *tikvClusterLister) TikvClusters(namespace string) TikvClusterNamespaceLister { 54 | return tikvClusterNamespaceLister{indexer: s.indexer, namespace: namespace} 55 | } 56 | 57 | // TikvClusterNamespaceLister helps list and get TikvClusters. 58 | type TikvClusterNamespaceLister interface { 59 | // List lists all TikvClusters in the indexer for a given namespace. 60 | List(selector labels.Selector) (ret []*v1alpha1.TikvCluster, err error) 61 | // Get retrieves the TikvCluster from the indexer for a given namespace and name. 62 | Get(name string) (*v1alpha1.TikvCluster, error) 63 | TikvClusterNamespaceListerExpansion 64 | } 65 | 66 | // tikvClusterNamespaceLister implements the TikvClusterNamespaceLister 67 | // interface. 68 | type tikvClusterNamespaceLister struct { 69 | indexer cache.Indexer 70 | namespace string 71 | } 72 | 73 | // List lists all TikvClusters in the indexer for a given namespace. 74 | func (s tikvClusterNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.TikvCluster, err error) { 75 | err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { 76 | ret = append(ret, m.(*v1alpha1.TikvCluster)) 77 | }) 78 | return ret, err 79 | } 80 | 81 | // Get retrieves the TikvCluster from the indexer for a given namespace and name. 82 | func (s tikvClusterNamespaceLister) Get(name string) (*v1alpha1.TikvCluster, error) { 83 | obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) 84 | if err != nil { 85 | return nil, err 86 | } 87 | if !exists { 88 | return nil, errors.NewNotFound(v1alpha1.Resource("tikvcluster"), name) 89 | } 90 | return obj.(*v1alpha1.TikvCluster), nil 91 | } 92 | -------------------------------------------------------------------------------- /pkg/controller/equality.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package controller 15 | 16 | import ( 17 | "encoding/json" 18 | "fmt" 19 | 20 | appsv1 "k8s.io/api/apps/v1" 21 | corev1 "k8s.io/api/core/v1" 22 | extensionsv1beta1 "k8s.io/api/extensions/v1beta1" 23 | apiequality "k8s.io/apimachinery/pkg/api/equality" 24 | "k8s.io/klog" 25 | ) 26 | 27 | const ( 28 | // LastAppliedPodTemplate is annotation key of the last applied pod template 29 | LastAppliedPodTemplate = "tikv.org/last-applied-podtemplate" 30 | 31 | // LastAppliedConfigAnnotation is annotation key of last applied configuration 32 | LastAppliedConfigAnnotation = "tikv.org/last-applied-configuration" 33 | ) 34 | 35 | // GetDeploymentLastAppliedPodTemplate set last applied pod template from Deployment's annotation 36 | func GetDeploymentLastAppliedPodTemplate(dep *appsv1.Deployment) (*corev1.PodSpec, error) { 37 | applied, ok := dep.Annotations[LastAppliedPodTemplate] 38 | if !ok { 39 | return nil, fmt.Errorf("deployment:[%s/%s] not found spec's apply config", dep.GetNamespace(), dep.GetName()) 40 | } 41 | podSpec := &corev1.PodSpec{} 42 | err := json.Unmarshal([]byte(applied), podSpec) 43 | if err != nil { 44 | return nil, err 45 | } 46 | return podSpec, nil 47 | } 48 | 49 | // DeploymentPodSpecChanged checks whether the new deployment differs with the old one's last-applied-config 50 | func DeploymentPodSpecChanged(newDep *appsv1.Deployment, oldDep *appsv1.Deployment) bool { 51 | lastAppliedPodTemplate, err := GetDeploymentLastAppliedPodTemplate(oldDep) 52 | if err != nil { 53 | klog.Warningf("error get last-applied-config of deployment %s/%s: %v", oldDep.Namespace, oldDep.Name, err) 54 | return true 55 | } 56 | return !apiequality.Semantic.DeepEqual(newDep.Spec.Template.Spec, lastAppliedPodTemplate) 57 | } 58 | 59 | // SetServiceLastAppliedConfigAnnotation set last applied config info to Service's annotation 60 | func SetServiceLastAppliedConfigAnnotation(svc *corev1.Service) error { 61 | b, err := json.Marshal(svc.Spec) 62 | if err != nil { 63 | return err 64 | } 65 | if svc.Annotations == nil { 66 | svc.Annotations = map[string]string{} 67 | } 68 | svc.Annotations[LastAppliedConfigAnnotation] = string(b) 69 | return nil 70 | } 71 | 72 | // ServiceEqual compares the new Service's spec with old Service's last applied config 73 | func ServiceEqual(newSvc, oldSvc *corev1.Service) (bool, error) { 74 | oldSpec := corev1.ServiceSpec{} 75 | if lastAppliedConfig, ok := oldSvc.Annotations[LastAppliedConfigAnnotation]; ok { 76 | err := json.Unmarshal([]byte(lastAppliedConfig), &oldSpec) 77 | if err != nil { 78 | klog.Errorf("unmarshal ServiceSpec: [%s/%s]'s applied config failed,error: %v", oldSvc.GetNamespace(), oldSvc.GetName(), err) 79 | return false, err 80 | } 81 | return apiequality.Semantic.DeepEqual(oldSpec, newSvc.Spec), nil 82 | } 83 | return false, nil 84 | } 85 | 86 | func IngressEqual(newIngress, oldIngres *extensionsv1beta1.Ingress) (bool, error) { 87 | oldIngressSpec := extensionsv1beta1.IngressSpec{} 88 | if lastAppliedConfig, ok := oldIngres.Annotations[LastAppliedConfigAnnotation]; ok { 89 | err := json.Unmarshal([]byte(lastAppliedConfig), &oldIngressSpec) 90 | if err != nil { 91 | klog.Errorf("unmarshal IngressSpec: [%s/%s]'s applied config failed,error: %v", oldIngres.GetNamespace(), oldIngres.GetName(), err) 92 | return false, err 93 | } 94 | return apiequality.Semantic.DeepEqual(oldIngressSpec, newIngress.Spec), nil 95 | } 96 | return false, nil 97 | } 98 | -------------------------------------------------------------------------------- /pkg/controller/general_pvc_control.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package controller 15 | 16 | import ( 17 | "fmt" 18 | "strings" 19 | 20 | "github.com/tikv/tikv-operator/pkg/label" 21 | corev1 "k8s.io/api/core/v1" 22 | "k8s.io/apimachinery/pkg/runtime" 23 | coreinformers "k8s.io/client-go/informers/core/v1" 24 | "k8s.io/client-go/kubernetes" 25 | corelisters "k8s.io/client-go/listers/core/v1" 26 | "k8s.io/client-go/tools/cache" 27 | "k8s.io/client-go/tools/record" 28 | "k8s.io/klog" 29 | ) 30 | 31 | // GeneralPVCControlInterface manages PVCs used in backup and restore's pvc 32 | type GeneralPVCControlInterface interface { 33 | CreatePVC(object runtime.Object, pvc *corev1.PersistentVolumeClaim) error 34 | } 35 | 36 | type realGeneralPVCControl struct { 37 | kubeCli kubernetes.Interface 38 | recorder record.EventRecorder 39 | } 40 | 41 | // NewRealGeneralPVCControl creates a new GeneralPVCControlInterface 42 | func NewRealGeneralPVCControl( 43 | kubeCli kubernetes.Interface, 44 | recorder record.EventRecorder, 45 | ) GeneralPVCControlInterface { 46 | return &realGeneralPVCControl{ 47 | kubeCli: kubeCli, 48 | recorder: recorder, 49 | } 50 | } 51 | 52 | func (gpc *realGeneralPVCControl) CreatePVC(object runtime.Object, pvc *corev1.PersistentVolumeClaim) error { 53 | ns := pvc.GetNamespace() 54 | pvcName := pvc.GetName() 55 | instanceName := pvc.GetLabels()[label.InstanceLabelKey] 56 | kind := object.GetObjectKind().GroupVersionKind().Kind 57 | 58 | _, err := gpc.kubeCli.CoreV1().PersistentVolumeClaims(ns).Create(pvc) 59 | if err != nil { 60 | klog.Errorf("failed to create pvc: [%s/%s], %s: %s, %v", ns, pvcName, kind, instanceName, err) 61 | } else { 62 | klog.V(4).Infof("create pvc: [%s/%s] successfully, %s: %s", ns, pvcName, kind, instanceName) 63 | } 64 | gpc.recordPVCEvent("create", object, pvc, err) 65 | return err 66 | } 67 | 68 | func (gpc *realGeneralPVCControl) recordPVCEvent(verb string, obj runtime.Object, pvc *corev1.PersistentVolumeClaim, err error) { 69 | pvcName := pvc.GetName() 70 | ns := pvc.GetNamespace() 71 | instanceName := pvc.GetLabels()[label.InstanceLabelKey] 72 | kind := obj.GetObjectKind().GroupVersionKind().Kind 73 | if err == nil { 74 | reason := fmt.Sprintf("Successful%s", strings.Title(verb)) 75 | msg := fmt.Sprintf("%s PVC %s/%s for %s/%s successful", 76 | strings.ToLower(verb), ns, pvcName, kind, instanceName) 77 | gpc.recorder.Event(obj, corev1.EventTypeNormal, reason, msg) 78 | } else { 79 | reason := fmt.Sprintf("Failed%s", strings.Title(verb)) 80 | msg := fmt.Sprintf("%s PVC %s/%s for %s/%s failed error: %s", 81 | strings.ToLower(verb), ns, pvcName, kind, instanceName, err) 82 | gpc.recorder.Event(obj, corev1.EventTypeWarning, reason, msg) 83 | } 84 | } 85 | 86 | var _ GeneralPVCControlInterface = &realGeneralPVCControl{} 87 | 88 | // FakeGeneralPVCControl is a fake GeneralPVCControlInterface 89 | type FakeGeneralPVCControl struct { 90 | PVCLister corelisters.PersistentVolumeClaimLister 91 | PVCIndexer cache.Indexer 92 | createPVCTracker RequestTracker 93 | } 94 | 95 | // NewFakeGeneralPVCControl returns a FakeGeneralPVCControl 96 | func NewFakeGeneralPVCControl(pvcInformer coreinformers.PersistentVolumeClaimInformer) *FakeGeneralPVCControl { 97 | return &FakeGeneralPVCControl{ 98 | pvcInformer.Lister(), 99 | pvcInformer.Informer().GetIndexer(), 100 | RequestTracker{}, 101 | } 102 | } 103 | 104 | // SetCreatePVCError sets the error attributes of createPVCTracker 105 | func (fjc *FakeGeneralPVCControl) SetCreatePVCError(err error, after int) { 106 | fjc.createPVCTracker.SetError(err).SetAfter(after) 107 | } 108 | 109 | // CreatePVC adds the pvc to PVCIndexer 110 | func (fjc *FakeGeneralPVCControl) CreatePVC(_ runtime.Object, pvc *corev1.PersistentVolumeClaim) error { 111 | defer fjc.createPVCTracker.Inc() 112 | if fjc.createPVCTracker.ErrorReady() { 113 | defer fjc.createPVCTracker.Reset() 114 | return fjc.createPVCTracker.GetError() 115 | } 116 | 117 | return fjc.PVCIndexer.Add(pvc) 118 | } 119 | 120 | var _ GeneralPVCControlInterface = &FakeGeneralPVCControl{} 121 | -------------------------------------------------------------------------------- /pkg/controller/pd_control.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package controller 15 | 16 | import ( 17 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 18 | "github.com/tikv/tikv-operator/pkg/pdapi" 19 | ) 20 | 21 | // GetPDClient gets the pd client from the TikvCluster 22 | func GetPDClient(pdControl pdapi.PDControlInterface, tc *v1alpha1.TikvCluster) pdapi.PDClient { 23 | return pdControl.GetPDClient(pdapi.Namespace(tc.GetNamespace()), tc.GetName(), tc.IsTLSClusterEnabled()) 24 | } 25 | 26 | // NewFakePDClient creates a fake pdclient that is set as the pd client 27 | func NewFakePDClient(pdControl *pdapi.FakePDControl, tc *v1alpha1.TikvCluster) *pdapi.FakePDClient { 28 | pdClient := pdapi.NewFakePDClient() 29 | pdControl.SetPDClient(pdapi.Namespace(tc.GetNamespace()), tc.GetName(), pdClient) 30 | return pdClient 31 | } 32 | -------------------------------------------------------------------------------- /pkg/controller/secret_control.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package controller 15 | 16 | import ( 17 | "crypto/tls" 18 | "crypto/x509" 19 | "encoding/pem" 20 | 21 | certutil "github.com/tikv/tikv-operator/pkg/util/crypto" 22 | v1 "k8s.io/api/core/v1" 23 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | "k8s.io/client-go/kubernetes" 25 | "k8s.io/klog" 26 | ) 27 | 28 | // SecretControlInterface manages certificates used by TiDB clusters 29 | type SecretControlInterface interface { 30 | Load(ns string, secretName string) ([]byte, []byte, error) 31 | Check(ns string, secretName string) bool 32 | } 33 | 34 | type realSecretControl struct { 35 | kubeCli kubernetes.Interface 36 | } 37 | 38 | // NewRealSecretControl creates a new SecretControlInterface 39 | func NewRealSecretControl( 40 | kubeCli kubernetes.Interface, 41 | ) SecretControlInterface { 42 | return &realSecretControl{ 43 | kubeCli: kubeCli, 44 | } 45 | } 46 | 47 | // Load loads cert and key from Secret matching the name 48 | func (rsc *realSecretControl) Load(ns string, secretName string) ([]byte, []byte, error) { 49 | secret, err := rsc.kubeCli.CoreV1().Secrets(ns).Get(secretName, metav1.GetOptions{}) 50 | if err != nil { 51 | return nil, nil, err 52 | } 53 | 54 | return secret.Data[v1.TLSCertKey], secret.Data[v1.TLSPrivateKeyKey], nil 55 | } 56 | 57 | // Check returns true if the secret already exist 58 | func (rsc *realSecretControl) Check(ns string, secretName string) bool { 59 | certBytes, keyBytes, err := rsc.Load(ns, secretName) 60 | if err != nil { 61 | klog.Errorf("certificate validation failed for [%s/%s], error loading cert from secret, %v", ns, secretName, err) 62 | return false 63 | } 64 | 65 | // validate if the certificate is valid 66 | block, _ := pem.Decode(certBytes) 67 | if block == nil { 68 | klog.Errorf("certificate validation failed for [%s/%s], can not decode cert to PEM", ns, secretName) 69 | return false 70 | } 71 | cert, err := x509.ParseCertificate(block.Bytes) 72 | if err != nil { 73 | klog.Errorf("certificate validation failed for [%s/%s], can not parse cert, %v", ns, secretName, err) 74 | return false 75 | } 76 | rootCAs, err := certutil.ReadCACerts() 77 | if err != nil { 78 | klog.Errorf("certificate validation failed for [%s/%s], error loading CAs, %v", ns, secretName, err) 79 | return false 80 | } 81 | 82 | verifyOpts := x509.VerifyOptions{ 83 | Roots: rootCAs, 84 | KeyUsages: []x509.ExtKeyUsage{ 85 | x509.ExtKeyUsageClientAuth, 86 | x509.ExtKeyUsageServerAuth, 87 | }, 88 | } 89 | _, err = cert.Verify(verifyOpts) 90 | if err != nil { 91 | klog.Errorf("certificate validation failed for [%s/%s], %v", ns, secretName, err) 92 | return false 93 | } 94 | 95 | // validate if the certificate and private key matches 96 | _, err = tls.X509KeyPair(certBytes, keyBytes) 97 | if err != nil { 98 | klog.Errorf("certificate validation failed for [%s/%s], error loading key pair, %v", ns, secretName, err) 99 | return false 100 | } 101 | 102 | return true 103 | } 104 | 105 | var _ SecretControlInterface = &realSecretControl{} 106 | 107 | type FakeSecretControl struct { 108 | realSecretControl 109 | } 110 | 111 | func NewFakeSecretControl( 112 | kubeCli kubernetes.Interface, 113 | ) SecretControlInterface { 114 | return &realSecretControl{ 115 | kubeCli: kubeCli, 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /pkg/controller/tikvcluster/tikv_cluster_condition_updater.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package tikvcluster 15 | 16 | import ( 17 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 18 | utiltikvcluster "github.com/tikv/tikv-operator/pkg/util/tikvcluster" 19 | appsv1 "k8s.io/api/apps/v1" 20 | v1 "k8s.io/api/core/v1" 21 | ) 22 | 23 | // TikvClusterConditionUpdater interface that translates cluster state into 24 | // into tikv cluster status conditions. 25 | type TikvClusterConditionUpdater interface { 26 | Update(*v1alpha1.TikvCluster) error 27 | } 28 | 29 | type tikvClusterConditionUpdater struct { 30 | } 31 | 32 | var _ TikvClusterConditionUpdater = &tikvClusterConditionUpdater{} 33 | 34 | func (u *tikvClusterConditionUpdater) Update(tc *v1alpha1.TikvCluster) error { 35 | u.updateReadyCondition(tc) 36 | // in the future, we may return error when we need to Kubernetes API, etc. 37 | return nil 38 | } 39 | 40 | func allStatefulSetsAreUpToDate(tc *v1alpha1.TikvCluster) bool { 41 | isUpToDate := func(status *appsv1.StatefulSetStatus, requireExist bool) bool { 42 | if status == nil { 43 | return !requireExist 44 | } 45 | return status.CurrentRevision == status.UpdateRevision 46 | } 47 | return isUpToDate(tc.Status.PD.StatefulSet, true) && 48 | isUpToDate(tc.Status.TiKV.StatefulSet, true) 49 | } 50 | 51 | func (u *tikvClusterConditionUpdater) updateReadyCondition(tc *v1alpha1.TikvCluster) { 52 | status := v1.ConditionFalse 53 | reason := "" 54 | message := "" 55 | 56 | switch { 57 | case !allStatefulSetsAreUpToDate(tc): 58 | reason = utiltikvcluster.StatfulSetNotUpToDate 59 | message = "Statefulset(s) are in progress" 60 | case !tc.PDAllMembersReady(): 61 | reason = utiltikvcluster.PDUnhealthy 62 | message = "PD(s) are not healthy" 63 | case !tc.TiKVAllStoresReady(): 64 | reason = utiltikvcluster.TiKVStoreNotUp 65 | message = "TiKV store(s) are not up" 66 | default: 67 | status = v1.ConditionTrue 68 | reason = utiltikvcluster.Ready 69 | message = "TiKV cluster is fully up and running" 70 | } 71 | cond := utiltikvcluster.NewTikvClusterCondition(v1alpha1.TikvClusterReady, status, reason, message) 72 | utiltikvcluster.SetTikvClusterCondition(&tc.Status, *cond) 73 | } 74 | -------------------------------------------------------------------------------- /pkg/controller/tikvcluster/tikv_cluster_condition_updater_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package tikvcluster 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/google/go-cmp/cmp" 20 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 21 | utiltikvcluster "github.com/tikv/tikv-operator/pkg/util/tikvcluster" 22 | appsv1 "k8s.io/api/apps/v1" 23 | v1 "k8s.io/api/core/v1" 24 | ) 25 | 26 | func TestTikvClusterConditionUpdater_Ready(t *testing.T) { 27 | tests := []struct { 28 | name string 29 | tc *v1alpha1.TikvCluster 30 | wantStatus v1.ConditionStatus 31 | wantReason string 32 | wantMessage string 33 | }{ 34 | { 35 | name: "statfulset(s) not up to date", 36 | tc: &v1alpha1.TikvCluster{ 37 | Status: v1alpha1.TikvClusterStatus{ 38 | PD: v1alpha1.PDStatus{ 39 | StatefulSet: &appsv1.StatefulSetStatus{ 40 | CurrentRevision: "1", 41 | UpdateRevision: "2", 42 | }, 43 | }, 44 | TiKV: v1alpha1.TiKVStatus{ 45 | StatefulSet: &appsv1.StatefulSetStatus{ 46 | CurrentRevision: "1", 47 | UpdateRevision: "2", 48 | }, 49 | }, 50 | }, 51 | }, 52 | wantStatus: v1.ConditionFalse, 53 | wantReason: utiltikvcluster.StatfulSetNotUpToDate, 54 | wantMessage: "Statefulset(s) are in progress", 55 | }, 56 | { 57 | name: "pd(s) not healthy", 58 | tc: &v1alpha1.TikvCluster{ 59 | Spec: v1alpha1.TikvClusterSpec{ 60 | PD: v1alpha1.PDSpec{ 61 | Replicas: 1, 62 | }, 63 | }, 64 | Status: v1alpha1.TikvClusterStatus{ 65 | PD: v1alpha1.PDStatus{ 66 | Members: map[string]v1alpha1.PDMember{ 67 | "pd-1": { 68 | Health: false, 69 | }, 70 | }, 71 | StatefulSet: &appsv1.StatefulSetStatus{ 72 | CurrentRevision: "2", 73 | UpdateRevision: "2", 74 | }, 75 | }, 76 | TiKV: v1alpha1.TiKVStatus{ 77 | StatefulSet: &appsv1.StatefulSetStatus{ 78 | CurrentRevision: "2", 79 | UpdateRevision: "2", 80 | }, 81 | }, 82 | }, 83 | }, 84 | wantStatus: v1.ConditionFalse, 85 | wantReason: utiltikvcluster.PDUnhealthy, 86 | wantMessage: "PD(s) are not healthy", 87 | }, 88 | { 89 | name: "tikv(s) not healthy", 90 | tc: &v1alpha1.TikvCluster{ 91 | Spec: v1alpha1.TikvClusterSpec{ 92 | PD: v1alpha1.PDSpec{ 93 | Replicas: 1, 94 | }, 95 | TiKV: v1alpha1.TiKVSpec{ 96 | Replicas: 1, 97 | }, 98 | }, 99 | Status: v1alpha1.TikvClusterStatus{ 100 | PD: v1alpha1.PDStatus{ 101 | Members: map[string]v1alpha1.PDMember{ 102 | "pd-0": { 103 | Health: true, 104 | }, 105 | }, 106 | StatefulSet: &appsv1.StatefulSetStatus{ 107 | CurrentRevision: "2", 108 | UpdateRevision: "2", 109 | }, 110 | }, 111 | TiKV: v1alpha1.TiKVStatus{ 112 | Stores: map[string]v1alpha1.TiKVStore{ 113 | "tikv-0": { 114 | State: "Down", 115 | }, 116 | }, 117 | StatefulSet: &appsv1.StatefulSetStatus{ 118 | CurrentRevision: "2", 119 | UpdateRevision: "2", 120 | }, 121 | }, 122 | }, 123 | }, 124 | wantStatus: v1.ConditionFalse, 125 | wantReason: utiltikvcluster.TiKVStoreNotUp, 126 | wantMessage: "TiKV store(s) are not up", 127 | }, 128 | { 129 | name: "all ready", 130 | tc: &v1alpha1.TikvCluster{ 131 | Spec: v1alpha1.TikvClusterSpec{ 132 | PD: v1alpha1.PDSpec{ 133 | Replicas: 1, 134 | }, 135 | TiKV: v1alpha1.TiKVSpec{ 136 | Replicas: 1, 137 | }, 138 | }, 139 | Status: v1alpha1.TikvClusterStatus{ 140 | PD: v1alpha1.PDStatus{ 141 | Members: map[string]v1alpha1.PDMember{ 142 | "pd-0": { 143 | Health: true, 144 | }, 145 | }, 146 | StatefulSet: &appsv1.StatefulSetStatus{ 147 | CurrentRevision: "2", 148 | UpdateRevision: "2", 149 | }, 150 | }, 151 | TiKV: v1alpha1.TiKVStatus{ 152 | Stores: map[string]v1alpha1.TiKVStore{ 153 | "tikv-0": { 154 | State: "Up", 155 | }, 156 | }, 157 | StatefulSet: &appsv1.StatefulSetStatus{ 158 | CurrentRevision: "2", 159 | UpdateRevision: "2", 160 | }, 161 | }, 162 | }, 163 | }, 164 | wantStatus: v1.ConditionTrue, 165 | wantReason: utiltikvcluster.Ready, 166 | wantMessage: "TiKV cluster is fully up and running", 167 | }, 168 | } 169 | 170 | for _, tt := range tests { 171 | t.Run(tt.name, func(t *testing.T) { 172 | conditionUpdater := &tikvClusterConditionUpdater{} 173 | conditionUpdater.Update(tt.tc) 174 | cond := utiltikvcluster.GetTikvClusterCondition(tt.tc.Status, v1alpha1.TikvClusterReady) 175 | if diff := cmp.Diff(tt.wantStatus, cond.Status); diff != "" { 176 | t.Errorf("unexpected status (-want, +got): %s", diff) 177 | } 178 | if diff := cmp.Diff(tt.wantReason, cond.Reason); diff != "" { 179 | t.Errorf("unexpected reason (-want, +got): %s", diff) 180 | } 181 | if diff := cmp.Diff(tt.wantMessage, cond.Message); diff != "" { 182 | t.Errorf("unexpected message (-want, +got): %s", diff) 183 | } 184 | }) 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /pkg/controller/tikvcluster_control_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package controller 15 | 16 | import ( 17 | "errors" 18 | "testing" 19 | 20 | "time" 21 | 22 | . "github.com/onsi/gomega" 23 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 24 | "github.com/tikv/tikv-operator/pkg/client/clientset/versioned/fake" 25 | listers "github.com/tikv/tikv-operator/pkg/client/listers/tikv/v1alpha1" 26 | apierrors "k8s.io/apimachinery/pkg/api/errors" 27 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 | "k8s.io/apimachinery/pkg/runtime" 29 | core "k8s.io/client-go/testing" 30 | "k8s.io/client-go/tools/cache" 31 | "k8s.io/client-go/tools/record" 32 | ) 33 | 34 | func TestTikvClusterControlUpdateTikvCluster(t *testing.T) { 35 | g := NewGomegaWithT(t) 36 | recorder := record.NewFakeRecorder(10) 37 | tc := newTikvCluster() 38 | tc.Spec.PD.Replicas = int32(5) 39 | fakeClient := &fake.Clientset{} 40 | control := NewRealTikvClusterControl(fakeClient, nil, recorder) 41 | fakeClient.AddReactor("update", "tikvclusters", func(action core.Action) (bool, runtime.Object, error) { 42 | update := action.(core.UpdateAction) 43 | return true, update.GetObject(), nil 44 | }) 45 | updateTC, err := control.UpdateTikvCluster(tc, &v1alpha1.TikvClusterStatus{}, &v1alpha1.TikvClusterStatus{}) 46 | g.Expect(err).To(Succeed()) 47 | g.Expect(updateTC.Spec.PD.Replicas).To(Equal(int32(5))) 48 | } 49 | 50 | func TestTikvClusterControlUpdateTikvClusterConflictSuccess(t *testing.T) { 51 | g := NewGomegaWithT(t) 52 | recorder := record.NewFakeRecorder(10) 53 | tc := newTikvCluster() 54 | fakeClient := &fake.Clientset{} 55 | indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) 56 | tcLister := listers.NewTikvClusterLister(indexer) 57 | control := NewRealTikvClusterControl(fakeClient, tcLister, recorder) 58 | conflict := false 59 | fakeClient.AddReactor("update", "tikvclusters", func(action core.Action) (bool, runtime.Object, error) { 60 | update := action.(core.UpdateAction) 61 | if !conflict { 62 | conflict = true 63 | return true, update.GetObject(), apierrors.NewConflict(action.GetResource().GroupResource(), tc.Name, errors.New("conflict")) 64 | } 65 | return true, update.GetObject(), nil 66 | }) 67 | _, err := control.UpdateTikvCluster(tc, &v1alpha1.TikvClusterStatus{}, &v1alpha1.TikvClusterStatus{}) 68 | g.Expect(err).To(Succeed()) 69 | } 70 | 71 | func TestDeepEqualExceptHeartbeatTime(t *testing.T) { 72 | g := NewGomegaWithT(t) 73 | 74 | new := &v1alpha1.TikvClusterStatus{ 75 | TiKV: v1alpha1.TiKVStatus{ 76 | Synced: true, 77 | Stores: map[string]v1alpha1.TiKVStore{ 78 | "1": { 79 | LastHeartbeatTime: metav1.Now(), 80 | ID: "1", 81 | }, 82 | }, 83 | }, 84 | } 85 | time.Sleep(1 * time.Second) 86 | old := &v1alpha1.TikvClusterStatus{ 87 | TiKV: v1alpha1.TiKVStatus{ 88 | Synced: true, 89 | Stores: map[string]v1alpha1.TiKVStore{ 90 | "1": { 91 | LastHeartbeatTime: metav1.Now(), 92 | ID: "1", 93 | }, 94 | }, 95 | }, 96 | } 97 | g.Expect(deepEqualExceptHeartbeatTime(new, old)).To(Equal(true)) 98 | } 99 | -------------------------------------------------------------------------------- /pkg/discovery/discovery.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package discovery 15 | 16 | import ( 17 | "fmt" 18 | "os" 19 | "strings" 20 | "sync" 21 | 22 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 23 | "github.com/tikv/tikv-operator/pkg/client/clientset/versioned" 24 | "github.com/tikv/tikv-operator/pkg/pdapi" 25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | "k8s.io/client-go/kubernetes" 27 | "k8s.io/klog" 28 | ) 29 | 30 | // PDDiscovery helps new PD member to discover all other members in cluster bootstrap phase. 31 | type PDDiscovery interface { 32 | Discover(string) (string, error) 33 | } 34 | 35 | type pdDiscovery struct { 36 | cli versioned.Interface 37 | lock sync.Mutex 38 | clusters map[string]*clusterInfo 39 | tcGetFn func(ns, tcName string) (*v1alpha1.TikvCluster, error) 40 | pdControl pdapi.PDControlInterface 41 | } 42 | 43 | type clusterInfo struct { 44 | resourceVersion string 45 | peers map[string]struct{} 46 | } 47 | 48 | // NewPDDiscovery returns a PDDiscovery 49 | func NewPDDiscovery(cli versioned.Interface, kubeCli kubernetes.Interface) PDDiscovery { 50 | td := &pdDiscovery{ 51 | cli: cli, 52 | pdControl: pdapi.NewDefaultPDControl(kubeCli), 53 | clusters: map[string]*clusterInfo{}, 54 | } 55 | td.tcGetFn = td.realTCGetFn 56 | return td 57 | } 58 | 59 | func (td *pdDiscovery) Discover(advertisePeerUrl string) (string, error) { 60 | td.lock.Lock() 61 | defer td.lock.Unlock() 62 | 63 | if advertisePeerUrl == "" { 64 | return "", fmt.Errorf("advertisePeerUrl is empty") 65 | } 66 | klog.Infof("advertisePeerUrl is: %s", advertisePeerUrl) 67 | strArr := strings.Split(advertisePeerUrl, ".") 68 | if len(strArr) != 4 { 69 | return "", fmt.Errorf("advertisePeerUrl format is wrong: %s", advertisePeerUrl) 70 | } 71 | 72 | podName, peerServiceName, ns := strArr[0], strArr[1], strArr[2] 73 | tcName := strings.TrimSuffix(peerServiceName, "-pd-peer") 74 | podNamespace := os.Getenv("MY_POD_NAMESPACE") 75 | if ns != podNamespace { 76 | return "", fmt.Errorf("the peer's namespace: %s is not equal to discovery namespace: %s", ns, podNamespace) 77 | } 78 | tc, err := td.tcGetFn(ns, tcName) 79 | if err != nil { 80 | return "", err 81 | } 82 | keyName := fmt.Sprintf("%s/%s", ns, tcName) 83 | // TODO: the replicas should be the total replicas of pd sets. 84 | replicas := tc.Spec.PD.Replicas 85 | 86 | currentCluster := td.clusters[keyName] 87 | if currentCluster == nil || currentCluster.resourceVersion != tc.ResourceVersion { 88 | td.clusters[keyName] = &clusterInfo{ 89 | resourceVersion: tc.ResourceVersion, 90 | peers: map[string]struct{}{}, 91 | } 92 | } 93 | currentCluster = td.clusters[keyName] 94 | currentCluster.peers[podName] = struct{}{} 95 | 96 | if len(currentCluster.peers) == int(replicas) { 97 | delete(currentCluster.peers, podName) 98 | return fmt.Sprintf("--initial-cluster=%s=%s://%s", podName, tc.Scheme(), advertisePeerUrl), nil 99 | } 100 | 101 | pdClient := td.pdControl.GetPDClient(pdapi.Namespace(tc.GetNamespace()), tc.GetName(), tc.IsTLSClusterEnabled()) 102 | membersInfo, err := pdClient.GetMembers() 103 | if err != nil { 104 | return "", err 105 | } 106 | 107 | membersArr := make([]string, 0) 108 | for _, member := range membersInfo.Members { 109 | memberURL := strings.ReplaceAll(member.PeerUrls[0], ":2380", ":2379") 110 | membersArr = append(membersArr, memberURL) 111 | } 112 | delete(currentCluster.peers, podName) 113 | return fmt.Sprintf("--join=%s", strings.Join(membersArr, ",")), nil 114 | } 115 | 116 | func (td *pdDiscovery) realTCGetFn(ns, tcName string) (*v1alpha1.TikvCluster, error) { 117 | return td.cli.TikvV1alpha1().TikvClusters(ns).Get(tcName, metav1.GetOptions{}) 118 | } 119 | -------------------------------------------------------------------------------- /pkg/discovery/server/mux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package server 15 | 16 | import ( 17 | "encoding/base64" 18 | "fmt" 19 | "io" 20 | "net/http" 21 | 22 | restful "github.com/emicklei/go-restful" 23 | "github.com/tikv/tikv-operator/pkg/client/clientset/versioned" 24 | "github.com/tikv/tikv-operator/pkg/discovery" 25 | "k8s.io/client-go/kubernetes" 26 | "k8s.io/klog" 27 | ) 28 | 29 | type server struct { 30 | discovery discovery.PDDiscovery 31 | } 32 | 33 | // StartServer starts a TiDB Discovery server 34 | func StartServer(cli versioned.Interface, kubeCli kubernetes.Interface, port int) { 35 | svr := &server{discovery.NewPDDiscovery(cli, kubeCli)} 36 | 37 | ws := new(restful.WebService) 38 | ws.Route(ws.GET("/new/{advertise-peer-url}").To(svr.newHandler)) 39 | restful.Add(ws) 40 | 41 | klog.Infof("starting PD Discovery server, listening on 0.0.0.0:%d", port) 42 | klog.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil)) 43 | } 44 | 45 | func (svr *server) newHandler(req *restful.Request, resp *restful.Response) { 46 | encodedAdvertisePeerURL := req.PathParameter("advertise-peer-url") 47 | data, err := base64.StdEncoding.DecodeString(encodedAdvertisePeerURL) 48 | if err != nil { 49 | klog.Errorf("failed to decode advertise-peer-url: %s", encodedAdvertisePeerURL) 50 | if err := resp.WriteError(http.StatusInternalServerError, err); err != nil { 51 | klog.Errorf("failed to write, error: %v", err) 52 | } 53 | return 54 | } 55 | advertisePeerURL := string(data) 56 | 57 | result, err := svr.discovery.Discover(advertisePeerURL) 58 | if err != nil { 59 | klog.Errorf("failed to discover: %s, %v", advertisePeerURL, err) 60 | if err := resp.WriteError(http.StatusInternalServerError, err); err != nil { 61 | klog.Errorf("failed to write, error: %v", err) 62 | } 63 | return 64 | } 65 | 66 | klog.Infof("generated args for %s: %s", advertisePeerURL, result) 67 | if _, err := io.WriteString(resp, result); err != nil { 68 | klog.Errorf("failed to write, string: %s, %v", result, err) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pkg/features/features.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package features 15 | 16 | import ( 17 | "flag" 18 | "fmt" 19 | "sort" 20 | "strconv" 21 | "strings" 22 | "sync" 23 | 24 | "k8s.io/apimachinery/pkg/util/sets" 25 | "k8s.io/klog" 26 | ) 27 | 28 | var ( 29 | allFeatures = sets.NewString(AdvancedStatefulSet) 30 | defaultFeatures = map[string]bool{ 31 | AdvancedStatefulSet: false, 32 | } 33 | // DefaultFeatureGate is a shared global FeatureGate. 34 | DefaultFeatureGate FeatureGate = NewFeatureGate() 35 | ) 36 | 37 | const ( 38 | // AdvancedStatefulSet controls whether to use AdvancedStatefulSet to manage pods 39 | AdvancedStatefulSet string = "AdvancedStatefulSet" 40 | ) 41 | 42 | type FeatureGate interface { 43 | // AddFlag adds a flag for setting global feature gates to the specified FlagSet. 44 | AddFlag(flagset *flag.FlagSet) 45 | // Enabled returns true if the key is enabled. 46 | Enabled(key string) bool 47 | // Set parses and stores flag gates for known features 48 | // from a string like feature1=true,feature2=false,... 49 | Set(value string) error 50 | // SetFromMap stores flag gates for enabled features from a map[string]bool 51 | SetFromMap(m map[string]bool) 52 | } 53 | 54 | var _ flag.Value = &featureGate{} 55 | 56 | type featureGate struct { 57 | lock sync.Mutex 58 | enabledFeatures map[string]bool 59 | } 60 | 61 | func (f *featureGate) AddFlag(flagset *flag.FlagSet) { 62 | flag.Var(f, "features", fmt.Sprintf("A set of key={true,false} pairs to enable/disable features, available features: %s", strings.Join(allFeatures.List(), ","))) 63 | } 64 | 65 | func (f *featureGate) Enabled(key string) bool { 66 | if b, ok := f.enabledFeatures[key]; ok { 67 | return b 68 | } 69 | return false 70 | } 71 | 72 | // String returns a string containing all enabled feature gates, formatted as "key1=value1,key2=value2,...". 73 | func (f *featureGate) String() string { 74 | pairs := []string{} 75 | for k, v := range f.enabledFeatures { 76 | pairs = append(pairs, fmt.Sprintf("%s=%t", k, v)) 77 | } 78 | sort.Strings(pairs) 79 | return strings.Join(pairs, ",") 80 | } 81 | 82 | func (f *featureGate) Set(value string) error { 83 | m := make(map[string]bool) 84 | for _, s := range strings.Split(value, ",") { 85 | if len(s) == 0 { 86 | continue 87 | } 88 | arr := strings.SplitN(s, "=", 2) 89 | k := strings.TrimSpace(arr[0]) 90 | if len(arr) != 2 { 91 | return fmt.Errorf("missing bool value for %s", k) 92 | } 93 | v := strings.TrimSpace(arr[1]) 94 | boolValue, err := strconv.ParseBool(v) 95 | if err != nil { 96 | return fmt.Errorf("invalid value of %s=%s, err: %v", k, v, err) 97 | } 98 | m[k] = boolValue 99 | } 100 | f.SetFromMap(m) 101 | return nil 102 | } 103 | 104 | func (f *featureGate) SetFromMap(m map[string]bool) { 105 | f.lock.Lock() 106 | defer f.lock.Unlock() 107 | 108 | for k, v := range m { 109 | f.enabledFeatures[k] = v 110 | } 111 | 112 | klog.V(1).Infof("feature gates: %v", f.enabledFeatures) 113 | } 114 | 115 | func NewFeatureGate() FeatureGate { 116 | f := &featureGate{ 117 | enabledFeatures: make(map[string]bool), 118 | } 119 | for k, v := range defaultFeatures { 120 | f.enabledFeatures[k] = v 121 | } 122 | return f 123 | } 124 | -------------------------------------------------------------------------------- /pkg/httputil/httputil.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package httputil 15 | 16 | import ( 17 | "fmt" 18 | "io" 19 | "io/ioutil" 20 | "net/http" 21 | 22 | "k8s.io/klog" 23 | ) 24 | 25 | const ( 26 | k8sCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" 27 | clientCert = "/var/lib/tls/client.crt" 28 | clientKey = "/var/lib/tls/client.key" 29 | ) 30 | 31 | // DeferClose captures and prints the error from closing (if an error occurs). 32 | // This is designed to be used in a defer statement. 33 | func DeferClose(c io.Closer) { 34 | if err := c.Close(); err != nil { 35 | klog.Error(err) 36 | } 37 | } 38 | 39 | // ReadErrorBody in the error case ready the body message. 40 | // But return it as an error (or return an error from reading the body). 41 | func ReadErrorBody(body io.Reader) (err error) { 42 | bodyBytes, err := ioutil.ReadAll(body) 43 | if err != nil { 44 | return err 45 | } 46 | return fmt.Errorf(string(bodyBytes)) 47 | } 48 | 49 | // GetBodyOK returns the body or an error if the response is not okay 50 | func GetBodyOK(httpClient *http.Client, apiURL string) ([]byte, error) { 51 | res, err := httpClient.Get(apiURL) 52 | if err != nil { 53 | return nil, err 54 | } 55 | defer DeferClose(res.Body) 56 | body, err := ioutil.ReadAll(res.Body) 57 | if err != nil { 58 | return nil, err 59 | } 60 | if res.StatusCode >= 400 { 61 | errMsg := fmt.Errorf("Error response %v URL %s,body response: %s", res.StatusCode, apiURL, string(body[:])) 62 | return nil, errMsg 63 | } 64 | return body, err 65 | } 66 | -------------------------------------------------------------------------------- /pkg/manager/manager.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package manager 15 | 16 | import "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 17 | 18 | // Manager implements the logic for syncing tikvcluster. 19 | type Manager interface { 20 | // Sync implements the logic for syncing tikvcluster. 21 | Sync(*v1alpha1.TikvCluster) error 22 | } 23 | -------------------------------------------------------------------------------- /pkg/manager/member/failover.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package member 15 | 16 | import "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 17 | 18 | const ( 19 | unHealthEventReason = "Unhealthy" 20 | unHealthEventMsgPattern = "%s pod[%s] is unhealthy, msg:%s" 21 | ) 22 | 23 | // Failover implements the logic for pd/tikv/tidb's failover and recovery. 24 | type Failover interface { 25 | Failover(*v1alpha1.TikvCluster) error 26 | Recover(*v1alpha1.TikvCluster) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/manager/member/pd_discovery_manager_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package member 15 | 16 | import ( 17 | "context" 18 | "fmt" 19 | "testing" 20 | 21 | . "github.com/onsi/gomega" 22 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 23 | "github.com/tikv/tikv-operator/pkg/controller" 24 | appsv1 "k8s.io/api/apps/v1" 25 | corev1 "k8s.io/api/core/v1" 26 | "k8s.io/apimachinery/pkg/api/resource" 27 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 | "k8s.io/apimachinery/pkg/types" 29 | ) 30 | 31 | func newTikvClusterForPDDiscovery() *v1alpha1.TikvCluster { 32 | return &v1alpha1.TikvCluster{ 33 | TypeMeta: metav1.TypeMeta{ 34 | Kind: "TikvCluster", 35 | APIVersion: "tikv.org/v1alpha1", 36 | }, 37 | ObjectMeta: metav1.ObjectMeta{ 38 | Name: "test", 39 | Namespace: corev1.NamespaceDefault, 40 | UID: types.UID("test"), 41 | }, 42 | Spec: v1alpha1.TikvClusterSpec{ 43 | PD: v1alpha1.PDSpec{ 44 | ComponentSpec: v1alpha1.ComponentSpec{ 45 | Image: v1alpha1.PDMemberType.String(), 46 | }, 47 | ResourceRequirements: corev1.ResourceRequirements{ 48 | Requests: corev1.ResourceList{ 49 | corev1.ResourceCPU: resource.MustParse("1"), 50 | corev1.ResourceMemory: resource.MustParse("2Gi"), 51 | }, 52 | }, 53 | Replicas: 3, 54 | }, 55 | }, 56 | } 57 | } 58 | 59 | func TestPDDiscoveryManager_Reconcile(t *testing.T) { 60 | g := NewGomegaWithT(t) 61 | type testcase struct { 62 | name string 63 | prepare func(tc *v1alpha1.TikvCluster, ctrl *controller.FakeGenericControl) 64 | errOnCreateOrUpdate bool 65 | expect func([]appsv1.Deployment, *v1alpha1.TikvCluster, error) 66 | } 67 | testFn := func(tt *testcase) { 68 | t.Log(tt.name) 69 | 70 | tc := newTikvClusterForPDDiscovery() 71 | dm, ctrl := newFakePDDiscoveryManager() 72 | if tt.prepare != nil { 73 | tt.prepare(tc, ctrl) 74 | } 75 | if tt.errOnCreateOrUpdate { 76 | ctrl.SetCreateOrUpdateError(fmt.Errorf("API server down"), 0) 77 | } 78 | err := dm.Reconcile(tc) 79 | deployList := &appsv1.DeploymentList{} 80 | _ = ctrl.FakeCli.List(context.TODO(), deployList) 81 | tt.expect(deployList.Items, tc, err) 82 | } 83 | 84 | cases := []*testcase{ 85 | { 86 | name: "Basic", 87 | expect: func(deploys []appsv1.Deployment, tc *v1alpha1.TikvCluster, err error) { 88 | g.Expect(err).To(Succeed()) 89 | g.Expect(deploys).To(HaveLen(1)) 90 | g.Expect(deploys[0].Name).To((Equal("test-discovery"))) 91 | }, 92 | errOnCreateOrUpdate: false, 93 | }, 94 | { 95 | name: "Setting discovery resource", 96 | prepare: func(tc *v1alpha1.TikvCluster, ctrl *controller.FakeGenericControl) { 97 | tc.Spec.Discovery.ResourceRequirements = corev1.ResourceRequirements{ 98 | Requests: corev1.ResourceList{ 99 | corev1.ResourceCPU: resource.MustParse("1"), 100 | corev1.ResourceMemory: resource.MustParse("2Gi"), 101 | corev1.ResourceEphemeralStorage: resource.MustParse("10Gi"), 102 | }, 103 | Limits: corev1.ResourceList{ 104 | corev1.ResourceCPU: resource.MustParse("1"), 105 | corev1.ResourceMemory: resource.MustParse("2Gi"), 106 | corev1.ResourceEphemeralStorage: resource.MustParse("10Gi"), 107 | }, 108 | } 109 | }, 110 | expect: func(deploys []appsv1.Deployment, tc *v1alpha1.TikvCluster, err error) { 111 | g.Expect(err).To(Succeed()) 112 | g.Expect(deploys).To(HaveLen(1)) 113 | g.Expect(deploys[0].Spec.Template.Spec.Containers[0].Resources).To(Equal(corev1.ResourceRequirements{ 114 | Requests: corev1.ResourceList{ 115 | corev1.ResourceCPU: resource.MustParse("1"), 116 | corev1.ResourceMemory: resource.MustParse("2Gi"), 117 | corev1.ResourceEphemeralStorage: resource.MustParse("10Gi"), 118 | }, 119 | Limits: corev1.ResourceList{ 120 | corev1.ResourceCPU: resource.MustParse("1"), 121 | corev1.ResourceMemory: resource.MustParse("2Gi"), 122 | corev1.ResourceEphemeralStorage: resource.MustParse("10Gi"), 123 | }, 124 | })) 125 | g.Expect(deploys[0].Name).To((Equal("test-discovery"))) 126 | }, 127 | errOnCreateOrUpdate: false, 128 | }, 129 | { 130 | name: "Create or update resource error", 131 | expect: func(deploys []appsv1.Deployment, tc *v1alpha1.TikvCluster, err error) { 132 | g.Expect(err).NotTo(Succeed()) 133 | g.Expect(deploys).To(BeEmpty()) 134 | }, 135 | errOnCreateOrUpdate: true, 136 | }, 137 | } 138 | for _, tt := range cases { 139 | testFn(tt) 140 | } 141 | } 142 | 143 | func newFakePDDiscoveryManager() (*realPDDiscoveryManager, *controller.FakeGenericControl) { 144 | ctrl := controller.NewFakeGenericControl() 145 | return &realPDDiscoveryManager{ 146 | ctrl: controller.NewTypedControl(ctrl), 147 | }, ctrl 148 | } 149 | -------------------------------------------------------------------------------- /pkg/manager/member/tikv_failover.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package member 15 | 16 | import ( 17 | "fmt" 18 | "time" 19 | 20 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 21 | "github.com/tikv/tikv-operator/pkg/util" 22 | corev1 "k8s.io/api/core/v1" 23 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | "k8s.io/client-go/tools/record" 25 | "k8s.io/klog" 26 | ) 27 | 28 | type tikvFailover struct { 29 | tikvFailoverPeriod time.Duration 30 | recorder record.EventRecorder 31 | } 32 | 33 | // NewTiKVFailover returns a tikv Failover 34 | func NewTiKVFailover(tikvFailoverPeriod time.Duration, recorder record.EventRecorder) Failover { 35 | return &tikvFailover{tikvFailoverPeriod, recorder} 36 | } 37 | 38 | func (tf *tikvFailover) isPodDesired(tc *v1alpha1.TikvCluster, podName string) bool { 39 | ordinals := tc.TiKVStsDesiredOrdinals(true) 40 | ordinal, err := util.GetOrdinalFromPodName(podName) 41 | if err != nil { 42 | klog.Errorf("unexpected pod name %q: %v", podName, err) 43 | return false 44 | } 45 | return ordinals.Has(ordinal) 46 | } 47 | 48 | func (tf *tikvFailover) Failover(tc *v1alpha1.TikvCluster) error { 49 | ns := tc.GetNamespace() 50 | tcName := tc.GetName() 51 | 52 | for storeID, store := range tc.Status.TiKV.Stores { 53 | podName := store.PodName 54 | if store.LastTransitionTime.IsZero() { 55 | continue 56 | } 57 | if !tf.isPodDesired(tc, podName) { 58 | // we should ignore the store record of deleted pod, otherwise the 59 | // record of deleted pod may be added back to failure stores 60 | // (before it enters into Offline/Tombstone state) 61 | continue 62 | } 63 | deadline := store.LastTransitionTime.Add(tf.tikvFailoverPeriod) 64 | exist := false 65 | for _, failureStore := range tc.Status.TiKV.FailureStores { 66 | if failureStore.PodName == podName { 67 | exist = true 68 | break 69 | } 70 | } 71 | if store.State == v1alpha1.TiKVStateDown && time.Now().After(deadline) && !exist { 72 | if tc.Status.TiKV.FailureStores == nil { 73 | tc.Status.TiKV.FailureStores = map[string]v1alpha1.TiKVFailureStore{} 74 | } 75 | if tc.Spec.TiKV.MaxFailoverCount != nil && *tc.Spec.TiKV.MaxFailoverCount > 0 { 76 | maxFailoverCount := *tc.Spec.TiKV.MaxFailoverCount 77 | if len(tc.Status.TiKV.FailureStores) >= int(maxFailoverCount) { 78 | klog.Warningf("%s/%s failure stores count reached the limit: %d", ns, tcName, tc.Spec.TiKV.MaxFailoverCount) 79 | return nil 80 | } 81 | tc.Status.TiKV.FailureStores[storeID] = v1alpha1.TiKVFailureStore{ 82 | PodName: podName, 83 | StoreID: store.ID, 84 | CreatedAt: metav1.Now(), 85 | } 86 | msg := fmt.Sprintf("store[%s] is Down", store.ID) 87 | tf.recorder.Event(tc, corev1.EventTypeWarning, unHealthEventReason, fmt.Sprintf(unHealthEventMsgPattern, "tikv", podName, msg)) 88 | } 89 | } 90 | } 91 | 92 | return nil 93 | } 94 | 95 | func (tf *tikvFailover) Recover(tc *v1alpha1.TikvCluster) { 96 | for key, failureStore := range tc.Status.TiKV.FailureStores { 97 | if !tf.isPodDesired(tc, failureStore.PodName) { 98 | // If we delete the pods, e.g. by using advanced statefulset delete 99 | // slots feature. We should remove the record of undesired pods, 100 | // otherwise an extra replacement pod will be created. 101 | delete(tc.Status.TiKV.FailureStores, key) 102 | } 103 | } 104 | } 105 | 106 | type fakeTiKVFailover struct{} 107 | 108 | // NewFakeTiKVFailover returns a fake Failover 109 | func NewFakeTiKVFailover() Failover { 110 | return &fakeTiKVFailover{} 111 | } 112 | 113 | func (ftf *fakeTiKVFailover) Failover(_ *v1alpha1.TikvCluster) error { 114 | return nil 115 | } 116 | 117 | func (ftf *fakeTiKVFailover) Recover(_ *v1alpha1.TikvCluster) { 118 | return 119 | } 120 | -------------------------------------------------------------------------------- /pkg/manager/member/upgrader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package member 15 | 16 | import ( 17 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 18 | apps "k8s.io/api/apps/v1" 19 | ) 20 | 21 | // Upgrader implements the logic for upgrading the tidb cluster. 22 | type Upgrader interface { 23 | // Upgrade upgrade the cluster 24 | Upgrade(*v1alpha1.TikvCluster, *apps.StatefulSet, *apps.StatefulSet) error 25 | } 26 | -------------------------------------------------------------------------------- /pkg/manager/member/utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package member 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/google/go-cmp/cmp" 20 | . "github.com/onsi/gomega" 21 | "github.com/pingcap/advanced-statefulset/client/apis/apps/v1/helper" 22 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 23 | "github.com/tikv/tikv-operator/pkg/label" 24 | apps "k8s.io/api/apps/v1" 25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | ) 27 | 28 | func TestStatefulSetIsUpgrading(t *testing.T) { 29 | g := NewGomegaWithT(t) 30 | 31 | type testcase struct { 32 | name string 33 | update func(*apps.StatefulSet) 34 | expectUpgrading bool 35 | } 36 | 37 | testFn := func(test *testcase, t *testing.T) { 38 | t.Log(test.name) 39 | 40 | set := &apps.StatefulSet{ 41 | ObjectMeta: metav1.ObjectMeta{ 42 | Name: "test", 43 | Namespace: metav1.NamespaceDefault, 44 | }, 45 | } 46 | if test.update != nil { 47 | test.update(set) 48 | } 49 | b := statefulSetIsUpgrading(set) 50 | if test.expectUpgrading { 51 | g.Expect(b).To(BeTrue()) 52 | } else { 53 | g.Expect(b).To(BeFalse()) 54 | } 55 | } 56 | tests := []*testcase{ 57 | { 58 | name: "ObservedGeneration is nil", 59 | update: nil, 60 | expectUpgrading: false, 61 | }, 62 | { 63 | name: "CurrentRevision not equal UpdateRevision", 64 | update: func(set *apps.StatefulSet) { 65 | set.Status.ObservedGeneration = 1000 66 | set.Status.CurrentRevision = "v1" 67 | set.Status.UpdateRevision = "v2" 68 | }, 69 | expectUpgrading: true, 70 | }, 71 | { 72 | name: "set.Generation > *set.Status.ObservedGeneration && *set.Spec.Replicas == set.Status.Replicas", 73 | update: func(set *apps.StatefulSet) { 74 | set.Generation = 1001 75 | set.Status.ObservedGeneration = 1000 76 | set.Status.CurrentRevision = "v1" 77 | set.Status.UpdateRevision = "v1" 78 | set.Status.Replicas = 3 79 | set.Spec.Replicas = func() *int32 { var i int32; i = 3; return &i }() 80 | }, 81 | expectUpgrading: true, 82 | }, 83 | { 84 | name: "replicas not equal", 85 | update: func(set *apps.StatefulSet) { 86 | set.Generation = 1001 87 | set.Status.ObservedGeneration = 1000 88 | set.Status.CurrentRevision = "v1" 89 | set.Status.UpdateRevision = "v1" 90 | set.Status.Replicas = 3 91 | set.Spec.Replicas = func() *int32 { var i int32; i = 2; return &i }() 92 | }, 93 | expectUpgrading: false, 94 | }, 95 | } 96 | 97 | for _, test := range tests { 98 | testFn(test, t) 99 | } 100 | } 101 | 102 | func TestGetStsAnnotations(t *testing.T) { 103 | tests := []struct { 104 | name string 105 | tc *v1alpha1.TikvCluster 106 | component string 107 | expected map[string]string 108 | }{ 109 | { 110 | name: "nil", 111 | tc: &v1alpha1.TikvCluster{ 112 | ObjectMeta: metav1.ObjectMeta{ 113 | Annotations: nil, 114 | }, 115 | }, 116 | component: label.TiKVLabelVal, 117 | expected: map[string]string{}, 118 | }, 119 | { 120 | name: "empty", 121 | tc: &v1alpha1.TikvCluster{ 122 | ObjectMeta: metav1.ObjectMeta{ 123 | Annotations: map[string]string{}, 124 | }, 125 | }, 126 | component: label.TiKVLabelVal, 127 | expected: map[string]string{}, 128 | }, 129 | { 130 | name: "tikv", 131 | tc: &v1alpha1.TikvCluster{ 132 | ObjectMeta: metav1.ObjectMeta{ 133 | Annotations: map[string]string{ 134 | label.AnnTiKVDeleteSlots: "[1,2]", 135 | }, 136 | }, 137 | }, 138 | component: label.TiKVLabelVal, 139 | expected: map[string]string{ 140 | helper.DeleteSlotsAnn: "[1,2]", 141 | }, 142 | }, 143 | } 144 | for _, tt := range tests { 145 | t.Run(tt.name, func(t *testing.T) { 146 | got := getStsAnnotations(tt.tc, tt.component) 147 | if diff := cmp.Diff(tt.expected, got); diff != "" { 148 | t.Errorf("unexpected (-want, +got): %s", diff) 149 | } 150 | }) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /pkg/manager/meta/meta_manager.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package meta 15 | 16 | import ( 17 | "errors" 18 | 19 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 20 | "github.com/tikv/tikv-operator/pkg/controller" 21 | "github.com/tikv/tikv-operator/pkg/label" 22 | "github.com/tikv/tikv-operator/pkg/manager" 23 | corev1 "k8s.io/api/core/v1" 24 | corelisters "k8s.io/client-go/listers/core/v1" 25 | "k8s.io/klog" 26 | ) 27 | 28 | var errPVCNotFound = errors.New("PVC is not found") 29 | 30 | type metaManager struct { 31 | pvcLister corelisters.PersistentVolumeClaimLister 32 | pvcControl controller.PVCControlInterface 33 | pvLister corelisters.PersistentVolumeLister 34 | pvControl controller.PVControlInterface 35 | podLister corelisters.PodLister 36 | podControl controller.PodControlInterface 37 | } 38 | 39 | // NewMetaManager returns a *metaManager 40 | func NewMetaManager( 41 | pvcLister corelisters.PersistentVolumeClaimLister, 42 | pvcControl controller.PVCControlInterface, 43 | pvLister corelisters.PersistentVolumeLister, 44 | pvControl controller.PVControlInterface, 45 | podLister corelisters.PodLister, 46 | podControl controller.PodControlInterface, 47 | ) manager.Manager { 48 | return &metaManager{ 49 | pvcLister: pvcLister, 50 | pvcControl: pvcControl, 51 | pvLister: pvLister, 52 | pvControl: pvControl, 53 | podLister: podLister, 54 | podControl: podControl, 55 | } 56 | } 57 | 58 | func (pmm *metaManager) Sync(tc *v1alpha1.TikvCluster) error { 59 | ns := tc.GetNamespace() 60 | 61 | l, err := label.New().Selector() 62 | if err != nil { 63 | return err 64 | } 65 | pods, err := pmm.podLister.Pods(ns).List(l) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | for _, pod := range pods { 71 | // update meta info for pod 72 | _, err := pmm.podControl.UpdateMetaInfo(tc, pod) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if component := pod.Labels[label.ComponentLabelKey]; component != label.PDLabelVal && component != label.TiKVLabelVal { 78 | // Skip syncing meta info for pod that doesn't use PV 79 | // Currently only PD/TiKV uses PV 80 | continue 81 | } 82 | // update meta info for pvc 83 | pvcs, err := pmm.resolvePVCFromPod(pod) 84 | if err != nil { 85 | return err 86 | } 87 | for _, pvc := range pvcs { 88 | _, err = pmm.pvcControl.UpdateMetaInfo(tc, pvc, pod) 89 | if err != nil { 90 | return err 91 | } 92 | if pvc.Spec.VolumeName == "" { 93 | continue 94 | } 95 | // update meta info for pv 96 | pv, err := pmm.pvLister.Get(pvc.Spec.VolumeName) 97 | if err != nil { 98 | klog.Errorf("Get PV %s error: %v", pvc.Spec.VolumeName, err) 99 | return err 100 | } 101 | _, err = pmm.pvControl.UpdateMetaInfo(tc, pv) 102 | if err != nil { 103 | return err 104 | } 105 | } 106 | } 107 | 108 | return nil 109 | } 110 | 111 | func (pmm *metaManager) resolvePVCFromPod(pod *corev1.Pod) ([]*corev1.PersistentVolumeClaim, error) { 112 | var pvcs []*corev1.PersistentVolumeClaim 113 | var pvcName string 114 | for _, vol := range pod.Spec.Volumes { 115 | if vol.PersistentVolumeClaim != nil { 116 | pvcName = vol.PersistentVolumeClaim.ClaimName 117 | if len(pvcName) == 0 { 118 | continue 119 | } 120 | pvc, err := pmm.pvcLister.PersistentVolumeClaims(pod.Namespace).Get(pvcName) 121 | if err != nil { 122 | klog.Errorf("Get PVC %s/%s error: %v", pod.Namespace, pvcName, err) 123 | continue 124 | } 125 | pvcs = append(pvcs, pvc) 126 | } 127 | } 128 | if len(pvcs) == 0 { 129 | return nil, errPVCNotFound 130 | } 131 | return pvcs, nil 132 | } 133 | 134 | var _ manager.Manager = &metaManager{} 135 | 136 | type FakeMetaManager struct { 137 | err error 138 | } 139 | 140 | func NewFakeMetaManager() *FakeMetaManager { 141 | return &FakeMetaManager{} 142 | } 143 | 144 | func (fmm *FakeMetaManager) SetSyncError(err error) { 145 | fmm.err = err 146 | } 147 | 148 | func (fmm *FakeMetaManager) Sync(_ *v1alpha1.TikvCluster) error { 149 | return fmm.err 150 | } 151 | -------------------------------------------------------------------------------- /pkg/pdapi/pdetcd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package pdapi 15 | 16 | import ( 17 | "context" 18 | "crypto/tls" 19 | "time" 20 | 21 | etcdclientv3 "github.com/coreos/etcd/clientv3" 22 | etcdclientv3util "github.com/coreos/etcd/clientv3/clientv3util" 23 | ) 24 | 25 | type PDEtcdClient interface { 26 | // PutKey will put key to the target pd etcd cluster 27 | PutKey(key, value string) error 28 | // DeleteKey will delete key from the target pd etcd cluster 29 | DeleteKey(key string) error 30 | } 31 | 32 | type pdEtcdClient struct { 33 | timeout time.Duration 34 | etcdClient *etcdclientv3.Client 35 | } 36 | 37 | func NewPdEtcdClient(url string, timeout time.Duration, tlsConfig *tls.Config) (PDEtcdClient, error) { 38 | etcdClient, err := etcdclientv3.New(etcdclientv3.Config{ 39 | Endpoints: []string{url}, 40 | DialTimeout: timeout, 41 | TLS: tlsConfig, 42 | }) 43 | if err != nil { 44 | return nil, err 45 | } 46 | return &pdEtcdClient{ 47 | etcdClient: etcdClient, 48 | timeout: timeout, 49 | }, nil 50 | } 51 | 52 | func (pec *pdEtcdClient) PutKey(key, value string) error { 53 | ctx, cancel := context.WithTimeout(context.Background(), pec.timeout) 54 | defer cancel() 55 | _, err := pec.etcdClient.Put(ctx, key, value) 56 | return err 57 | } 58 | 59 | func (pec *pdEtcdClient) DeleteKey(key string) error { 60 | ctx, cancel := context.WithTimeout(context.Background(), pec.timeout) 61 | defer cancel() 62 | kvc := etcdclientv3.NewKV(pec.etcdClient) 63 | 64 | // perform a delete only if key already exists 65 | _, err := kvc.Txn(ctx). 66 | If(etcdclientv3util.KeyExists(key)). 67 | Then(etcdclientv3.OpDelete(key)). 68 | Commit() 69 | if err != nil { 70 | return err 71 | } 72 | return nil 73 | } 74 | -------------------------------------------------------------------------------- /pkg/registry/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package registry 15 | 16 | var ( 17 | Strategies = []CreateUpdateStrategy{ 18 | TikvClusterStrategy{}, 19 | } 20 | ) 21 | -------------------------------------------------------------------------------- /pkg/registry/strategy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package registry 15 | 16 | import ( 17 | "context" 18 | 19 | "k8s.io/apimachinery/pkg/runtime" 20 | "k8s.io/apimachinery/pkg/util/validation/field" 21 | ) 22 | 23 | // CreateUpdateStrategy is a sub set of the RESTCreateUpdateStrategy interface of kube-apiserver, which abstracts the 24 | // defaulting and validation logic of each custom resources in the kube-apiserver way but allow using webhook as 25 | // an alternative implementation. 26 | // Note that PrepareForCreate/Update method is different with the defaultingFunc of kube-apiserver, the latter one 27 | // is applied to versioned resource, and PrepareForCreate/Update is applied to resource after conversion. 28 | // MutatingAdmissionWebhook is also applied to resource after conversion, which allows it to implement the correct 29 | // semantic of PrepareForCreate/Update as the following figure shows: 30 | // + 31 | // Resource 32 | // | 33 | // +--------v---------+ 34 | // | Conversion | 35 | // +--------+---------+ 36 | // | MutatingWebhook +---------------------+ 37 | // +---------------------->+ Prepare(Webhook) | 38 | // +----------+----------+ 39 | // | 40 | // +----------------------------------+ 41 | // | 42 | // +--------v---------+ 43 | // | Prepare(Server) | 44 | // +--------+---------+ 45 | // | 46 | // +--------v---------+ 47 | // | Validate(Server) | 48 | // +--------+---------+ 49 | // | ValidatingWebhook +---------------------+ 50 | // +---------------------->+ Validate(Webhook) | 51 | // +----------+----------+ 52 | // | 53 | // +----------------------------------+ 54 | // | 55 | // +--------v---------+ 56 | // | ETCD | 57 | // +------------------+ 58 | // 59 | // There is a special case for custom resource, if there is only one version specified, the conversion is actually no-op. 60 | // And if there is multiple versions specified, a storage version could be specified in CustomResourceDefinition which acts 61 | // the conversion target. So, the strategy is always applied to a certain version of CustomResource instead of an "internal" 62 | // version. 63 | // 64 | // TODO(aylei): we may place this definition in a more reasonable package 65 | type CreateUpdateStrategy interface { 66 | // NewObject create an empty object that the strategt applied to 67 | NewObject() runtime.Object 68 | // PrepareForCreate mutate a new resource before persistent it 69 | PrepareForCreate(ctx context.Context, obj runtime.Object) 70 | // PrepareForUpdate mutate a new resource before it replace the existing on in storage 71 | PrepareForUpdate(ctx context.Context, obj, old runtime.Object) 72 | // Validate validates a new resource 73 | Validate(ctx context.Context, obj runtime.Object) field.ErrorList 74 | // ValidateUpdate validates an update request for existing resource 75 | ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList 76 | } 77 | -------------------------------------------------------------------------------- /pkg/registry/tikvcluster_strategy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package registry 15 | 16 | import ( 17 | "context" 18 | 19 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 20 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1/defaulting" 21 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1/validation" 22 | "k8s.io/apimachinery/pkg/runtime" 23 | "k8s.io/apimachinery/pkg/util/validation/field" 24 | "k8s.io/klog" 25 | ) 26 | 27 | // +k8s:deepcopy-gen=false 28 | type TikvClusterStrategy struct{} 29 | 30 | func (TikvClusterStrategy) NewObject() runtime.Object { 31 | return &v1alpha1.TikvCluster{} 32 | } 33 | 34 | func (TikvClusterStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { 35 | if tc, ok := castTikvCluster(obj); ok { 36 | defaulting.SetTikvClusterDefault(tc) 37 | } 38 | } 39 | 40 | func (TikvClusterStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { 41 | // no op to not affect the cluster managed by old versions of the helm chart 42 | } 43 | 44 | func (TikvClusterStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { 45 | if tc, ok := castTikvCluster(obj); ok { 46 | return validation.ValidateCreateTikvCluster(tc) 47 | } 48 | return field.ErrorList{} 49 | } 50 | 51 | func (TikvClusterStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { 52 | oldTc, oldOk := castTikvCluster(old) 53 | tc, ok := castTikvCluster(obj) 54 | if ok && oldOk { 55 | return validation.ValidateUpdateTikvCluster(oldTc, tc) 56 | } 57 | return field.ErrorList{} 58 | } 59 | 60 | func castTikvCluster(obj runtime.Object) (*v1alpha1.TikvCluster, bool) { 61 | tc, ok := obj.(*v1alpha1.TikvCluster) 62 | if !ok { 63 | // impossible for non-malicious request, this usually indicates a client error when the strategy is used by webhook, 64 | // we simply ignore error requests 65 | klog.Errorf("Object %T is not v1alpah1.TikvCluster, cannot processed by TikvClusterStrategy", obj) 66 | return nil, false 67 | } 68 | return tc, true 69 | } 70 | -------------------------------------------------------------------------------- /pkg/scheme/scheme.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package scheme 15 | 16 | import ( 17 | tikvscheme "github.com/tikv/tikv-operator/pkg/client/clientset/versioned/scheme" 18 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 | "k8s.io/apimachinery/pkg/runtime" 20 | "k8s.io/apimachinery/pkg/runtime/schema" 21 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 22 | kubescheme "k8s.io/client-go/kubernetes/scheme" 23 | ) 24 | 25 | // Scheme gathers the schemes of native resources and custom resources used by tikv-operator 26 | // in favor of the generic controller-runtime/client 27 | var Scheme = runtime.NewScheme() 28 | 29 | func init() { 30 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 31 | utilruntime.Must(kubescheme.AddToScheme(Scheme)) 32 | utilruntime.Must(tikvscheme.AddToScheme(Scheme)) 33 | } 34 | -------------------------------------------------------------------------------- /pkg/util/config/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package config 15 | 16 | import ( 17 | "github.com/mohae/deepcopy" 18 | ) 19 | 20 | // Note: un-exported field inside struct won't be copied and should not be included in config 21 | // GenericConfig is a wrapper of go interface{} that makes deepcopy-gen happy 22 | type GenericConfig struct { 23 | Config map[string]interface{} `json:"config,omitempty"` 24 | } 25 | 26 | func New(o map[string]interface{}) GenericConfig { 27 | return GenericConfig{o} 28 | } 29 | 30 | func (c *GenericConfig) Unwrap() interface{} { 31 | return c.Config 32 | } 33 | 34 | func (c *GenericConfig) DeepCopyJsonObject() *GenericConfig { 35 | // FIXME: mohae/deepcopy is based on reflection, which will lost un-exported field (if any) 36 | if c == nil { 37 | return nil 38 | } 39 | return deepcopy.Copy(c).(*GenericConfig) 40 | } 41 | 42 | func (c *GenericConfig) DeepCopy() *GenericConfig { 43 | return c.DeepCopyJsonObject() 44 | } 45 | 46 | func (c *GenericConfig) DeepCopyInto(out *GenericConfig) { 47 | *out = *c 48 | out.Config = c.DeepCopyJsonObject().Config 49 | } 50 | -------------------------------------------------------------------------------- /pkg/util/config/config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package config 15 | 16 | import ( 17 | "testing" 18 | 19 | . "github.com/onsi/gomega" 20 | ) 21 | 22 | type Simple struct { 23 | A string 24 | B int 25 | } 26 | 27 | func TestDeepCopyJsonObject(t *testing.T) { 28 | g := NewGomegaWithT(t) 29 | 30 | objects := []GenericConfig{ 31 | New(nil), 32 | New(map[string]interface{}{ 33 | "k1": true, 34 | "k2": "v2", 35 | "k3": 1, 36 | "k4": 'v', 37 | "k5": []byte("v5"), 38 | "k6": nil, 39 | }), 40 | New(map[string]interface{}{ 41 | "k1": map[string]interface{}{ 42 | "nest-1": map[string]interface{}{ 43 | "nest-2": map[string]interface{}{ 44 | "nest-3": "internal", 45 | }, 46 | }, 47 | }, 48 | }), 49 | New(map[string]interface{}{ 50 | "k1": map[string]interface{}{ 51 | "nest-1": &Simple{ 52 | A: "xx", 53 | B: 1, 54 | }, 55 | "nest-2": Simple{ 56 | A: "xx", 57 | B: 2, 58 | }, 59 | }, 60 | }), 61 | } 62 | 63 | for _, obj := range objects { 64 | copied := obj.DeepCopy() 65 | g.Expect(copied).To(Equal(&obj)) 66 | 67 | out := New(nil) 68 | obj.DeepCopyInto(&out) 69 | g.Expect(out).To(Equal(obj)) 70 | } 71 | copied := objects[1].DeepCopy() 72 | copied.Config["k1"] = false 73 | g.Expect(objects[1].Config["k1"]).To(Equal(true), "Mutation copy should net affect origin") 74 | } 75 | -------------------------------------------------------------------------------- /pkg/util/crypto/certs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package crypto 15 | 16 | import ( 17 | "crypto/rand" 18 | "crypto/rsa" 19 | "crypto/tls" 20 | "crypto/x509" 21 | "crypto/x509/pkix" 22 | "encoding/pem" 23 | "fmt" 24 | "io/ioutil" 25 | "net" 26 | 27 | corev1 "k8s.io/api/core/v1" 28 | "k8s.io/klog" 29 | ) 30 | 31 | const ( 32 | rsaKeySize = 2048 33 | k8sCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" 34 | ) 35 | 36 | // generate a new private key 37 | func newPrivateKey(size int) (*rsa.PrivateKey, error) { 38 | // TODO: support more key types 39 | privateKey, err := rsa.GenerateKey(rand.Reader, size) 40 | if err != nil { 41 | return nil, err 42 | } 43 | return privateKey, nil 44 | } 45 | 46 | // convert private key to PEM format 47 | func convertKeyToPEM(blockType string, dataBytes *rsa.PrivateKey) []byte { 48 | return pem.EncodeToMemory( 49 | &pem.Block{ 50 | Type: blockType, 51 | Headers: nil, 52 | Bytes: x509.MarshalPKCS1PrivateKey(dataBytes), 53 | }, 54 | ) 55 | } 56 | 57 | func NewCSR(commonName string, hostList []string, IPList []string) ([]byte, []byte, error) { 58 | // TODO: option to use an exist private key 59 | privKey, err := newPrivateKey(rsaKeySize) 60 | if err != nil { 61 | return nil, nil, err 62 | } 63 | 64 | var ipAddrList []net.IP 65 | for _, ip := range IPList { 66 | ipAddr := net.ParseIP(ip) 67 | ipAddrList = append(ipAddrList, ipAddr) 68 | } 69 | 70 | // set CSR attributes 71 | csrTemplate := &x509.CertificateRequest{ 72 | Subject: pkix.Name{ 73 | Organization: []string{"PingCAP"}, 74 | OrganizationalUnit: []string{"TiDB Operator"}, 75 | CommonName: commonName, 76 | }, 77 | DNSNames: hostList, 78 | IPAddresses: ipAddrList, 79 | } 80 | csr, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, privKey) 81 | if err != nil { 82 | return nil, nil, err 83 | } 84 | 85 | return csr, convertKeyToPEM("RSA PRIVATE KEY", privKey), nil 86 | } 87 | 88 | func ReadCACerts() (*x509.CertPool, error) { 89 | // try to load system CA certs 90 | rootCAs, err := x509.SystemCertPool() 91 | if err != nil { 92 | return nil, err 93 | } 94 | if rootCAs == nil { 95 | rootCAs = x509.NewCertPool() 96 | } 97 | 98 | // load k8s CA cert 99 | caCert, err := ioutil.ReadFile(k8sCAFile) 100 | if err != nil { 101 | klog.Errorf("fail to read CA file %s, error: %v", k8sCAFile, err) 102 | return nil, err 103 | } 104 | if ok := rootCAs.AppendCertsFromPEM(caCert); !ok { 105 | klog.Warningf("fail to append CA file to pool, using system CAs only") 106 | } 107 | return rootCAs, nil 108 | } 109 | 110 | func LoadTlsConfigFromSecret(secret *corev1.Secret, caCert []byte) (*tls.Config, error) { 111 | rootCAs := x509.NewCertPool() 112 | var tlsCert tls.Certificate 113 | 114 | if len(caCert) > 0 { 115 | rootCAs.AppendCertsFromPEM(caCert) 116 | } else { 117 | rootCAs.AppendCertsFromPEM(secret.Data[corev1.ServiceAccountRootCAKey]) 118 | } 119 | 120 | clientCert, certExists := secret.Data[corev1.TLSCertKey] 121 | clientKey, keyExists := secret.Data[corev1.TLSPrivateKeyKey] 122 | if !certExists || !keyExists { 123 | return nil, fmt.Errorf("cert or key does not exist in secret %s/%s", secret.Namespace, secret.Name) 124 | } 125 | tlsCert, err := tls.X509KeyPair(clientCert, clientKey) 126 | if err != nil { 127 | return nil, fmt.Errorf("unable to load certificates from secret %s/%s: %v", secret.Namespace, secret.Name, err) 128 | } 129 | 130 | return &tls.Config{ 131 | RootCAs: rootCAs, 132 | Certificates: []tls.Certificate{tlsCert}, 133 | }, nil 134 | } 135 | -------------------------------------------------------------------------------- /pkg/util/discovery/discovery.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package discovery 15 | 16 | import ( 17 | "k8s.io/apimachinery/pkg/runtime/schema" 18 | "k8s.io/client-go/discovery" 19 | ) 20 | 21 | // IsAPIGroupVersionSupported checks if given groupVersion is supported by the cluster. 22 | func IsAPIGroupVersionSupported(discoveryCli discovery.DiscoveryInterface, groupVersion string) (bool, error) { 23 | gv, err := schema.ParseGroupVersion(groupVersion) 24 | if err != nil { 25 | return false, err 26 | } 27 | apiGroupList, err := discoveryCli.ServerGroups() 28 | if err != nil { 29 | return false, err 30 | } 31 | for _, apiGroup := range apiGroupList.Groups { 32 | if apiGroup.Name != gv.Group { 33 | continue 34 | } 35 | for _, version := range apiGroup.Versions { 36 | if version.GroupVersion == gv.String() { 37 | return true, nil 38 | } 39 | } 40 | } 41 | return false, nil 42 | } 43 | 44 | // IsAPIGroupSupported checks if given group is supported by the cluster. 45 | func IsAPIGroupSupported(discoveryCli discovery.DiscoveryInterface, group string) (bool, error) { 46 | apiGroupList, err := discoveryCli.ServerGroups() 47 | if err != nil { 48 | return false, err 49 | } 50 | for _, apiGroup := range apiGroupList.Groups { 51 | if apiGroup.Name == group { 52 | return true, nil 53 | } 54 | } 55 | return false, nil 56 | } 57 | -------------------------------------------------------------------------------- /pkg/util/discovery/discovery_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package discovery 15 | 16 | import ( 17 | "testing" 18 | 19 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 | discoveryfake "k8s.io/client-go/discovery/fake" 21 | k8stesting "k8s.io/client-go/testing" 22 | ) 23 | 24 | func TestIsAPIGroupVersionSupported(t *testing.T) { 25 | tests := []struct { 26 | name string 27 | groupVersion string 28 | wantOK bool 29 | }{ 30 | { 31 | name: "found", 32 | groupVersion: "apiextensions.k8s.io/v1beta1", 33 | wantOK: true, 34 | }, 35 | { 36 | name: "not found", 37 | groupVersion: "apiextensions.k8s.io/v1", 38 | wantOK: false, 39 | }, 40 | } 41 | 42 | for _, tt := range tests { 43 | t.Run(tt.name, func(t *testing.T) { 44 | fake := &k8stesting.Fake{ 45 | Resources: []*metav1.APIResourceList{ 46 | { 47 | GroupVersion: "apiextensions.k8s.io/v1beta1", 48 | APIResources: []metav1.APIResource{ 49 | { 50 | Name: "customresourcedefinitions", 51 | Group: "apiextensions.k8s.io", 52 | Version: "v1beta1", 53 | }, 54 | }, 55 | }, 56 | }, 57 | } 58 | discoveryClient := &discoveryfake.FakeDiscovery{ 59 | Fake: fake, 60 | } 61 | ok, _ := IsAPIGroupVersionSupported(discoveryClient, tt.groupVersion) 62 | if ok != tt.wantOK { 63 | t.Errorf("got %v, want %v", ok, tt.wantOK) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestIsAPIGroupSupported(t *testing.T) { 70 | tests := []struct { 71 | name string 72 | group string 73 | wantOK bool 74 | }{ 75 | { 76 | name: "found", 77 | group: "apiextensions.k8s.io", 78 | wantOK: true, 79 | }, 80 | { 81 | name: "not found", 82 | group: "apps.pingcap.com", 83 | wantOK: false, 84 | }, 85 | } 86 | 87 | for _, tt := range tests { 88 | t.Run(tt.name, func(t *testing.T) { 89 | fake := &k8stesting.Fake{ 90 | Resources: []*metav1.APIResourceList{ 91 | { 92 | GroupVersion: "apiextensions.k8s.io/v1beta1", 93 | APIResources: []metav1.APIResource{ 94 | { 95 | Name: "customresourcedefinitions", 96 | Group: "apiextensions.k8s.io", 97 | Version: "v1beta1", 98 | }, 99 | }, 100 | }, 101 | }, 102 | } 103 | discoveryClient := &discoveryfake.FakeDiscovery{ 104 | Fake: fake, 105 | } 106 | ok, _ := IsAPIGroupSupported(discoveryClient, tt.group) 107 | if ok != tt.wantOK { 108 | t.Errorf("got %v, want %v", ok, tt.wantOK) 109 | } 110 | }) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /pkg/util/tikvcluster/tidbcluster.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package tikvcluster 15 | 16 | import ( 17 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 18 | v1 "k8s.io/api/core/v1" 19 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 | ) 21 | 22 | const ( 23 | // Reasons for TikvCluster conditions. 24 | 25 | // Ready 26 | Ready = "Ready" 27 | // StatefulSetNotUpToDate is added when one of statefulsets is not up to date. 28 | StatfulSetNotUpToDate = "StatefulSetNotUpToDate" 29 | // PDUnhealthy is added when one of pd members is unhealthy. 30 | PDUnhealthy = "PDUnhealthy" 31 | // TiKVStoreNotUp is added when one of tikv stores is not up. 32 | TiKVStoreNotUp = "TiKVStoreNotUp" 33 | ) 34 | 35 | // NewTikvClusterCondition creates a new tikvcluster condition. 36 | func NewTikvClusterCondition(condType v1alpha1.TikvClusterConditionType, status v1.ConditionStatus, reason, message string) *v1alpha1.TikvClusterCondition { 37 | return &v1alpha1.TikvClusterCondition{ 38 | Type: condType, 39 | Status: status, 40 | LastUpdateTime: metav1.Now(), 41 | LastTransitionTime: metav1.Now(), 42 | Reason: reason, 43 | Message: message, 44 | } 45 | } 46 | 47 | // GetTikvClusterCondition returns the condition with the provided type. 48 | func GetTikvClusterCondition(status v1alpha1.TikvClusterStatus, condType v1alpha1.TikvClusterConditionType) *v1alpha1.TikvClusterCondition { 49 | for i := range status.Conditions { 50 | c := status.Conditions[i] 51 | if c.Type == condType { 52 | return &c 53 | } 54 | } 55 | return nil 56 | } 57 | 58 | // SetTikvClusterCondition updates the tikv cluster to include the provided condition. If the condition that 59 | // we are about to add already exists and has the same status and reason then we are not going to update. 60 | func SetTikvClusterCondition(status *v1alpha1.TikvClusterStatus, condition v1alpha1.TikvClusterCondition) { 61 | currentCond := GetTikvClusterCondition(*status, condition.Type) 62 | if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason { 63 | return 64 | } 65 | // Do not update lastTransitionTime if the status of the condition doesn't change. 66 | if currentCond != nil && currentCond.Status == condition.Status { 67 | condition.LastTransitionTime = currentCond.LastTransitionTime 68 | } 69 | newConditions := filterOutCondition(status.Conditions, condition.Type) 70 | status.Conditions = append(newConditions, condition) 71 | } 72 | 73 | // filterOutCondition returns a new slice of tikvcluster conditions without conditions with the provided type. 74 | func filterOutCondition(conditions []v1alpha1.TikvClusterCondition, condType v1alpha1.TikvClusterConditionType) []v1alpha1.TikvClusterCondition { 75 | var newConditions []v1alpha1.TikvClusterCondition 76 | for _, c := range conditions { 77 | if c.Type == condType { 78 | continue 79 | } 80 | newConditions = append(newConditions, c) 81 | } 82 | return newConditions 83 | } 84 | 85 | // GetTikvClusterReadyCondition extracts the tikvcluster ready condition from the given status and returns that. 86 | // Returns nil if the condition is not present. 87 | func GetTikvClusterReadyCondition(status v1alpha1.TikvClusterStatus) *v1alpha1.TikvClusterCondition { 88 | return GetTikvClusterCondition(status, v1alpha1.TikvClusterReady) 89 | } 90 | -------------------------------------------------------------------------------- /pkg/util/utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package util 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/google/go-cmp/cmp" 20 | . "github.com/onsi/gomega" 21 | "github.com/tikv/tikv-operator/pkg/apis/tikv/v1alpha1" 22 | "github.com/tikv/tikv-operator/pkg/label" 23 | corev1 "k8s.io/api/core/v1" 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | "k8s.io/apimachinery/pkg/util/sets" 26 | ) 27 | 28 | func TestGetOrdinalFromPodName(t *testing.T) { 29 | g := NewGomegaWithT(t) 30 | 31 | i, err := GetOrdinalFromPodName("pod-1") 32 | g.Expect(err).NotTo(HaveOccurred()) 33 | g.Expect(i).To(Equal(int32(1))) 34 | 35 | i, err = GetOrdinalFromPodName("pod-notint") 36 | g.Expect(err).To(HaveOccurred()) 37 | g.Expect(i).To(Equal(int32(0))) 38 | } 39 | 40 | func TestIsSubMapOf(t *testing.T) { 41 | g := NewGomegaWithT(t) 42 | 43 | g.Expect(IsSubMapOf( 44 | nil, 45 | map[string]string{ 46 | "k1": "v1", 47 | })).To(BeTrue()) 48 | g.Expect(IsSubMapOf( 49 | map[string]string{ 50 | "k1": "v1", 51 | }, 52 | map[string]string{ 53 | "k1": "v1", 54 | })).To(BeTrue()) 55 | g.Expect(IsSubMapOf( 56 | map[string]string{ 57 | "k1": "v1", 58 | }, 59 | map[string]string{ 60 | "k1": "v1", 61 | "k2": "v2", 62 | })).To(BeTrue()) 63 | g.Expect(IsSubMapOf( 64 | map[string]string{}, 65 | map[string]string{ 66 | "k1": "v1", 67 | })).To(BeTrue()) 68 | g.Expect(IsSubMapOf( 69 | map[string]string{ 70 | "k1": "v1", 71 | "k2": "v2", 72 | }, 73 | map[string]string{ 74 | "k1": "v1", 75 | })).To(BeFalse()) 76 | } 77 | 78 | func TestGetPodOrdinals(t *testing.T) { 79 | tests := []struct { 80 | name string 81 | tc *v1alpha1.TikvCluster 82 | memberType v1alpha1.MemberType 83 | deleteSlots sets.Int32 84 | }{ 85 | { 86 | name: "no delete slots", 87 | tc: &v1alpha1.TikvCluster{ 88 | ObjectMeta: metav1.ObjectMeta{ 89 | Annotations: map[string]string{}, 90 | }, 91 | Spec: v1alpha1.TikvClusterSpec{ 92 | TiKV: v1alpha1.TiKVSpec{ 93 | Replicas: 3, 94 | }, 95 | }, 96 | }, 97 | memberType: v1alpha1.TiKVMemberType, 98 | deleteSlots: sets.NewInt32(0, 1, 2), 99 | }, 100 | { 101 | name: "delete slots", 102 | tc: &v1alpha1.TikvCluster{ 103 | ObjectMeta: metav1.ObjectMeta{ 104 | Annotations: map[string]string{ 105 | label.AnnTiKVDeleteSlots: "[1,2]", 106 | }, 107 | }, 108 | Spec: v1alpha1.TikvClusterSpec{ 109 | TiKV: v1alpha1.TiKVSpec{ 110 | Replicas: 3, 111 | }, 112 | }, 113 | }, 114 | memberType: v1alpha1.TiKVMemberType, 115 | deleteSlots: sets.NewInt32(0, 3, 4), 116 | }, 117 | } 118 | for _, tt := range tests { 119 | t.Run(tt.name, func(t *testing.T) { 120 | got, err := GetPodOrdinals(tt.tc, tt.memberType) 121 | if err != nil { 122 | t.Error(err) 123 | } 124 | if !got.Equal(tt.deleteSlots) { 125 | t.Errorf("expects %v got %v", tt.deleteSlots.List(), got.List()) 126 | } 127 | }) 128 | } 129 | } 130 | 131 | func TestAppendEnv(t *testing.T) { 132 | tests := []struct { 133 | name string 134 | a []corev1.EnvVar 135 | b []corev1.EnvVar 136 | want []corev1.EnvVar 137 | }{ 138 | { 139 | name: "envs whose names exist are ignored", 140 | a: []corev1.EnvVar{ 141 | { 142 | Name: "foo", 143 | Value: "bar", 144 | }, 145 | { 146 | Name: "xxx", 147 | Value: "xxx", 148 | }, 149 | }, 150 | b: []corev1.EnvVar{ 151 | { 152 | Name: "foo", 153 | Value: "barbar", 154 | }, 155 | { 156 | Name: "new", 157 | Value: "bar", 158 | }, 159 | { 160 | Name: "xxx", 161 | Value: "yyy", 162 | }, 163 | }, 164 | want: []corev1.EnvVar{ 165 | { 166 | Name: "foo", 167 | Value: "bar", 168 | }, 169 | { 170 | Name: "xxx", 171 | Value: "xxx", 172 | }, 173 | { 174 | Name: "new", 175 | Value: "bar", 176 | }, 177 | }, 178 | }, 179 | } 180 | 181 | for _, tt := range tests { 182 | t.Run(tt.name, func(t *testing.T) { 183 | got := AppendEnv(tt.a, tt.b) 184 | if diff := cmp.Diff(tt.want, got); diff != "" { 185 | t.Errorf("unwant (-want, +got): %s", diff) 186 | } 187 | }) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /pkg/verflag/verflag.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Package verflag defines utility functions to handle command line flags 15 | // related to version of Advanced StatefulSet. 16 | // This is modifed from k8s.io/component-base/version/verflag. Use it when it's 17 | // possible to customize version prefix. 18 | 19 | package verflag 20 | 21 | import ( 22 | "fmt" 23 | "os" 24 | "strconv" 25 | 26 | flag "github.com/spf13/pflag" 27 | "k8s.io/component-base/version" 28 | ) 29 | 30 | type versionValue int 31 | 32 | const ( 33 | VersionFalse versionValue = 0 34 | VersionTrue versionValue = 1 35 | VersionRaw versionValue = 2 36 | ) 37 | 38 | const strRawVersion string = "raw" 39 | 40 | func (v *versionValue) IsBoolFlag() bool { 41 | return true 42 | } 43 | 44 | func (v *versionValue) Get() interface{} { 45 | return versionValue(*v) 46 | } 47 | 48 | func (v *versionValue) Set(s string) error { 49 | if s == strRawVersion { 50 | *v = VersionRaw 51 | return nil 52 | } 53 | boolVal, err := strconv.ParseBool(s) 54 | if boolVal { 55 | *v = VersionTrue 56 | } else { 57 | *v = VersionFalse 58 | } 59 | return err 60 | } 61 | 62 | func (v *versionValue) String() string { 63 | if *v == VersionRaw { 64 | return strRawVersion 65 | } 66 | return fmt.Sprintf("%v", bool(*v == VersionTrue)) 67 | } 68 | 69 | // The type of the flag as required by the pflag.Value interface 70 | func (v *versionValue) Type() string { 71 | return "version" 72 | } 73 | 74 | func VersionVar(p *versionValue, name string, value versionValue, usage string) { 75 | *p = value 76 | flag.Var(p, name, usage) 77 | // "--version" will be treated as "--version=true" 78 | flag.Lookup(name).NoOptDefVal = "true" 79 | } 80 | 81 | func Version(name string, value versionValue, usage string) *versionValue { 82 | p := new(versionValue) 83 | VersionVar(p, name, value, usage) 84 | return p 85 | } 86 | 87 | const versionFlagName = "version" 88 | 89 | var ( 90 | versionFlag = Version(versionFlagName, VersionFalse, "Print version information and quit") 91 | ) 92 | 93 | // AddFlags registers this package's flags on arbitrary FlagSets, such that they point to the 94 | // same value as the global flags. 95 | func AddFlags(fs *flag.FlagSet) { 96 | fs.AddFlag(flag.Lookup(versionFlagName)) 97 | } 98 | 99 | // PrintAndExitIfRequested will check if the -version flag was passed 100 | // and, if so, print the version and exit. 101 | func PrintAndExitIfRequested() { 102 | if *versionFlag == VersionRaw { 103 | fmt.Printf("%#v\n", version.Get()) 104 | os.Exit(0) 105 | } else if *versionFlag == VersionTrue { 106 | fmt.Printf("Advanced StatefulSet Controller Manager %s\n", version.Get()) 107 | os.Exit(0) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /test/examples/001-basic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/../.. && pwd) 17 | cd $ROOT 18 | 19 | source "${ROOT}/hack/lib.sh" 20 | source "${ROOT}/test/examples/t.sh" 21 | 22 | NS=$(basename ${0%.*}) 23 | 24 | function cleanup() { 25 | kubectl -n $NS delete -f examples/basic/tikv-cluster.yaml 26 | kubectl delete ns $NS 27 | } 28 | 29 | trap cleanup EXIT 30 | 31 | kubectl create ns $NS 32 | hack::wait_for_success 10 3 "t::ns_is_active $NS" 33 | 34 | kubectl -n $NS apply -f examples/basic/tikv-cluster.yaml 35 | 36 | hack::wait_for_success 1800 30 "t::tc_is_ready $NS basic" 37 | -------------------------------------------------------------------------------- /test/examples/t.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2020 TiKV Project Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/../.. && pwd) 17 | cd $ROOT 18 | 19 | function t::tc_is_ready() { 20 | local ns="$1" 21 | local name="$2" 22 | kubectl -n $ns wait --for=condition=Ready --timeout 10s tikvcluster/$name 23 | if [ $? -ne 0 ]; then 24 | echo "info: cluster $ns/$name is not ready, status: " 25 | kubectl -n $ns get tikvcluster/$name -o wide 26 | return 1 27 | fi 28 | return 0 29 | } 30 | 31 | function t::crds_are_ready() { 32 | for name in $@; do 33 | local established=$(kubectl get crd $name -o json | jq '.status["conditions"][] | select(.type == "Established") | .status') 34 | if [ $? -ne 0 ]; then 35 | echo "error: crd $name is not found" 36 | return 1 37 | fi 38 | if [[ "$established" != "True" ]]; then 39 | echo "error: crd $name is not ready" 40 | return 1 41 | fi 42 | done 43 | return 0 44 | } 45 | 46 | function t::ns_is_active() { 47 | local ns="$1" 48 | local phase=$(kubectl get ns $ns -ojsonpath='{.status.phase}') 49 | [[ "$phase" == "Active" ]] 50 | } 51 | 52 | function t::deploy_is_ready() { 53 | local ns="$1" 54 | local name="$2" 55 | read a b <<<$(kubectl -n $ns get deploy/$name -ojsonpath='{.spec.replicas} {.status.readyReplicas}{"\n"}') 56 | if [[ "$a" -gt 0 && "$a" -eq "$b" ]]; then 57 | echo "info: all pods of deployment $ns/$name are ready (desired: $a, ready: $b)" 58 | return 0 59 | fi 60 | echo "info: pods of deployment $ns/$name (desired: $a, ready: $b)" 61 | return 1 62 | } 63 | 64 | -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 TiKV Project Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // +build tools 15 | 16 | // Tool dependencies are tracked here to make go module happy 17 | // Refer https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 18 | package tools 19 | 20 | import ( 21 | _ "k8s.io/code-generator" 22 | ) 23 | --------------------------------------------------------------------------------