diff --git a/Makefile b/Makefile
index 48ecaa47b4..6b9f8bb3d9 100644
--- a/Makefile
+++ b/Makefile
@@ -31,8 +31,9 @@ default: build
docker-push: docker
docker push "${DOCKER_REPO}/tidb-operator:${IMAGE_TAG}"
docker push "${DOCKER_REPO}/tidb-backup-manager:${IMAGE_TAG}"
+ docker push "${DOCKER_REPO}/br-federation-manager:${IMAGE_TAG}"
-docker: operator-docker backup-docker
+docker: operator-docker backup-docker br-federation-docker
ifeq ($(NO_BUILD),y)
operator-docker:
@@ -46,7 +47,7 @@ else
docker build --tag "${DOCKER_REPO}/tidb-operator:${IMAGE_TAG}" --build-arg=TARGETARCH=$(GOARCH) images/tidb-operator
endif
-build: controller-manager scheduler discovery admission-webhook backup-manager
+build: controller-manager scheduler discovery admission-webhook backup-manager br-federation-manager
controller-manager:
ifeq ($(E2E),y)
@@ -83,6 +84,13 @@ else
$(GO_BUILD) -ldflags '$(LDFLAGS)' -o images/tidb-backup-manager/bin/$(GOARCH)/tidb-backup-manager cmd/backup-manager/main.go
endif
+br-federation-manager:
+ifeq ($(E2E),y)
+ $(GO_TEST) -ldflags '$(LDFLAGS)' -c -o images/br-federation-manager/bin/br-federation-manager ./cmd/br-federation-manager
+else
+ $(GO_BUILD) -ldflags '$(LDFLAGS)' -o images/br-federation-manager/bin/$(GOARCH)/br-federation-manager ./cmd/br-federation-manager
+endif
+
ifeq ($(NO_BUILD),y)
backup-docker:
@echo "NO_BUILD=y, skip build for $@"
@@ -95,6 +103,18 @@ else
docker build --tag "${DOCKER_REPO}/tidb-backup-manager:${IMAGE_TAG}" --build-arg=TARGETARCH=$(GOARCH) images/tidb-backup-manager
endif
+ifeq ($(NO_BUILD),y)
+br-federation-docker:
+ @echo "NO_BUILD=y, skip build for $@"
+else
+br-federation-docker: br-federation-manager
+endif
+ifeq ($(E2E),y)
+ docker build --tag "${DOCKER_REPO}/br-federation-manager:${IMAGE_TAG}" -f images/br-federation-manager/Dockerfile.e2e images/br-federation-manager
+else
+ docker build --tag "${DOCKER_REPO}/br-federation-manager:${IMAGE_TAG}" --build-arg=TARGETARCH=$(GOARCH) images/br-federation-manager
+endif
+
e2e-docker-push: e2e-docker
docker push "${DOCKER_REPO}/tidb-operator-e2e:${IMAGE_TAG}"
diff --git a/charts/br-federation/.helmignore b/charts/br-federation/.helmignore
new file mode 100644
index 0000000000..f0c1319444
--- /dev/null
+++ b/charts/br-federation/.helmignore
@@ -0,0 +1,21 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
diff --git a/charts/br-federation/Chart.yaml b/charts/br-federation/Chart.yaml
new file mode 100644
index 0000000000..be875dbf1f
--- /dev/null
+++ b/charts/br-federation/Chart.yaml
@@ -0,0 +1,20 @@
+apiVersion: v1
+description: br-federation Helm chart for Kubernetes
+name: br-federation
+version: v1-canary
+appVersion: v1-canary
+home: https://github.com/pingcap/tidb-operator
+sources:
+ - https://github.com/pingcap/tidb-operator
+keywords:
+ - operator
+ - newsql
+ - htap
+ - database
+ - mysql
+ - raft
+maintainers:
+- name: csuzhangxc
+ email: zhangxuecheng@pingcap.com
+- name: WangLe1321
+ email: le.wang@pingcap.com
diff --git a/charts/br-federation/templates/NOTES.txt b/charts/br-federation/templates/NOTES.txt
new file mode 100644
index 0000000000..f2c0b39d78
--- /dev/null
+++ b/charts/br-federation/templates/NOTES.txt
@@ -0,0 +1,3 @@
+Make sure br-federation components are running:
+
+ kubectl get pods --namespace {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }}
diff --git a/charts/br-federation/templates/_helpers.tpl b/charts/br-federation/templates/_helpers.tpl
new file mode 100644
index 0000000000..22e513b4cd
--- /dev/null
+++ b/charts/br-federation/templates/_helpers.tpl
@@ -0,0 +1,24 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "chart.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+{{- define "br-federation.fullname" -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{- define "helm-toolkit.utils.template" -}}
+{{- $name := index . 0 -}}
+{{- $context := index . 1 -}}
+{{- $last := base $context.Template.Name }}
+{{- $wtf := $context.Template.Name | replace $last $name -}}
+{{ include $wtf $context }}
+{{- end }}
diff --git a/charts/br-federation/templates/controller-manager-deployment.yaml b/charts/br-federation/templates/controller-manager-deployment.yaml
new file mode 100644
index 0000000000..fa08cf741d
--- /dev/null
+++ b/charts/br-federation/templates/controller-manager-deployment.yaml
@@ -0,0 +1,125 @@
+{{- if (hasKey .Values.brFederationManager "create" | ternary .Values.brFederationManager.create true) }}
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ {{- if eq .Values.appendReleaseSuffix true}}
+ name: br-federation-manager-{{.Release.Name }}
+ {{- else }}
+ name: br-federation-manager
+ {{- end }}
+ namespace: {{ .Release.Namespace }}
+ labels:
+ app.kubernetes.io/name: {{ template "chart.name" . }}
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/component: br-federation-manager
+ helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+spec:
+ replicas: {{ .Values.brFederationManager.replicas }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ template "chart.name" . }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/component: br-federation-manager
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: {{ template "chart.name" . }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/component: br-federation-manager
+{{- if .Values.brFederationManager.podAnnotations }}
+ annotations:
+{{ toYaml .Values.brFederationManager.podAnnotations | indent 8 }}
+{{ end }}
+ spec:
+ {{- if .Values.brFederationManager.serviceAccount }}
+ {{- if eq .Values.appendReleaseSuffix true}}
+ serviceAccount: {{ .Values.brFederationManager.serviceAccount }}-{{ .Release.Name }}
+ {{- else }}
+ serviceAccount: {{ .Values.brFederationManager.serviceAccount }}
+ {{- end }}
+ {{- end }}
+ {{- if .Values.imagePullSecrets }}
+ imagePullSecrets:
+ {{ toYaml .Values.imagePullSecrets | indent 6 }}
+ {{- end }}
+ containers:
+ - name: br-federation-manager
+ image: {{ .Values.image }}
+ imagePullPolicy: {{ .Values.imagePullPolicy | default "IfNotPresent" }}
+ {{- if .Values.brFederationManager.resources }}
+ resources:
+{{ toYaml .Values.brFederationManager.resources | indent 12 }}
+ {{- end }}
+ livenessProbe:
+ tcpSocket:
+ port: 6060
+ initialDelaySeconds: 30
+ periodSeconds: 10
+ failureThreshold: 10
+ command:
+ - /usr/local/bin/br-federation-manager
+ - -v={{ .Values.brFederationManager.logLevel }}
+ {{- if .Values.brFederationManager.workers }}
+ - -workers={{ .Values.brFederationManager.workers | default 5 }}
+ {{- end }}
+ {{- if .Values.brFederationManager.leaderLeaseDuration }}
+ - -leader-lease-duration={{ .Values.brFederationManager.leaderLeaseDuration }}
+ {{- end }}
+ {{- if .Values.brFederationManager.leaderRenewDeadline }}
+ - -leader-renew-deadline={{ .Values.brFederationManager.leaderRenewDeadline }}
+ {{- end }}
+ {{- if .Values.brFederationManager.leaderRetryPeriod }}
+ - -leader-retry-period={{ .Values.brFederationManager.leaderRetryPeriod }}
+ {{- end }}
+ {{- if .Values.brFederationManager.kubeClientQPS }}
+ - -kube-client-qps={{ .Values.brFederationManager.kubeClientQPS }}
+ {{- end }}
+ {{- if .Values.brFederationManager.kubeClientBurst }}
+ - -kube-client-burst={{ .Values.brFederationManager.kubeClientBurst }}
+ {{- end }}
+ env:
+ - name: NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ - name: TZ
+ value: {{ .Values.timezone | default "UTC" }}
+ {{- if eq .Values.appendReleaseSuffix true}}
+ - name: HELM_RELEASE
+ value: {{ .Release.Name }}
+ {{- end }}
+ {{- with .Values.brFederationManager.env }}
+{{ toYaml . | indent 10 }}
+ {{- end }}
+ volumeMounts:
+ - name: federation-kubeconfig
+ mountPath: /etc/br-federation/federation-kubeconfig
+ readOnly: true
+ volumes:
+ - name: federation-kubeconfig
+ secret:
+ secretName: {{ .Values.brFederationManager.federationKubeconfigSecret }}
+ {{- with .Values.brFederationManager.nodeSelector }}
+ nodeSelector:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+
+ {{- with .Values.brFederationManager.affinity }}
+ affinity:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+
+ {{- with .Values.brFederationManager.tolerations }}
+ tolerations:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+
+ {{- if .Values.brFederationManager.priorityClassName }}
+ priorityClassName: {{ .Values.brFederationManager.priorityClassName }}
+ {{- end }}
+ {{- with .Values.brFederationManager.securityContext }}
+ securityContext:
+{{ toYaml . | indent 8 }}
+ {{- end}}
+{{- end }}
diff --git a/charts/br-federation/templates/controller-manager-rbac.yaml b/charts/br-federation/templates/controller-manager-rbac.yaml
new file mode 100644
index 0000000000..44e8ecb8b8
--- /dev/null
+++ b/charts/br-federation/templates/controller-manager-rbac.yaml
@@ -0,0 +1,58 @@
+{{- if and .Values.rbac.create (hasKey .Values.brFederationManager "create" | ternary .Values.brFederationManager.create true) }}
+kind: ServiceAccount
+apiVersion: v1
+metadata:
+ {{- if eq .Values.appendReleaseSuffix true}}
+ name: {{ .Values.brFederationManager.serviceAccount }}-{{ .Release.Name }}
+ {{- else }}
+ name: {{ .Values.brFederationManager.serviceAccount }}
+ {{- end }}
+ namespace: {{ .Release.Namespace }}
+ labels:
+ app.kubernetes.io/name: {{ template "chart.name" . }}
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/component: br-federation-manager
+ helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: {{ .Release.Name }}:br-federation-manager
+ labels:
+ app.kubernetes.io/name: {{ template "chart.name" . }}
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/component: br-federation-manager
+ helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+rules:
+- apiGroups: [""]
+ resources: ["endpoints"]
+ verbs: ["create", "get", "list", "watch", "update", "delete"]
+- apiGroups: ["federation.pingcap.com"]
+ resources: ["*"]
+ verbs: ["*"]
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: {{ .Release.Name }}:br-federation-manager
+ labels:
+ app.kubernetes.io/name: {{ template "chart.name" . }}
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ app.kubernetes.io/component: br-federation-manager
+ helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+subjects:
+- kind: ServiceAccount
+ {{- if eq .Values.appendReleaseSuffix true}}
+ name: {{ .Values.brFederationManager.serviceAccount }}-{{ .Release.Name }}
+ {{- else }}
+ name: {{ .Values.brFederationManager.serviceAccount }}
+ {{- end }}
+ namespace: {{ .Release.Namespace }}
+roleRef:
+ kind: ClusterRole
+ name: {{ .Release.Name }}:br-federation-manager
+ apiGroup: rbac.authorization.k8s.io
+{{- end }}
diff --git a/charts/br-federation/values.yaml b/charts/br-federation/values.yaml
new file mode 100644
index 0000000000..3234847dc2
--- /dev/null
+++ b/charts/br-federation/values.yaml
@@ -0,0 +1,77 @@
+# Default values for br-federation
+
+rbac:
+ create: true
+
+# timezone is the default system timzone
+timezone: UTC
+
+image: pingcap/br-federation-manager:v1.5.0-beta.1
+imagePullPolicy: IfNotPresent
+# imagePullSecrets: []
+
+appendReleaseSuffix: false
+
+brFederationManager:
+ create: true
+ # With rbac.create=false, the user is responsible for creating this account
+ # With rbac.create=true, this service account will be created
+ # Also see rbac.create
+ serviceAccount: br-federation-manager
+
+ # Secret name of the kubeconfig for the federation Kubernetes clusters
+ # The data item key is the cluster name, and the data item value is the base64 encoded kubeconfig
+ federationKubeconfigSecret: br-federation-kubeconfig
+
+ logLevel: 2
+ replicas: 1
+ resources:
+ requests:
+ cpu: 80m
+ memory: 50Mi
+ # REF: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
+ # priorityClassName: system-cluster-critical
+
+
+ # REF: https://pkg.go.dev/k8s.io/client-go/tools/leaderelection#LeaderElectionConfig
+ ## leaderLeaseDuration is the duration that non-leader candidates will wait to force acquire leadership
+ # leaderLeaseDuration: 15s
+ ## leaderRenewDeadline is the duration that the acting master will retry refreshing leadership before giving up
+ # leaderRenewDeadline: 10s
+ ## leaderRetryPeriod is the duration the LeaderElector clients should wait between tries of actions
+ # leaderRetryPeriod: 2s
+
+ ## number of workers that are allowed to sync concurrently. default 5
+ # workers: 5
+
+ ## affinity defines pod scheduling rules,affinity default settings is empty.
+ ## please read the affinity document before set your scheduling rule:
+ ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ affinity: {}
+ ## nodeSelector ensure pods only assigning to nodes which have each of the indicated key-value pairs as labels
+ ## ref:https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
+ nodeSelector: {}
+ ## Tolerations are applied to pods, and allow pods to schedule onto nodes with matching taints.
+ ## refer to https://kubernetes.io/docs/concepts/configuration/taint-and-toleration
+ tolerations: []
+ ## Env define environments for the controller manager.
+ ## NOTE that the following env names is reserved:
+ ## - NAMESPACE
+ ## - TZ
+ ## - HELM_RELEASE
+ env: []
+ # - name: AWS_REGION
+ # value: us-west-2
+ # SecurityContext is security config of this component, it will set template.spec.securityContext
+ # Refer to https://kubernetes.io/docs/tasks/configure-pod-container/security-context
+ securityContext: {}
+ # runAsUser: 1000
+ # runAsGroup: 2000
+ # fsGroup: 2000
+ # PodAnnotations will set template.metadata.annotations
+ # Refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
+ podAnnotations: {}
+ ## KubeClientQPS indicates the maximum QPS to the kubenetes API server from client.
+ # kubeClientQPS: 5
+ ## Maximum burst for throttle.
+ # kubeClientBurst: 10
diff --git a/cmd/br-federation-manager/main.go b/cmd/br-federation-manager/main.go
new file mode 100644
index 0000000000..e562c5de77
--- /dev/null
+++ b/cmd/br-federation-manager/main.go
@@ -0,0 +1,263 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "context"
+ "flag"
+ "net/http"
+ _ "net/http/pprof"
+ "os"
+ "os/signal"
+ "path/filepath"
+ "reflect"
+ "syscall"
+
+ "github.com/prometheus/client_golang/prometheus/promhttp"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/util/wait"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/clientcmd"
+ "k8s.io/client-go/tools/leaderelection"
+ "k8s.io/client-go/tools/leaderelection/resourcelock"
+ "k8s.io/client-go/tools/record"
+ "k8s.io/component-base/logs"
+ "k8s.io/klog/v2"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ fedversioned "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned"
+ "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/controller/fedvolumebackup"
+ "github.com/pingcap/tidb-operator/pkg/controller/fedvolumebackupschedule"
+ "github.com/pingcap/tidb-operator/pkg/controller/fedvolumerestore"
+ "github.com/pingcap/tidb-operator/pkg/metrics"
+ "github.com/pingcap/tidb-operator/pkg/version"
+)
+
+func main() {
+ var cfg *rest.Config
+ cliCfg := controller.DefaultBrFedCLIConfig()
+ cliCfg.AddFlag(flag.CommandLine)
+ flag.Parse()
+
+ if cliCfg.PrintVersion {
+ version.PrintVersionInfo()
+ os.Exit(0)
+ }
+
+ logs.InitLogs()
+ defer logs.FlushLogs()
+
+ version.LogVersionInfo()
+ flag.VisitAll(func(flag *flag.Flag) {
+ klog.V(1).Infof("FLAG: --%s=%q", flag.Name, flag.Value)
+ })
+
+ hostName, err := os.Hostname()
+ if err != nil {
+ klog.Fatalf("failed to get hostname: %v", err)
+ }
+
+ ns := os.Getenv("NAMESPACE")
+ if ns == "" {
+ klog.Fatal("NAMESPACE environment variable not set")
+ }
+
+ helmRelease := os.Getenv("HELM_RELEASE")
+ if helmRelease == "" {
+ klog.Info("HELM_RELEASE environment variable not set")
+ }
+
+ kubconfig := os.Getenv("KUBECONFIG")
+ if kubconfig != "" {
+ cfg, err = clientcmd.BuildConfigFromFlags("", kubconfig)
+ } else {
+ cfg, err = rest.InClusterConfig()
+ }
+ if err != nil {
+ klog.Fatalf("failed to get config: %v", err)
+ }
+
+ // If they are zero, the created client will use the default values: 5, 10.
+ cfg.QPS = float32(cliCfg.KubeClientQPS)
+ cfg.Burst = cliCfg.KubeClientBurst
+
+ cli, err := versioned.NewForConfig(cfg)
+ if err != nil {
+ klog.Fatalf("failed to create Clientset: %v", err)
+ }
+ var kubeCli kubernetes.Interface
+ kubeCli, err = kubernetes.NewForConfig(cfg)
+ if err != nil {
+ klog.Fatalf("failed to get kubernetes Clientset: %v", err)
+ }
+ // TODO: optimize the read of genericCli with the shared cache
+ genericCli, err := client.New(cfg, client.Options{Scheme: Scheme})
+ if err != nil {
+ klog.Fatalf("failed to get the generic kube-apiserver client: %v", err)
+ }
+
+ // init kube clients to the federation K8s clusters
+ fedClients, err := initFederationKubeClients(cliCfg)
+ if err != nil {
+ klog.Fatalf("failed to init federation kube clients: %v", err)
+ }
+
+ deps := controller.NewBrFedDependencies(cliCfg, cli, kubeCli, genericCli, fedClients)
+
+ onStarted := func(ctx context.Context) {
+ // Define some nested types to simplify the codebase
+ type Controller interface {
+ Run(int, <-chan struct{})
+ Name() string
+ }
+ type InformerFactory interface {
+ Start(stopCh <-chan struct{})
+ WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
+ }
+
+ initMetrics := func(c Controller) {
+ metrics.ActiveWorkers.WithLabelValues(c.Name()).Set(0)
+ }
+
+ // Initialize all controllers
+ controllers := []Controller{
+ fedvolumebackup.NewController(deps),
+ fedvolumerestore.NewController(deps),
+ fedvolumebackupschedule.NewController(deps),
+ }
+
+ // Start informer factories after all controllers are initialized.
+ informerFactories := []InformerFactory{
+ deps.InformerFactory,
+ deps.KubeInformerFactory,
+ deps.LabelFilterKubeInformerFactory,
+ }
+ for _, f := range informerFactories {
+ f.Start(ctx.Done())
+ for v, synced := range f.WaitForCacheSync(wait.NeverStop) {
+ if !synced {
+ klog.Fatalf("error syncing informer for %v", v)
+ }
+ }
+ }
+ klog.Info("cache of informer factories sync successfully")
+
+ // Start syncLoop for all controllers
+ for _, controller := range controllers {
+ c := controller
+ initMetrics(c)
+ go wait.Forever(func() { c.Run(cliCfg.Workers, ctx.Done()) }, cliCfg.WaitDuration)
+ }
+ }
+ onStopped := func() {
+ klog.Fatal("leader election lost")
+ }
+
+ endPointsName := "br-federation-manager"
+ if helmRelease != "" {
+ endPointsName += "-" + helmRelease
+ }
+ // leader election for multiple br-federation-manager instances
+ go wait.Forever(func() {
+ leaderelection.RunOrDie(context.TODO(), leaderelection.LeaderElectionConfig{
+ Lock: &resourcelock.EndpointsLock{
+ EndpointsMeta: metav1.ObjectMeta{
+ Namespace: ns,
+ Name: endPointsName,
+ },
+ Client: kubeCli.CoreV1(),
+ LockConfig: resourcelock.ResourceLockConfig{
+ Identity: hostName,
+ EventRecorder: &record.FakeRecorder{},
+ },
+ },
+ LeaseDuration: cliCfg.LeaseDuration,
+ RenewDeadline: cliCfg.RenewDeadline,
+ RetryPeriod: cliCfg.RetryPeriod,
+ Callbacks: leaderelection.LeaderCallbacks{
+ OnStartedLeading: onStarted,
+ OnStoppedLeading: onStopped,
+ },
+ })
+ }, cliCfg.WaitDuration)
+
+ srv := createHTTPServer()
+ sc := make(chan os.Signal, 1)
+ signal.Notify(sc,
+ syscall.SIGHUP,
+ syscall.SIGINT,
+ syscall.SIGTERM,
+ syscall.SIGQUIT,
+ )
+
+ go func() {
+ sig := <-sc
+ klog.Infof("got signal %s to exit", sig)
+ if err2 := srv.Shutdown(context.Background()); err2 != nil {
+ klog.Fatal("fail to shutdown the HTTP server", err2)
+ }
+ }()
+
+ if err = srv.ListenAndServe(); err != http.ErrServerClosed {
+ klog.Fatal(err)
+ }
+ klog.Infof("br-federation-manager exited")
+}
+
+func initFederationKubeClients(cliCfg *controller.BrFedCLIConfig) (map[string]*fedversioned.Clientset, error) {
+ files, err := os.ReadDir(cliCfg.FederationKubeConfigPath)
+ if err != nil {
+ return nil, err
+ }
+
+ clients := make(map[string]*fedversioned.Clientset)
+ for _, f := range files {
+ if f.IsDir() || f.Name() == "..data" {
+ continue
+ }
+
+ cfg, err := clientcmd.BuildConfigFromFlags("", filepath.Join(cliCfg.FederationKubeConfigPath, f.Name()))
+ if err != nil {
+ return nil, err // return error if any kube client init failed
+ }
+
+ // we use the same QPS and Burst as for the API server which is running this manager now
+ cfg.QPS = float32(cliCfg.KubeClientQPS)
+ cfg.Burst = cliCfg.KubeClientBurst
+
+ cli, err := fedversioned.NewForConfig(cfg)
+ if err != nil {
+ return nil, err
+ }
+ clients[f.Name()] = cli
+ }
+
+ return clients, nil
+}
+
+func createHTTPServer() *http.Server {
+ serverMux := http.NewServeMux()
+ // HTTP path for pprof
+ serverMux.Handle("/", http.DefaultServeMux)
+ // HTTP path for prometheus.
+ serverMux.Handle("/metrics", promhttp.Handler())
+
+ return &http.Server{
+ Addr: ":6060",
+ Handler: serverMux,
+ }
+}
diff --git a/cmd/br-federation-manager/main_test.go b/cmd/br-federation-manager/main_test.go
new file mode 100644
index 0000000000..7e639dc08a
--- /dev/null
+++ b/cmd/br-federation-manager/main_test.go
@@ -0,0 +1,40 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "os"
+ "strings"
+ "testing"
+)
+
+var _ = func() bool {
+ testing.Init()
+ return true
+}()
+
+func TestRunMain(t *testing.T) {
+ var args []string
+ for _, arg := range os.Args {
+ switch {
+ case arg == "E2E":
+ case strings.HasPrefix(arg, "-test."):
+ default:
+ args = append(args, arg)
+ }
+ }
+
+ os.Args = args
+ main()
+}
diff --git a/cmd/br-federation-manager/scheme.go b/cmd/br-federation-manager/scheme.go
new file mode 100644
index 0000000000..277f03708d
--- /dev/null
+++ b/cmd/br-federation-manager/scheme.go
@@ -0,0 +1,33 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ fedscheme "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned/scheme"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ kubescheme "k8s.io/client-go/kubernetes/scheme"
+)
+
+// Scheme gathers the schemes of native resources and custom resources used by br-federation-manager
+// in favor of the generic controller-runtime/client
+var Scheme = runtime.NewScheme()
+
+func init() {
+ v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
+ utilruntime.Must(fedscheme.AddToScheme(Scheme))
+ utilruntime.Must(kubescheme.AddToScheme(Scheme))
+}
diff --git a/docs/api-references/federation-docs.md b/docs/api-references/federation-docs.md
new file mode 100644
index 0000000000..8565e7d5f4
--- /dev/null
+++ b/docs/api-references/federation-docs.md
@@ -0,0 +1,454 @@
+---
+title: TiDB Operator API Document
+summary: Reference of TiDB Operator API
+category: how-to
+---
+
API Document
+federation.pingcap.com/v1alpha1
+Resource Types:
+
+VolumeBackup
+
+
VolumeBackup is the control script’s spec
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+apiVersion
+string |
+
+
+federation.pingcap.com/v1alpha1
+
+ |
+
+
+
+kind
+string
+ |
+VolumeBackup |
+
+
+
+metadata
+
+
+Kubernetes meta/v1.ObjectMeta
+
+
+ |
+
+Refer to the Kubernetes API documentation for the fields of the
+metadata field.
+ |
+
+
+
+spec
+
+
+VolumeBackupSpec
+
+
+ |
+
+
+
+
+ |
+
+
+
+status
+
+
+VolumeBackupStatus
+
+
+ |
+
+ |
+
+
+
+VolumeBackupSchedule
+
+
VolumeBackupSchedule is the control script’s spec
+
+
+VolumeRestore
+
+
VolumeRestore is the control script’s spec
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+apiVersion
+string |
+
+
+federation.pingcap.com/v1alpha1
+
+ |
+
+
+
+kind
+string
+ |
+VolumeRestore |
+
+
+
+metadata
+
+
+Kubernetes meta/v1.ObjectMeta
+
+
+ |
+
+Refer to the Kubernetes API documentation for the fields of the
+metadata field.
+ |
+
+
+
+spec
+
+
+VolumeRestoreSpec
+
+
+ |
+
+
+
+
+ |
+
+
+
+status
+
+
+VolumeRestoreStatus
+
+
+ |
+
+ |
+
+
+
+VolumeBackupCondition
+
+(Appears on:
+VolumeBackupStatus)
+
+
+
VolumeBackupCondition describes the observed state of a VolumeBackup at a certain point.
+
+
+VolumeBackupScheduleSpec
+
+(Appears on:
+VolumeBackupSchedule)
+
+
+
VolumeBackupScheduleSpec describes the attributes that a user creates on a volume backup schedule.
+
+VolumeBackupScheduleStatus
+
+(Appears on:
+VolumeBackupSchedule)
+
+
+
VolumeBackupScheduleStatus represents the current status of a volume backup schedule.
+
+VolumeBackupSpec
+
+(Appears on:
+VolumeBackup)
+
+
+
VolumeBackupSpec describes the attributes that a user creates on a volume backup.
+
+VolumeBackupStatus
+
+(Appears on:
+VolumeBackup)
+
+
+
VolumeBackupStatus represents the current status of a volume backup.
+
+
+VolumeRestoreCondition
+
+(Appears on:
+VolumeRestoreStatus)
+
+
+
VolumeRestoreCondition describes the observed state of a VolumeRestore at a certain point.
+
+
+VolumeRestoreSpec
+
+(Appears on:
+VolumeRestore)
+
+
+
VolumeRestoreSpec describes the attributes that a user creates on a volume restore.
+
+VolumeRestoreStatus
+
+(Appears on:
+VolumeRestore)
+
+
+
VolumeRestoreStatus represents the current status of a volume restore.
+
+
+
+
+Generated with gen-crd-api-reference-docs
+
diff --git a/hack/update-api-references.sh b/hack/update-api-references.sh
index fb9d0fdc62..44da3c360e 100755
--- a/hack/update-api-references.sh
+++ b/hack/update-api-references.sh
@@ -25,6 +25,8 @@ source hack/lib.sh
hack::ensure_gen_crd_api_references_docs
DOCS_PATH="$ROOT/docs/api-references"
+
+echo "Generating API references docs ..."
API_DIR="${ROOT}/pkg/apis/pingcap/v1alpha1"
pushd ${API_DIR} >/dev/null
@@ -34,3 +36,14 @@ pushd ${API_DIR} >/dev/null
-api-dir "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" \
-out-file "$DOCS_PATH/docs.md"
popd >/dev/null
+
+echo "Generating API references docs for federation ..."
+API_DIR="${ROOT}/pkg/apis/federation/pingcap/v1alpha1"
+
+pushd ${API_DIR} >/dev/null
+ GOROOT=$(go env GOROOT) ${DOCS_BIN} \
+ -config "$DOCS_PATH/config.json" \
+ -template-dir "$DOCS_PATH/template" \
+ -api-dir "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1" \
+ -out-file "$DOCS_PATH/federation-docs.md"
+popd >/dev/null
diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh
index fdd6ee29d9..c085ae41dd 100755
--- a/hack/update-codegen.sh
+++ b/hack/update-codegen.sh
@@ -31,5 +31,12 @@ GOBIN=$OUTPUT_BIN bash $ROOT/hack/generate-groups.sh "deepcopy,client,informer,l
--output-base $ROOT \
--go-header-file ./hack/boilerplate/boilerplate.generatego.txt
+GOBIN=$OUTPUT_BIN bash $ROOT/hack/generate-groups.sh "deepcopy,client,informer,lister" \
+ github.com/pingcap/tidb-operator/pkg/client/federation \
+ github.com/pingcap/tidb-operator/pkg/apis/federation \
+ pingcap:v1alpha1 \
+ --output-base $ROOT \
+ --go-header-file ./hack/boilerplate/boilerplate.generatego.txt
+
# then we merge generated code with our code base and clean up
cp -r github.com/pingcap/tidb-operator/pkg $ROOT && rm -rf github.com
diff --git a/hack/update-crd.sh b/hack/update-crd.sh
index 55b5f0e29a..05d09b2a83 100755
--- a/hack/update-crd.sh
+++ b/hack/update-crd.sh
@@ -28,7 +28,9 @@ source hack/lib.sh
CONTROLLER_GEN=${OUTPUT_BIN}/controller-gen
hack::ensure_controller_gen
-API_PACKAGES="github.com/pingcap/tidb-operator/pkg/apis/..."
+echo "Generating CRDs ..."
+
+API_PACKAGES="github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1/..."
CRD_OUTPUT_DIR=${ROOT}/manifests/crd
CRD_OPTIONS="preserveUnknownFields=false,allowDangerousTypes=true,maxDescLen=0"
@@ -50,3 +52,30 @@ done
# merge all CRDs
find ${CRD_OUTPUT_DIR}/v1 -name "*.yaml" | sort | xargs cat > ${ROOT}/manifests/crd.yaml
find ${CRD_OUTPUT_DIR}/v1beta1 -name "*.yaml" | sort | xargs cat > ${ROOT}/manifests/crd_v1beta1.yaml
+
+
+# generate CRDs for federation into separate files so that most users don't need to install them
+echo "Generating CRDs for federation ..."
+
+API_PACKAGES="github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1/..."
+CRD_OUTPUT_DIR=${ROOT}/manifests/crd/federation
+CRD_OPTIONS="preserveUnknownFields=false,allowDangerousTypes=true,maxDescLen=0"
+
+# generate CRDs
+${CONTROLLER_GEN} \
+ crd:crdVersions=v1beta1,${CRD_OPTIONS} \
+ paths=${API_PACKAGES} \
+ output:crd:dir=${CRD_OUTPUT_DIR}/v1beta1
+${CONTROLLER_GEN} \
+ crd:crdVersions=v1,${CRD_OPTIONS} \
+ paths=${API_PACKAGES} \
+ output:crd:dir=${CRD_OUTPUT_DIR}/v1
+
+for file in ${SKIP_CRD_FILES[@]}; do
+ rm -f ${CRD_OUTPUT_DIR}/v1beta1/${file}
+ rm -f ${CRD_OUTPUT_DIR}/v1/${file}
+done
+
+# merge all CRDs
+find ${CRD_OUTPUT_DIR}/v1 -name "*.yaml" | sort | xargs cat > ${ROOT}/manifests/federation-crd.yaml
+find ${CRD_OUTPUT_DIR}/v1beta1 -name "*.yaml" | sort | xargs cat > ${ROOT}/manifests/federation-crd_v1beta1.yaml
diff --git a/hack/update-openapi-spec.sh b/hack/update-openapi-spec.sh
index beba384ef9..7c678f5667 100755
--- a/hack/update-openapi-spec.sh
+++ b/hack/update-openapi-spec.sh
@@ -26,3 +26,7 @@ hack::ensure_openapi
$OUTPUT_BIN/openapi-gen --go-header-file=./hack/boilerplate/boilerplate.generatego.txt \
-i github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1,k8s.io/apimachinery/pkg/apis/meta/v1,k8s.io/api/core/v1 \
-p apis/pingcap/v1alpha1 -O openapi_generated -o ./pkg
+
+$OUTPUT_BIN/openapi-gen --go-header-file=./hack/boilerplate/boilerplate.generatego.txt \
+ -i github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1 \
+ -p apis/federation/pingcap/v1alpha1 -O openapi_generated -o ./pkg
diff --git a/hack/verify-api-references.sh b/hack/verify-api-references.sh
index a58e009c42..74a665ac10 100755
--- a/hack/verify-api-references.sh
+++ b/hack/verify-api-references.sh
@@ -23,9 +23,13 @@ cd $ROOT
targetDocs="$ROOT/docs/api-references/docs.md"
verifyDocs_tmp=$(mktemp)
trap "rm -f $verifyDocs_tmp" EXIT
-
cp "$targetDocs" "${verifyDocs_tmp}"
+targetFedDocs="$ROOT/docs/api-references/federation-docs.md"
+verifyFedDocs_tmp=$(mktemp)
+trap "rm -f $verifyFedDocs_tmp" EXIT
+cp "$targetFedDocs" "${verifyFedDocs_tmp}"
+
hack/update-api-references.sh
echo "diffing $targetDocs with $verifyDocs_tmp" >&2
@@ -36,3 +40,13 @@ if [[ -n "${diff}" ]]; then
echo "Run ./hack/update-api-references.sh" >&2
exit 1
fi
+
+# verify for federation
+echo "diffing $targetFedDocs with $verifyFedDocs_tmp" >&2
+diff=$(diff "$targetFedDocs" "$verifyFedDocs_tmp") || true
+if [[ -n "${diff}" ]]; then
+ echo "${diff}" >&2
+ echo >&2
+ echo "Run ./hack/update-api-references.sh" >&2
+ exit 1
+fi
diff --git a/hack/verify-crd.sh b/hack/verify-crd.sh
index 8c7ed46bee..6f956d716f 100755
--- a/hack/verify-crd.sh
+++ b/hack/verify-crd.sh
@@ -41,3 +41,19 @@ for version in v1beta1 v1; do
fi
done
done
+
+# verify for federation
+for version in v1beta1 v1; do
+ for file in `ls $TARGET_DIR/federation/$version`; do
+ targetFile=$TARGET_DIR/federation/$version/$file
+ verifyFile=$VERIFY_TMP_DIR/federation/$version/$file
+ echo "diffing $targetFile with $verifyFile" >&2
+ diff=$(diff "$targetFile" "$verifyFile") || true
+ if [[ -n "${diff}" ]]; then
+ echo "${diff}" >&2
+ echo >&2
+ echo "Run ./hack/update-crd.sh" >&2
+ exit 1
+ fi
+ done
+done
diff --git a/hack/verify-openapi-spec.sh b/hack/verify-openapi-spec.sh
index bd23bd974c..16cc9d21c0 100755
--- a/hack/verify-openapi-spec.sh
+++ b/hack/verify-openapi-spec.sh
@@ -23,9 +23,13 @@ cd $ROOT
target="pkg/apis/pingcap/v1alpha1/openapi_generated.go"
verify_tmp=$(mktemp)
trap "rm -f $verify_tmp" EXIT
-
cp "$target" "${verify_tmp}"
+targetFed="pkg/apis/federation/pingcap/v1alpha1/openapi_generated.go"
+verifyFed_tmp=$(mktemp)
+trap "rm -f $verifyFed_tmp" EXIT
+cp "$targetFed" "${verifyFed_tmp}"
+
hack/update-openapi-spec.sh
echo "diffing $target with $verify_tmp" >&2
@@ -36,3 +40,12 @@ if [[ -n "${diff}" ]]; then
echo "Run ./hack/update-openapi-spec.sh" >&2
exit 1
fi
+
+echo "diffing $targetFed with $verifyFed_tmp" >&2
+diff=$(diff "$targetFed" "$verifyFed_tmp") || true
+if [[ -n "${diff}" ]]; then
+ echo "${diff}" >&2
+ echo >&2
+ echo "Run ./hack/update-openapi-spec.sh" >&2
+ exit 1
+fi
diff --git a/images/br-federation-manager/Dockerfile b/images/br-federation-manager/Dockerfile
new file mode 100644
index 0000000000..9ae82b78b2
--- /dev/null
+++ b/images/br-federation-manager/Dockerfile
@@ -0,0 +1,5 @@
+FROM alpine:3.14
+
+ARG TARGETARCH
+RUN apk add tzdata bind-tools --no-cache
+ADD bin/${TARGETARCH}/br-federation-manager /usr/local/bin/br-federation-manager
diff --git a/images/br-federation-manager/Dockerfile.e2e b/images/br-federation-manager/Dockerfile.e2e
new file mode 100644
index 0000000000..3bc5cb9756
--- /dev/null
+++ b/images/br-federation-manager/Dockerfile.e2e
@@ -0,0 +1,5 @@
+FROM alpine:3.14
+
+ARG TARGETARCH
+RUN apk add tzdata bind-tools --no-cache
+ADD bin/br-federation-manager /usr/local/bin/br-federation-manager
diff --git a/manifests/crd/federation/v1/federation.pingcap.com_volumebackups.yaml b/manifests/crd/federation/v1/federation.pingcap.com_volumebackups.yaml
new file mode 100644
index 0000000000..0ee18b3336
--- /dev/null
+++ b/manifests/crd/federation/v1/federation.pingcap.com_volumebackups.yaml
@@ -0,0 +1,65 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.6.2
+ creationTimestamp: null
+ name: volumebackups.federation.pingcap.com
+spec:
+ group: federation.pingcap.com
+ names:
+ kind: VolumeBackup
+ listKind: VolumeBackupList
+ plural: volumebackups
+ shortNames:
+ - vbk
+ singular: volumebackup
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ type: object
+ status:
+ properties:
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ nullable: true
+ type: string
+ message:
+ type: string
+ reason:
+ type: string
+ status:
+ type: string
+ required:
+ - status
+ type: object
+ nullable: true
+ type: array
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
diff --git a/manifests/crd/federation/v1/federation.pingcap.com_volumebackupschedules.yaml b/manifests/crd/federation/v1/federation.pingcap.com_volumebackupschedules.yaml
new file mode 100644
index 0000000000..748bab61d9
--- /dev/null
+++ b/manifests/crd/federation/v1/federation.pingcap.com_volumebackupschedules.yaml
@@ -0,0 +1,46 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.6.2
+ creationTimestamp: null
+ name: volumebackupschedules.federation.pingcap.com
+spec:
+ group: federation.pingcap.com
+ names:
+ kind: VolumeBackupSchedule
+ listKind: VolumeBackupScheduleList
+ plural: volumebackupschedules
+ shortNames:
+ - vbks
+ singular: volumebackupschedule
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ type: object
+ status:
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
diff --git a/manifests/crd/federation/v1/federation.pingcap.com_volumerestores.yaml b/manifests/crd/federation/v1/federation.pingcap.com_volumerestores.yaml
new file mode 100644
index 0000000000..3f57ee7f5a
--- /dev/null
+++ b/manifests/crd/federation/v1/federation.pingcap.com_volumerestores.yaml
@@ -0,0 +1,65 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.6.2
+ creationTimestamp: null
+ name: volumerestores.federation.pingcap.com
+spec:
+ group: federation.pingcap.com
+ names:
+ kind: VolumeRestore
+ listKind: VolumeRestoreList
+ plural: volumerestores
+ shortNames:
+ - vrt
+ singular: volumerestore
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ type: object
+ status:
+ properties:
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ nullable: true
+ type: string
+ message:
+ type: string
+ reason:
+ type: string
+ status:
+ type: string
+ required:
+ - status
+ type: object
+ nullable: true
+ type: array
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
diff --git a/manifests/crd/federation/v1beta1/federation.pingcap.com_volumebackups.yaml b/manifests/crd/federation/v1beta1/federation.pingcap.com_volumebackups.yaml
new file mode 100644
index 0000000000..546fc9a90b
--- /dev/null
+++ b/manifests/crd/federation/v1beta1/federation.pingcap.com_volumebackups.yaml
@@ -0,0 +1,67 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.6.2
+ creationTimestamp: null
+ name: volumebackups.federation.pingcap.com
+spec:
+ group: federation.pingcap.com
+ names:
+ kind: VolumeBackup
+ listKind: VolumeBackupList
+ plural: volumebackups
+ shortNames:
+ - vbk
+ singular: volumebackup
+ preserveUnknownFields: false
+ scope: Namespaced
+ validation:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ type: object
+ status:
+ properties:
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ nullable: true
+ type: string
+ message:
+ type: string
+ reason:
+ type: string
+ status:
+ type: string
+ required:
+ - status
+ type: object
+ nullable: true
+ type: array
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ version: v1alpha1
+ versions:
+ - name: v1alpha1
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
diff --git a/manifests/crd/federation/v1beta1/federation.pingcap.com_volumebackupschedules.yaml b/manifests/crd/federation/v1beta1/federation.pingcap.com_volumebackupschedules.yaml
new file mode 100644
index 0000000000..29d26b06e9
--- /dev/null
+++ b/manifests/crd/federation/v1beta1/federation.pingcap.com_volumebackupschedules.yaml
@@ -0,0 +1,48 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.6.2
+ creationTimestamp: null
+ name: volumebackupschedules.federation.pingcap.com
+spec:
+ group: federation.pingcap.com
+ names:
+ kind: VolumeBackupSchedule
+ listKind: VolumeBackupScheduleList
+ plural: volumebackupschedules
+ shortNames:
+ - vbks
+ singular: volumebackupschedule
+ preserveUnknownFields: false
+ scope: Namespaced
+ validation:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ type: object
+ status:
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ version: v1alpha1
+ versions:
+ - name: v1alpha1
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
diff --git a/manifests/crd/federation/v1beta1/federation.pingcap.com_volumerestores.yaml b/manifests/crd/federation/v1beta1/federation.pingcap.com_volumerestores.yaml
new file mode 100644
index 0000000000..2d31fb2c84
--- /dev/null
+++ b/manifests/crd/federation/v1beta1/federation.pingcap.com_volumerestores.yaml
@@ -0,0 +1,67 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.6.2
+ creationTimestamp: null
+ name: volumerestores.federation.pingcap.com
+spec:
+ group: federation.pingcap.com
+ names:
+ kind: VolumeRestore
+ listKind: VolumeRestoreList
+ plural: volumerestores
+ shortNames:
+ - vrt
+ singular: volumerestore
+ preserveUnknownFields: false
+ scope: Namespaced
+ validation:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ type: object
+ status:
+ properties:
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ nullable: true
+ type: string
+ message:
+ type: string
+ reason:
+ type: string
+ status:
+ type: string
+ required:
+ - status
+ type: object
+ nullable: true
+ type: array
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ version: v1alpha1
+ versions:
+ - name: v1alpha1
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
diff --git a/manifests/federation-crd.yaml b/manifests/federation-crd.yaml
new file mode 100644
index 0000000000..52b8a31bab
--- /dev/null
+++ b/manifests/federation-crd.yaml
@@ -0,0 +1,176 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.6.2
+ creationTimestamp: null
+ name: volumebackups.federation.pingcap.com
+spec:
+ group: federation.pingcap.com
+ names:
+ kind: VolumeBackup
+ listKind: VolumeBackupList
+ plural: volumebackups
+ shortNames:
+ - vbk
+ singular: volumebackup
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ type: object
+ status:
+ properties:
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ nullable: true
+ type: string
+ message:
+ type: string
+ reason:
+ type: string
+ status:
+ type: string
+ required:
+ - status
+ type: object
+ nullable: true
+ type: array
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.6.2
+ creationTimestamp: null
+ name: volumebackupschedules.federation.pingcap.com
+spec:
+ group: federation.pingcap.com
+ names:
+ kind: VolumeBackupSchedule
+ listKind: VolumeBackupScheduleList
+ plural: volumebackupschedules
+ shortNames:
+ - vbks
+ singular: volumebackupschedule
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ type: object
+ status:
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.6.2
+ creationTimestamp: null
+ name: volumerestores.federation.pingcap.com
+spec:
+ group: federation.pingcap.com
+ names:
+ kind: VolumeRestore
+ listKind: VolumeRestoreList
+ plural: volumerestores
+ shortNames:
+ - vrt
+ singular: volumerestore
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ type: object
+ status:
+ properties:
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ nullable: true
+ type: string
+ message:
+ type: string
+ reason:
+ type: string
+ status:
+ type: string
+ required:
+ - status
+ type: object
+ nullable: true
+ type: array
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
diff --git a/manifests/federation-crd_v1beta1.yaml b/manifests/federation-crd_v1beta1.yaml
new file mode 100644
index 0000000000..9439e128a6
--- /dev/null
+++ b/manifests/federation-crd_v1beta1.yaml
@@ -0,0 +1,182 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.6.2
+ creationTimestamp: null
+ name: volumebackups.federation.pingcap.com
+spec:
+ group: federation.pingcap.com
+ names:
+ kind: VolumeBackup
+ listKind: VolumeBackupList
+ plural: volumebackups
+ shortNames:
+ - vbk
+ singular: volumebackup
+ preserveUnknownFields: false
+ scope: Namespaced
+ validation:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ type: object
+ status:
+ properties:
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ nullable: true
+ type: string
+ message:
+ type: string
+ reason:
+ type: string
+ status:
+ type: string
+ required:
+ - status
+ type: object
+ nullable: true
+ type: array
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ version: v1alpha1
+ versions:
+ - name: v1alpha1
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
+
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.6.2
+ creationTimestamp: null
+ name: volumebackupschedules.federation.pingcap.com
+spec:
+ group: federation.pingcap.com
+ names:
+ kind: VolumeBackupSchedule
+ listKind: VolumeBackupScheduleList
+ plural: volumebackupschedules
+ shortNames:
+ - vbks
+ singular: volumebackupschedule
+ preserveUnknownFields: false
+ scope: Namespaced
+ validation:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ type: object
+ status:
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ version: v1alpha1
+ versions:
+ - name: v1alpha1
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
+
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.6.2
+ creationTimestamp: null
+ name: volumerestores.federation.pingcap.com
+spec:
+ group: federation.pingcap.com
+ names:
+ kind: VolumeRestore
+ listKind: VolumeRestoreList
+ plural: volumerestores
+ shortNames:
+ - vrt
+ singular: volumerestore
+ preserveUnknownFields: false
+ scope: Namespaced
+ validation:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ type: object
+ status:
+ properties:
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ nullable: true
+ type: string
+ message:
+ type: string
+ reason:
+ type: string
+ status:
+ type: string
+ required:
+ - status
+ type: object
+ nullable: true
+ type: array
+ type: object
+ required:
+ - metadata
+ - spec
+ type: object
+ version: v1alpha1
+ versions:
+ - name: v1alpha1
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
diff --git a/pkg/apis/federation/pingcap/v1alpha1/doc.go b/pkg/apis/federation/pingcap/v1alpha1/doc.go
new file mode 100644
index 0000000000..b34cfdc624
--- /dev/null
+++ b/pkg/apis/federation/pingcap/v1alpha1/doc.go
@@ -0,0 +1,21 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +k8s:deepcopy-gen=package,register
+
+// Package v1alpha1 is the v1alpha1 version of the federation API.
+// +groupName=federation.pingcap.com
+
+// We use a separate package for federation so that most users do not need to install these CRDs.
+
+package v1alpha1
diff --git a/pkg/apis/federation/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/federation/pingcap/v1alpha1/openapi_generated.go
new file mode 100644
index 0000000000..47520f4712
--- /dev/null
+++ b/pkg/apis/federation/pingcap/v1alpha1/openapi_generated.go
@@ -0,0 +1,328 @@
+//go:build !ignore_autogenerated
+// +build !ignore_autogenerated
+
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by openapi-gen. DO NOT EDIT.
+
+// This file was autogenerated by openapi-gen. Do not edit it manually!
+
+package v1alpha1
+
+import (
+ spec "github.com/go-openapi/spec"
+ common "k8s.io/kube-openapi/pkg/common"
+)
+
+func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
+ return map[string]common.OpenAPIDefinition{
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackup": schema_apis_federation_pingcap_v1alpha1_VolumeBackup(ref),
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackupList": schema_apis_federation_pingcap_v1alpha1_VolumeBackupList(ref),
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackupSchedule": schema_apis_federation_pingcap_v1alpha1_VolumeBackupSchedule(ref),
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackupScheduleList": schema_apis_federation_pingcap_v1alpha1_VolumeBackupScheduleList(ref),
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackupScheduleSpec": schema_apis_federation_pingcap_v1alpha1_VolumeBackupScheduleSpec(ref),
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackupSpec": schema_apis_federation_pingcap_v1alpha1_VolumeBackupSpec(ref),
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeRestore": schema_apis_federation_pingcap_v1alpha1_VolumeRestore(ref),
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeRestoreList": schema_apis_federation_pingcap_v1alpha1_VolumeRestoreList(ref),
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeRestoreSpec": schema_apis_federation_pingcap_v1alpha1_VolumeRestoreSpec(ref),
+ }
+}
+
+func schema_apis_federation_pingcap_v1alpha1_VolumeBackup(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "VolumeBackup is the control script's spec",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "kind": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "apiVersion": {
+ SchemaProps: spec.SchemaProps{
+ Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "spec": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackupSpec"),
+ },
+ },
+ },
+ Required: []string{"spec"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackupSpec"},
+ }
+}
+
+func schema_apis_federation_pingcap_v1alpha1_VolumeBackupList(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "VolumeBackupList is VolumeBackup list",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "kind": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "apiVersion": {
+ SchemaProps: spec.SchemaProps{
+ Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "metadata": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
+ },
+ },
+ "items": {
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackup"),
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"metadata", "items"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackup", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
+ }
+}
+
+func schema_apis_federation_pingcap_v1alpha1_VolumeBackupSchedule(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "VolumeBackupSchedule is the control script's spec",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "kind": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "apiVersion": {
+ SchemaProps: spec.SchemaProps{
+ Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "spec": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackupScheduleSpec"),
+ },
+ },
+ },
+ Required: []string{"spec"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackupScheduleSpec"},
+ }
+}
+
+func schema_apis_federation_pingcap_v1alpha1_VolumeBackupScheduleList(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "VolumeBackupScheduleList is VolumeBackupSchedule list",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "kind": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "apiVersion": {
+ SchemaProps: spec.SchemaProps{
+ Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "metadata": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
+ },
+ },
+ "items": {
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackupSchedule"),
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"metadata", "items"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeBackupSchedule", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
+ }
+}
+
+func schema_apis_federation_pingcap_v1alpha1_VolumeBackupScheduleSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "VolumeBackupScheduleSpec describes the attributes that a user creates on a volume backup schedule.",
+ Type: []string{"object"},
+ },
+ },
+ }
+}
+
+func schema_apis_federation_pingcap_v1alpha1_VolumeBackupSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "VolumeBackupSpec describes the attributes that a user creates on a volume backup.",
+ Type: []string{"object"},
+ },
+ },
+ }
+}
+
+func schema_apis_federation_pingcap_v1alpha1_VolumeRestore(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "VolumeRestore is the control script's spec",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "kind": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "apiVersion": {
+ SchemaProps: spec.SchemaProps{
+ Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "spec": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeRestoreSpec"),
+ },
+ },
+ },
+ Required: []string{"spec"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeRestoreSpec"},
+ }
+}
+
+func schema_apis_federation_pingcap_v1alpha1_VolumeRestoreList(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "VolumeRestoreList is VolumeRestore list",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "kind": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "apiVersion": {
+ SchemaProps: spec.SchemaProps{
+ Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "metadata": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
+ },
+ },
+ "items": {
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeRestore"),
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"metadata", "items"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1.VolumeRestore", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
+ }
+}
+
+func schema_apis_federation_pingcap_v1alpha1_VolumeRestoreSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "VolumeRestoreSpec describes the attributes that a user creates on a volume restore.",
+ Type: []string{"object"},
+ },
+ },
+ }
+}
diff --git a/pkg/apis/federation/pingcap/v1alpha1/register.go b/pkg/apis/federation/pingcap/v1alpha1/register.go
new file mode 100644
index 0000000000..ff96e7c772
--- /dev/null
+++ b/pkg/apis/federation/pingcap/v1alpha1/register.go
@@ -0,0 +1,63 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package v1alpha1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+var (
+ // SchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
+ SchemeBuilder runtime.SchemeBuilder
+ localSchemeBuilder = &SchemeBuilder
+ // AddToScheme applies all the stored functions to the scheme.
+ AddToScheme = localSchemeBuilder.AddToScheme
+ // Scheme is the scheme instance of operator
+ Scheme *runtime.Scheme
+
+ groupName = "federation.pingcap.com"
+)
+
+// SchemeGroupVersion is group version used to register these objects
+var SchemeGroupVersion = schema.GroupVersion{Group: groupName, Version: "v1alpha1"}
+
+func init() {
+ // We only register manually written functions here. The registration of the
+ // generated functions takes place in the generated files. The separation
+ // makes the code compile even when the generated files are missing.
+ localSchemeBuilder.Register(addKnownTypes)
+}
+
+// Resource takes an unqualified resource and returns back a Group qualified GroupResource
+func Resource(resource string) schema.GroupResource {
+ return SchemeGroupVersion.WithResource(resource).GroupResource()
+}
+
+// Adds the list of known types to api.Scheme.
+func addKnownTypes(scheme *runtime.Scheme) error {
+ Scheme = scheme
+ scheme.AddKnownTypes(SchemeGroupVersion,
+ &VolumeBackup{},
+ &VolumeBackupList{},
+ &VolumeBackupSchedule{},
+ &VolumeBackupScheduleList{},
+ &VolumeRestore{},
+ &VolumeRestoreList{},
+ )
+
+ metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
+ return nil
+}
diff --git a/pkg/apis/federation/pingcap/v1alpha1/types.go b/pkg/apis/federation/pingcap/v1alpha1/types.go
new file mode 100644
index 0000000000..8242b58c35
--- /dev/null
+++ b/pkg/apis/federation/pingcap/v1alpha1/types.go
@@ -0,0 +1,159 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package v1alpha1
+
+import (
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// TODO(federation): add `kubebuilder:printcolumn` after fileds are defined
+
+// +genclient
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// VolumeBackup is the control script's spec
+//
+// +k8s:openapi-gen=true
+// +kubebuilder:resource:shortName="vbk"
+// +genclient:noStatus
+type VolumeBackup struct {
+ metav1.TypeMeta `json:",inline"`
+ // +k8s:openapi-gen=false
+ metav1.ObjectMeta `json:"metadata"`
+
+ Spec VolumeBackupSpec `json:"spec"`
+
+ // +k8s:openapi-gen=false
+ Status VolumeBackupStatus `json:"status,omitempty"`
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// VolumeBackupList is VolumeBackup list
+// +k8s:openapi-gen=true
+type VolumeBackupList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata"`
+ Items []VolumeBackup `json:"items"`
+}
+
+// VolumeBackupSpec describes the attributes that a user creates on a volume backup.
+// +k8s:openapi-gen=true
+type VolumeBackupSpec struct {
+}
+
+// VolumeBackupStatus represents the current status of a volume backup.
+type VolumeBackupStatus struct {
+ // +nullable
+ Conditions []VolumeBackupCondition `json:"conditions,omitempty"`
+}
+
+// VolumeBackupCondition describes the observed state of a VolumeBackup at a certain point.
+type VolumeBackupCondition struct {
+ Status corev1.ConditionStatus `json:"status"`
+
+ // +nullable
+ LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
+ Reason string `json:"reason,omitempty"`
+ Message string `json:"message,omitempty"`
+}
+
+// +genclient
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// VolumeBackupSchedule is the control script's spec
+//
+// +k8s:openapi-gen=true
+// +kubebuilder:resource:shortName="vbks"
+// +genclient:noStatus
+type VolumeBackupSchedule struct {
+ metav1.TypeMeta `json:",inline"`
+ // +k8s:openapi-gen=false
+ metav1.ObjectMeta `json:"metadata"`
+
+ Spec VolumeBackupScheduleSpec `json:"spec"`
+
+ // +k8s:openapi-gen=false
+ Status VolumeBackupScheduleStatus `json:"status,omitempty"`
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// VolumeBackupScheduleList is VolumeBackupSchedule list
+// +k8s:openapi-gen=true
+type VolumeBackupScheduleList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata"`
+ Items []VolumeBackupSchedule `json:"items"`
+}
+
+// VolumeBackupScheduleSpec describes the attributes that a user creates on a volume backup schedule.
+// +k8s:openapi-gen=true
+type VolumeBackupScheduleSpec struct {
+}
+
+// VolumeBackupScheduleStatus represents the current status of a volume backup schedule.
+type VolumeBackupScheduleStatus struct {
+}
+
+// +genclient
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// VolumeRestore is the control script's spec
+//
+// +k8s:openapi-gen=true
+// +kubebuilder:resource:shortName="vrt"
+// +genclient:noStatus
+type VolumeRestore struct {
+ metav1.TypeMeta `json:",inline"`
+ // +k8s:openapi-gen=false
+ metav1.ObjectMeta `json:"metadata"`
+
+ Spec VolumeRestoreSpec `json:"spec"`
+
+ // +k8s:openapi-gen=false
+ Status VolumeRestoreStatus `json:"status,omitempty"`
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// VolumeRestoreList is VolumeRestore list
+// +k8s:openapi-gen=true
+type VolumeRestoreList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata"`
+ Items []VolumeRestore `json:"items"`
+}
+
+// VolumeRestoreSpec describes the attributes that a user creates on a volume restore.
+// +k8s:openapi-gen=true
+type VolumeRestoreSpec struct {
+}
+
+// VolumeRestoreStatus represents the current status of a volume restore.
+type VolumeRestoreStatus struct {
+ // +nullable
+ Conditions []VolumeRestoreCondition `json:"conditions,omitempty"`
+}
+
+// VolumeRestoreCondition describes the observed state of a VolumeRestore at a certain point.
+type VolumeRestoreCondition struct {
+ Status corev1.ConditionStatus `json:"status"`
+
+ // +nullable
+ LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
+ Reason string `json:"reason,omitempty"`
+ Message string `json:"message,omitempty"`
+}
diff --git a/pkg/apis/federation/pingcap/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/federation/pingcap/v1alpha1/zz_generated.deepcopy.go
new file mode 100644
index 0000000000..6eeccc0ef7
--- /dev/null
+++ b/pkg/apis/federation/pingcap/v1alpha1/zz_generated.deepcopy.go
@@ -0,0 +1,350 @@
+//go:build !ignore_autogenerated
+// +build !ignore_autogenerated
+
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by deepcopy-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ runtime "k8s.io/apimachinery/pkg/runtime"
+)
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeBackup) DeepCopyInto(out *VolumeBackup) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ out.Spec = in.Spec
+ in.Status.DeepCopyInto(&out.Status)
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeBackup.
+func (in *VolumeBackup) DeepCopy() *VolumeBackup {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeBackup)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *VolumeBackup) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeBackupCondition) DeepCopyInto(out *VolumeBackupCondition) {
+ *out = *in
+ in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeBackupCondition.
+func (in *VolumeBackupCondition) DeepCopy() *VolumeBackupCondition {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeBackupCondition)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeBackupList) DeepCopyInto(out *VolumeBackupList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]VolumeBackup, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeBackupList.
+func (in *VolumeBackupList) DeepCopy() *VolumeBackupList {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeBackupList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *VolumeBackupList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeBackupSchedule) DeepCopyInto(out *VolumeBackupSchedule) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ out.Spec = in.Spec
+ out.Status = in.Status
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeBackupSchedule.
+func (in *VolumeBackupSchedule) DeepCopy() *VolumeBackupSchedule {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeBackupSchedule)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *VolumeBackupSchedule) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeBackupScheduleList) DeepCopyInto(out *VolumeBackupScheduleList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]VolumeBackupSchedule, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeBackupScheduleList.
+func (in *VolumeBackupScheduleList) DeepCopy() *VolumeBackupScheduleList {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeBackupScheduleList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *VolumeBackupScheduleList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeBackupScheduleSpec) DeepCopyInto(out *VolumeBackupScheduleSpec) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeBackupScheduleSpec.
+func (in *VolumeBackupScheduleSpec) DeepCopy() *VolumeBackupScheduleSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeBackupScheduleSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeBackupScheduleStatus) DeepCopyInto(out *VolumeBackupScheduleStatus) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeBackupScheduleStatus.
+func (in *VolumeBackupScheduleStatus) DeepCopy() *VolumeBackupScheduleStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeBackupScheduleStatus)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeBackupSpec) DeepCopyInto(out *VolumeBackupSpec) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeBackupSpec.
+func (in *VolumeBackupSpec) DeepCopy() *VolumeBackupSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeBackupSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeBackupStatus) DeepCopyInto(out *VolumeBackupStatus) {
+ *out = *in
+ if in.Conditions != nil {
+ in, out := &in.Conditions, &out.Conditions
+ *out = make([]VolumeBackupCondition, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeBackupStatus.
+func (in *VolumeBackupStatus) DeepCopy() *VolumeBackupStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeBackupStatus)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeRestore) DeepCopyInto(out *VolumeRestore) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ out.Spec = in.Spec
+ in.Status.DeepCopyInto(&out.Status)
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeRestore.
+func (in *VolumeRestore) DeepCopy() *VolumeRestore {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeRestore)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *VolumeRestore) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeRestoreCondition) DeepCopyInto(out *VolumeRestoreCondition) {
+ *out = *in
+ in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeRestoreCondition.
+func (in *VolumeRestoreCondition) DeepCopy() *VolumeRestoreCondition {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeRestoreCondition)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeRestoreList) DeepCopyInto(out *VolumeRestoreList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]VolumeRestore, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeRestoreList.
+func (in *VolumeRestoreList) DeepCopy() *VolumeRestoreList {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeRestoreList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *VolumeRestoreList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeRestoreSpec) DeepCopyInto(out *VolumeRestoreSpec) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeRestoreSpec.
+func (in *VolumeRestoreSpec) DeepCopy() *VolumeRestoreSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeRestoreSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VolumeRestoreStatus) DeepCopyInto(out *VolumeRestoreStatus) {
+ *out = *in
+ if in.Conditions != nil {
+ in, out := &in.Conditions, &out.Conditions
+ *out = make([]VolumeRestoreCondition, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeRestoreStatus.
+func (in *VolumeRestoreStatus) DeepCopy() *VolumeRestoreStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(VolumeRestoreStatus)
+ in.DeepCopyInto(out)
+ return out
+}
diff --git a/pkg/apis/label/label.go b/pkg/apis/label/label.go
index c0311bdce4..e6957c88ee 100644
--- a/pkg/apis/label/label.go
+++ b/pkg/apis/label/label.go
@@ -59,7 +59,7 @@ const (
// RestoreLabelKey is restore key
RestoreLabelKey string = "tidb.pingcap.com/restore"
- // BackupProtectionFinalizer is the name of finalizer on backups
+ // BackupProtectionFinalizer is the name of finalizer on backups or federation backups
BackupProtectionFinalizer string = "tidb.pingcap.com/backup-protection"
// AutoScalingGroupLabelKey describes the autoscaling group of the TiDB
diff --git a/pkg/client/federation/clientset/versioned/clientset.go b/pkg/client/federation/clientset/versioned/clientset.go
new file mode 100644
index 0000000000..c0c2fe3d0c
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/clientset.go
@@ -0,0 +1,94 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package versioned
+
+import (
+ "fmt"
+
+ federationv1alpha1 "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1"
+ discovery "k8s.io/client-go/discovery"
+ rest "k8s.io/client-go/rest"
+ flowcontrol "k8s.io/client-go/util/flowcontrol"
+)
+
+type Interface interface {
+ Discovery() discovery.DiscoveryInterface
+ FederationV1alpha1() federationv1alpha1.FederationV1alpha1Interface
+}
+
+// Clientset contains the clients for groups. Each group has exactly one
+// version included in a Clientset.
+type Clientset struct {
+ *discovery.DiscoveryClient
+ federationV1alpha1 *federationv1alpha1.FederationV1alpha1Client
+}
+
+// FederationV1alpha1 retrieves the FederationV1alpha1Client
+func (c *Clientset) FederationV1alpha1() federationv1alpha1.FederationV1alpha1Interface {
+ return c.federationV1alpha1
+}
+
+// Discovery retrieves the DiscoveryClient
+func (c *Clientset) Discovery() discovery.DiscoveryInterface {
+ if c == nil {
+ return nil
+ }
+ return c.DiscoveryClient
+}
+
+// NewForConfig creates a new Clientset for the given config.
+// If config's RateLimiter is not set and QPS and Burst are acceptable,
+// NewForConfig will generate a rate-limiter in configShallowCopy.
+func NewForConfig(c *rest.Config) (*Clientset, error) {
+ configShallowCopy := *c
+ if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
+ if configShallowCopy.Burst <= 0 {
+ return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
+ }
+ configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
+ }
+ var cs Clientset
+ var err error
+ cs.federationV1alpha1, err = federationv1alpha1.NewForConfig(&configShallowCopy)
+ if err != nil {
+ return nil, err
+ }
+
+ cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
+ if err != nil {
+ return nil, err
+ }
+ return &cs, nil
+}
+
+// NewForConfigOrDie creates a new Clientset for the given config and
+// panics if there is an error in the config.
+func NewForConfigOrDie(c *rest.Config) *Clientset {
+ var cs Clientset
+ cs.federationV1alpha1 = federationv1alpha1.NewForConfigOrDie(c)
+
+ cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
+ return &cs
+}
+
+// New creates a new Clientset for the given RESTClient.
+func New(c rest.Interface) *Clientset {
+ var cs Clientset
+ cs.federationV1alpha1 = federationv1alpha1.New(c)
+
+ cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
+ return &cs
+}
diff --git a/pkg/client/federation/clientset/versioned/doc.go b/pkg/client/federation/clientset/versioned/doc.go
new file mode 100644
index 0000000000..19edbe1dd7
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/doc.go
@@ -0,0 +1,17 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package has the automatically generated clientset.
+package versioned
diff --git a/pkg/client/federation/clientset/versioned/fake/clientset_generated.go b/pkg/client/federation/clientset/versioned/fake/clientset_generated.go
new file mode 100644
index 0000000000..0ac7474689
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/fake/clientset_generated.go
@@ -0,0 +1,79 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ clientset "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned"
+ federationv1alpha1 "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1"
+ fakefederationv1alpha1 "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/watch"
+ "k8s.io/client-go/discovery"
+ fakediscovery "k8s.io/client-go/discovery/fake"
+ "k8s.io/client-go/testing"
+)
+
+// NewSimpleClientset returns a clientset that will respond with the provided objects.
+// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
+// without applying any validations and/or defaults. It shouldn't be considered a replacement
+// for a real clientset and is mostly useful in simple unit tests.
+func NewSimpleClientset(objects ...runtime.Object) *Clientset {
+ o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
+ for _, obj := range objects {
+ if err := o.Add(obj); err != nil {
+ panic(err)
+ }
+ }
+
+ cs := &Clientset{tracker: o}
+ cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
+ cs.AddReactor("*", "*", testing.ObjectReaction(o))
+ cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
+ gvr := action.GetResource()
+ ns := action.GetNamespace()
+ watch, err := o.Watch(gvr, ns)
+ if err != nil {
+ return false, nil, err
+ }
+ return true, watch, nil
+ })
+
+ return cs
+}
+
+// Clientset implements clientset.Interface. Meant to be embedded into a
+// struct to get a default implementation. This makes faking out just the method
+// you want to test easier.
+type Clientset struct {
+ testing.Fake
+ discovery *fakediscovery.FakeDiscovery
+ tracker testing.ObjectTracker
+}
+
+func (c *Clientset) Discovery() discovery.DiscoveryInterface {
+ return c.discovery
+}
+
+func (c *Clientset) Tracker() testing.ObjectTracker {
+ return c.tracker
+}
+
+var _ clientset.Interface = &Clientset{}
+
+// FederationV1alpha1 retrieves the FederationV1alpha1Client
+func (c *Clientset) FederationV1alpha1() federationv1alpha1.FederationV1alpha1Interface {
+ return &fakefederationv1alpha1.FakeFederationV1alpha1{Fake: &c.Fake}
+}
diff --git a/pkg/client/federation/clientset/versioned/fake/doc.go b/pkg/client/federation/clientset/versioned/fake/doc.go
new file mode 100644
index 0000000000..28ae66a70a
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/fake/doc.go
@@ -0,0 +1,17 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package has the automatically generated fake clientset.
+package fake
diff --git a/pkg/client/federation/clientset/versioned/fake/register.go b/pkg/client/federation/clientset/versioned/fake/register.go
new file mode 100644
index 0000000000..99d6d46bdf
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/fake/register.go
@@ -0,0 +1,53 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ federationv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ serializer "k8s.io/apimachinery/pkg/runtime/serializer"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+)
+
+var scheme = runtime.NewScheme()
+var codecs = serializer.NewCodecFactory(scheme)
+
+var localSchemeBuilder = runtime.SchemeBuilder{
+ federationv1alpha1.AddToScheme,
+}
+
+// AddToScheme adds all types of this clientset into the given scheme. This allows composition
+// of clientsets, like in:
+//
+// import (
+// "k8s.io/client-go/kubernetes"
+// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
+// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
+// )
+//
+// kclientset, _ := kubernetes.NewForConfig(c)
+// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
+//
+// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
+// correctly.
+var AddToScheme = localSchemeBuilder.AddToScheme
+
+func init() {
+ v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
+ utilruntime.Must(AddToScheme(scheme))
+}
diff --git a/pkg/client/federation/clientset/versioned/scheme/doc.go b/pkg/client/federation/clientset/versioned/scheme/doc.go
new file mode 100644
index 0000000000..c1f8b3a163
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/scheme/doc.go
@@ -0,0 +1,17 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package contains the scheme of the automatically generated clientset.
+package scheme
diff --git a/pkg/client/federation/clientset/versioned/scheme/register.go b/pkg/client/federation/clientset/versioned/scheme/register.go
new file mode 100644
index 0000000000..b21cdb06a2
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/scheme/register.go
@@ -0,0 +1,53 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package scheme
+
+import (
+ federationv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ serializer "k8s.io/apimachinery/pkg/runtime/serializer"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+)
+
+var Scheme = runtime.NewScheme()
+var Codecs = serializer.NewCodecFactory(Scheme)
+var ParameterCodec = runtime.NewParameterCodec(Scheme)
+var localSchemeBuilder = runtime.SchemeBuilder{
+ federationv1alpha1.AddToScheme,
+}
+
+// AddToScheme adds all types of this clientset into the given scheme. This allows composition
+// of clientsets, like in:
+//
+// import (
+// "k8s.io/client-go/kubernetes"
+// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
+// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
+// )
+//
+// kclientset, _ := kubernetes.NewForConfig(c)
+// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
+//
+// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
+// correctly.
+var AddToScheme = localSchemeBuilder.AddToScheme
+
+func init() {
+ v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
+ utilruntime.Must(AddToScheme(Scheme))
+}
diff --git a/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/doc.go b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/doc.go
new file mode 100644
index 0000000000..4a7fe7662b
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/doc.go
@@ -0,0 +1,17 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package has the automatically generated typed clients.
+package v1alpha1
diff --git a/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/doc.go b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/doc.go
new file mode 100644
index 0000000000..385e3174d3
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/doc.go
@@ -0,0 +1,17 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// Package fake has the automatically generated clients.
+package fake
diff --git a/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_pingcap_client.go b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_pingcap_client.go
new file mode 100644
index 0000000000..40e7a119c3
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_pingcap_client.go
@@ -0,0 +1,45 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1"
+ rest "k8s.io/client-go/rest"
+ testing "k8s.io/client-go/testing"
+)
+
+type FakeFederationV1alpha1 struct {
+ *testing.Fake
+}
+
+func (c *FakeFederationV1alpha1) VolumeBackups(namespace string) v1alpha1.VolumeBackupInterface {
+ return &FakeVolumeBackups{c, namespace}
+}
+
+func (c *FakeFederationV1alpha1) VolumeBackupSchedules(namespace string) v1alpha1.VolumeBackupScheduleInterface {
+ return &FakeVolumeBackupSchedules{c, namespace}
+}
+
+func (c *FakeFederationV1alpha1) VolumeRestores(namespace string) v1alpha1.VolumeRestoreInterface {
+ return &FakeVolumeRestores{c, namespace}
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *FakeFederationV1alpha1) RESTClient() rest.Interface {
+ var ret *rest.RESTClient
+ return ret
+}
diff --git a/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_volumebackup.go b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_volumebackup.go
new file mode 100644
index 0000000000..9fbcdb8b66
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_volumebackup.go
@@ -0,0 +1,127 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ "context"
+
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ labels "k8s.io/apimachinery/pkg/labels"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ testing "k8s.io/client-go/testing"
+)
+
+// FakeVolumeBackups implements VolumeBackupInterface
+type FakeVolumeBackups struct {
+ Fake *FakeFederationV1alpha1
+ ns string
+}
+
+var volumebackupsResource = schema.GroupVersionResource{Group: "federation.pingcap.com", Version: "v1alpha1", Resource: "volumebackups"}
+
+var volumebackupsKind = schema.GroupVersionKind{Group: "federation.pingcap.com", Version: "v1alpha1", Kind: "VolumeBackup"}
+
+// Get takes name of the volumeBackup, and returns the corresponding volumeBackup object, and an error if there is any.
+func (c *FakeVolumeBackups) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.VolumeBackup, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewGetAction(volumebackupsResource, c.ns, name), &v1alpha1.VolumeBackup{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.VolumeBackup), err
+}
+
+// List takes label and field selectors, and returns the list of VolumeBackups that match those selectors.
+func (c *FakeVolumeBackups) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.VolumeBackupList, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewListAction(volumebackupsResource, volumebackupsKind, c.ns, opts), &v1alpha1.VolumeBackupList{})
+
+ if obj == nil {
+ return nil, err
+ }
+
+ label, _, _ := testing.ExtractFromListOptions(opts)
+ if label == nil {
+ label = labels.Everything()
+ }
+ list := &v1alpha1.VolumeBackupList{ListMeta: obj.(*v1alpha1.VolumeBackupList).ListMeta}
+ for _, item := range obj.(*v1alpha1.VolumeBackupList).Items {
+ if label.Matches(labels.Set(item.Labels)) {
+ list.Items = append(list.Items, item)
+ }
+ }
+ return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested volumeBackups.
+func (c *FakeVolumeBackups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ return c.Fake.
+ InvokesWatch(testing.NewWatchAction(volumebackupsResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a volumeBackup and creates it. Returns the server's representation of the volumeBackup, and an error, if there is any.
+func (c *FakeVolumeBackups) Create(ctx context.Context, volumeBackup *v1alpha1.VolumeBackup, opts v1.CreateOptions) (result *v1alpha1.VolumeBackup, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewCreateAction(volumebackupsResource, c.ns, volumeBackup), &v1alpha1.VolumeBackup{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.VolumeBackup), err
+}
+
+// Update takes the representation of a volumeBackup and updates it. Returns the server's representation of the volumeBackup, and an error, if there is any.
+func (c *FakeVolumeBackups) Update(ctx context.Context, volumeBackup *v1alpha1.VolumeBackup, opts v1.UpdateOptions) (result *v1alpha1.VolumeBackup, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewUpdateAction(volumebackupsResource, c.ns, volumeBackup), &v1alpha1.VolumeBackup{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.VolumeBackup), err
+}
+
+// Delete takes name of the volumeBackup and deletes it. Returns an error if one occurs.
+func (c *FakeVolumeBackups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ _, err := c.Fake.
+ Invokes(testing.NewDeleteAction(volumebackupsResource, c.ns, name), &v1alpha1.VolumeBackup{})
+
+ return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeVolumeBackups) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ action := testing.NewDeleteCollectionAction(volumebackupsResource, c.ns, listOpts)
+
+ _, err := c.Fake.Invokes(action, &v1alpha1.VolumeBackupList{})
+ return err
+}
+
+// Patch applies the patch and returns the patched volumeBackup.
+func (c *FakeVolumeBackups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.VolumeBackup, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewPatchSubresourceAction(volumebackupsResource, c.ns, name, pt, data, subresources...), &v1alpha1.VolumeBackup{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.VolumeBackup), err
+}
diff --git a/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_volumebackupschedule.go b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_volumebackupschedule.go
new file mode 100644
index 0000000000..7431f7f560
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_volumebackupschedule.go
@@ -0,0 +1,127 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ "context"
+
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ labels "k8s.io/apimachinery/pkg/labels"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ testing "k8s.io/client-go/testing"
+)
+
+// FakeVolumeBackupSchedules implements VolumeBackupScheduleInterface
+type FakeVolumeBackupSchedules struct {
+ Fake *FakeFederationV1alpha1
+ ns string
+}
+
+var volumebackupschedulesResource = schema.GroupVersionResource{Group: "federation.pingcap.com", Version: "v1alpha1", Resource: "volumebackupschedules"}
+
+var volumebackupschedulesKind = schema.GroupVersionKind{Group: "federation.pingcap.com", Version: "v1alpha1", Kind: "VolumeBackupSchedule"}
+
+// Get takes name of the volumeBackupSchedule, and returns the corresponding volumeBackupSchedule object, and an error if there is any.
+func (c *FakeVolumeBackupSchedules) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.VolumeBackupSchedule, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewGetAction(volumebackupschedulesResource, c.ns, name), &v1alpha1.VolumeBackupSchedule{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.VolumeBackupSchedule), err
+}
+
+// List takes label and field selectors, and returns the list of VolumeBackupSchedules that match those selectors.
+func (c *FakeVolumeBackupSchedules) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.VolumeBackupScheduleList, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewListAction(volumebackupschedulesResource, volumebackupschedulesKind, c.ns, opts), &v1alpha1.VolumeBackupScheduleList{})
+
+ if obj == nil {
+ return nil, err
+ }
+
+ label, _, _ := testing.ExtractFromListOptions(opts)
+ if label == nil {
+ label = labels.Everything()
+ }
+ list := &v1alpha1.VolumeBackupScheduleList{ListMeta: obj.(*v1alpha1.VolumeBackupScheduleList).ListMeta}
+ for _, item := range obj.(*v1alpha1.VolumeBackupScheduleList).Items {
+ if label.Matches(labels.Set(item.Labels)) {
+ list.Items = append(list.Items, item)
+ }
+ }
+ return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested volumeBackupSchedules.
+func (c *FakeVolumeBackupSchedules) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ return c.Fake.
+ InvokesWatch(testing.NewWatchAction(volumebackupschedulesResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a volumeBackupSchedule and creates it. Returns the server's representation of the volumeBackupSchedule, and an error, if there is any.
+func (c *FakeVolumeBackupSchedules) Create(ctx context.Context, volumeBackupSchedule *v1alpha1.VolumeBackupSchedule, opts v1.CreateOptions) (result *v1alpha1.VolumeBackupSchedule, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewCreateAction(volumebackupschedulesResource, c.ns, volumeBackupSchedule), &v1alpha1.VolumeBackupSchedule{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.VolumeBackupSchedule), err
+}
+
+// Update takes the representation of a volumeBackupSchedule and updates it. Returns the server's representation of the volumeBackupSchedule, and an error, if there is any.
+func (c *FakeVolumeBackupSchedules) Update(ctx context.Context, volumeBackupSchedule *v1alpha1.VolumeBackupSchedule, opts v1.UpdateOptions) (result *v1alpha1.VolumeBackupSchedule, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewUpdateAction(volumebackupschedulesResource, c.ns, volumeBackupSchedule), &v1alpha1.VolumeBackupSchedule{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.VolumeBackupSchedule), err
+}
+
+// Delete takes name of the volumeBackupSchedule and deletes it. Returns an error if one occurs.
+func (c *FakeVolumeBackupSchedules) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ _, err := c.Fake.
+ Invokes(testing.NewDeleteAction(volumebackupschedulesResource, c.ns, name), &v1alpha1.VolumeBackupSchedule{})
+
+ return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeVolumeBackupSchedules) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ action := testing.NewDeleteCollectionAction(volumebackupschedulesResource, c.ns, listOpts)
+
+ _, err := c.Fake.Invokes(action, &v1alpha1.VolumeBackupScheduleList{})
+ return err
+}
+
+// Patch applies the patch and returns the patched volumeBackupSchedule.
+func (c *FakeVolumeBackupSchedules) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.VolumeBackupSchedule, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewPatchSubresourceAction(volumebackupschedulesResource, c.ns, name, pt, data, subresources...), &v1alpha1.VolumeBackupSchedule{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.VolumeBackupSchedule), err
+}
diff --git a/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_volumerestore.go b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_volumerestore.go
new file mode 100644
index 0000000000..542b5e6363
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_volumerestore.go
@@ -0,0 +1,127 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ "context"
+
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ labels "k8s.io/apimachinery/pkg/labels"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ testing "k8s.io/client-go/testing"
+)
+
+// FakeVolumeRestores implements VolumeRestoreInterface
+type FakeVolumeRestores struct {
+ Fake *FakeFederationV1alpha1
+ ns string
+}
+
+var volumerestoresResource = schema.GroupVersionResource{Group: "federation.pingcap.com", Version: "v1alpha1", Resource: "volumerestores"}
+
+var volumerestoresKind = schema.GroupVersionKind{Group: "federation.pingcap.com", Version: "v1alpha1", Kind: "VolumeRestore"}
+
+// Get takes name of the volumeRestore, and returns the corresponding volumeRestore object, and an error if there is any.
+func (c *FakeVolumeRestores) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.VolumeRestore, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewGetAction(volumerestoresResource, c.ns, name), &v1alpha1.VolumeRestore{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.VolumeRestore), err
+}
+
+// List takes label and field selectors, and returns the list of VolumeRestores that match those selectors.
+func (c *FakeVolumeRestores) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.VolumeRestoreList, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewListAction(volumerestoresResource, volumerestoresKind, c.ns, opts), &v1alpha1.VolumeRestoreList{})
+
+ if obj == nil {
+ return nil, err
+ }
+
+ label, _, _ := testing.ExtractFromListOptions(opts)
+ if label == nil {
+ label = labels.Everything()
+ }
+ list := &v1alpha1.VolumeRestoreList{ListMeta: obj.(*v1alpha1.VolumeRestoreList).ListMeta}
+ for _, item := range obj.(*v1alpha1.VolumeRestoreList).Items {
+ if label.Matches(labels.Set(item.Labels)) {
+ list.Items = append(list.Items, item)
+ }
+ }
+ return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested volumeRestores.
+func (c *FakeVolumeRestores) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ return c.Fake.
+ InvokesWatch(testing.NewWatchAction(volumerestoresResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a volumeRestore and creates it. Returns the server's representation of the volumeRestore, and an error, if there is any.
+func (c *FakeVolumeRestores) Create(ctx context.Context, volumeRestore *v1alpha1.VolumeRestore, opts v1.CreateOptions) (result *v1alpha1.VolumeRestore, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewCreateAction(volumerestoresResource, c.ns, volumeRestore), &v1alpha1.VolumeRestore{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.VolumeRestore), err
+}
+
+// Update takes the representation of a volumeRestore and updates it. Returns the server's representation of the volumeRestore, and an error, if there is any.
+func (c *FakeVolumeRestores) Update(ctx context.Context, volumeRestore *v1alpha1.VolumeRestore, opts v1.UpdateOptions) (result *v1alpha1.VolumeRestore, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewUpdateAction(volumerestoresResource, c.ns, volumeRestore), &v1alpha1.VolumeRestore{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.VolumeRestore), err
+}
+
+// Delete takes name of the volumeRestore and deletes it. Returns an error if one occurs.
+func (c *FakeVolumeRestores) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ _, err := c.Fake.
+ Invokes(testing.NewDeleteAction(volumerestoresResource, c.ns, name), &v1alpha1.VolumeRestore{})
+
+ return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeVolumeRestores) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ action := testing.NewDeleteCollectionAction(volumerestoresResource, c.ns, listOpts)
+
+ _, err := c.Fake.Invokes(action, &v1alpha1.VolumeRestoreList{})
+ return err
+}
+
+// Patch applies the patch and returns the patched volumeRestore.
+func (c *FakeVolumeRestores) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.VolumeRestore, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewPatchSubresourceAction(volumerestoresResource, c.ns, name, pt, data, subresources...), &v1alpha1.VolumeRestore{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*v1alpha1.VolumeRestore), err
+}
diff --git a/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/generated_expansion.go b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/generated_expansion.go
new file mode 100644
index 0000000000..ecf9f49463
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/generated_expansion.go
@@ -0,0 +1,22 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+type VolumeBackupExpansion interface{}
+
+type VolumeBackupScheduleExpansion interface{}
+
+type VolumeRestoreExpansion interface{}
diff --git a/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/pingcap_client.go b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/pingcap_client.go
new file mode 100644
index 0000000000..8aa5c0626b
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/pingcap_client.go
@@ -0,0 +1,96 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned/scheme"
+ rest "k8s.io/client-go/rest"
+)
+
+type FederationV1alpha1Interface interface {
+ RESTClient() rest.Interface
+ VolumeBackupsGetter
+ VolumeBackupSchedulesGetter
+ VolumeRestoresGetter
+}
+
+// FederationV1alpha1Client is used to interact with features provided by the federation.pingcap.com group.
+type FederationV1alpha1Client struct {
+ restClient rest.Interface
+}
+
+func (c *FederationV1alpha1Client) VolumeBackups(namespace string) VolumeBackupInterface {
+ return newVolumeBackups(c, namespace)
+}
+
+func (c *FederationV1alpha1Client) VolumeBackupSchedules(namespace string) VolumeBackupScheduleInterface {
+ return newVolumeBackupSchedules(c, namespace)
+}
+
+func (c *FederationV1alpha1Client) VolumeRestores(namespace string) VolumeRestoreInterface {
+ return newVolumeRestores(c, namespace)
+}
+
+// NewForConfig creates a new FederationV1alpha1Client for the given config.
+func NewForConfig(c *rest.Config) (*FederationV1alpha1Client, error) {
+ config := *c
+ if err := setConfigDefaults(&config); err != nil {
+ return nil, err
+ }
+ client, err := rest.RESTClientFor(&config)
+ if err != nil {
+ return nil, err
+ }
+ return &FederationV1alpha1Client{client}, nil
+}
+
+// NewForConfigOrDie creates a new FederationV1alpha1Client for the given config and
+// panics if there is an error in the config.
+func NewForConfigOrDie(c *rest.Config) *FederationV1alpha1Client {
+ client, err := NewForConfig(c)
+ if err != nil {
+ panic(err)
+ }
+ return client
+}
+
+// New creates a new FederationV1alpha1Client for the given RESTClient.
+func New(c rest.Interface) *FederationV1alpha1Client {
+ return &FederationV1alpha1Client{c}
+}
+
+func setConfigDefaults(config *rest.Config) error {
+ gv := v1alpha1.SchemeGroupVersion
+ config.GroupVersion = &gv
+ config.APIPath = "/apis"
+ config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
+
+ if config.UserAgent == "" {
+ config.UserAgent = rest.DefaultKubernetesUserAgent()
+ }
+
+ return nil
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *FederationV1alpha1Client) RESTClient() rest.Interface {
+ if c == nil {
+ return nil
+ }
+ return c.restClient
+}
diff --git a/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/volumebackup.go b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/volumebackup.go
new file mode 100644
index 0000000000..0e155f541d
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/volumebackup.go
@@ -0,0 +1,175 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ "context"
+ "time"
+
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ scheme "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned/scheme"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ rest "k8s.io/client-go/rest"
+)
+
+// VolumeBackupsGetter has a method to return a VolumeBackupInterface.
+// A group's client should implement this interface.
+type VolumeBackupsGetter interface {
+ VolumeBackups(namespace string) VolumeBackupInterface
+}
+
+// VolumeBackupInterface has methods to work with VolumeBackup resources.
+type VolumeBackupInterface interface {
+ Create(ctx context.Context, volumeBackup *v1alpha1.VolumeBackup, opts v1.CreateOptions) (*v1alpha1.VolumeBackup, error)
+ Update(ctx context.Context, volumeBackup *v1alpha1.VolumeBackup, opts v1.UpdateOptions) (*v1alpha1.VolumeBackup, error)
+ Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
+ DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
+ Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.VolumeBackup, error)
+ List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.VolumeBackupList, error)
+ Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
+ Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.VolumeBackup, err error)
+ VolumeBackupExpansion
+}
+
+// volumeBackups implements VolumeBackupInterface
+type volumeBackups struct {
+ client rest.Interface
+ ns string
+}
+
+// newVolumeBackups returns a VolumeBackups
+func newVolumeBackups(c *FederationV1alpha1Client, namespace string) *volumeBackups {
+ return &volumeBackups{
+ client: c.RESTClient(),
+ ns: namespace,
+ }
+}
+
+// Get takes name of the volumeBackup, and returns the corresponding volumeBackup object, and an error if there is any.
+func (c *volumeBackups) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.VolumeBackup, err error) {
+ result = &v1alpha1.VolumeBackup{}
+ err = c.client.Get().
+ Namespace(c.ns).
+ Resource("volumebackups").
+ Name(name).
+ VersionedParams(&options, scheme.ParameterCodec).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// List takes label and field selectors, and returns the list of VolumeBackups that match those selectors.
+func (c *volumeBackups) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.VolumeBackupList, err error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ result = &v1alpha1.VolumeBackupList{}
+ err = c.client.Get().
+ Namespace(c.ns).
+ Resource("volumebackups").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Watch returns a watch.Interface that watches the requested volumeBackups.
+func (c *volumeBackups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ opts.Watch = true
+ return c.client.Get().
+ Namespace(c.ns).
+ Resource("volumebackups").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Watch(ctx)
+}
+
+// Create takes the representation of a volumeBackup and creates it. Returns the server's representation of the volumeBackup, and an error, if there is any.
+func (c *volumeBackups) Create(ctx context.Context, volumeBackup *v1alpha1.VolumeBackup, opts v1.CreateOptions) (result *v1alpha1.VolumeBackup, err error) {
+ result = &v1alpha1.VolumeBackup{}
+ err = c.client.Post().
+ Namespace(c.ns).
+ Resource("volumebackups").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(volumeBackup).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Update takes the representation of a volumeBackup and updates it. Returns the server's representation of the volumeBackup, and an error, if there is any.
+func (c *volumeBackups) Update(ctx context.Context, volumeBackup *v1alpha1.VolumeBackup, opts v1.UpdateOptions) (result *v1alpha1.VolumeBackup, err error) {
+ result = &v1alpha1.VolumeBackup{}
+ err = c.client.Put().
+ Namespace(c.ns).
+ Resource("volumebackups").
+ Name(volumeBackup.Name).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(volumeBackup).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Delete takes name of the volumeBackup and deletes it. Returns an error if one occurs.
+func (c *volumeBackups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ return c.client.Delete().
+ Namespace(c.ns).
+ Resource("volumebackups").
+ Name(name).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *volumeBackups) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ var timeout time.Duration
+ if listOpts.TimeoutSeconds != nil {
+ timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
+ }
+ return c.client.Delete().
+ Namespace(c.ns).
+ Resource("volumebackups").
+ VersionedParams(&listOpts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// Patch applies the patch and returns the patched volumeBackup.
+func (c *volumeBackups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.VolumeBackup, err error) {
+ result = &v1alpha1.VolumeBackup{}
+ err = c.client.Patch(pt).
+ Namespace(c.ns).
+ Resource("volumebackups").
+ Name(name).
+ SubResource(subresources...).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(data).
+ Do(ctx).
+ Into(result)
+ return
+}
diff --git a/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/volumebackupschedule.go b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/volumebackupschedule.go
new file mode 100644
index 0000000000..4c7d85845c
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/volumebackupschedule.go
@@ -0,0 +1,175 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ "context"
+ "time"
+
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ scheme "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned/scheme"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ rest "k8s.io/client-go/rest"
+)
+
+// VolumeBackupSchedulesGetter has a method to return a VolumeBackupScheduleInterface.
+// A group's client should implement this interface.
+type VolumeBackupSchedulesGetter interface {
+ VolumeBackupSchedules(namespace string) VolumeBackupScheduleInterface
+}
+
+// VolumeBackupScheduleInterface has methods to work with VolumeBackupSchedule resources.
+type VolumeBackupScheduleInterface interface {
+ Create(ctx context.Context, volumeBackupSchedule *v1alpha1.VolumeBackupSchedule, opts v1.CreateOptions) (*v1alpha1.VolumeBackupSchedule, error)
+ Update(ctx context.Context, volumeBackupSchedule *v1alpha1.VolumeBackupSchedule, opts v1.UpdateOptions) (*v1alpha1.VolumeBackupSchedule, error)
+ Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
+ DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
+ Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.VolumeBackupSchedule, error)
+ List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.VolumeBackupScheduleList, error)
+ Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
+ Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.VolumeBackupSchedule, err error)
+ VolumeBackupScheduleExpansion
+}
+
+// volumeBackupSchedules implements VolumeBackupScheduleInterface
+type volumeBackupSchedules struct {
+ client rest.Interface
+ ns string
+}
+
+// newVolumeBackupSchedules returns a VolumeBackupSchedules
+func newVolumeBackupSchedules(c *FederationV1alpha1Client, namespace string) *volumeBackupSchedules {
+ return &volumeBackupSchedules{
+ client: c.RESTClient(),
+ ns: namespace,
+ }
+}
+
+// Get takes name of the volumeBackupSchedule, and returns the corresponding volumeBackupSchedule object, and an error if there is any.
+func (c *volumeBackupSchedules) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.VolumeBackupSchedule, err error) {
+ result = &v1alpha1.VolumeBackupSchedule{}
+ err = c.client.Get().
+ Namespace(c.ns).
+ Resource("volumebackupschedules").
+ Name(name).
+ VersionedParams(&options, scheme.ParameterCodec).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// List takes label and field selectors, and returns the list of VolumeBackupSchedules that match those selectors.
+func (c *volumeBackupSchedules) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.VolumeBackupScheduleList, err error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ result = &v1alpha1.VolumeBackupScheduleList{}
+ err = c.client.Get().
+ Namespace(c.ns).
+ Resource("volumebackupschedules").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Watch returns a watch.Interface that watches the requested volumeBackupSchedules.
+func (c *volumeBackupSchedules) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ opts.Watch = true
+ return c.client.Get().
+ Namespace(c.ns).
+ Resource("volumebackupschedules").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Watch(ctx)
+}
+
+// Create takes the representation of a volumeBackupSchedule and creates it. Returns the server's representation of the volumeBackupSchedule, and an error, if there is any.
+func (c *volumeBackupSchedules) Create(ctx context.Context, volumeBackupSchedule *v1alpha1.VolumeBackupSchedule, opts v1.CreateOptions) (result *v1alpha1.VolumeBackupSchedule, err error) {
+ result = &v1alpha1.VolumeBackupSchedule{}
+ err = c.client.Post().
+ Namespace(c.ns).
+ Resource("volumebackupschedules").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(volumeBackupSchedule).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Update takes the representation of a volumeBackupSchedule and updates it. Returns the server's representation of the volumeBackupSchedule, and an error, if there is any.
+func (c *volumeBackupSchedules) Update(ctx context.Context, volumeBackupSchedule *v1alpha1.VolumeBackupSchedule, opts v1.UpdateOptions) (result *v1alpha1.VolumeBackupSchedule, err error) {
+ result = &v1alpha1.VolumeBackupSchedule{}
+ err = c.client.Put().
+ Namespace(c.ns).
+ Resource("volumebackupschedules").
+ Name(volumeBackupSchedule.Name).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(volumeBackupSchedule).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Delete takes name of the volumeBackupSchedule and deletes it. Returns an error if one occurs.
+func (c *volumeBackupSchedules) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ return c.client.Delete().
+ Namespace(c.ns).
+ Resource("volumebackupschedules").
+ Name(name).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *volumeBackupSchedules) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ var timeout time.Duration
+ if listOpts.TimeoutSeconds != nil {
+ timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
+ }
+ return c.client.Delete().
+ Namespace(c.ns).
+ Resource("volumebackupschedules").
+ VersionedParams(&listOpts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// Patch applies the patch and returns the patched volumeBackupSchedule.
+func (c *volumeBackupSchedules) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.VolumeBackupSchedule, err error) {
+ result = &v1alpha1.VolumeBackupSchedule{}
+ err = c.client.Patch(pt).
+ Namespace(c.ns).
+ Resource("volumebackupschedules").
+ Name(name).
+ SubResource(subresources...).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(data).
+ Do(ctx).
+ Into(result)
+ return
+}
diff --git a/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/volumerestore.go b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/volumerestore.go
new file mode 100644
index 0000000000..dab92d22bb
--- /dev/null
+++ b/pkg/client/federation/clientset/versioned/typed/pingcap/v1alpha1/volumerestore.go
@@ -0,0 +1,175 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ "context"
+ "time"
+
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ scheme "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned/scheme"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ rest "k8s.io/client-go/rest"
+)
+
+// VolumeRestoresGetter has a method to return a VolumeRestoreInterface.
+// A group's client should implement this interface.
+type VolumeRestoresGetter interface {
+ VolumeRestores(namespace string) VolumeRestoreInterface
+}
+
+// VolumeRestoreInterface has methods to work with VolumeRestore resources.
+type VolumeRestoreInterface interface {
+ Create(ctx context.Context, volumeRestore *v1alpha1.VolumeRestore, opts v1.CreateOptions) (*v1alpha1.VolumeRestore, error)
+ Update(ctx context.Context, volumeRestore *v1alpha1.VolumeRestore, opts v1.UpdateOptions) (*v1alpha1.VolumeRestore, error)
+ Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
+ DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
+ Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.VolumeRestore, error)
+ List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.VolumeRestoreList, error)
+ Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
+ Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.VolumeRestore, err error)
+ VolumeRestoreExpansion
+}
+
+// volumeRestores implements VolumeRestoreInterface
+type volumeRestores struct {
+ client rest.Interface
+ ns string
+}
+
+// newVolumeRestores returns a VolumeRestores
+func newVolumeRestores(c *FederationV1alpha1Client, namespace string) *volumeRestores {
+ return &volumeRestores{
+ client: c.RESTClient(),
+ ns: namespace,
+ }
+}
+
+// Get takes name of the volumeRestore, and returns the corresponding volumeRestore object, and an error if there is any.
+func (c *volumeRestores) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.VolumeRestore, err error) {
+ result = &v1alpha1.VolumeRestore{}
+ err = c.client.Get().
+ Namespace(c.ns).
+ Resource("volumerestores").
+ Name(name).
+ VersionedParams(&options, scheme.ParameterCodec).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// List takes label and field selectors, and returns the list of VolumeRestores that match those selectors.
+func (c *volumeRestores) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.VolumeRestoreList, err error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ result = &v1alpha1.VolumeRestoreList{}
+ err = c.client.Get().
+ Namespace(c.ns).
+ Resource("volumerestores").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Watch returns a watch.Interface that watches the requested volumeRestores.
+func (c *volumeRestores) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ opts.Watch = true
+ return c.client.Get().
+ Namespace(c.ns).
+ Resource("volumerestores").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Watch(ctx)
+}
+
+// Create takes the representation of a volumeRestore and creates it. Returns the server's representation of the volumeRestore, and an error, if there is any.
+func (c *volumeRestores) Create(ctx context.Context, volumeRestore *v1alpha1.VolumeRestore, opts v1.CreateOptions) (result *v1alpha1.VolumeRestore, err error) {
+ result = &v1alpha1.VolumeRestore{}
+ err = c.client.Post().
+ Namespace(c.ns).
+ Resource("volumerestores").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(volumeRestore).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Update takes the representation of a volumeRestore and updates it. Returns the server's representation of the volumeRestore, and an error, if there is any.
+func (c *volumeRestores) Update(ctx context.Context, volumeRestore *v1alpha1.VolumeRestore, opts v1.UpdateOptions) (result *v1alpha1.VolumeRestore, err error) {
+ result = &v1alpha1.VolumeRestore{}
+ err = c.client.Put().
+ Namespace(c.ns).
+ Resource("volumerestores").
+ Name(volumeRestore.Name).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(volumeRestore).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Delete takes name of the volumeRestore and deletes it. Returns an error if one occurs.
+func (c *volumeRestores) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ return c.client.Delete().
+ Namespace(c.ns).
+ Resource("volumerestores").
+ Name(name).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *volumeRestores) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ var timeout time.Duration
+ if listOpts.TimeoutSeconds != nil {
+ timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
+ }
+ return c.client.Delete().
+ Namespace(c.ns).
+ Resource("volumerestores").
+ VersionedParams(&listOpts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// Patch applies the patch and returns the patched volumeRestore.
+func (c *volumeRestores) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.VolumeRestore, err error) {
+ result = &v1alpha1.VolumeRestore{}
+ err = c.client.Patch(pt).
+ Namespace(c.ns).
+ Resource("volumerestores").
+ Name(name).
+ SubResource(subresources...).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(data).
+ Do(ctx).
+ Into(result)
+ return
+}
diff --git a/pkg/client/federation/informers/externalversions/factory.go b/pkg/client/federation/informers/externalversions/factory.go
new file mode 100644
index 0000000000..28bf8b7323
--- /dev/null
+++ b/pkg/client/federation/informers/externalversions/factory.go
@@ -0,0 +1,177 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package externalversions
+
+import (
+ reflect "reflect"
+ sync "sync"
+ time "time"
+
+ versioned "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned"
+ internalinterfaces "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions/internalinterfaces"
+ pingcap "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions/pingcap"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ cache "k8s.io/client-go/tools/cache"
+)
+
+// SharedInformerOption defines the functional option type for SharedInformerFactory.
+type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory
+
+type sharedInformerFactory struct {
+ client versioned.Interface
+ namespace string
+ tweakListOptions internalinterfaces.TweakListOptionsFunc
+ lock sync.Mutex
+ defaultResync time.Duration
+ customResync map[reflect.Type]time.Duration
+
+ informers map[reflect.Type]cache.SharedIndexInformer
+ // startedInformers is used for tracking which informers have been started.
+ // This allows Start() to be called multiple times safely.
+ startedInformers map[reflect.Type]bool
+}
+
+// WithCustomResyncConfig sets a custom resync period for the specified informer types.
+func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption {
+ return func(factory *sharedInformerFactory) *sharedInformerFactory {
+ for k, v := range resyncConfig {
+ factory.customResync[reflect.TypeOf(k)] = v
+ }
+ return factory
+ }
+}
+
+// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory.
+func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption {
+ return func(factory *sharedInformerFactory) *sharedInformerFactory {
+ factory.tweakListOptions = tweakListOptions
+ return factory
+ }
+}
+
+// WithNamespace limits the SharedInformerFactory to the specified namespace.
+func WithNamespace(namespace string) SharedInformerOption {
+ return func(factory *sharedInformerFactory) *sharedInformerFactory {
+ factory.namespace = namespace
+ return factory
+ }
+}
+
+// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces.
+func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory {
+ return NewSharedInformerFactoryWithOptions(client, defaultResync)
+}
+
+// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory.
+// Listers obtained via this SharedInformerFactory will be subject to the same filters
+// as specified here.
+// Deprecated: Please use NewSharedInformerFactoryWithOptions instead
+func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory {
+ return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions))
+}
+
+// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options.
+func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory {
+ factory := &sharedInformerFactory{
+ client: client,
+ namespace: v1.NamespaceAll,
+ defaultResync: defaultResync,
+ informers: make(map[reflect.Type]cache.SharedIndexInformer),
+ startedInformers: make(map[reflect.Type]bool),
+ customResync: make(map[reflect.Type]time.Duration),
+ }
+
+ // Apply all options
+ for _, opt := range options {
+ factory = opt(factory)
+ }
+
+ return factory
+}
+
+// Start initializes all requested informers.
+func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ for informerType, informer := range f.informers {
+ if !f.startedInformers[informerType] {
+ go informer.Run(stopCh)
+ f.startedInformers[informerType] = true
+ }
+ }
+}
+
+// WaitForCacheSync waits for all started informers' cache were synced.
+func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {
+ informers := func() map[reflect.Type]cache.SharedIndexInformer {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ informers := map[reflect.Type]cache.SharedIndexInformer{}
+ for informerType, informer := range f.informers {
+ if f.startedInformers[informerType] {
+ informers[informerType] = informer
+ }
+ }
+ return informers
+ }()
+
+ res := map[reflect.Type]bool{}
+ for informType, informer := range informers {
+ res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)
+ }
+ return res
+}
+
+// InternalInformerFor returns the SharedIndexInformer for obj using an internal
+// client.
+func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ informerType := reflect.TypeOf(obj)
+ informer, exists := f.informers[informerType]
+ if exists {
+ return informer
+ }
+
+ resyncPeriod, exists := f.customResync[informerType]
+ if !exists {
+ resyncPeriod = f.defaultResync
+ }
+
+ informer = newFunc(f.client, resyncPeriod)
+ f.informers[informerType] = informer
+
+ return informer
+}
+
+// SharedInformerFactory provides shared informers for resources in all known
+// API group versions.
+type SharedInformerFactory interface {
+ internalinterfaces.SharedInformerFactory
+ ForResource(resource schema.GroupVersionResource) (GenericInformer, error)
+ WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
+
+ Federation() pingcap.Interface
+}
+
+func (f *sharedInformerFactory) Federation() pingcap.Interface {
+ return pingcap.New(f, f.namespace, f.tweakListOptions)
+}
diff --git a/pkg/client/federation/informers/externalversions/generic.go b/pkg/client/federation/informers/externalversions/generic.go
new file mode 100644
index 0000000000..1c5ac0d290
--- /dev/null
+++ b/pkg/client/federation/informers/externalversions/generic.go
@@ -0,0 +1,63 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package externalversions
+
+import (
+ "fmt"
+
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ cache "k8s.io/client-go/tools/cache"
+)
+
+// GenericInformer is type of SharedIndexInformer which will locate and delegate to other
+// sharedInformers based on type
+type GenericInformer interface {
+ Informer() cache.SharedIndexInformer
+ Lister() cache.GenericLister
+}
+
+type genericInformer struct {
+ informer cache.SharedIndexInformer
+ resource schema.GroupResource
+}
+
+// Informer returns the SharedIndexInformer.
+func (f *genericInformer) Informer() cache.SharedIndexInformer {
+ return f.informer
+}
+
+// Lister returns the GenericLister.
+func (f *genericInformer) Lister() cache.GenericLister {
+ return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource)
+}
+
+// ForResource gives generic access to a shared informer of the matching type
+// TODO extend this to unknown resources with a client pool
+func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
+ switch resource {
+ // Group=federation.pingcap.com, Version=v1alpha1
+ case v1alpha1.SchemeGroupVersion.WithResource("volumebackups"):
+ return &genericInformer{resource: resource.GroupResource(), informer: f.Federation().V1alpha1().VolumeBackups().Informer()}, nil
+ case v1alpha1.SchemeGroupVersion.WithResource("volumebackupschedules"):
+ return &genericInformer{resource: resource.GroupResource(), informer: f.Federation().V1alpha1().VolumeBackupSchedules().Informer()}, nil
+ case v1alpha1.SchemeGroupVersion.WithResource("volumerestores"):
+ return &genericInformer{resource: resource.GroupResource(), informer: f.Federation().V1alpha1().VolumeRestores().Informer()}, nil
+
+ }
+
+ return nil, fmt.Errorf("no informer found for %v", resource)
+}
diff --git a/pkg/client/federation/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/client/federation/informers/externalversions/internalinterfaces/factory_interfaces.go
new file mode 100644
index 0000000000..a0c811d099
--- /dev/null
+++ b/pkg/client/federation/informers/externalversions/internalinterfaces/factory_interfaces.go
@@ -0,0 +1,37 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package internalinterfaces
+
+import (
+ time "time"
+
+ versioned "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ cache "k8s.io/client-go/tools/cache"
+)
+
+// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer.
+type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer
+
+// SharedInformerFactory a small interface to allow for adding an informer without an import cycle
+type SharedInformerFactory interface {
+ Start(stopCh <-chan struct{})
+ InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
+}
+
+// TweakListOptionsFunc is a function that transforms a v1.ListOptions.
+type TweakListOptionsFunc func(*v1.ListOptions)
diff --git a/pkg/client/federation/informers/externalversions/pingcap/interface.go b/pkg/client/federation/informers/externalversions/pingcap/interface.go
new file mode 100644
index 0000000000..1e7f073e21
--- /dev/null
+++ b/pkg/client/federation/informers/externalversions/pingcap/interface.go
@@ -0,0 +1,43 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package pingcap
+
+import (
+ internalinterfaces "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions/internalinterfaces"
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions/pingcap/v1alpha1"
+)
+
+// Interface provides access to each of this group's versions.
+type Interface interface {
+ // V1alpha1 provides access to shared informers for resources in V1alpha1.
+ V1alpha1() v1alpha1.Interface
+}
+
+type group struct {
+ factory internalinterfaces.SharedInformerFactory
+ namespace string
+ tweakListOptions internalinterfaces.TweakListOptionsFunc
+}
+
+// New returns a new Interface.
+func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
+ return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
+}
+
+// V1alpha1 returns a new v1alpha1.Interface.
+func (g *group) V1alpha1() v1alpha1.Interface {
+ return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions)
+}
diff --git a/pkg/client/federation/informers/externalversions/pingcap/v1alpha1/interface.go b/pkg/client/federation/informers/externalversions/pingcap/v1alpha1/interface.go
new file mode 100644
index 0000000000..78cfa28b9a
--- /dev/null
+++ b/pkg/client/federation/informers/externalversions/pingcap/v1alpha1/interface.go
@@ -0,0 +1,56 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ internalinterfaces "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions/internalinterfaces"
+)
+
+// Interface provides access to all the informers in this group version.
+type Interface interface {
+ // VolumeBackups returns a VolumeBackupInformer.
+ VolumeBackups() VolumeBackupInformer
+ // VolumeBackupSchedules returns a VolumeBackupScheduleInformer.
+ VolumeBackupSchedules() VolumeBackupScheduleInformer
+ // VolumeRestores returns a VolumeRestoreInformer.
+ VolumeRestores() VolumeRestoreInformer
+}
+
+type version struct {
+ factory internalinterfaces.SharedInformerFactory
+ namespace string
+ tweakListOptions internalinterfaces.TweakListOptionsFunc
+}
+
+// New returns a new Interface.
+func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
+ return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
+}
+
+// VolumeBackups returns a VolumeBackupInformer.
+func (v *version) VolumeBackups() VolumeBackupInformer {
+ return &volumeBackupInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
+}
+
+// VolumeBackupSchedules returns a VolumeBackupScheduleInformer.
+func (v *version) VolumeBackupSchedules() VolumeBackupScheduleInformer {
+ return &volumeBackupScheduleInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
+}
+
+// VolumeRestores returns a VolumeRestoreInformer.
+func (v *version) VolumeRestores() VolumeRestoreInformer {
+ return &volumeRestoreInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
+}
diff --git a/pkg/client/federation/informers/externalversions/pingcap/v1alpha1/volumebackup.go b/pkg/client/federation/informers/externalversions/pingcap/v1alpha1/volumebackup.go
new file mode 100644
index 0000000000..963a74f282
--- /dev/null
+++ b/pkg/client/federation/informers/externalversions/pingcap/v1alpha1/volumebackup.go
@@ -0,0 +1,87 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ "context"
+ time "time"
+
+ pingcapv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ versioned "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned"
+ internalinterfaces "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions/internalinterfaces"
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/client/federation/listers/pingcap/v1alpha1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ watch "k8s.io/apimachinery/pkg/watch"
+ cache "k8s.io/client-go/tools/cache"
+)
+
+// VolumeBackupInformer provides access to a shared informer and lister for
+// VolumeBackups.
+type VolumeBackupInformer interface {
+ Informer() cache.SharedIndexInformer
+ Lister() v1alpha1.VolumeBackupLister
+}
+
+type volumeBackupInformer struct {
+ factory internalinterfaces.SharedInformerFactory
+ tweakListOptions internalinterfaces.TweakListOptionsFunc
+ namespace string
+}
+
+// NewVolumeBackupInformer constructs a new informer for VolumeBackup type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewVolumeBackupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
+ return NewFilteredVolumeBackupInformer(client, namespace, resyncPeriod, indexers, nil)
+}
+
+// NewFilteredVolumeBackupInformer constructs a new informer for VolumeBackup type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewFilteredVolumeBackupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
+ return cache.NewSharedIndexInformer(
+ &cache.ListWatch{
+ ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
+ if tweakListOptions != nil {
+ tweakListOptions(&options)
+ }
+ return client.FederationV1alpha1().VolumeBackups(namespace).List(context.TODO(), options)
+ },
+ WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
+ if tweakListOptions != nil {
+ tweakListOptions(&options)
+ }
+ return client.FederationV1alpha1().VolumeBackups(namespace).Watch(context.TODO(), options)
+ },
+ },
+ &pingcapv1alpha1.VolumeBackup{},
+ resyncPeriod,
+ indexers,
+ )
+}
+
+func (f *volumeBackupInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
+ return NewFilteredVolumeBackupInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
+}
+
+func (f *volumeBackupInformer) Informer() cache.SharedIndexInformer {
+ return f.factory.InformerFor(&pingcapv1alpha1.VolumeBackup{}, f.defaultInformer)
+}
+
+func (f *volumeBackupInformer) Lister() v1alpha1.VolumeBackupLister {
+ return v1alpha1.NewVolumeBackupLister(f.Informer().GetIndexer())
+}
diff --git a/pkg/client/federation/informers/externalversions/pingcap/v1alpha1/volumebackupschedule.go b/pkg/client/federation/informers/externalversions/pingcap/v1alpha1/volumebackupschedule.go
new file mode 100644
index 0000000000..aea6bfbd89
--- /dev/null
+++ b/pkg/client/federation/informers/externalversions/pingcap/v1alpha1/volumebackupschedule.go
@@ -0,0 +1,87 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ "context"
+ time "time"
+
+ pingcapv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ versioned "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned"
+ internalinterfaces "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions/internalinterfaces"
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/client/federation/listers/pingcap/v1alpha1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ watch "k8s.io/apimachinery/pkg/watch"
+ cache "k8s.io/client-go/tools/cache"
+)
+
+// VolumeBackupScheduleInformer provides access to a shared informer and lister for
+// VolumeBackupSchedules.
+type VolumeBackupScheduleInformer interface {
+ Informer() cache.SharedIndexInformer
+ Lister() v1alpha1.VolumeBackupScheduleLister
+}
+
+type volumeBackupScheduleInformer struct {
+ factory internalinterfaces.SharedInformerFactory
+ tweakListOptions internalinterfaces.TweakListOptionsFunc
+ namespace string
+}
+
+// NewVolumeBackupScheduleInformer constructs a new informer for VolumeBackupSchedule type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewVolumeBackupScheduleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
+ return NewFilteredVolumeBackupScheduleInformer(client, namespace, resyncPeriod, indexers, nil)
+}
+
+// NewFilteredVolumeBackupScheduleInformer constructs a new informer for VolumeBackupSchedule type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewFilteredVolumeBackupScheduleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
+ return cache.NewSharedIndexInformer(
+ &cache.ListWatch{
+ ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
+ if tweakListOptions != nil {
+ tweakListOptions(&options)
+ }
+ return client.FederationV1alpha1().VolumeBackupSchedules(namespace).List(context.TODO(), options)
+ },
+ WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
+ if tweakListOptions != nil {
+ tweakListOptions(&options)
+ }
+ return client.FederationV1alpha1().VolumeBackupSchedules(namespace).Watch(context.TODO(), options)
+ },
+ },
+ &pingcapv1alpha1.VolumeBackupSchedule{},
+ resyncPeriod,
+ indexers,
+ )
+}
+
+func (f *volumeBackupScheduleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
+ return NewFilteredVolumeBackupScheduleInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
+}
+
+func (f *volumeBackupScheduleInformer) Informer() cache.SharedIndexInformer {
+ return f.factory.InformerFor(&pingcapv1alpha1.VolumeBackupSchedule{}, f.defaultInformer)
+}
+
+func (f *volumeBackupScheduleInformer) Lister() v1alpha1.VolumeBackupScheduleLister {
+ return v1alpha1.NewVolumeBackupScheduleLister(f.Informer().GetIndexer())
+}
diff --git a/pkg/client/federation/informers/externalversions/pingcap/v1alpha1/volumerestore.go b/pkg/client/federation/informers/externalversions/pingcap/v1alpha1/volumerestore.go
new file mode 100644
index 0000000000..4ec15da08b
--- /dev/null
+++ b/pkg/client/federation/informers/externalversions/pingcap/v1alpha1/volumerestore.go
@@ -0,0 +1,87 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ "context"
+ time "time"
+
+ pingcapv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ versioned "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned"
+ internalinterfaces "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions/internalinterfaces"
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/client/federation/listers/pingcap/v1alpha1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ watch "k8s.io/apimachinery/pkg/watch"
+ cache "k8s.io/client-go/tools/cache"
+)
+
+// VolumeRestoreInformer provides access to a shared informer and lister for
+// VolumeRestores.
+type VolumeRestoreInformer interface {
+ Informer() cache.SharedIndexInformer
+ Lister() v1alpha1.VolumeRestoreLister
+}
+
+type volumeRestoreInformer struct {
+ factory internalinterfaces.SharedInformerFactory
+ tweakListOptions internalinterfaces.TweakListOptionsFunc
+ namespace string
+}
+
+// NewVolumeRestoreInformer constructs a new informer for VolumeRestore type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewVolumeRestoreInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
+ return NewFilteredVolumeRestoreInformer(client, namespace, resyncPeriod, indexers, nil)
+}
+
+// NewFilteredVolumeRestoreInformer constructs a new informer for VolumeRestore type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewFilteredVolumeRestoreInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
+ return cache.NewSharedIndexInformer(
+ &cache.ListWatch{
+ ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
+ if tweakListOptions != nil {
+ tweakListOptions(&options)
+ }
+ return client.FederationV1alpha1().VolumeRestores(namespace).List(context.TODO(), options)
+ },
+ WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
+ if tweakListOptions != nil {
+ tweakListOptions(&options)
+ }
+ return client.FederationV1alpha1().VolumeRestores(namespace).Watch(context.TODO(), options)
+ },
+ },
+ &pingcapv1alpha1.VolumeRestore{},
+ resyncPeriod,
+ indexers,
+ )
+}
+
+func (f *volumeRestoreInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
+ return NewFilteredVolumeRestoreInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
+}
+
+func (f *volumeRestoreInformer) Informer() cache.SharedIndexInformer {
+ return f.factory.InformerFor(&pingcapv1alpha1.VolumeRestore{}, f.defaultInformer)
+}
+
+func (f *volumeRestoreInformer) Lister() v1alpha1.VolumeRestoreLister {
+ return v1alpha1.NewVolumeRestoreLister(f.Informer().GetIndexer())
+}
diff --git a/pkg/client/federation/listers/pingcap/v1alpha1/expansion_generated.go b/pkg/client/federation/listers/pingcap/v1alpha1/expansion_generated.go
new file mode 100644
index 0000000000..0775b85e0d
--- /dev/null
+++ b/pkg/client/federation/listers/pingcap/v1alpha1/expansion_generated.go
@@ -0,0 +1,40 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1alpha1
+
+// VolumeBackupListerExpansion allows custom methods to be added to
+// VolumeBackupLister.
+type VolumeBackupListerExpansion interface{}
+
+// VolumeBackupNamespaceListerExpansion allows custom methods to be added to
+// VolumeBackupNamespaceLister.
+type VolumeBackupNamespaceListerExpansion interface{}
+
+// VolumeBackupScheduleListerExpansion allows custom methods to be added to
+// VolumeBackupScheduleLister.
+type VolumeBackupScheduleListerExpansion interface{}
+
+// VolumeBackupScheduleNamespaceListerExpansion allows custom methods to be added to
+// VolumeBackupScheduleNamespaceLister.
+type VolumeBackupScheduleNamespaceListerExpansion interface{}
+
+// VolumeRestoreListerExpansion allows custom methods to be added to
+// VolumeRestoreLister.
+type VolumeRestoreListerExpansion interface{}
+
+// VolumeRestoreNamespaceListerExpansion allows custom methods to be added to
+// VolumeRestoreNamespaceLister.
+type VolumeRestoreNamespaceListerExpansion interface{}
diff --git a/pkg/client/federation/listers/pingcap/v1alpha1/volumebackup.go b/pkg/client/federation/listers/pingcap/v1alpha1/volumebackup.go
new file mode 100644
index 0000000000..5b131b1588
--- /dev/null
+++ b/pkg/client/federation/listers/pingcap/v1alpha1/volumebackup.go
@@ -0,0 +1,96 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/client-go/tools/cache"
+)
+
+// VolumeBackupLister helps list VolumeBackups.
+// All objects returned here must be treated as read-only.
+type VolumeBackupLister interface {
+ // List lists all VolumeBackups in the indexer.
+ // Objects returned here must be treated as read-only.
+ List(selector labels.Selector) (ret []*v1alpha1.VolumeBackup, err error)
+ // VolumeBackups returns an object that can list and get VolumeBackups.
+ VolumeBackups(namespace string) VolumeBackupNamespaceLister
+ VolumeBackupListerExpansion
+}
+
+// volumeBackupLister implements the VolumeBackupLister interface.
+type volumeBackupLister struct {
+ indexer cache.Indexer
+}
+
+// NewVolumeBackupLister returns a new VolumeBackupLister.
+func NewVolumeBackupLister(indexer cache.Indexer) VolumeBackupLister {
+ return &volumeBackupLister{indexer: indexer}
+}
+
+// List lists all VolumeBackups in the indexer.
+func (s *volumeBackupLister) List(selector labels.Selector) (ret []*v1alpha1.VolumeBackup, err error) {
+ err = cache.ListAll(s.indexer, selector, func(m interface{}) {
+ ret = append(ret, m.(*v1alpha1.VolumeBackup))
+ })
+ return ret, err
+}
+
+// VolumeBackups returns an object that can list and get VolumeBackups.
+func (s *volumeBackupLister) VolumeBackups(namespace string) VolumeBackupNamespaceLister {
+ return volumeBackupNamespaceLister{indexer: s.indexer, namespace: namespace}
+}
+
+// VolumeBackupNamespaceLister helps list and get VolumeBackups.
+// All objects returned here must be treated as read-only.
+type VolumeBackupNamespaceLister interface {
+ // List lists all VolumeBackups in the indexer for a given namespace.
+ // Objects returned here must be treated as read-only.
+ List(selector labels.Selector) (ret []*v1alpha1.VolumeBackup, err error)
+ // Get retrieves the VolumeBackup from the indexer for a given namespace and name.
+ // Objects returned here must be treated as read-only.
+ Get(name string) (*v1alpha1.VolumeBackup, error)
+ VolumeBackupNamespaceListerExpansion
+}
+
+// volumeBackupNamespaceLister implements the VolumeBackupNamespaceLister
+// interface.
+type volumeBackupNamespaceLister struct {
+ indexer cache.Indexer
+ namespace string
+}
+
+// List lists all VolumeBackups in the indexer for a given namespace.
+func (s volumeBackupNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.VolumeBackup, err error) {
+ err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
+ ret = append(ret, m.(*v1alpha1.VolumeBackup))
+ })
+ return ret, err
+}
+
+// Get retrieves the VolumeBackup from the indexer for a given namespace and name.
+func (s volumeBackupNamespaceLister) Get(name string) (*v1alpha1.VolumeBackup, error) {
+ obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
+ if err != nil {
+ return nil, err
+ }
+ if !exists {
+ return nil, errors.NewNotFound(v1alpha1.Resource("volumebackup"), name)
+ }
+ return obj.(*v1alpha1.VolumeBackup), nil
+}
diff --git a/pkg/client/federation/listers/pingcap/v1alpha1/volumebackupschedule.go b/pkg/client/federation/listers/pingcap/v1alpha1/volumebackupschedule.go
new file mode 100644
index 0000000000..2efc1720c1
--- /dev/null
+++ b/pkg/client/federation/listers/pingcap/v1alpha1/volumebackupschedule.go
@@ -0,0 +1,96 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/client-go/tools/cache"
+)
+
+// VolumeBackupScheduleLister helps list VolumeBackupSchedules.
+// All objects returned here must be treated as read-only.
+type VolumeBackupScheduleLister interface {
+ // List lists all VolumeBackupSchedules in the indexer.
+ // Objects returned here must be treated as read-only.
+ List(selector labels.Selector) (ret []*v1alpha1.VolumeBackupSchedule, err error)
+ // VolumeBackupSchedules returns an object that can list and get VolumeBackupSchedules.
+ VolumeBackupSchedules(namespace string) VolumeBackupScheduleNamespaceLister
+ VolumeBackupScheduleListerExpansion
+}
+
+// volumeBackupScheduleLister implements the VolumeBackupScheduleLister interface.
+type volumeBackupScheduleLister struct {
+ indexer cache.Indexer
+}
+
+// NewVolumeBackupScheduleLister returns a new VolumeBackupScheduleLister.
+func NewVolumeBackupScheduleLister(indexer cache.Indexer) VolumeBackupScheduleLister {
+ return &volumeBackupScheduleLister{indexer: indexer}
+}
+
+// List lists all VolumeBackupSchedules in the indexer.
+func (s *volumeBackupScheduleLister) List(selector labels.Selector) (ret []*v1alpha1.VolumeBackupSchedule, err error) {
+ err = cache.ListAll(s.indexer, selector, func(m interface{}) {
+ ret = append(ret, m.(*v1alpha1.VolumeBackupSchedule))
+ })
+ return ret, err
+}
+
+// VolumeBackupSchedules returns an object that can list and get VolumeBackupSchedules.
+func (s *volumeBackupScheduleLister) VolumeBackupSchedules(namespace string) VolumeBackupScheduleNamespaceLister {
+ return volumeBackupScheduleNamespaceLister{indexer: s.indexer, namespace: namespace}
+}
+
+// VolumeBackupScheduleNamespaceLister helps list and get VolumeBackupSchedules.
+// All objects returned here must be treated as read-only.
+type VolumeBackupScheduleNamespaceLister interface {
+ // List lists all VolumeBackupSchedules in the indexer for a given namespace.
+ // Objects returned here must be treated as read-only.
+ List(selector labels.Selector) (ret []*v1alpha1.VolumeBackupSchedule, err error)
+ // Get retrieves the VolumeBackupSchedule from the indexer for a given namespace and name.
+ // Objects returned here must be treated as read-only.
+ Get(name string) (*v1alpha1.VolumeBackupSchedule, error)
+ VolumeBackupScheduleNamespaceListerExpansion
+}
+
+// volumeBackupScheduleNamespaceLister implements the VolumeBackupScheduleNamespaceLister
+// interface.
+type volumeBackupScheduleNamespaceLister struct {
+ indexer cache.Indexer
+ namespace string
+}
+
+// List lists all VolumeBackupSchedules in the indexer for a given namespace.
+func (s volumeBackupScheduleNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.VolumeBackupSchedule, err error) {
+ err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
+ ret = append(ret, m.(*v1alpha1.VolumeBackupSchedule))
+ })
+ return ret, err
+}
+
+// Get retrieves the VolumeBackupSchedule from the indexer for a given namespace and name.
+func (s volumeBackupScheduleNamespaceLister) Get(name string) (*v1alpha1.VolumeBackupSchedule, error) {
+ obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
+ if err != nil {
+ return nil, err
+ }
+ if !exists {
+ return nil, errors.NewNotFound(v1alpha1.Resource("volumebackupschedule"), name)
+ }
+ return obj.(*v1alpha1.VolumeBackupSchedule), nil
+}
diff --git a/pkg/client/federation/listers/pingcap/v1alpha1/volumerestore.go b/pkg/client/federation/listers/pingcap/v1alpha1/volumerestore.go
new file mode 100644
index 0000000000..706f1057eb
--- /dev/null
+++ b/pkg/client/federation/listers/pingcap/v1alpha1/volumerestore.go
@@ -0,0 +1,96 @@
+// Copyright PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/client-go/tools/cache"
+)
+
+// VolumeRestoreLister helps list VolumeRestores.
+// All objects returned here must be treated as read-only.
+type VolumeRestoreLister interface {
+ // List lists all VolumeRestores in the indexer.
+ // Objects returned here must be treated as read-only.
+ List(selector labels.Selector) (ret []*v1alpha1.VolumeRestore, err error)
+ // VolumeRestores returns an object that can list and get VolumeRestores.
+ VolumeRestores(namespace string) VolumeRestoreNamespaceLister
+ VolumeRestoreListerExpansion
+}
+
+// volumeRestoreLister implements the VolumeRestoreLister interface.
+type volumeRestoreLister struct {
+ indexer cache.Indexer
+}
+
+// NewVolumeRestoreLister returns a new VolumeRestoreLister.
+func NewVolumeRestoreLister(indexer cache.Indexer) VolumeRestoreLister {
+ return &volumeRestoreLister{indexer: indexer}
+}
+
+// List lists all VolumeRestores in the indexer.
+func (s *volumeRestoreLister) List(selector labels.Selector) (ret []*v1alpha1.VolumeRestore, err error) {
+ err = cache.ListAll(s.indexer, selector, func(m interface{}) {
+ ret = append(ret, m.(*v1alpha1.VolumeRestore))
+ })
+ return ret, err
+}
+
+// VolumeRestores returns an object that can list and get VolumeRestores.
+func (s *volumeRestoreLister) VolumeRestores(namespace string) VolumeRestoreNamespaceLister {
+ return volumeRestoreNamespaceLister{indexer: s.indexer, namespace: namespace}
+}
+
+// VolumeRestoreNamespaceLister helps list and get VolumeRestores.
+// All objects returned here must be treated as read-only.
+type VolumeRestoreNamespaceLister interface {
+ // List lists all VolumeRestores in the indexer for a given namespace.
+ // Objects returned here must be treated as read-only.
+ List(selector labels.Selector) (ret []*v1alpha1.VolumeRestore, err error)
+ // Get retrieves the VolumeRestore from the indexer for a given namespace and name.
+ // Objects returned here must be treated as read-only.
+ Get(name string) (*v1alpha1.VolumeRestore, error)
+ VolumeRestoreNamespaceListerExpansion
+}
+
+// volumeRestoreNamespaceLister implements the VolumeRestoreNamespaceLister
+// interface.
+type volumeRestoreNamespaceLister struct {
+ indexer cache.Indexer
+ namespace string
+}
+
+// List lists all VolumeRestores in the indexer for a given namespace.
+func (s volumeRestoreNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.VolumeRestore, err error) {
+ err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
+ ret = append(ret, m.(*v1alpha1.VolumeRestore))
+ })
+ return ret, err
+}
+
+// Get retrieves the VolumeRestore from the indexer for a given namespace and name.
+func (s volumeRestoreNamespaceLister) Get(name string) (*v1alpha1.VolumeRestore, error) {
+ obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
+ if err != nil {
+ return nil, err
+ }
+ if !exists {
+ return nil, errors.NewNotFound(v1alpha1.Resource("volumerestore"), name)
+ }
+ return obj.(*v1alpha1.VolumeRestore), nil
+}
diff --git a/pkg/controller/br_fed_config.go b/pkg/controller/br_fed_config.go
new file mode 100644
index 0000000000..25e2f39361
--- /dev/null
+++ b/pkg/controller/br_fed_config.go
@@ -0,0 +1,77 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package controller
+
+import (
+ "flag"
+ "time"
+)
+
+const (
+ defaultFederationKubeConfigPath = "/etc/br-federation/federation-kubeconfig"
+)
+
+// BrFedCLIConfig is used to save the configuration of br-federation-manager read from command line.
+type BrFedCLIConfig struct {
+ PrintVersion bool
+ // The number of workers that are allowed to sync concurrently.
+ // Larger number = more responsive management, but more CPU
+ // (and network) load
+ Workers int
+
+ LeaseDuration time.Duration
+ RenewDeadline time.Duration
+ RetryPeriod time.Duration
+ WaitDuration time.Duration
+ // ResyncDuration is the resync time of informer
+ ResyncDuration time.Duration
+
+ // KubeClientQPS indicates the maximum QPS to the kubenetes API server from client.
+ KubeClientQPS float64
+ KubeClientBurst int
+
+ // FederationKubeConfigPath is the path to the directory containing the federation kubeconfig files
+ FederationKubeConfigPath string
+}
+
+// DefaultBrFedCLIConfig returns the default command line configuration
+func DefaultBrFedCLIConfig() *BrFedCLIConfig {
+ return &BrFedCLIConfig{
+ Workers: 5,
+ LeaseDuration: 15 * time.Second,
+ RenewDeadline: 10 * time.Second,
+ RetryPeriod: 2 * time.Second,
+ WaitDuration: 5 * time.Second,
+ ResyncDuration: 30 * time.Second,
+
+ FederationKubeConfigPath: defaultFederationKubeConfigPath,
+ }
+}
+
+// AddFlag adds a flag for setting global feature gates to the specified FlagSet.
+func (c *BrFedCLIConfig) AddFlag(_ *flag.FlagSet) {
+ flag.BoolVar(&c.PrintVersion, "V", false, "Show version and quit")
+ flag.BoolVar(&c.PrintVersion, "version", false, "Show version and quit")
+ flag.IntVar(&c.Workers, "workers", c.Workers, "The number of workers that are allowed to sync concurrently. Larger number = more responsive management, but more CPU (and network) load")
+ flag.DurationVar(&c.ResyncDuration, "resync-duration", c.ResyncDuration, "Resync time of informer")
+
+ // see https://pkg.go.dev/k8s.io/client-go/tools/leaderelection#LeaderElectionConfig for the config
+ flag.DurationVar(&c.LeaseDuration, "leader-lease-duration", c.LeaseDuration, "leader-lease-duration is the duration that non-leader candidates will wait to force acquire leadership")
+ flag.DurationVar(&c.RenewDeadline, "leader-renew-deadline", c.RenewDeadline, "leader-renew-deadline is the duration that the acting master will retry refreshing leadership before giving up")
+ flag.DurationVar(&c.RetryPeriod, "leader-retry-period", c.RetryPeriod, "leader-retry-period is the duration the LeaderElector clients should wait between tries of actions")
+ flag.Float64Var(&c.KubeClientQPS, "kube-client-qps", c.KubeClientQPS, "The maximum QPS to the kubenetes API server from client")
+ flag.IntVar(&c.KubeClientBurst, "kube-client-burst", c.KubeClientBurst, "The maximum burst for throttle to the kubenetes API server from client")
+
+ flag.StringVar(&c.FederationKubeConfigPath, "federation-kubeconfig-path", c.FederationKubeConfigPath, "The path to the directory containing the federation kubeconfig files")
+}
diff --git a/pkg/controller/br_fed_dependences.go b/pkg/controller/br_fed_dependences.go
new file mode 100644
index 0000000000..faf69b9e91
--- /dev/null
+++ b/pkg/controller/br_fed_dependences.go
@@ -0,0 +1,133 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package controller
+
+import (
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ kubeinformers "k8s.io/client-go/informers"
+ "k8s.io/client-go/kubernetes"
+ eventv1 "k8s.io/client-go/kubernetes/typed/core/v1"
+ "k8s.io/client-go/tools/record"
+ "k8s.io/klog/v2"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ fedversioned "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned"
+ "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned"
+ informers "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions"
+ listers "github.com/pingcap/tidb-operator/pkg/client/federation/listers/pingcap/v1alpha1"
+)
+
+type BrFedControls struct {
+ FedVolumeBackupControl FedVolumeBackupControlInterface
+}
+
+// BrFedDependencies is used to store all shared dependent resources to avoid
+// pass parameters everywhere.
+type BrFedDependencies struct {
+ // CLIConfig represents all parameters read from command line
+ CLIConfig *BrFedCLIConfig
+ // Operator client interface
+ Clientset versioned.Interface
+ // Kubernetes client interface
+ KubeClientset kubernetes.Interface
+ GenericClient client.Client
+ InformerFactory informers.SharedInformerFactory
+ KubeInformerFactory kubeinformers.SharedInformerFactory
+ LabelFilterKubeInformerFactory kubeinformers.SharedInformerFactory
+ Recorder record.EventRecorder
+
+ // Listers
+ VolumeBackupLister listers.VolumeBackupLister
+ VolumeRestoreLister listers.VolumeRestoreLister
+ VolumeBackupScheduleLister listers.VolumeBackupScheduleLister
+
+ // Controls
+ BrFedControls
+
+ // FedClientset is the clientset for the federation clusters
+ FedClientset map[string]*fedversioned.Clientset
+}
+
+// NewBrFedDependencies is used to construct the dependencies
+func NewBrFedDependencies(cliCfg *BrFedCLIConfig, clientset versioned.Interface, kubeClientset kubernetes.Interface,
+ genericCli client.Client, fedClientset map[string]*fedversioned.Clientset) *BrFedDependencies {
+ tweakListOptionsFunc := func(options *metav1.ListOptions) {
+ if len(options.LabelSelector) > 0 {
+ options.LabelSelector += ",app.kubernetes.io/managed-by=tidb-operator"
+ } else {
+ options.LabelSelector = "app.kubernetes.io/managed-by=tidb-operator"
+ }
+ }
+ tweakListOptions := kubeinformers.WithTweakListOptions(tweakListOptionsFunc)
+
+ // Initialize the informer factories
+ informerFactory := informers.NewSharedInformerFactoryWithOptions(clientset, cliCfg.ResyncDuration)
+ kubeInformerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClientset, cliCfg.ResyncDuration)
+ labelFilterKubeInformerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClientset, cliCfg.ResyncDuration, tweakListOptions)
+
+ // Initialize the event recorder
+ eventBroadcaster := record.NewBroadcasterWithCorrelatorOptions(record.CorrelatorOptions{QPS: 1})
+ eventBroadcaster.StartLogging(klog.V(2).Infof)
+ eventBroadcaster.StartRecordingToSink(&eventv1.EventSinkImpl{
+ Interface: eventv1.New(kubeClientset.CoreV1().RESTClient()).Events("")})
+ recorder := eventBroadcaster.NewRecorder(v1alpha1.Scheme, corev1.EventSource{Component: "br-federation-manager"})
+
+ deps := newBrFedDependencies(cliCfg, clientset, kubeClientset, genericCli, informerFactory, kubeInformerFactory, labelFilterKubeInformerFactory, recorder, fedClientset)
+ deps.BrFedControls = newRealBrFedControls(cliCfg, clientset, kubeClientset, genericCli, informerFactory, kubeInformerFactory, recorder)
+ return deps
+}
+
+func newBrFedDependencies(
+ cliCfg *BrFedCLIConfig,
+ clientset versioned.Interface,
+ kubeClientset kubernetes.Interface,
+ genericCli client.Client,
+ informerFactory informers.SharedInformerFactory,
+ kubeInformerFactory kubeinformers.SharedInformerFactory,
+ labelFilterKubeInformerFactory kubeinformers.SharedInformerFactory,
+ recorder record.EventRecorder,
+ fedClientset map[string]*fedversioned.Clientset) *BrFedDependencies {
+ return &BrFedDependencies{
+ CLIConfig: cliCfg,
+ Clientset: clientset,
+ KubeClientset: kubeClientset,
+ GenericClient: genericCli,
+ InformerFactory: informerFactory,
+ KubeInformerFactory: kubeInformerFactory,
+ LabelFilterKubeInformerFactory: labelFilterKubeInformerFactory,
+ Recorder: recorder,
+
+ // Listers
+ VolumeBackupLister: informerFactory.Federation().V1alpha1().VolumeBackups().Lister(),
+ VolumeRestoreLister: informerFactory.Federation().V1alpha1().VolumeRestores().Lister(),
+ VolumeBackupScheduleLister: informerFactory.Federation().V1alpha1().VolumeBackupSchedules().Lister(),
+
+ FedClientset: fedClientset,
+ }
+}
+
+func newRealBrFedControls(
+ cliCfg *BrFedCLIConfig,
+ clientset versioned.Interface,
+ kubeClientset kubernetes.Interface,
+ genericCli client.Client,
+ informerFactory informers.SharedInformerFactory,
+ kubeInformerFactory kubeinformers.SharedInformerFactory,
+ recorder record.EventRecorder) BrFedControls {
+ return BrFedControls{
+ FedVolumeBackupControl: NewRealFedVolumeBackupControl(clientset, recorder),
+ }
+}
diff --git a/pkg/controller/controller_utils.go b/pkg/controller/controller_utils.go
index cd081e77ac..b2c141dd35 100644
--- a/pkg/controller/controller_utils.go
+++ b/pkg/controller/controller_utils.go
@@ -20,6 +20,7 @@ import (
"regexp"
"github.com/dustin/go-humanize"
+ fedv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
"github.com/pingcap/tidb-operator/pkg/scheme"
"github.com/pingcap/tidb-operator/pkg/util"
@@ -64,6 +65,15 @@ var (
// tidbDashboardKind contains the schema.GroupVersionKind for TidbDashboard controller type.
tidbDashboardKind = v1alpha1.SchemeGroupVersion.WithKind("TidbDashboard")
+
+ // FedVolumeBackupControllerKind contains the schema.GroupVersionKind for federation VolumeBackup controller type.
+ FedVolumeBackupControllerKind = fedv1alpha1.SchemeGroupVersion.WithKind("VolumeBackup")
+
+ // FedVolumeRestoreControllerKind contains the schema.GroupVersionKind for federation VolumeRestore controller type.
+ FedVolumeRestoreControllerKind = fedv1alpha1.SchemeGroupVersion.WithKind("VolumeRestore")
+
+ // FedVolumeBackupScheduleControllerKind contains the schema.GroupVersionKind for federation VolumeBackupSchedule controller type.
+ FedVolumeBackupScheduleControllerKind = fedv1alpha1.SchemeGroupVersion.WithKind("VolumeBackupSchedule")
)
// RequeueError is used to requeue the item, this error type should't be considered as a real error
diff --git a/pkg/controller/fed_volume_backup_control.go b/pkg/controller/fed_volume_backup_control.go
new file mode 100644
index 0000000000..0de42e3bb4
--- /dev/null
+++ b/pkg/controller/fed_volume_backup_control.go
@@ -0,0 +1,156 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package controller
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/tools/cache"
+ "k8s.io/client-go/tools/record"
+ "k8s.io/klog/v2"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/apis/label"
+ "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned"
+ informers "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions/pingcap/v1alpha1"
+ listers "github.com/pingcap/tidb-operator/pkg/client/federation/listers/pingcap/v1alpha1"
+)
+
+// FedVolumeBackupControlInterface manages federaton VolumeBackups used in VolumeBackupSchedule
+type FedVolumeBackupControlInterface interface {
+ CreateVolumeBackup(backup *v1alpha1.VolumeBackup) (*v1alpha1.VolumeBackup, error)
+ DeleteVolumeBackup(backup *v1alpha1.VolumeBackup) error
+}
+
+type realFedVolumeBackupControl struct {
+ cli versioned.Interface
+ recorder record.EventRecorder
+}
+
+// NewRealFedVolumeBackupControl creates a new FedVolumeBackupControlInterface
+func NewRealFedVolumeBackupControl(
+ cli versioned.Interface,
+ recorder record.EventRecorder,
+) FedVolumeBackupControlInterface {
+ return &realFedVolumeBackupControl{
+ cli: cli,
+ recorder: recorder,
+ }
+}
+
+func (c *realFedVolumeBackupControl) CreateVolumeBackup(volumeBackup *v1alpha1.VolumeBackup) (*v1alpha1.VolumeBackup, error) {
+ ns := volumeBackup.GetNamespace()
+ backupName := volumeBackup.GetName()
+
+ bsName := volumeBackup.GetLabels()[label.BackupScheduleLabelKey]
+ volumeBackup, err := c.cli.FederationV1alpha1().VolumeBackups(ns).Create(context.TODO(), volumeBackup, metav1.CreateOptions{})
+ if err != nil {
+ klog.Errorf("failed to create VolumeBackup: [%s/%s] for volumeBackupSchedule/%s, err: %v", ns, backupName, bsName, err)
+ } else {
+ klog.V(4).Infof("create VolumeBackup: [%s/%s] for volumeBackupSchedule/%s successfully", ns, backupName, bsName)
+ }
+ c.recordVolumeBackupEvent("create", volumeBackup, err)
+ return volumeBackup, err
+}
+
+func (c *realFedVolumeBackupControl) DeleteVolumeBackup(volumeBackup *v1alpha1.VolumeBackup) error {
+ ns := volumeBackup.GetNamespace()
+ backupName := volumeBackup.GetName()
+
+ bsName := volumeBackup.GetLabels()[label.BackupScheduleLabelKey]
+ err := c.cli.FederationV1alpha1().VolumeBackups(ns).Delete(context.TODO(), backupName, metav1.DeleteOptions{})
+ if err != nil {
+ klog.Errorf("failed to delete VolumeBackup: [%s/%s] for volumeBackupSchedule/%s, err: %v", ns, backupName, bsName, err)
+ } else {
+ klog.V(4).Infof("delete Volumebackup: [%s/%s] successfully, volumeBackupSchedule/%s", ns, backupName, bsName)
+ }
+ c.recordVolumeBackupEvent("delete", volumeBackup, err)
+ return err
+}
+
+func (c *realFedVolumeBackupControl) recordVolumeBackupEvent(verb string, volumeBackup *v1alpha1.VolumeBackup, err error) {
+ backupName := volumeBackup.GetName()
+ ns := volumeBackup.GetNamespace()
+
+ bsName := volumeBackup.GetLabels()[label.BackupScheduleLabelKey]
+ if err == nil {
+ reason := fmt.Sprintf("Successful%s", strings.Title(verb))
+ msg := fmt.Sprintf("%s VolumeBackup %s/%s for volumeBackupSchedule/%s successful",
+ strings.ToLower(verb), ns, backupName, bsName)
+ c.recorder.Event(volumeBackup, corev1.EventTypeNormal, reason, msg)
+ } else {
+ reason := fmt.Sprintf("Failed%s", strings.Title(verb))
+ msg := fmt.Sprintf("%s VolumeBackup %s/%s for volumeBackupSchedule/%s failed error: %s",
+ strings.ToLower(verb), ns, backupName, bsName, err)
+ c.recorder.Event(volumeBackup, corev1.EventTypeWarning, reason, msg)
+ }
+}
+
+var _ FedVolumeBackupControlInterface = &realFedVolumeBackupControl{}
+
+// FakeFedVolumeBackupControl is a fake FedVolumeBackupControlInterface
+type FakeFedVolumeBackupControl struct {
+ volumeBackupLister listers.VolumeBackupLister
+ volumeBackupIndexer cache.Indexer
+ createVolumeBackupTracker RequestTracker
+ deleteVolumeBackupTracker RequestTracker
+}
+
+// NewFakeBackupControl returns a FakeBackupControl
+func NewFakeFedVolumeBackupControl(volumeBackupInformer informers.VolumeBackupInformer) *FakeFedVolumeBackupControl {
+ return &FakeFedVolumeBackupControl{
+ volumeBackupInformer.Lister(),
+ volumeBackupInformer.Informer().GetIndexer(),
+ RequestTracker{},
+ RequestTracker{},
+ }
+}
+
+// SetCreateVolumeBackupError sets the error attributes of createVolumeBackupTracker
+func (fbc *FakeFedVolumeBackupControl) SetCreateVolumeBackupError(err error, after int) {
+ fbc.createVolumeBackupTracker.SetError(err).SetAfter(after)
+}
+
+// SetDeleteVolumeBackupError sets the error attributes of deleteVolumeBackupTracker
+func (fbc *FakeFedVolumeBackupControl) SetDeleteVolumeBackupError(err error, after int) {
+ fbc.deleteVolumeBackupTracker.SetError(err).SetAfter(after)
+}
+
+// CreateVolumeBackup adds the volumeBackup to VolumeBackupIndexer
+func (fbc *FakeFedVolumeBackupControl) CreateVolumeBackup(volumeBackup *v1alpha1.VolumeBackup) (*v1alpha1.VolumeBackup, error) {
+ defer fbc.createVolumeBackupTracker.Inc()
+ if fbc.createVolumeBackupTracker.ErrorReady() {
+ defer fbc.createVolumeBackupTracker.Reset()
+ return volumeBackup, fbc.createVolumeBackupTracker.GetError()
+ }
+
+ return volumeBackup, fbc.volumeBackupIndexer.Add(volumeBackup)
+}
+
+// DeleteVolumeBackup deletes the volumeBackup from VolumeBackupIndexer
+func (fbc *FakeFedVolumeBackupControl) DeleteVolumeBackup(volumeBackup *v1alpha1.VolumeBackup) error {
+ defer fbc.createVolumeBackupTracker.Inc()
+ if fbc.createVolumeBackupTracker.ErrorReady() {
+ defer fbc.createVolumeBackupTracker.Reset()
+ return fbc.createVolumeBackupTracker.GetError()
+ }
+
+ return fbc.volumeBackupIndexer.Delete(volumeBackup)
+}
+
+var _ BackupControlInterface = &FakeBackupControl{}
diff --git a/pkg/controller/fedvolumebackup/fed_volume_backup_control.go b/pkg/controller/fedvolumebackup/fed_volume_backup_control.go
new file mode 100644
index 0000000000..9ad843b5ea
--- /dev/null
+++ b/pkg/controller/fedvolumebackup/fed_volume_backup_control.go
@@ -0,0 +1,171 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fedvolumebackup
+
+import (
+ "context"
+ "fmt"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/tools/cache"
+ "k8s.io/klog/v2"
+ "k8s.io/kubernetes/pkg/util/slice"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/apis/label"
+ "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned"
+ informers "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/fedvolumebackup"
+)
+
+// ControlInterface implements the control logic for updating VolumeBackup
+// It is implemented as an interface to allow for extensions that provide different semantics.
+// Currently, there is only one implementation.
+type ControlInterface interface {
+ // UpdateBackup implements the control logic for VolumeBackup's creation, deletion
+ UpdateBackup(volumeBackup *v1alpha1.VolumeBackup) error
+ // UpdateStatus updates the status for a VolumeBackup, include condition and status info
+ // NOTE(federation): add a separate struct for newStatus as non-federation backup control did if needed
+ UpdateStatus(volumeBackup *v1alpha1.VolumeBackup, newStatus *v1alpha1.VolumeBackupStatus) error
+}
+
+// NewDefaultVolumeBackupControl returns a new instance of the default VolumeBackup ControlInterface implementation.
+func NewDefaultVolumeBackupControl(
+ cli versioned.Interface,
+ backupManager fedvolumebackup.BackupManager) ControlInterface {
+ return &defaultBackupControl{
+ cli,
+ backupManager,
+ }
+}
+
+type defaultBackupControl struct {
+ cli versioned.Interface
+ backupManager fedvolumebackup.BackupManager
+}
+
+// UpdateBackup executes the core logic loop for a VolumeBackup.
+func (c *defaultBackupControl) UpdateBackup(volumeBackup *v1alpha1.VolumeBackup) error {
+ volumeBackup.SetGroupVersionKind(controller.FedVolumeBackupControllerKind)
+ if err := c.addProtectionFinalizer(volumeBackup); err != nil {
+ return err
+ }
+
+ if err := c.removeProtectionFinalizer(volumeBackup); err != nil {
+ return err
+ }
+
+ return c.updateBackup(volumeBackup)
+}
+
+// UpdateStatus updates the status for a VolumeBackup, include condition and status info
+func (c *defaultBackupControl) UpdateStatus(volumeBackup *v1alpha1.VolumeBackup, newStatus *v1alpha1.VolumeBackupStatus) error {
+ return c.backupManager.UpdateStatus(volumeBackup, newStatus)
+}
+
+func (c *defaultBackupControl) updateBackup(volumeBackup *v1alpha1.VolumeBackup) error {
+ return c.backupManager.Sync(volumeBackup)
+}
+
+// addProtectionFinalizer will be called when the VolumeBackup CR is created
+func (c *defaultBackupControl) addProtectionFinalizer(volumeBackup *v1alpha1.VolumeBackup) error {
+ ns := volumeBackup.GetNamespace()
+ name := volumeBackup.GetName()
+
+ if needToAddFinalizer(volumeBackup) {
+ volumeBackup.Finalizers = append(volumeBackup.Finalizers, label.BackupProtectionFinalizer)
+ _, err := c.cli.FederationV1alpha1().VolumeBackups(ns).Update(context.TODO(), volumeBackup, metav1.UpdateOptions{})
+ if err != nil {
+ return fmt.Errorf("add VolumeBackup %s/%s protection finalizers failed, err: %v", ns, name, err)
+ }
+ }
+ return nil
+}
+
+func (c *defaultBackupControl) removeProtectionFinalizer(volumeBackup *v1alpha1.VolumeBackup) error {
+ ns := volumeBackup.GetNamespace()
+ name := volumeBackup.GetName()
+
+ if needToRemoveFinalizer(volumeBackup) {
+ volumeBackup.Finalizers = slice.RemoveString(volumeBackup.Finalizers, label.BackupProtectionFinalizer, nil)
+ _, err := c.cli.FederationV1alpha1().VolumeBackups(ns).Update(context.TODO(), volumeBackup, metav1.UpdateOptions{})
+ if err != nil {
+ return fmt.Errorf("remove VolumeBackup %s/%s protection finalizers failed, err: %v", ns, name, err)
+ }
+ klog.Infof("remove VolumeBackup %s/%s protection finalizers success", ns, name)
+ return controller.RequeueErrorf(fmt.Sprintf("VolumeBackup %s/%s has been cleaned up", ns, name))
+ }
+ return nil
+}
+
+func needToAddFinalizer(volumeBackup *v1alpha1.VolumeBackup) bool {
+ // TODO(federation): add something like non-federation's `IsCleanCandidate` check if needed
+ return volumeBackup.DeletionTimestamp == nil &&
+ !slice.ContainsString(volumeBackup.Finalizers, label.BackupProtectionFinalizer, nil)
+}
+
+func needToRemoveFinalizer(volumeBackup *v1alpha1.VolumeBackup) bool {
+ // TODO(federation): add something like non-federation's
+ // `IsCleanCandidate`, `IsBackupClean`, `NeedNotClean` check if needed
+ return isDeletionCandidate(volumeBackup)
+}
+
+func isDeletionCandidate(volumeBackup *v1alpha1.VolumeBackup) bool {
+ return volumeBackup.DeletionTimestamp != nil && slice.ContainsString(volumeBackup.Finalizers, label.BackupProtectionFinalizer, nil)
+}
+
+var _ ControlInterface = &defaultBackupControl{}
+
+// FakeBackupControl is a fake BackupControlInterface
+type FakeBackupControl struct {
+ backupIndexer cache.Indexer
+ updateBackupTracker controller.RequestTracker
+ condition *v1alpha1.VolumeBackupCondition
+}
+
+// NewFakeBackupControl returns a FakeBackupControl
+func NewFakeBackupControl(backupInformer informers.VolumeBackupInformer) *FakeBackupControl {
+ return &FakeBackupControl{
+ backupInformer.Informer().GetIndexer(),
+ controller.RequestTracker{},
+ nil,
+ }
+}
+
+// SetUpdateBackupError sets the error attributes of updateBackupTracker
+func (c *FakeBackupControl) SetUpdateBackupError(err error, after int) {
+ c.updateBackupTracker.SetError(err).SetAfter(after)
+}
+
+// UpdateBackup adds the VolumeBackup to BackupIndexer
+func (c *FakeBackupControl) UpdateBackup(volumeBackup *v1alpha1.VolumeBackup) error {
+ defer c.updateBackupTracker.Inc()
+ if c.updateBackupTracker.ErrorReady() {
+ defer c.updateBackupTracker.Reset()
+ return c.updateBackupTracker.GetError()
+ }
+
+ return c.backupIndexer.Add(volumeBackup)
+}
+
+// UpdateStatus updates the status for a VolumeBackup, include condition and status info
+func (c *FakeBackupControl) UpdateStatus(_ *v1alpha1.VolumeBackup, newStatus *v1alpha1.VolumeBackupStatus) error {
+ if len(newStatus.Conditions) > 0 {
+ c.condition = &newStatus.Conditions[0]
+ }
+ return nil
+}
+
+var _ ControlInterface = &FakeBackupControl{}
diff --git a/pkg/controller/fedvolumebackup/fed_volume_backup_controller.go b/pkg/controller/fedvolumebackup/fed_volume_backup_controller.go
new file mode 100644
index 0000000000..e195c9592b
--- /dev/null
+++ b/pkg/controller/fedvolumebackup/fed_volume_backup_controller.go
@@ -0,0 +1,176 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fedvolumebackup
+
+import (
+ "fmt"
+ "time"
+
+ perrors "github.com/pingcap/errors"
+ "k8s.io/apimachinery/pkg/api/errors"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ "k8s.io/apimachinery/pkg/util/wait"
+ "k8s.io/client-go/tools/cache"
+ "k8s.io/client-go/util/workqueue"
+ "k8s.io/klog/v2"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/fedvolumebackup/backup"
+ "github.com/pingcap/tidb-operator/pkg/metrics"
+)
+
+// Controller controls VolumeBackup.
+type Controller struct {
+ deps *controller.BrFedDependencies
+ // control returns an interface capable of syncing a VolumeBackup.
+ // Abstracted out for testing.
+ control ControlInterface
+ // VolumeBackups that need to be synced.
+ queue workqueue.RateLimitingInterface
+}
+
+// NewController creates a VolumeBackup controller.
+func NewController(deps *controller.BrFedDependencies) *Controller {
+ c := &Controller{
+ deps: deps,
+ control: NewDefaultVolumeBackupControl(deps.Clientset, backup.NewBackupManager(deps)),
+ queue: workqueue.NewNamedRateLimitingQueue(
+ controller.NewControllerRateLimiter(1*time.Second, 100*time.Second),
+ "volumeBackup",
+ ),
+ }
+
+ volumeBackupInformer := deps.InformerFactory.Federation().V1alpha1().VolumeBackups()
+ volumeBackupInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
+ AddFunc: c.updateBackup,
+ UpdateFunc: func(old, cur interface{}) {
+ c.updateBackup(cur)
+ },
+ DeleteFunc: c.updateBackup,
+ })
+
+ return c
+}
+
+// Name returns VolumeBackup controller name.
+func (c *Controller) Name() string {
+ return "volumeBackup"
+}
+
+// Run runs the VolumeBackup controller.
+func (c *Controller) Run(workers int, stopCh <-chan struct{}) {
+ defer utilruntime.HandleCrash()
+ defer c.queue.ShutDown()
+
+ klog.Info("Starting volumeBackup controller")
+ defer klog.Info("Shutting down volumeBackup controller")
+
+ for i := 0; i < workers; i++ {
+ go wait.Until(c.worker, time.Second, stopCh)
+ }
+
+ <-stopCh
+}
+
+// worker runs a worker goroutine that invokes processNextWorkItem until the the controller's queue is closed
+func (c *Controller) worker() {
+ for c.processNextWorkItem() {
+ }
+}
+
+// processNextWorkItem dequeues items, processes them, and marks them done. It enforces that the syncHandler is never
+// invoked concurrently with the same key.
+func (c *Controller) processNextWorkItem() bool {
+ metrics.ActiveWorkers.WithLabelValues(c.Name()).Add(1)
+ defer metrics.ActiveWorkers.WithLabelValues(c.Name()).Add(-1)
+
+ key, quit := c.queue.Get()
+ if quit {
+ return false
+ }
+ defer c.queue.Done(key)
+ if err := c.sync(key.(string)); err != nil {
+ if perrors.Find(err, controller.IsRequeueError) != nil {
+ klog.Infof("VolumeBackup: %v, still need sync: %v, requeuing", key.(string), err)
+ c.queue.AddRateLimited(key)
+ } else if perrors.Find(err, controller.IsIgnoreError) != nil {
+ klog.V(4).Infof("VolumeBackup: %v, ignore err: %v", key.(string), err)
+ } else {
+ utilruntime.HandleError(fmt.Errorf("VolumeBackup: %v, sync failed, err: %v, requeuing", key.(string), err))
+ c.queue.AddRateLimited(key)
+ }
+ } else {
+ c.queue.Forget(key)
+ }
+ return true
+}
+
+// sync syncs the given VolumeBackup.
+func (c *Controller) sync(key string) error {
+ startTime := time.Now()
+ defer func() {
+ duration := time.Since(startTime)
+ metrics.ReconcileTime.WithLabelValues(c.Name()).Observe(duration.Seconds())
+ klog.V(4).Infof("Finished syncing VolumeBackup %q (%v)", key, duration)
+ }()
+
+ ns, name, err := cache.SplitMetaNamespaceKey(key)
+ if err != nil {
+ return err
+ }
+ volumeBackup, err := c.deps.VolumeBackupLister.VolumeBackups(ns).Get(name)
+ if errors.IsNotFound(err) {
+ klog.Infof("VolumeBackup has been deleted %v", key)
+ return nil
+ }
+ if err != nil {
+ return err
+ }
+
+ return c.syncBackup(volumeBackup.DeepCopy())
+}
+
+func (c *Controller) syncBackup(volumeBackup *v1alpha1.VolumeBackup) error {
+ return c.control.UpdateBackup(volumeBackup)
+}
+
+func (c *Controller) updateBackup(cur interface{}) {
+ newVolumeBackup := cur.(*v1alpha1.VolumeBackup)
+ ns := newVolumeBackup.GetNamespace()
+ name := newVolumeBackup.GetName()
+
+ if newVolumeBackup.DeletionTimestamp != nil {
+ // the backup is being deleted, we need to do some cleanup work, enqueue backup.
+ klog.Infof("VolumeBackup %s/%s is being deleted", ns, name)
+ c.enqueueBackup(newVolumeBackup)
+ return
+ }
+
+ // TODO(federation): check something like non-federation's
+ // `IsBackupInvalid`, `IsBackupComplete`, `IsBackupFailed`, `IsBackupScheduled`, `IsBackupRunning`, `IsBackupPrepared`, `IsLogBackupStopped
+
+ klog.V(4).Infof("VolumeBackup object %s/%s enqueue", ns, name)
+ c.enqueueBackup(newVolumeBackup)
+}
+
+// enqueueBackup enqueues the given VolumeBackup in the work queue.
+func (c *Controller) enqueueBackup(obj interface{}) {
+ key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
+ if err != nil {
+ utilruntime.HandleError(fmt.Errorf("cound't get key for object %+v: %v", obj, err))
+ return
+ }
+ c.queue.Add(key)
+}
diff --git a/pkg/controller/fedvolumebackupschedule/fed_volume_backup_schedule_control.go b/pkg/controller/fedvolumebackupschedule/fed_volume_backup_schedule_control.go
new file mode 100644
index 0000000000..ad568e2eaf
--- /dev/null
+++ b/pkg/controller/fedvolumebackupschedule/fed_volume_backup_schedule_control.go
@@ -0,0 +1,86 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fedvolumebackupschedule
+
+import (
+ "k8s.io/client-go/tools/cache"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned"
+ informers "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/fedvolumebackup"
+)
+
+// ControlInterface implements the control logic for updating VolumeBackupSchedule
+// It is implemented as an interface to allow for extensions that provide different semantics.
+// Currently, there is only one implementation.
+type ControlInterface interface {
+ // UpdateBackupSchedule implements the control logic for VolumeBackupSchedule
+ UpdateBackupSchedule(volumeBackupSchedule *v1alpha1.VolumeBackupSchedule) error
+}
+
+// NewDefaultVolumeBackupScheduleControl returns a new instance of the default VolumeBackupSchedue ControlInterface implementation.
+func NewDefaultVolumeBackupScheduleControl(
+ cli versioned.Interface,
+ backupScheduleManager fedvolumebackup.BackupScheduleManager) ControlInterface {
+ return &defaultBackupScheduleControl{
+ cli,
+ backupScheduleManager,
+ }
+}
+
+type defaultBackupScheduleControl struct {
+ cli versioned.Interface
+ bsManager fedvolumebackup.BackupScheduleManager
+}
+
+// UpdateBackupSchedule executes the core logic loop for a VolumeBackupSchedule.
+func (c *defaultBackupScheduleControl) UpdateBackupSchedule(volumeBackupSchedule *v1alpha1.VolumeBackupSchedule) error {
+ return c.bsManager.Sync(volumeBackupSchedule)
+}
+
+var _ ControlInterface = &defaultBackupScheduleControl{}
+
+// FakeBackupScheduleControl is a fake BackupScheduleControlInterface
+type FakeBackupScheduleControl struct {
+ bsIndexer cache.Indexer
+ updateBsTracker controller.RequestTracker
+}
+
+// NewFakeBackupScheduleControl returns a FakeBackupScheduleControl
+func NewFakeBackupScheduleControl(bsInformer informers.VolumeBackupScheduleInformer) *FakeBackupScheduleControl {
+ return &FakeBackupScheduleControl{
+ bsInformer.Informer().GetIndexer(),
+ controller.RequestTracker{},
+ }
+}
+
+// SetUpdateBackupError sets the error attributes of updateBackupTracker
+func (c *FakeBackupScheduleControl) SetUpdateBackupError(err error, after int) {
+ c.updateBsTracker.SetError(err).SetAfter(after)
+}
+
+// UpdateBackupSchedule updates the VolumeBackupSchedule to BackupScheduleIndexer
+func (c *FakeBackupScheduleControl) UpdateBackupSchedule(volumeBackupSchedule *v1alpha1.VolumeBackupSchedule) error {
+ defer c.updateBsTracker.Inc()
+ if c.updateBsTracker.ErrorReady() {
+ defer c.updateBsTracker.Reset()
+ return c.updateBsTracker.GetError()
+ }
+
+ return c.bsIndexer.Add(volumeBackupSchedule)
+}
+
+var _ ControlInterface = &FakeBackupScheduleControl{}
diff --git a/pkg/controller/fedvolumebackupschedule/fed_volume_backup_schedule_controller.go b/pkg/controller/fedvolumebackupschedule/fed_volume_backup_schedule_controller.go
new file mode 100644
index 0000000000..d3db48aa16
--- /dev/null
+++ b/pkg/controller/fedvolumebackupschedule/fed_volume_backup_schedule_controller.go
@@ -0,0 +1,166 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fedvolumebackupschedule
+
+import (
+ "fmt"
+ "time"
+
+ perrors "github.com/pingcap/errors"
+ "k8s.io/apimachinery/pkg/api/errors"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ "k8s.io/apimachinery/pkg/util/wait"
+ "k8s.io/client-go/tools/cache"
+ "k8s.io/client-go/util/workqueue"
+ "k8s.io/klog/v2"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/fedvolumebackup/backupschedule"
+ "github.com/pingcap/tidb-operator/pkg/metrics"
+)
+
+// Controller controls VolumeBackupSchedule.
+type Controller struct {
+ deps *controller.BrFedDependencies
+ // control returns an interface capable of syncing a VolumeBackupSchedule.
+ // Abstracted out for testing.
+ control ControlInterface
+ // VolumeBackupSchedules that need to be synced.
+ queue workqueue.RateLimitingInterface
+}
+
+// NewController creates a VolumeBackupSchedule controller.
+func NewController(deps *controller.BrFedDependencies) *Controller {
+ c := &Controller{
+ deps: deps,
+ control: NewDefaultVolumeBackupScheduleControl(deps.Clientset, backupschedule.NewBackupScheduleManager(deps)),
+ queue: workqueue.NewNamedRateLimitingQueue(
+ controller.NewControllerRateLimiter(1*time.Second, 100*time.Second),
+ "volumeBackupSchedule",
+ ),
+ }
+
+ volumeBackupScheduleInformer := deps.InformerFactory.Federation().V1alpha1().VolumeBackupSchedules()
+ volumeBackupScheduleInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
+ AddFunc: c.updateBackupSchedule,
+ UpdateFunc: func(old, cur interface{}) {
+ c.updateBackupSchedule(cur)
+ },
+ DeleteFunc: c.updateBackupSchedule,
+ })
+
+ return c
+}
+
+// Name returns VolumeBackupSchedule controller name.
+func (c *Controller) Name() string {
+ return "volumeBackupSchedule"
+}
+
+// Run runs the VolumeBackupSchedule controller.
+func (c *Controller) Run(workers int, stopCh <-chan struct{}) {
+ defer utilruntime.HandleCrash()
+ defer c.queue.ShutDown()
+
+ klog.Info("Starting volumeBackupSchedule controller")
+ defer klog.Info("Shutting down volumeBackupSchedule controller")
+
+ for i := 0; i < workers; i++ {
+ go wait.Until(c.worker, time.Second, stopCh)
+ }
+
+ <-stopCh
+}
+
+// worker runs a worker goroutine that invokes processNextWorkItem until the the controller's queue is closed
+func (c *Controller) worker() {
+ for c.processNextWorkItem() {
+ }
+}
+
+// processNextWorkItem dequeues items, processes them, and marks them done. It enforces that the syncHandler is never
+// invoked concurrently with the same key.
+func (c *Controller) processNextWorkItem() bool {
+ metrics.ActiveWorkers.WithLabelValues(c.Name()).Add(1)
+ defer metrics.ActiveWorkers.WithLabelValues(c.Name()).Add(-1)
+
+ key, quit := c.queue.Get()
+ if quit {
+ return false
+ }
+ defer c.queue.Done(key)
+ if err := c.sync(key.(string)); err != nil {
+ if perrors.Find(err, controller.IsRequeueError) != nil {
+ klog.Infof("VolumeBackupSchedule: %v, still need sync: %v, requeuing", key.(string), err)
+ c.queue.AddRateLimited(key)
+ } else if perrors.Find(err, controller.IsIgnoreError) != nil {
+ klog.V(4).Infof("VolumeBackupSchedule: %v, ignore err: %v", key.(string), err)
+ } else {
+ utilruntime.HandleError(fmt.Errorf("VolumeBackupSchedule: %v, sync failed, err: %v, requeuing", key.(string), err))
+ c.queue.AddRateLimited(key)
+ }
+ } else {
+ c.queue.Forget(key)
+ }
+ return true
+}
+
+// sync syncs the given VolumeBackupSchedule.
+func (c *Controller) sync(key string) error {
+ startTime := time.Now()
+ defer func() {
+ duration := time.Since(startTime)
+ metrics.ReconcileTime.WithLabelValues(c.Name()).Observe(duration.Seconds())
+ klog.V(4).Infof("Finished syncing VolumeBackupSchedule %q (%v)", key, duration)
+ }()
+
+ ns, name, err := cache.SplitMetaNamespaceKey(key)
+ if err != nil {
+ return err
+ }
+ volumeBackupSchedule, err := c.deps.VolumeBackupScheduleLister.VolumeBackupSchedules(ns).Get(name)
+ if errors.IsNotFound(err) {
+ klog.Infof("VolumeBackupSchedule has been deleted %v", key)
+ return nil
+ }
+ if err != nil {
+ return err
+ }
+
+ return c.syncBackupSchedule(volumeBackupSchedule.DeepCopy())
+}
+
+func (c *Controller) syncBackupSchedule(volumeBackupSchedule *v1alpha1.VolumeBackupSchedule) error {
+ return c.control.UpdateBackupSchedule(volumeBackupSchedule)
+}
+
+func (c *Controller) updateBackupSchedule(cur interface{}) {
+ newVolumeBackupSchedule := cur.(*v1alpha1.VolumeBackupSchedule)
+ ns := newVolumeBackupSchedule.GetNamespace()
+ name := newVolumeBackupSchedule.GetName()
+ klog.V(4).Infof("VolumeBackupSchedule object %s/%s enqueue", ns, name)
+
+ c.enqueueBackupSchedule(newVolumeBackupSchedule)
+}
+
+// enqueueBackupSchedule enqueues the given VolumeBackupSchedule in the work queue.
+func (c *Controller) enqueueBackupSchedule(obj interface{}) {
+ key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
+ if err != nil {
+ utilruntime.HandleError(fmt.Errorf("cound't get key for object %+v: %v", obj, err))
+ return
+ }
+ c.queue.Add(key)
+}
diff --git a/pkg/controller/fedvolumerestore/fed_volume_restore_control.go b/pkg/controller/fedvolumerestore/fed_volume_restore_control.go
new file mode 100644
index 0000000000..44f5d77b3e
--- /dev/null
+++ b/pkg/controller/fedvolumerestore/fed_volume_restore_control.go
@@ -0,0 +1,102 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fedvolumerestore
+
+import (
+ "k8s.io/client-go/tools/cache"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/client/federation/clientset/versioned"
+ informers "github.com/pingcap/tidb-operator/pkg/client/federation/informers/externalversions/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/fedvolumebackup"
+)
+
+// ControlInterface implements the control logic for updating VolumeRestore
+// It is implemented as an interface to allow for extensions that provide different semantics.
+// Currently, there is only one implementation.
+type ControlInterface interface {
+ // UpdateRestore implements the control logic for VolumeRestore's creation, deletion
+ UpdateRestore(volumeRestore *v1alpha1.VolumeRestore) error
+ // UpdateCondition updates the condition for a VolumeRestore
+ UpdateCondition(volumeRestore *v1alpha1.VolumeRestore, condition *v1alpha1.VolumeRestoreCondition) error
+}
+
+// NewDefaultVolumeRestoreControl returns a new instance of the default VolumeRestore ControlInterface implementation.
+func NewDefaultVolumeRestoreControl(
+ cli versioned.Interface,
+ restoreManager fedvolumebackup.RestoreManager) ControlInterface {
+ return &defaultRestoreControl{
+ cli,
+ restoreManager,
+ }
+}
+
+type defaultRestoreControl struct {
+ cli versioned.Interface
+ restoreManager fedvolumebackup.RestoreManager
+}
+
+// UpdateRestore executes the core logic loop for a VolumeRestore.
+func (c *defaultRestoreControl) UpdateRestore(volumeRestore *v1alpha1.VolumeRestore) error {
+ volumeRestore.SetGroupVersionKind(controller.FedVolumeRestoreControllerKind)
+ return c.restoreManager.Sync(volumeRestore)
+}
+
+// UpdateCondition updates the condition for a VolumeRestore
+func (c *defaultRestoreControl) UpdateCondition(volumeRestore *v1alpha1.VolumeRestore, condition *v1alpha1.VolumeRestoreCondition) error {
+ return c.restoreManager.UpdateCondition(volumeRestore, condition)
+}
+
+var _ ControlInterface = &defaultRestoreControl{}
+
+// FakeRestoreControl is a fake RestoreControlInterface
+type FakeRestoreControl struct {
+ restoreIndexer cache.Indexer
+ updateRestoreTracker controller.RequestTracker
+ condition *v1alpha1.VolumeRestoreCondition
+}
+
+// NewFakeRestoreControl returns a FakeRestoreControl
+func NewFakeRestoreControl(restoreInformer informers.VolumeRestoreInformer) *FakeRestoreControl {
+ return &FakeRestoreControl{
+ restoreInformer.Informer().GetIndexer(),
+ controller.RequestTracker{},
+ nil,
+ }
+}
+
+// SetUpdateRestoreError sets the error attributes of updateRestoreTracker
+func (c *FakeRestoreControl) SetUpdateRestoreError(err error, after int) {
+ c.updateRestoreTracker.SetError(err).SetAfter(after)
+}
+
+// UpdateRestore adds the VolumeRestore to RestoreIndexer
+func (c *FakeRestoreControl) UpdateRestore(volumeRestore *v1alpha1.VolumeRestore) error {
+ defer c.updateRestoreTracker.Inc()
+ if c.updateRestoreTracker.ErrorReady() {
+ defer c.updateRestoreTracker.Reset()
+ return c.updateRestoreTracker.GetError()
+ }
+
+ return c.restoreIndexer.Add(volumeRestore)
+}
+
+// UpdateStatus updates the condition for a VolumeRestore, include condition and status info
+func (c *FakeRestoreControl) UpdateCondition(_ *v1alpha1.VolumeRestore, condition *v1alpha1.VolumeRestoreCondition) error {
+ c.condition = condition
+ return nil
+}
+
+var _ ControlInterface = &FakeRestoreControl{}
diff --git a/pkg/controller/fedvolumerestore/fed_volume_restore_controller.go b/pkg/controller/fedvolumerestore/fed_volume_restore_controller.go
new file mode 100644
index 0000000000..53287b9d98
--- /dev/null
+++ b/pkg/controller/fedvolumerestore/fed_volume_restore_controller.go
@@ -0,0 +1,169 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fedvolumerestore
+
+import (
+ "fmt"
+ "time"
+
+ perrors "github.com/pingcap/errors"
+ "k8s.io/apimachinery/pkg/api/errors"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ "k8s.io/apimachinery/pkg/util/wait"
+ "k8s.io/client-go/tools/cache"
+ "k8s.io/client-go/util/workqueue"
+ "k8s.io/klog/v2"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/fedvolumebackup/restore"
+ "github.com/pingcap/tidb-operator/pkg/metrics"
+)
+
+// Controller controls VolumeRestore.
+type Controller struct {
+ deps *controller.BrFedDependencies
+ // control returns an interface capable of syncing a VolumeRestore.
+ // Abstracted out for testing.
+ control ControlInterface
+ // VolumeRestores that need to be synced.
+ queue workqueue.RateLimitingInterface
+}
+
+// NewController creates a VolumeRestore controller.
+func NewController(deps *controller.BrFedDependencies) *Controller {
+ c := &Controller{
+ deps: deps,
+ control: NewDefaultVolumeRestoreControl(deps.Clientset, restore.NewRestoreManager(deps)),
+ queue: workqueue.NewNamedRateLimitingQueue(
+ controller.NewControllerRateLimiter(1*time.Second, 100*time.Second),
+ "volumeRestore",
+ ),
+ }
+
+ volumeRestoreInformer := deps.InformerFactory.Federation().V1alpha1().VolumeRestores()
+ volumeRestoreInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
+ AddFunc: c.updateRestore,
+ UpdateFunc: func(old, cur interface{}) {
+ c.updateRestore(cur)
+ },
+ DeleteFunc: c.updateRestore,
+ })
+
+ return c
+}
+
+// Name returns VolumeRestore controller name.
+func (c *Controller) Name() string {
+ return "volumeRestore"
+}
+
+// Run runs the VolumeRestore controller.
+func (c *Controller) Run(workers int, stopCh <-chan struct{}) {
+ defer utilruntime.HandleCrash()
+ defer c.queue.ShutDown()
+
+ klog.Info("Starting volumeRestore controller")
+ defer klog.Info("Shutting down volumeRestore controller")
+
+ for i := 0; i < workers; i++ {
+ go wait.Until(c.worker, time.Second, stopCh)
+ }
+
+ <-stopCh
+}
+
+// worker runs a worker goroutine that invokes processNextWorkItem until the the controller's queue is closed
+func (c *Controller) worker() {
+ for c.processNextWorkItem() {
+ }
+}
+
+// processNextWorkItem dequeues items, processes them, and marks them done. It enforces that the syncHandler is never
+// invoked concurrently with the same key.
+func (c *Controller) processNextWorkItem() bool {
+ metrics.ActiveWorkers.WithLabelValues(c.Name()).Add(1)
+ defer metrics.ActiveWorkers.WithLabelValues(c.Name()).Add(-1)
+
+ key, quit := c.queue.Get()
+ if quit {
+ return false
+ }
+ defer c.queue.Done(key)
+ if err := c.sync(key.(string)); err != nil {
+ if perrors.Find(err, controller.IsRequeueError) != nil {
+ klog.Infof("VolumeRestore: %v, still need sync: %v, requeuing", key.(string), err)
+ c.queue.AddRateLimited(key)
+ } else if perrors.Find(err, controller.IsIgnoreError) != nil {
+ klog.V(4).Infof("VolumeRestore: %v, ignore err: %v", key.(string), err)
+ } else {
+ utilruntime.HandleError(fmt.Errorf("VolumeRestore: %v, sync failed, err: %v, requeuing", key.(string), err))
+ c.queue.AddRateLimited(key)
+ }
+ } else {
+ c.queue.Forget(key)
+ }
+ return true
+}
+
+// sync syncs the given VolumeRestore.
+func (c *Controller) sync(key string) error {
+ startTime := time.Now()
+ defer func() {
+ duration := time.Since(startTime)
+ metrics.ReconcileTime.WithLabelValues(c.Name()).Observe(duration.Seconds())
+ klog.V(4).Infof("Finished syncing VolumeRestore %q (%v)", key, duration)
+ }()
+
+ ns, name, err := cache.SplitMetaNamespaceKey(key)
+ if err != nil {
+ return err
+ }
+ volumeRestore, err := c.deps.VolumeRestoreLister.VolumeRestores(ns).Get(name)
+ if errors.IsNotFound(err) {
+ klog.Infof("VolumeRestore has been deleted %v", key)
+ return nil
+ }
+ if err != nil {
+ return err
+ }
+
+ return c.syncRestore(volumeRestore.DeepCopy())
+}
+
+func (c *Controller) syncRestore(volumeRestore *v1alpha1.VolumeRestore) error {
+ return c.control.UpdateRestore(volumeRestore)
+}
+
+func (c *Controller) updateRestore(cur interface{}) {
+ newVolumeRestore := cur.(*v1alpha1.VolumeRestore)
+ ns := newVolumeRestore.GetNamespace()
+ name := newVolumeRestore.GetName()
+
+ // TODO(federation): check something like non-federation's
+ // `IsRestoreInvalid`, `IsRestoreComplete`, `IsRestoreFailed`, `IsRestoreDataComplete`, `IsRestoreVolumeComplete`, `IsRestoreScheduled`, `IsRestoreRunning`
+
+ klog.V(4).Infof("VolumeRestore object %s/%s enqueue", ns, name)
+ c.enqueueRestore(newVolumeRestore)
+}
+
+// enqueueRestore enqueues the given VolumeRestore in the work queue.
+func (c *Controller) enqueueRestore(obj interface{}) {
+ key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
+ if err != nil {
+ utilruntime.HandleError(fmt.Errorf("cound't get key for object %+v: %v", obj, err))
+ return
+ }
+ c.queue.Add(key)
+}
diff --git a/pkg/fedvolumebackup/backup.go b/pkg/fedvolumebackup/backup.go
new file mode 100644
index 0000000000..60911fdfa1
--- /dev/null
+++ b/pkg/fedvolumebackup/backup.go
@@ -0,0 +1,40 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fedvolumebackup
+
+import (
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+)
+
+// BackupManager implements the logic for manage federation VolumeBackup.
+type BackupManager interface {
+ // Sync implements the logic for syncing VolumeBackup.
+ Sync(volumeBackup *v1alpha1.VolumeBackup) error
+ // UpdateStatus updates the status for a VolumeBackup, include condition and status info.
+ UpdateStatus(volumeBackup *v1alpha1.VolumeBackup, newStatus *v1alpha1.VolumeBackupStatus) error
+}
+
+// RestoreManager implements the logic for manage federation VolumeRestore.
+type RestoreManager interface {
+ // Sync implements the logic for syncing VolumeRestore.
+ Sync(volumeRestore *v1alpha1.VolumeRestore) error
+ // UpdateCondition updates the condition for a VolumeRestore.
+ UpdateCondition(volumeRestore *v1alpha1.VolumeRestore, condition *v1alpha1.VolumeRestoreCondition) error
+}
+
+// BackupScheduleManager implements the logic for manage federation VolumeBackupSchedule.
+type BackupScheduleManager interface {
+ // Sync implements the logic for syncing VolumeBackupSchedule.
+ Sync(volumeBackup *v1alpha1.VolumeBackupSchedule) error
+}
diff --git a/pkg/fedvolumebackup/backup/backup_manager.go b/pkg/fedvolumebackup/backup/backup_manager.go
new file mode 100644
index 0000000000..e210a6affb
--- /dev/null
+++ b/pkg/fedvolumebackup/backup/backup_manager.go
@@ -0,0 +1,103 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package backup
+
+import (
+ "context"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/klog/v2"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/fedvolumebackup"
+)
+
+type backupManager struct {
+ deps *controller.BrFedDependencies
+}
+
+// NewBackupManager return backupManager
+func NewBackupManager(deps *controller.BrFedDependencies) fedvolumebackup.BackupManager {
+ return &backupManager{
+ deps: deps,
+ }
+}
+
+func (bm *backupManager) Sync(volumeBackup *v1alpha1.VolumeBackup) error {
+ // because a finalizer is installed on the VolumeBackup on creation, when the VolumeBackup is deleted,
+ // volumeBackup.DeletionTimestamp will be set, controller will be informed with an onUpdate event,
+ // this is the moment that we can do clean up work.
+ // TODO(federation): do clean up work
+
+ if volumeBackup.DeletionTimestamp != nil {
+ // volumeBackup is being deleted, don't do more things, return directly.
+ return nil
+ }
+
+ return bm.syncBackup(volumeBackup)
+}
+
+// UpdateStatus updates the status for a Backup, include condition and status info.
+func (bm *backupManager) UpdateStatus(backup *v1alpha1.VolumeBackup, newStatus *v1alpha1.VolumeBackupStatus) error {
+ // TODO(federation): update status
+ return nil
+}
+
+func (bm *backupManager) syncBackup(volumeBackup *v1alpha1.VolumeBackup) error {
+ ns := volumeBackup.GetNamespace()
+ name := volumeBackup.GetName()
+
+ // TODO(federation): implement the main logic of backup
+ klog.Infof("sync VolumeBackup %s/%s", ns, name)
+
+ // TODO(federation): remove the following code
+ for k8sName, k8sClient := range bm.deps.FedClientset {
+ bkList, err := k8sClient.PingcapV1alpha1().Backups("default").List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ klog.Errorf("failed to list backups in %s: %v", k8sName, err)
+ continue
+ }
+ for _, bk := range bkList.Items {
+ klog.Infof("get backup %s/%s", bk.Namespace, bk.Name)
+ }
+ }
+
+ return nil
+}
+
+var _ fedvolumebackup.BackupManager = &backupManager{}
+
+type FakeBackupManager struct {
+ err error
+}
+
+func NewFakeBackupManager() *FakeBackupManager {
+ return &FakeBackupManager{}
+}
+
+func (m *FakeBackupManager) SetSyncError(err error) {
+ m.err = err
+}
+
+func (m *FakeBackupManager) Sync(_ *v1alpha1.VolumeBackup) error {
+ return m.err
+}
+
+// UpdateStatus updates the status for a Backup, include condition and status info.
+func (m *FakeBackupManager) UpdateStatus(_ *v1alpha1.VolumeBackup, newStatus *v1alpha1.VolumeBackupStatus) error {
+ return nil
+}
+
+var _ fedvolumebackup.BackupManager = &FakeBackupManager{}
diff --git a/pkg/fedvolumebackup/backupschedule/backup_schedule_manager.go b/pkg/fedvolumebackup/backupschedule/backup_schedule_manager.go
new file mode 100644
index 0000000000..deeb552f97
--- /dev/null
+++ b/pkg/fedvolumebackup/backupschedule/backup_schedule_manager.go
@@ -0,0 +1,68 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package backupschedule
+
+import (
+ "time"
+
+ "k8s.io/klog/v2"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/fedvolumebackup"
+)
+
+type nowFn func() time.Time
+
+type backupScheduleManager struct {
+ deps *controller.BrFedDependencies
+ now nowFn
+}
+
+// NewBackupScheduleManager return backupScheduleManager
+func NewBackupScheduleManager(deps *controller.BrFedDependencies) fedvolumebackup.BackupScheduleManager {
+ return &backupScheduleManager{
+ deps: deps,
+ now: time.Now,
+ }
+}
+
+func (bm *backupScheduleManager) Sync(volumeBackupSchedule *v1alpha1.VolumeBackupSchedule) error {
+ ns := volumeBackupSchedule.GetNamespace()
+ name := volumeBackupSchedule.GetName()
+ // TODO(federation): implement the main logic of backupSchedule
+ klog.Infof("sync VolumeBackupSchedule %s/%s", ns, name)
+
+ return nil
+}
+
+var _ fedvolumebackup.BackupScheduleManager = &backupScheduleManager{}
+
+type FakeBackupScheduleManager struct {
+ err error
+}
+
+func NewFakeBackupScheduleManager() *FakeBackupScheduleManager {
+ return &FakeBackupScheduleManager{}
+}
+
+func (m *FakeBackupScheduleManager) SetSyncError(err error) {
+ m.err = err
+}
+
+func (m *FakeBackupScheduleManager) Sync(_ *v1alpha1.VolumeBackupSchedule) error {
+ return m.err
+}
+
+var _ fedvolumebackup.BackupScheduleManager = &FakeBackupScheduleManager{}
diff --git a/pkg/fedvolumebackup/restore/restore_manager.go b/pkg/fedvolumebackup/restore/restore_manager.go
new file mode 100644
index 0000000000..d241b920e2
--- /dev/null
+++ b/pkg/fedvolumebackup/restore/restore_manager.go
@@ -0,0 +1,78 @@
+// Copyright 2023 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package restore
+
+import (
+ "k8s.io/klog/v2"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/federation/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/fedvolumebackup"
+)
+
+type restoreManager struct {
+ deps *controller.BrFedDependencies
+}
+
+// NewRestoreManager return restoreManager
+func NewRestoreManager(deps *controller.BrFedDependencies) fedvolumebackup.RestoreManager {
+ return &restoreManager{
+ deps: deps,
+ }
+}
+
+func (bm *restoreManager) Sync(volumeRestore *v1alpha1.VolumeRestore) error {
+ return bm.syncRestore(volumeRestore)
+}
+
+// UpdateCondition updates the condition for a Restore.
+func (bm *restoreManager) UpdateCondition(volumeRestore *v1alpha1.VolumeRestore, condition *v1alpha1.VolumeRestoreCondition) error {
+ // TODO(federation): update condition
+ return nil
+}
+
+func (bm *restoreManager) syncRestore(volumeRestore *v1alpha1.VolumeRestore) error {
+ ns := volumeRestore.GetNamespace()
+ name := volumeRestore.GetName()
+
+ // TODO(federation): implement the main logic of restore
+ klog.Infof("sync VolumeRestore %s/%s", ns, name)
+
+ return nil
+}
+
+var _ fedvolumebackup.RestoreManager = &restoreManager{}
+
+type FakeRestoreManager struct {
+ err error
+}
+
+func NewFakeRestoreManager() *FakeRestoreManager {
+ return &FakeRestoreManager{}
+}
+
+func (m *FakeRestoreManager) SetSyncError(err error) {
+ m.err = err
+}
+
+func (m *FakeRestoreManager) Sync(_ *v1alpha1.VolumeRestore) error {
+ return m.err
+}
+
+// UpdateStatus updates the condition for a Restore.
+func (m *FakeRestoreManager) UpdateCondition(_ *v1alpha1.VolumeRestore, condition *v1alpha1.VolumeRestoreCondition) error {
+ return nil
+}
+
+var _ fedvolumebackup.RestoreManager = &FakeRestoreManager{}