diff --git a/charts/tidb-operator/templates/controller-manager-deployment.yaml b/charts/tidb-operator/templates/controller-manager-deployment.yaml
index 9aead4e806..56a86edfe2 100644
--- a/charts/tidb-operator/templates/controller-manager-deployment.yaml
+++ b/charts/tidb-operator/templates/controller-manager-deployment.yaml
@@ -46,6 +46,7 @@ spec:
{{- end }}
- -pd-failover-period={{ .Values.controllerManager.pdFailoverPeriod | default "5m" }}
- -tikv-failover-period={{ .Values.controllerManager.tikvFailoverPeriod | default "5m" }}
+ - -tiflash-failover-period={{ .Values.controllerManager.tiflashFailoverPeriod | default "5m" }}
- -tidb-failover-period={{ .Values.controllerManager.tidbFailoverPeriod | default "5m" }}
- -v={{ .Values.controllerManager.logLevel }}
{{- if .Values.testMode }}
diff --git a/charts/tidb-operator/values.yaml b/charts/tidb-operator/values.yaml
index 30f1c8951e..f9e25006dc 100644
--- a/charts/tidb-operator/values.yaml
+++ b/charts/tidb-operator/values.yaml
@@ -58,6 +58,8 @@ controllerManager:
tikvFailoverPeriod: 5m
# tidb failover period default(5m)
tidbFailoverPeriod: 5m
+ # tiflash failover period default(5m)
+ tiflashFailoverPeriod: 5m
## 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
diff --git a/cmd/controller-manager/main.go b/cmd/controller-manager/main.go
index aca09196a9..a5ae55c7a3 100644
--- a/cmd/controller-manager/main.go
+++ b/cmd/controller-manager/main.go
@@ -52,16 +52,17 @@ import (
)
var (
- printVersion bool
- workers int
- autoFailover bool
- pdFailoverPeriod time.Duration
- tikvFailoverPeriod time.Duration
- tidbFailoverPeriod time.Duration
- leaseDuration = 15 * time.Second
- renewDuration = 5 * time.Second
- retryPeriod = 3 * time.Second
- waitDuration = 5 * time.Second
+ printVersion bool
+ workers int
+ autoFailover bool
+ pdFailoverPeriod time.Duration
+ tikvFailoverPeriod time.Duration
+ tidbFailoverPeriod time.Duration
+ tiflashFailoverPeriod time.Duration
+ leaseDuration = 15 * time.Second
+ renewDuration = 5 * time.Second
+ retryPeriod = 3 * time.Second
+ waitDuration = 5 * time.Second
)
func init() {
@@ -72,6 +73,7 @@ func init() {
flag.BoolVar(&autoFailover, "auto-failover", true, "Auto failover")
flag.DurationVar(&pdFailoverPeriod, "pd-failover-period", time.Duration(5*time.Minute), "PD failover period default(5m)")
flag.DurationVar(&tikvFailoverPeriod, "tikv-failover-period", time.Duration(5*time.Minute), "TiKV failover period default(5m)")
+ flag.DurationVar(&tiflashFailoverPeriod, "tiflash-failover-period", time.Duration(5*time.Minute), "TiFlash failover period default(5m)")
flag.DurationVar(&tidbFailoverPeriod, "tidb-failover-period", time.Duration(5*time.Minute), "TiDB failover period")
flag.DurationVar(&controller.ResyncDuration, "resync-duration", time.Duration(30*time.Second), "Resync time of informer")
flag.BoolVar(&controller.TestMode, "test-mode", false, "whether tidb-operator run in test mode")
@@ -179,7 +181,7 @@ func main() {
klog.Fatalf("failed to upgrade: %v", err)
}
- tcController := tidbcluster.NewController(kubeCli, cli, genericCli, informerFactory, kubeInformerFactory, autoFailover, pdFailoverPeriod, tikvFailoverPeriod, tidbFailoverPeriod)
+ tcController := tidbcluster.NewController(kubeCli, cli, genericCli, informerFactory, kubeInformerFactory, autoFailover, pdFailoverPeriod, tikvFailoverPeriod, tidbFailoverPeriod, tiflashFailoverPeriod)
backupController := backup.NewController(kubeCli, cli, informerFactory, kubeInformerFactory)
restoreController := restore.NewController(kubeCli, cli, informerFactory, kubeInformerFactory)
bsController := backupschedule.NewController(kubeCli, cli, informerFactory, kubeInformerFactory)
diff --git a/docs/api-references/config.json b/docs/api-references/config.json
index cc214e90bd..f0888cb044 100644
--- a/docs/api-references/config.json
+++ b/docs/api-references/config.json
@@ -9,12 +9,13 @@
"TCPPort",
"HTTPPort",
"InternalServerHTTPPort",
- "Errorlog",
+ "ErrorLog",
"ServerLog",
"TiDBStatusAddr",
"ServiceAddr",
"ProxyConfig",
"ClusterManagerPath",
+ "Flash",
"FlashStatus",
"FlashQuota",
"FlashUser",
@@ -27,7 +28,18 @@
"hideTypePatterns": [
"ParseError$",
"List$",
- "DataResource"
+ "DataResource",
+ "ProxyConfig",
+ "^Flash$",
+ "FlashCluster",
+ "FlashStatus",
+ "FlashQuota",
+ "FlashUser",
+ "FlashProfile",
+ "FlashApplication",
+ "FlashProxy",
+ "FlashServerConfig",
+ "FlashRaft"
],
"externalPackages": [
{
diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md
index e2ee9cfbc8..189656a5fa 100644
--- a/docs/api-references/docs.md
+++ b/docs/api-references/docs.md
@@ -2489,19 +2489,6 @@ int64
-flash
-
-
-Flash
-
-
- |
-
-(Optional)
- |
-
-
-
loger
@@ -3207,92 +3194,6 @@ FlashCluster
|
-FlashApplication
-
-
-(Appears on:
-CommonConfig)
-
-
-
FlashApplication is the configuration of [application] section.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-runAsDaemon
-
-bool
-
- |
-
-(Optional)
- Optional: Defaults to true
- |
-
-
-
-FlashCluster
-
-
-(Appears on:
-Flash)
-
-
-
FlashCluster is the configuration of [flash.flash_cluster] section.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-refresh_interval
-
-int32
-
- |
-
-(Optional)
- Optional: Defaults to 20
- |
-
-
-
-update_rule_interval
-
-int32
-
- |
-
-(Optional)
- Optional: Defaults to 10
- |
-
-
-
-master_ttl
-
-int32
-
- |
-
-(Optional)
- Optional: Defaults to 60
- |
-
-
-
FlashLogger
@@ -3348,335 +3249,6 @@ int32
-
FlashProfile
-
-
-(Appears on:
-CommonConfig)
-
-
-
FlashProfile is the configuration of [profiles] section.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-readonly
-
-
-Profile
-
-
- |
-
-(Optional)
- |
-
-
-
-default
-
-
-Profile
-
-
- |
-
-(Optional)
- |
-
-
-
-FlashProxy
-
-
-(Appears on:
-Flash)
-
-
-
FlashProxy is the configuration of [flash.proxy] section.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-addr
-
-string
-
- |
-
-(Optional)
- Optional: Defaults to 0.0.0.0:20170
- |
-
-
-
-advertise-addr
-
-string
-
- |
-
-(Optional)
- |
-
-
-
-data-dir
-
-string
-
- |
-
-(Optional)
- Optional: Defaults to /data/proxy
- |
-
-
-
-config
-
-string
-
- |
-
-(Optional)
- Optional: Defaults to /data/proxy.toml
- |
-
-
-
-log-file
-
-string
-
- |
-
-(Optional)
- Optional: Defaults to /data/logs/proxy.log
- |
-
-
-
-FlashQuota
-
-
-(Appears on:
-CommonConfig)
-
-
-
FlashQuota is the configuration of [quotas] section.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-default
-
-
-Quota
-
-
- |
-
-(Optional)
- |
-
-
-
-FlashRaft
-
-
-(Appears on:
-CommonConfig)
-
-
-
FlashRaft is the configuration of [raft] section.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-pd_addr
-
-string
-
- |
-
-(Optional)
- |
-
-
-
-kvstore_path
-
-string
-
- |
-
-(Optional)
- Optional: Defaults to /data/kvstore
- |
-
-
-
-storage_engine
-
-string
-
- |
-
-(Optional)
- Optional: Defaults to dt
- |
-
-
-
-FlashServerConfig
-
-
-(Appears on:
-ProxyConfig)
-
-
-
FlashServerConfig is the configuration of Proxy server.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-engine-addr
-
-string
-
- |
-
-(Optional)
- |
-
-
-
-TiKVServerConfig
-
-
-TiKVServerConfig
-
-
- |
-
-
-(Members of TiKVServerConfig are embedded into this type.)
-
- |
-
-
-
-FlashStatus
-
-
-(Appears on:
-CommonConfig)
-
-
-
FlashStatus is the configuration of [status] section.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-metrics_port
-
-int32
-
- |
-
-(Optional)
- Optional: Defaults to 8234
- |
-
-
-
-FlashUser
-
-
-(Appears on:
-CommonConfig)
-
-
-
FlashUser is the configuration of [users] section.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-readonly
-
-
-User
-
-
- |
-
-(Optional)
- |
-
-
-
-default
-
-
-User
-
-
- |
-
- |
-
-
-
GcsStorageProvider
@@ -4250,6 +3822,40 @@ uint32
+
LogTailerSpec
+
+
+(Appears on:
+TiFlashSpec)
+
+
+
LogTailerSpec represents an optional log tailer sidecar container
+
+
MasterKeyFileConfig
@@ -6721,10 +6327,6 @@ float64
Profile
-(Appears on:
-FlashProfile)
-
-
Profile is the configuration profiles.
@@ -6848,215 +6450,6 @@ int
-ProxyConfig
-
-
-(Appears on:
-TiFlashConfig)
-
-
-
ProxyConfig is the configuration of TiFlash proxy process.
-All the configurations are same with those of TiKV except adding engine-addr
in the TiKVServerConfig
-
-
ProxyProtocol
@@ -7102,32 +6495,6 @@ uint
-
ProxyServer
-
-
-
ProxyServer is the configuration of TiFlash proxy server.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-engine-addr
-
-string
-
- |
-
-(Optional)
- |
-
-
-
PumpSpec
@@ -7287,10 +6654,6 @@ Kubernetes apps/v1.StatefulSetStatus
Quota
-(Appears on:
-FlashQuota)
-
-
Quota is the configuration of [quotas.default] section.
@@ -9599,6 +8962,20 @@ TiFlashConfig
Config is the Configuration of TiFlash
+
+
+logTailer
+
+
+LogTailerSpec
+
+
+ |
+
+(Optional)
+ LogTailer is the configurations of the log tailers for TiFlash
+ |
+
TiKVBlockCacheConfig
@@ -10488,7 +9865,6 @@ TiKVEncryptionConfig
(Appears on:
-ProxyConfig,
TiKVConfig)
@@ -10698,7 +10074,6 @@ string
(Appears on:
-ProxyConfig,
TiKVConfig)
@@ -11180,7 +10555,6 @@ Kubernetes meta/v1.Time
(Appears on:
-ProxyConfig,
TiKVConfig)
@@ -11222,7 +10596,6 @@ string
(Appears on:
-ProxyConfig,
TiKVConfig)
@@ -11405,7 +10778,6 @@ If the type set to kms, this config should be filled
(Appears on:
-ProxyConfig,
TiKVConfig)
@@ -11480,7 +10852,6 @@ Optional: Defaults to 10
(Appears on:
-ProxyConfig,
TiKVConfig)
@@ -11765,7 +11136,6 @@ TiKVCfConfig
(Appears on:
-ProxyConfig,
TiKVConfig)
@@ -12356,7 +11726,6 @@ bool
(Appears on:
-ProxyConfig,
TiKVConfig)
@@ -12401,7 +11770,6 @@ TiKVStorageReadPoolConfig
(Appears on:
-ProxyConfig,
TiKVConfig)
@@ -12487,7 +11855,6 @@ string
(Appears on:
-FlashServerConfig,
TiKVConfig)
@@ -13011,7 +12378,6 @@ string
(Appears on:
-ProxyConfig,
TiKVConfig)
@@ -14052,6 +13418,18 @@ PumpStatus
|
+
+
+tiflash
+
+
+TiFlashStatus
+
+
+ |
+
+ |
+
TidbInitializerSpec
@@ -14593,10 +13971,6 @@ Kubernetes meta/v1.Time
User
-(Appears on:
-FlashUser)
-
-
User is the configuration of users.
diff --git a/manifests/crd.yaml b/manifests/crd.yaml
index b4eb49031e..cdb4d96c7a 100644
--- a/manifests/crd.yaml
+++ b/manifests/crd.yaml
@@ -5215,6 +5215,21 @@ spec:
description: 'Limits describes the maximum amount of compute resources
allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
+ logTailer:
+ description: LogTailerSpec represents an optional log tailer sidecar
+ container
+ properties:
+ limits:
+ description: 'Limits describes the maximum amount of compute
+ resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+ type: object
+ requests:
+ description: 'Requests describes the minimum amount of compute
+ resources required. If Requests is omitted for a container,
+ it defaults to Limits if that is explicitly specified, otherwise
+ to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+ type: object
+ type: object
maxFailoverCount:
description: 'MaxFailoverCount limit the max replicas could be added
in failover, 0 means no failover Optional: Defaults to 3'
diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go
index 6dc513a64e..18c0663c61 100644
--- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go
+++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go
@@ -47,6 +47,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.HelperSpec": schema_pkg_apis_pingcap_v1alpha1_HelperSpec(ref),
"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.IsolationRead": schema_pkg_apis_pingcap_v1alpha1_IsolationRead(ref),
"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.Log": schema_pkg_apis_pingcap_v1alpha1_Log(ref),
+ "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LogTailerSpec": schema_pkg_apis_pingcap_v1alpha1_LogTailerSpec(ref),
"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.MasterKeyFileConfig": schema_pkg_apis_pingcap_v1alpha1_MasterKeyFileConfig(ref),
"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.MasterKeyKMSConfig": schema_pkg_apis_pingcap_v1alpha1_MasterKeyKMSConfig(ref),
"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.MonitorContainer": schema_pkg_apis_pingcap_v1alpha1_MonitorContainer(ref),
@@ -1486,6 +1487,49 @@ func schema_pkg_apis_pingcap_v1alpha1_Log(ref common.ReferenceCallback) common.O
}
}
+func schema_pkg_apis_pingcap_v1alpha1_LogTailerSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "LogTailerSpec represents an optional log tailer sidecar container",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "limits": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"),
+ },
+ },
+ },
+ },
+ },
+ "requests": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Dependencies: []string{
+ "k8s.io/apimachinery/pkg/api/resource.Quantity"},
+ }
+}
+
func schema_pkg_apis_pingcap_v1alpha1_MasterKeyFileConfig(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -4416,12 +4460,18 @@ func schema_pkg_apis_pingcap_v1alpha1_TiFlashSpec(ref common.ReferenceCallback)
Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiFlashConfig"),
},
},
+ "logTailer": {
+ SchemaProps: spec.SchemaProps{
+ Description: "LogTailer is the configurations of the log tailers for TiFlash",
+ Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LogTailerSpec"),
+ },
+ },
},
Required: []string{"replicas", "storageClaims"},
},
},
Dependencies: []string{
- "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.StorageClaim", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiFlashConfig", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/apimachinery/pkg/api/resource.Quantity"},
+ "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LogTailerSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.StorageClaim", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiFlashConfig", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/apimachinery/pkg/api/resource.Quantity"},
}
}
diff --git a/pkg/apis/pingcap/v1alpha1/tidbcluster.go b/pkg/apis/pingcap/v1alpha1/tidbcluster.go
index 35829bf6ab..828d332481 100644
--- a/pkg/apis/pingcap/v1alpha1/tidbcluster.go
+++ b/pkg/apis/pingcap/v1alpha1/tidbcluster.go
@@ -74,6 +74,28 @@ func (tc *TidbCluster) TiKVContainerPrivilege() *bool {
return tc.Spec.TiKV.Privileged
}
+func (tc *TidbCluster) TiFlashImage() string {
+ image := tc.Spec.TiFlash.Image
+ baseImage := tc.Spec.TiFlash.BaseImage
+ // base image takes higher priority
+ if baseImage != "" {
+ version := tc.Spec.TiFlash.Version
+ if version == nil {
+ version = &tc.Spec.Version
+ }
+ image = fmt.Sprintf("%s:%s", baseImage, *version)
+ }
+ return image
+}
+
+func (tc *TidbCluster) TiFlashContainerPrivilege() *bool {
+ if tc.Spec.TiFlash.Privileged == nil {
+ pri := false
+ return &pri
+ }
+ return tc.Spec.TiFlash.Privileged
+}
+
func (tc *TidbCluster) TiDBImage() string {
image := tc.Spec.TiDB.Image
baseImage := tc.Spec.TiDB.BaseImage
@@ -224,6 +246,36 @@ func (tc *TidbCluster) TiKVStsActualReplicas() int32 {
return stsStatus.Replicas
}
+func (tc *TidbCluster) TiFlashAllPodsStarted() bool {
+ return tc.TiFlashStsDesiredReplicas() == tc.TiFlashStsActualReplicas()
+}
+
+func (tc *TidbCluster) TiFlashAllStoresReady() bool {
+ if int(tc.TiFlashStsDesiredReplicas()) != len(tc.Status.TiFlash.Stores) {
+ return false
+ }
+
+ for _, store := range tc.Status.TiFlash.Stores {
+ if store.State != TiKVStateUp {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (tc *TidbCluster) TiFlashStsDesiredReplicas() int32 {
+ return tc.Spec.TiFlash.Replicas + int32(len(tc.Status.TiFlash.FailureStores))
+}
+
+func (tc *TidbCluster) TiFlashStsActualReplicas() int32 {
+ stsStatus := tc.Status.TiFlash.StatefulSet
+ if stsStatus == nil {
+ return 0
+ }
+ return stsStatus.Replicas
+}
+
func (tc *TidbCluster) TiDBAllPodsStarted() bool {
return tc.TiDBStsDesiredReplicas() == tc.TiDBStsActualReplicas()
}
diff --git a/pkg/apis/pingcap/v1alpha1/tidbcluster_component.go b/pkg/apis/pingcap/v1alpha1/tidbcluster_component.go
index f8d6d62637..840c24e756 100644
--- a/pkg/apis/pingcap/v1alpha1/tidbcluster_component.go
+++ b/pkg/apis/pingcap/v1alpha1/tidbcluster_component.go
@@ -175,6 +175,11 @@ func (tc *TidbCluster) BaseTiKVSpec() ComponentAccessor {
return &componentAccessorImpl{&tc.Spec, &tc.Spec.TiKV.ComponentSpec}
}
+// BaseTiFlashSpec returns the base spec of TiFlash servers
+func (tc *TidbCluster) BaseTiFlashSpec() ComponentAccessor {
+ return &componentAccessorImpl{&tc.Spec, &tc.Spec.TiFlash.ComponentSpec}
+}
+
// BasePDSpec returns the base spec of PD servers
func (tc *TidbCluster) BasePDSpec() ComponentAccessor {
return &componentAccessorImpl{&tc.Spec, &tc.Spec.PD.ComponentSpec}
diff --git a/pkg/apis/pingcap/v1alpha1/tiflash_config.go b/pkg/apis/pingcap/v1alpha1/tiflash_config.go
index 8aa70999dc..be35c1da41 100644
--- a/pkg/apis/pingcap/v1alpha1/tiflash_config.go
+++ b/pkg/apis/pingcap/v1alpha1/tiflash_config.go
@@ -1,4 +1,4 @@
-// Copyright 2019 PingCAP, Inc.
+// Copyright 2020 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -74,17 +74,10 @@ type ProxyConfig struct {
Security *TiKVSecurityConfig `json:"security,omitempty" toml:"security,omitempty"`
}
-// ProxyServer is the configuration of TiFlash proxy server.
-// +k8s:openapi-gen=false
-type ProxyServer struct {
- // +optional
- EngineAddr string `json:"engine-addr,omitempty" toml:"engine-addr,omitempty"`
-}
-
// CommonConfig is the configuration of TiFlash process.
// +k8s:openapi-gen=true
type CommonConfig struct {
- // Optional: Defaults to "/data/tmp"
+ // Optional: Defaults to "/data0/tmp"
// +optional
// +k8s:openapi-gen=false
TmpPath string `json:"tmp_path,omitempty" toml:"tmp_path,omitempty"`
@@ -99,7 +92,7 @@ type CommonConfig struct {
// +k8s:openapi-gen=false
DefaultProfile string `json:"default_profile,omitempty" toml:"default_profile,omitempty"`
- // Optional: Defaults to "/data/db"
+ // Optional: Defaults to "/data0/db"
// +optional
// +k8s:openapi-gen=false
Path string `json:"path,omitempty" toml:"path,omitempty"`
@@ -191,7 +184,7 @@ type FlashUser struct {
// +k8s:openapi-gen=false
type User struct {
// +optional
- Password string `json:"password,omitempty" toml:"password,omitempty"`
+ Password string `json:"password,omitempty" toml:"password"`
// +optional
Profile string `json:"profile,omitempty" toml:"profile,omitempty"`
// +optional
@@ -257,7 +250,7 @@ type FlashStatus struct {
type FlashRaft struct {
// +optional
PDAddr string `json:"pd_addr,omitempty" toml:"pd_addr,omitempty"`
- // Optional: Defaults to /data/kvstore
+ // Optional: Defaults to /data0/kvstore
// +optional
KVStorePath string `json:"kvstore_path,omitempty" toml:"kvstore_path,omitempty"`
// Optional: Defaults to dt
@@ -276,14 +269,14 @@ type FlashApplication struct {
// FlashLogger is the configuration of [logger] section.
// +k8s:openapi-gen=true
type FlashLogger struct {
- // Optional: Defaults to /data/logs/error.log
+ // Optional: Defaults to /data0/logs/error.log
// +optional
// +k8s:openapi-gen=false
- Errorlog string `json:"errorlog,omitempty" toml:"errorlog,omitempty"`
+ ErrorLog string `json:"errorlog,omitempty" toml:"errorlog,omitempty"`
// Optional: Defaults to 100M
// +optional
Size string `json:"size,omitempty" toml:"size,omitempty"`
- // Optional: Defaults to /data/logs/server.log
+ // Optional: Defaults to /data0/logs/server.log
// +optional
// +k8s:openapi-gen=false
ServerLog string `json:"log,omitempty" toml:"log,omitempty"`
@@ -324,7 +317,7 @@ type FlashCluster struct {
// +optional
// +k8s:openapi-gen=false
ClusterManagerPath string `json:"cluster_manager_path,omitempty" toml:"cluster_manager_path,omitempty"`
- // Optional: Defaults to /data/logs/flash_cluster_manager.log
+ // Optional: Defaults to /data0/logs/flash_cluster_manager.log
// +optional
// +k8s:openapi-gen=false
ClusterLog string `json:"log,omitempty" toml:"log,omitempty"`
@@ -347,13 +340,13 @@ type FlashProxy struct {
Addr string `json:"addr,omitempty" toml:"addr,omitempty"`
// +optional
AdvertiseAddr string `json:"advertise-addr,omitempty" toml:"advertise-addr,omitempty"`
- // Optional: Defaults to /data/proxy
+ // Optional: Defaults to /data0/proxy
// +optional
DataDir string `json:"data-dir,omitempty" toml:"data-dir,omitempty"`
- // Optional: Defaults to /data/proxy.toml
+ // Optional: Defaults to /data0/proxy.toml
// +optional
Config string `json:"config,omitempty" toml:"config,omitempty"`
- // Optional: Defaults to /data/logs/proxy.log
+ // Optional: Defaults to /data0/logs/proxy.log
// +optional
LogFile string `json:"log-file,omitempty" toml:"log-file,omitempty"`
}
diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go
index a3d1bfe8fa..5d9e4ce89c 100644
--- a/pkg/apis/pingcap/v1alpha1/types.go
+++ b/pkg/apis/pingcap/v1alpha1/types.go
@@ -42,6 +42,8 @@ const (
TiDBMemberType MemberType = "tidb"
// TiKVMemberType is tikv container type
TiKVMemberType MemberType = "tikv"
+ // TiFlashMemberType is tiflash container type
+ TiFlashMemberType MemberType = "tiflash"
// SlowLogTailerMemberType is tidb log tailer container type
SlowLogTailerMemberType MemberType = "slowlog"
// UnknownMemberType is unknown container type
@@ -203,11 +205,12 @@ type TidbClusterSpec struct {
// TidbClusterStatus represents the current status of a tidb cluster.
type TidbClusterStatus struct {
- ClusterID string `json:"clusterID,omitempty"`
- PD PDStatus `json:"pd,omitempty"`
- TiKV TiKVStatus `json:"tikv,omitempty"`
- TiDB TiDBStatus `json:"tidb,omitempty"`
- Pump PumpStatus `josn:"pump,omitempty"`
+ ClusterID string `json:"clusterID,omitempty"`
+ PD PDStatus `json:"pd,omitempty"`
+ TiKV TiKVStatus `json:"tikv,omitempty"`
+ TiDB TiDBStatus `json:"tidb,omitempty"`
+ Pump PumpStatus `josn:"pump,omitempty"`
+ TiFlash TiFlashStatus `json:"tiflash,omitempty"`
}
// +k8s:openapi-gen=true
@@ -319,6 +322,16 @@ type TiFlashSpec struct {
// Config is the Configuration of TiFlash
// +optional
Config *TiFlashConfig `json:"config,omitempty"`
+
+ // LogTailer is the configurations of the log tailers for TiFlash
+ // +optional
+ LogTailer *LogTailerSpec `json:"logTailer,omitempty"`
+}
+
+// +k8s:openapi-gen=true
+// LogTailerSpec represents an optional log tailer sidecar container
+type LogTailerSpec struct {
+ corev1.ResourceRequirements `json:",inline"`
}
// +k8s:openapi-gen=true
@@ -655,6 +668,17 @@ type TiKVStatus struct {
Image string `json:"image,omitempty"`
}
+// TiFlashStatus is TiFlash status
+type TiFlashStatus struct {
+ Synced bool `json:"synced,omitempty"`
+ Phase MemberPhase `json:"phase,omitempty"`
+ StatefulSet *apps.StatefulSetStatus `json:"statefulSet,omitempty"`
+ Stores map[string]TiKVStore `json:"stores,omitempty"`
+ TombstoneStores map[string]TiKVStore `json:"tombstoneStores,omitempty"`
+ FailureStores map[string]TiKVFailureStore `json:"failureStores,omitempty"`
+ Image string `json:"image,omitempty"`
+}
+
// TiKVStores is either Up/Down/Offline/Tombstone
type TiKVStore struct {
// store id is also uint64, due to the same reason as pd id, we store id as string
diff --git a/pkg/apis/pingcap/v1alpha1/validation/validation.go b/pkg/apis/pingcap/v1alpha1/validation/validation.go
index 1196fb8271..5f5fd799a3 100644
--- a/pkg/apis/pingcap/v1alpha1/validation/validation.go
+++ b/pkg/apis/pingcap/v1alpha1/validation/validation.go
@@ -16,7 +16,9 @@ package validation
import (
"encoding/json"
"fmt"
+ "os"
"reflect"
+ "strings"
"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
"github.com/pingcap/tidb-operator/pkg/label"
@@ -78,6 +80,10 @@ func validateTiFlashSpec(spec *v1alpha1.TiFlashSpec, fldPath *field.Path) field.
allErrs := field.ErrorList{}
allErrs = append(allErrs, validateComponentSpec(&spec.ComponentSpec, fldPath)...)
allErrs = append(allErrs, validateTiFlashConfig(spec.Config, fldPath)...)
+ if len(spec.StorageClaims) < 1 {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("spec.StorageClaims"),
+ spec.StorageClaims, "storageClaims should be configured at least one item."))
+ }
return allErrs
}
@@ -86,11 +92,58 @@ func validateTiFlashConfig(config *v1alpha1.TiFlashConfig, path *field.Path) fie
if config == nil {
return allErrs
}
- if config.CommonConfig.Flash.OverlapThreshold != nil {
- if *config.CommonConfig.Flash.OverlapThreshold < 0 || *config.CommonConfig.Flash.OverlapThreshold > 1 {
- allErrs = append(allErrs, field.Invalid(path.Child("config.config.flash.overlap_threshold"),
- config.CommonConfig.Flash.OverlapThreshold,
- "overlap_threshold must be in the range of [0,1]."))
+
+ if config.CommonConfig != nil {
+ if config.CommonConfig.Flash != nil {
+ if config.CommonConfig.Flash.OverlapThreshold != nil {
+ if *config.CommonConfig.Flash.OverlapThreshold < 0 || *config.CommonConfig.Flash.OverlapThreshold > 1 {
+ allErrs = append(allErrs, field.Invalid(path.Child("config.config.flash.overlap_threshold"),
+ config.CommonConfig.Flash.OverlapThreshold,
+ "overlap_threshold must be in the range of [0,1]."))
+ }
+ }
+ if config.CommonConfig.Flash.FlashCluster != nil {
+ if config.CommonConfig.Flash.FlashCluster.ClusterLog != "" {
+ splitPath := strings.Split(config.CommonConfig.Flash.FlashCluster.ClusterLog, string(os.PathSeparator))
+ // The log path should be at least /dir/base.log
+ if len(splitPath) < 3 {
+ allErrs = append(allErrs, field.Invalid(path.Child("config.config.flash.flash_cluster.log"),
+ config.CommonConfig.Flash.FlashCluster.ClusterLog,
+ "log path should include at least one level dir."))
+ }
+ }
+ }
+ if config.CommonConfig.Flash.FlashProxy != nil {
+ if config.CommonConfig.Flash.FlashProxy.LogFile != "" {
+ splitPath := strings.Split(config.CommonConfig.Flash.FlashProxy.LogFile, string(os.PathSeparator))
+ // The log path should be at least /dir/base.log
+ if len(splitPath) < 3 {
+ allErrs = append(allErrs, field.Invalid(path.Child("config.config.flash.flash_proxy.log-file"),
+ config.CommonConfig.Flash.FlashProxy.LogFile,
+ "log path should include at least one level dir."))
+ }
+ }
+ }
+ }
+ if config.CommonConfig.FlashLogger != nil {
+ if config.CommonConfig.FlashLogger.ServerLog != "" {
+ splitPath := strings.Split(config.CommonConfig.FlashLogger.ServerLog, string(os.PathSeparator))
+ // The log path should be at least /dir/base.log
+ if len(splitPath) < 3 {
+ allErrs = append(allErrs, field.Invalid(path.Child("config.config.logger.log"),
+ config.CommonConfig.FlashLogger.ServerLog,
+ "log path should include at least one level dir."))
+ }
+ }
+ if config.CommonConfig.FlashLogger.ErrorLog != "" {
+ splitPath := strings.Split(config.CommonConfig.FlashLogger.ErrorLog, string(os.PathSeparator))
+ // The log path should be at least /dir/base.log
+ if len(splitPath) < 3 {
+ allErrs = append(allErrs, field.Invalid(path.Child("config.config.logger.errorlog"),
+ config.CommonConfig.FlashLogger.ErrorLog,
+ "log path should include at least one level dir."))
+ }
+ }
}
}
return allErrs
diff --git a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go
index d2be3759ea..4d30fc65f6 100644
--- a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go
@@ -1308,6 +1308,23 @@ func (in *Log) DeepCopy() *Log {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *LogTailerSpec) DeepCopyInto(out *LogTailerSpec) {
+ *out = *in
+ in.ResourceRequirements.DeepCopyInto(&out.ResourceRequirements)
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogTailerSpec.
+func (in *LogTailerSpec) DeepCopy() *LogTailerSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(LogTailerSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MasterKeyFileConfig) DeepCopyInto(out *MasterKeyFileConfig) {
*out = *in
@@ -2500,22 +2517,6 @@ func (in *ProxyProtocol) DeepCopy() *ProxyProtocol {
return out
}
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *ProxyServer) DeepCopyInto(out *ProxyServer) {
- *out = *in
- return
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyServer.
-func (in *ProxyServer) DeepCopy() *ProxyServer {
- if in == nil {
- return nil
- }
- out := new(ProxyServer)
- in.DeepCopyInto(out)
- return out
-}
-
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PumpSpec) DeepCopyInto(out *PumpSpec) {
*out = *in
@@ -3497,6 +3498,11 @@ func (in *TiFlashSpec) DeepCopyInto(out *TiFlashSpec) {
*out = new(TiFlashConfig)
(*in).DeepCopyInto(*out)
}
+ if in.LogTailer != nil {
+ in, out := &in.LogTailer, &out.LogTailer
+ *out = new(LogTailerSpec)
+ (*in).DeepCopyInto(*out)
+ }
return
}
@@ -3510,6 +3516,48 @@ func (in *TiFlashSpec) DeepCopy() *TiFlashSpec {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TiFlashStatus) DeepCopyInto(out *TiFlashStatus) {
+ *out = *in
+ if in.StatefulSet != nil {
+ in, out := &in.StatefulSet, &out.StatefulSet
+ *out = new(appsv1.StatefulSetStatus)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.Stores != nil {
+ in, out := &in.Stores, &out.Stores
+ *out = make(map[string]TiKVStore, len(*in))
+ for key, val := range *in {
+ (*out)[key] = *val.DeepCopy()
+ }
+ }
+ if in.TombstoneStores != nil {
+ in, out := &in.TombstoneStores, &out.TombstoneStores
+ *out = make(map[string]TiKVStore, len(*in))
+ for key, val := range *in {
+ (*out)[key] = *val.DeepCopy()
+ }
+ }
+ if in.FailureStores != nil {
+ in, out := &in.FailureStores, &out.FailureStores
+ *out = make(map[string]TiKVFailureStore, len(*in))
+ for key, val := range *in {
+ (*out)[key] = *val.DeepCopy()
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TiFlashStatus.
+func (in *TiFlashStatus) DeepCopy() *TiFlashStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(TiFlashStatus)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TiKVBlockCacheConfig) DeepCopyInto(out *TiKVBlockCacheConfig) {
*out = *in
@@ -5045,6 +5093,7 @@ func (in *TidbClusterStatus) DeepCopyInto(out *TidbClusterStatus) {
in.TiKV.DeepCopyInto(&out.TiKV)
in.TiDB.DeepCopyInto(&out.TiDB)
in.Pump.DeepCopyInto(&out.Pump)
+ in.TiFlash.DeepCopyInto(&out.TiFlash)
return
}
diff --git a/pkg/controller/tidbcluster/tidb_cluster_control.go b/pkg/controller/tidbcluster/tidb_cluster_control.go
index 442555f958..8671f7b5a9 100644
--- a/pkg/controller/tidbcluster/tidb_cluster_control.go
+++ b/pkg/controller/tidbcluster/tidb_cluster_control.go
@@ -47,6 +47,7 @@ func NewDefaultTidbClusterControl(
orphanPodsCleaner member.OrphanPodsCleaner,
pvcCleaner member.PVCCleanerInterface,
pumpMemberManager manager.Manager,
+ tiflashMemberManager manager.Manager,
discoveryManager member.TidbDiscoveryManager,
podRestarter member.PodRestarter,
recorder record.EventRecorder) ControlInterface {
@@ -60,6 +61,7 @@ func NewDefaultTidbClusterControl(
orphanPodsCleaner,
pvcCleaner,
pumpMemberManager,
+ tiflashMemberManager,
discoveryManager,
podRestarter,
recorder,
@@ -76,6 +78,7 @@ type defaultTidbClusterControl struct {
orphanPodsCleaner member.OrphanPodsCleaner
pvcCleaner member.PVCCleanerInterface
pumpMemberManager manager.Manager
+ tiflashMemberManager manager.Manager
discoveryManager member.TidbDiscoveryManager
podRestarter member.PodRestarter
recorder record.EventRecorder
@@ -180,6 +183,19 @@ func (tcc *defaultTidbClusterControl) updateTidbCluster(tc *v1alpha1.TidbCluster
return err
}
+ // works that should do to making the tiflash cluster current state match the desired state:
+ // - waiting for the tidb cluster available
+ // - create or update tiflash headless service
+ // - create the tiflash statefulset
+ // - sync tiflash cluster status from pd to TidbCluster object
+ // - set scheduler labels to tiflash stores
+ // - upgrade the tiflash cluster
+ // - scale out/in the tiflash cluster
+ // - failover the tiflash cluster
+ if err := tcc.tiflashMemberManager.Sync(tc); err != nil {
+ return err
+ }
+
// syncing the labels from Pod to PVC and PV, these labels include:
// - label.StoreIDLabelKey
// - label.MemberIDLabelKey
diff --git a/pkg/controller/tidbcluster/tidb_cluster_control_test.go b/pkg/controller/tidbcluster/tidb_cluster_control_test.go
index f12593cb92..41a73f3a51 100644
--- a/pkg/controller/tidbcluster/tidb_cluster_control_test.go
+++ b/pkg/controller/tidbcluster/tidb_cluster_control_test.go
@@ -316,6 +316,7 @@ func newFakeTidbClusterControl() (
orphanPodCleaner := mm.NewFakeOrphanPodsCleaner()
pvcCleaner := mm.NewFakePVCCleaner()
pumpMemberManager := mm.NewFakePumpMemberManager()
+ tiflashMemberManager := mm.NewFakeTiFlashMemberManager()
discoveryManager := mm.NewFakeDiscoveryManger()
podRestarter := mm.NewFakePodRestarter()
control := NewDefaultTidbClusterControl(
@@ -328,6 +329,7 @@ func newFakeTidbClusterControl() (
orphanPodCleaner,
pvcCleaner,
pumpMemberManager,
+ tiflashMemberManager,
discoveryManager,
podRestarter,
recorder,
diff --git a/pkg/controller/tidbcluster/tidb_cluster_controller.go b/pkg/controller/tidbcluster/tidb_cluster_controller.go
index dba0a9e7bb..a7dd4d3af5 100644
--- a/pkg/controller/tidbcluster/tidb_cluster_controller.go
+++ b/pkg/controller/tidbcluster/tidb_cluster_controller.go
@@ -75,6 +75,7 @@ func NewController(
pdFailoverPeriod time.Duration,
tikvFailoverPeriod time.Duration,
tidbFailoverPeriod time.Duration,
+ tiflashFailoverPeriod time.Duration,
) *Controller {
eventBroadcaster := record.NewBroadcasterWithCorrelatorOptions(record.CorrelatorOptions{QPS: 1})
eventBroadcaster.StartLogging(klog.V(2).Infof)
@@ -106,11 +107,14 @@ func NewController(
typedControl := controller.NewTypedControl(controller.NewRealGenericControl(genericCli, recorder))
pdScaler := mm.NewPDScaler(pdControl, pvcInformer.Lister(), pvcControl)
tikvScaler := mm.NewTiKVScaler(pdControl, pvcInformer.Lister(), pvcControl, podInformer.Lister())
+ tiflashScaler := mm.NewTiFlashScaler(pdControl, pvcInformer.Lister(), pvcControl, podInformer.Lister())
pdFailover := mm.NewPDFailover(cli, pdControl, pdFailoverPeriod, podInformer.Lister(), podControl, pvcInformer.Lister(), pvcControl, pvInformer.Lister(), recorder)
tikvFailover := mm.NewTiKVFailover(tikvFailoverPeriod)
+ tiflashFailover := mm.NewTiFlashFailover(tiflashFailoverPeriod)
tidbFailover := mm.NewTiDBFailover(tidbFailoverPeriod)
pdUpgrader := mm.NewPDUpgrader(pdControl, podControl, podInformer.Lister())
tikvUpgrader := mm.NewTiKVUpgrader(pdControl, podControl, podInformer.Lister())
+ tiflashUpgrader := mm.NewTiFlashUpgrader(pdControl, podControl, podInformer.Lister())
tidbUpgrader := mm.NewTiDBUpgrader(tidbControl, podInformer.Lister())
podRestarter := mm.NewPodRestarter(kubeCli, podInformer.Lister())
@@ -201,6 +205,21 @@ func NewController(
svcInformer.Lister(),
podInformer.Lister(),
),
+ mm.NewTiFlashMemberManager(
+ pdControl,
+ setControl,
+ svcControl,
+ certControl,
+ typedControl,
+ setInformer.Lister(),
+ svcInformer.Lister(),
+ podInformer.Lister(),
+ nodeInformer.Lister(),
+ autoFailover,
+ tiflashFailover,
+ tiflashScaler,
+ tiflashUpgrader,
+ ),
mm.NewTidbDiscoveryManager(typedControl),
podRestarter,
recorder,
diff --git a/pkg/controller/tidbcluster/tidb_cluster_controller_test.go b/pkg/controller/tidbcluster/tidb_cluster_controller_test.go
index f666195dd9..a3639023be 100644
--- a/pkg/controller/tidbcluster/tidb_cluster_controller_test.go
+++ b/pkg/controller/tidbcluster/tidb_cluster_controller_test.go
@@ -281,6 +281,7 @@ func newFakeTidbClusterController() (*Controller, cache.Indexer, *FakeTidbCluste
5*time.Minute,
5*time.Minute,
5*time.Minute,
+ 5*time.Minute,
)
tcc.tcListerSynced = alwaysReady
tcc.setListerSynced = alwaysReady
diff --git a/pkg/label/label.go b/pkg/label/label.go
index 7d0b06787a..472dc70810 100644
--- a/pkg/label/label.go
+++ b/pkg/label/label.go
@@ -130,6 +130,8 @@ const (
TiDBLabelVal string = "tidb"
// TiKVLabelVal is TiKV label value
TiKVLabelVal string = "tikv"
+ // TiFlashLabelVal is TiKV label value
+ TiFlashLabelVal string = "tiflash"
// PumpLabelVal is Pump label value
PumpLabelVal string = "pump"
// DiscoveryLabelVal is Discovery label value
@@ -307,6 +309,12 @@ func (l Label) TiKV() Label {
return l
}
+// TiFlash assigns tiflash to component key in label
+func (l Label) TiFlash() Label {
+ l.Component(TiFlashLabelVal)
+ return l
+}
+
// IsTiKV returns whether label is a TiKV
func (l Label) IsTiKV() bool {
return l[ComponentLabelKey] == TiKVLabelVal
diff --git a/pkg/manager/member/tiflash_failover.go b/pkg/manager/member/tiflash_failover.go
new file mode 100644
index 0000000000..08ffa919dd
--- /dev/null
+++ b/pkg/manager/member/tiflash_failover.go
@@ -0,0 +1,53 @@
+// Copyright 2020 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 member
+
+import (
+ "time"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
+)
+
+type tiflashFailover struct {
+ tiflashFailoverPeriod time.Duration
+}
+
+// NewTiFlashFailover returns a tiflash Failover
+func NewTiFlashFailover(tiflashFailoverPeriod time.Duration) Failover {
+ return &tiflashFailover{tiflashFailoverPeriod}
+}
+
+// TODO: Finish the failover logic
+func (tff *tiflashFailover) Failover(tc *v1alpha1.TidbCluster) error {
+ return nil
+}
+
+func (tff *tiflashFailover) Recover(_ *v1alpha1.TidbCluster) {
+ // Do nothing now
+}
+
+type fakeTiFlashFailover struct{}
+
+// NewFakeTiFlashFailover returns a fake Failover
+func NewFakeTiFlashFailover() Failover {
+ return &fakeTiFlashFailover{}
+}
+
+func (ftff *fakeTiFlashFailover) Failover(_ *v1alpha1.TidbCluster) error {
+ return nil
+}
+
+func (ftff *fakeTiFlashFailover) Recover(_ *v1alpha1.TidbCluster) {
+ return
+}
diff --git a/pkg/manager/member/tiflash_member_manager.go b/pkg/manager/member/tiflash_member_manager.go
new file mode 100644
index 0000000000..f7a57e12ec
--- /dev/null
+++ b/pkg/manager/member/tiflash_member_manager.go
@@ -0,0 +1,858 @@
+// Copyright 2020 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 member
+
+import (
+ "fmt"
+ "reflect"
+ "regexp"
+ "strings"
+
+ "github.com/pingcap/kvproto/pkg/metapb"
+ "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/label"
+ "github.com/pingcap/tidb-operator/pkg/manager"
+ "github.com/pingcap/tidb-operator/pkg/pdapi"
+ "github.com/pingcap/tidb-operator/pkg/util"
+ apps "k8s.io/api/apps/v1"
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/util/intstr"
+ "k8s.io/apimachinery/pkg/util/uuid"
+ v1 "k8s.io/client-go/listers/apps/v1"
+ corelisters "k8s.io/client-go/listers/core/v1"
+ "k8s.io/klog"
+)
+
+const (
+ // tiflashClusterCertPath is where the cert for inter-cluster communication stored (if any)
+ tiflashClusterCertPath = "/var/lib/tiflash-tls"
+
+ //find a better way to manage store only managed by tiflash in Operator
+ tiflashStoreLimitPattern = `%s-tiflash-\d+\.%s-tiflash-peer\.%s\.svc\:\d+`
+)
+
+// tiflashMemberManager implements manager.Manager.
+type tiflashMemberManager struct {
+ setControl controller.StatefulSetControlInterface
+ svcControl controller.ServiceControlInterface
+ pdControl pdapi.PDControlInterface
+ certControl controller.CertControlInterface
+ typedControl controller.TypedControlInterface
+ setLister v1.StatefulSetLister
+ svcLister corelisters.ServiceLister
+ podLister corelisters.PodLister
+ nodeLister corelisters.NodeLister
+ autoFailover bool
+ tiflashFailover Failover
+ tiflashScaler Scaler
+ tiflashUpgrader Upgrader
+ tiflashStatefulSetIsUpgradingFn func(corelisters.PodLister, pdapi.PDControlInterface, *apps.StatefulSet, *v1alpha1.TidbCluster) (bool, error)
+}
+
+// NewTiFlashMemberManager returns a *tiflashMemberManager
+func NewTiFlashMemberManager(
+ pdControl pdapi.PDControlInterface,
+ setControl controller.StatefulSetControlInterface,
+ svcControl controller.ServiceControlInterface,
+ certControl controller.CertControlInterface,
+ typedControl controller.TypedControlInterface,
+ setLister v1.StatefulSetLister,
+ svcLister corelisters.ServiceLister,
+ podLister corelisters.PodLister,
+ nodeLister corelisters.NodeLister,
+ autoFailover bool,
+ tiflashFailover Failover,
+ tiflashScaler Scaler,
+ tiflashUpgrader Upgrader) manager.Manager {
+ kvmm := tiflashMemberManager{
+ pdControl: pdControl,
+ podLister: podLister,
+ nodeLister: nodeLister,
+ setControl: setControl,
+ svcControl: svcControl,
+ certControl: certControl,
+ typedControl: typedControl,
+ setLister: setLister,
+ svcLister: svcLister,
+ autoFailover: autoFailover,
+ tiflashFailover: tiflashFailover,
+ tiflashScaler: tiflashScaler,
+ tiflashUpgrader: tiflashUpgrader,
+ }
+ kvmm.tiflashStatefulSetIsUpgradingFn = tiflashStatefulSetIsUpgrading
+ return &kvmm
+}
+
+// Sync fulfills the manager.Manager interface
+func (tfmm *tiflashMemberManager) Sync(tc *v1alpha1.TidbCluster) error {
+ if tc.Spec.TiFlash == nil {
+ return nil
+ }
+
+ ns := tc.GetNamespace()
+ tcName := tc.GetName()
+
+ if !tc.PDIsAvailable() {
+ return controller.RequeueErrorf("TidbCluster: [%s/%s], waiting for PD cluster running", ns, tcName)
+ }
+
+ // Sync TiFlash Headless Service
+ if err := tfmm.syncHeadlessService(tc); err != nil {
+ return err
+ }
+
+ return tfmm.syncStatefulSet(tc)
+}
+
+func (tfmm *tiflashMemberManager) syncHeadlessService(tc *v1alpha1.TidbCluster) error {
+ if tc.Spec.Paused {
+ klog.V(4).Infof("tiflash cluster %s/%s is paused, skip syncing for tiflash service", tc.GetNamespace(), tc.GetName())
+ return nil
+ }
+
+ ns := tc.GetNamespace()
+ tcName := tc.GetName()
+
+ newSvc := getNewHeadlessService(tc)
+ oldSvcTmp, err := tfmm.svcLister.Services(ns).Get(controller.TiFlashPeerMemberName(tcName))
+ if errors.IsNotFound(err) {
+ err = controller.SetServiceLastAppliedConfigAnnotation(newSvc)
+ if err != nil {
+ return err
+ }
+ return tfmm.svcControl.CreateService(tc, newSvc)
+ }
+ if err != nil {
+ return err
+ }
+
+ oldSvc := oldSvcTmp.DeepCopy()
+
+ equal, err := controller.ServiceEqual(newSvc, oldSvc)
+ if err != nil {
+ return err
+ }
+ if !equal {
+ svc := *oldSvc
+ svc.Spec = newSvc.Spec
+ err = controller.SetServiceLastAppliedConfigAnnotation(newSvc)
+ if err != nil {
+ return err
+ }
+ _, err = tfmm.svcControl.UpdateService(tc, &svc)
+ return err
+ }
+
+ return nil
+}
+
+func (tfmm *tiflashMemberManager) syncStatefulSet(tc *v1alpha1.TidbCluster) error {
+ ns := tc.GetNamespace()
+ tcName := tc.GetName()
+
+ oldSetTmp, err := tfmm.setLister.StatefulSets(ns).Get(controller.TiFlashMemberName(tcName))
+ if err != nil && !errors.IsNotFound(err) {
+ return err
+ }
+ setNotExist := errors.IsNotFound(err)
+
+ oldSet := oldSetTmp.DeepCopy()
+
+ if err := tfmm.syncTidbClusterStatus(tc, oldSet); err != nil {
+ return err
+ }
+
+ if tc.Spec.Paused {
+ klog.V(4).Infof("tiflash cluster %s/%s is paused, skip syncing for tiflash statefulset", tc.GetNamespace(), tc.GetName())
+ return nil
+ }
+
+ cm, err := tfmm.syncConfigMap(tc, oldSet)
+ if err != nil {
+ return err
+ }
+
+ newSet, err := getNewStatefulSet(tc, cm)
+ if err != nil {
+ return err
+ }
+ if setNotExist {
+ err = SetStatefulSetLastAppliedConfigAnnotation(newSet)
+ if err != nil {
+ return err
+ }
+ err = tfmm.setControl.CreateStatefulSet(tc, newSet)
+ if err != nil {
+ return err
+ }
+ tc.Status.TiFlash.StatefulSet = &apps.StatefulSetStatus{}
+ return nil
+ }
+
+ if _, err := tfmm.setStoreLabelsForTiFlash(tc); err != nil {
+ return err
+ }
+
+ if !templateEqual(newSet, oldSet) || tc.Status.TiFlash.Phase == v1alpha1.UpgradePhase {
+ if err := tfmm.tiflashUpgrader.Upgrade(tc, oldSet, newSet); err != nil {
+ return err
+ }
+ }
+
+ if err := tfmm.tiflashScaler.Scale(tc, oldSet, newSet); err != nil {
+ return err
+ }
+
+ if tfmm.autoFailover && tc.Spec.TiFlash.MaxFailoverCount != nil {
+ if tc.TiFlashAllPodsStarted() && !tc.TiFlashAllStoresReady() {
+ if err := tfmm.tiflashFailover.Failover(tc); err != nil {
+ return err
+ }
+ }
+ }
+
+ return updateStatefulSet(tfmm.setControl, tc, newSet, oldSet)
+}
+
+func (tfmm *tiflashMemberManager) syncConfigMap(tc *v1alpha1.TidbCluster, set *apps.StatefulSet) (*corev1.ConfigMap, error) {
+ newCm, err := getTiFlashConfigMap(tc)
+ if err != nil {
+ return nil, err
+ }
+ if set != nil && tc.BaseTiFlashSpec().ConfigUpdateStrategy() == v1alpha1.ConfigUpdateStrategyInPlace {
+ inUseName := FindConfigMapVolume(&set.Spec.Template.Spec, func(name string) bool {
+ return strings.HasPrefix(name, controller.TiFlashMemberName(tc.Name))
+ })
+ if inUseName != "" {
+ newCm.Name = inUseName
+ }
+ }
+
+ return tfmm.typedControl.CreateOrUpdateConfigMap(tc, newCm)
+}
+
+func getNewHeadlessService(tc *v1alpha1.TidbCluster) *corev1.Service {
+ ns := tc.Namespace
+ tcName := tc.Name
+ instanceName := tc.GetInstanceName()
+ svcName := controller.TiFlashPeerMemberName(tcName)
+ svcLabel := label.New().Instance(instanceName).TiFlash().Labels()
+
+ svc := corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: svcName,
+ Namespace: ns,
+ Labels: svcLabel,
+ OwnerReferences: []metav1.OwnerReference{controller.GetOwnerRef(tc)},
+ },
+ Spec: corev1.ServiceSpec{
+ ClusterIP: "None",
+ Ports: []corev1.ServicePort{
+ {
+ Name: "tiflash",
+ Port: 3930,
+ TargetPort: intstr.FromInt(int(3930)),
+ Protocol: corev1.ProtocolTCP,
+ },
+ {
+ Name: "proxy",
+ Port: 20170,
+ TargetPort: intstr.FromInt(int(20170)),
+ Protocol: corev1.ProtocolTCP,
+ },
+ },
+ Selector: svcLabel,
+ PublishNotReadyAddresses: true,
+ },
+ }
+ return &svc
+}
+
+func getNewStatefulSet(tc *v1alpha1.TidbCluster, cm *corev1.ConfigMap) (*apps.StatefulSet, error) {
+ ns := tc.GetNamespace()
+ tcName := tc.GetName()
+ baseTiFlashSpec := tc.BaseTiFlashSpec()
+ spec := tc.Spec.TiFlash
+
+ tiflashConfigMap := controller.MemberConfigMapName(tc, v1alpha1.TiFlashMemberType)
+ if cm != nil {
+ tiflashConfigMap = cm.Name
+ }
+
+ // This should not happen as we have validaton for this field
+ if len(spec.StorageClaims) < 1 {
+ return nil, fmt.Errorf("storageClaims should be configured at least one item for tiflash, tidbcluster %s/%s", tc.Namespace, tc.Name)
+ }
+ pvcs, err := flashVolumeClaimTemplate(tc.Spec.TiFlash.StorageClaims)
+ if err != nil {
+ return nil, fmt.Errorf("cannot parse storage request for tiflash.StorageClaims, tidbcluster %s/%s, error: %v", tc.Namespace, tc.Name, err)
+ }
+ annMount, annVolume := annotationsMountVolume()
+ volMounts := []corev1.VolumeMount{
+ annMount,
+ }
+ for k := range spec.StorageClaims {
+ volMounts = append(volMounts, corev1.VolumeMount{
+ Name: fmt.Sprintf("data%d", k), MountPath: fmt.Sprintf("/data%d", k)})
+ }
+
+ // TiFlash does not support TLS yet
+ // if tc.IsTLSClusterEnabled() {
+ // volMounts = append(volMounts, corev1.VolumeMount{
+ // Name: "tiflash-tls", ReadOnly: true, MountPath: "/var/lib/tiflash-tls",
+ // })
+ // }
+
+ vols := []corev1.Volume{
+ annVolume,
+ {Name: "config", VolumeSource: corev1.VolumeSource{
+ ConfigMap: &corev1.ConfigMapVolumeSource{
+ LocalObjectReference: corev1.LocalObjectReference{
+ Name: tiflashConfigMap,
+ },
+ }},
+ },
+ }
+
+ // if tc.IsTLSClusterEnabled() {
+ // vols = append(vols, corev1.Volume{
+ // Name: "tiflash-tls", VolumeSource: corev1.VolumeSource{
+ // Secret: &corev1.SecretVolumeSource{
+ // SecretName: util.ClusterTLSSecretName(tc.Name, label.TiFlashLabelVal),
+ // },
+ // },
+ // })
+ // }
+
+ sysctls := "sysctl -w"
+ var initContainers []corev1.Container
+ if baseTiFlashSpec.Annotations() != nil {
+ init, ok := baseTiFlashSpec.Annotations()[label.AnnSysctlInit]
+ if ok && (init == label.AnnSysctlInitVal) {
+ if baseTiFlashSpec.PodSecurityContext() != nil && len(baseTiFlashSpec.PodSecurityContext().Sysctls) > 0 {
+ for _, sysctl := range baseTiFlashSpec.PodSecurityContext().Sysctls {
+ sysctls = sysctls + fmt.Sprintf(" %s=%s", sysctl.Name, sysctl.Value)
+ }
+ privileged := true
+ initContainers = append(initContainers, corev1.Container{
+ Name: "init",
+ Image: tc.HelperImage(),
+ Command: []string{
+ "sh",
+ "-c",
+ sysctls,
+ },
+ SecurityContext: &corev1.SecurityContext{
+ Privileged: &privileged,
+ },
+ })
+ }
+ }
+ }
+ // Init container is only used for the case where allowed-unsafe-sysctls
+ // cannot be enabled for kubelet, so clean the sysctl in statefulset
+ // SecurityContext if init container is enabled
+ podSecurityContext := baseTiFlashSpec.PodSecurityContext().DeepCopy()
+ if len(initContainers) > 0 {
+ podSecurityContext.Sysctls = []corev1.Sysctl{}
+ }
+
+ // Append init container for config files initialization
+ initVolMounts := []corev1.VolumeMount{
+ {Name: "data0", MountPath: "/data0"},
+ {Name: "config", ReadOnly: true, MountPath: "/etc/tiflash"},
+ }
+ initEnv := []corev1.EnvVar{
+ {
+ Name: "POD_NAME",
+ ValueFrom: &corev1.EnvVarSource{
+ FieldRef: &corev1.ObjectFieldSelector{
+ FieldPath: "metadata.name",
+ },
+ },
+ },
+ }
+ initContainers = append(initContainers, corev1.Container{
+ Name: "init",
+ Image: tc.HelperImage(),
+ Command: []string{
+ "sh",
+ "-c",
+ "set -ex;ordinal=`echo ${POD_NAME} | awk -F- '{print $NF}'`;sed s/POD_NUM/${ordinal}/g /etc/tiflash/config_templ.toml > /data0/config.toml;sed s/POD_NUM/${ordinal}/g /etc/tiflash/proxy_templ.toml > /data0/proxy.toml",
+ },
+ Env: initEnv,
+ VolumeMounts: initVolMounts,
+ })
+
+ tiflashLabel := labelTiFlash(tc)
+ setName := controller.TiFlashMemberName(tcName)
+ podAnnotations := CombineAnnotations(controller.AnnProm(8234), baseTiFlashSpec.Annotations())
+ stsAnnotations := getStsAnnotations(tc, label.TiFlashLabelVal)
+ capacity := controller.TiKVCapacity(tc.Spec.TiFlash.Limits)
+ headlessSvcName := controller.TiFlashPeerMemberName(tcName)
+
+ env := []corev1.EnvVar{
+ {
+ Name: "NAMESPACE",
+ ValueFrom: &corev1.EnvVarSource{
+ FieldRef: &corev1.ObjectFieldSelector{
+ FieldPath: "metadata.namespace",
+ },
+ },
+ },
+ {
+ Name: "CLUSTER_NAME",
+ Value: tcName,
+ },
+ {
+ Name: "HEADLESS_SERVICE_NAME",
+ Value: headlessSvcName,
+ },
+ {
+ Name: "CAPACITY",
+ Value: capacity,
+ },
+ {
+ Name: "TZ",
+ Value: tc.Spec.Timezone,
+ },
+ }
+ tiflashContainer := corev1.Container{
+ Name: v1alpha1.TiFlashMemberType.String(),
+ Image: tc.TiFlashImage(),
+ ImagePullPolicy: baseTiFlashSpec.ImagePullPolicy(),
+ Command: []string{"/bin/sh", "-c", "/tiflash/tiflash server --config-file /data0/config.toml"},
+ SecurityContext: &corev1.SecurityContext{
+ Privileged: tc.TiFlashContainerPrivilege(),
+ },
+ Ports: []corev1.ContainerPort{
+ {
+ Name: "tiflash",
+ ContainerPort: int32(3930),
+ Protocol: corev1.ProtocolTCP,
+ },
+ {
+ Name: "proxy",
+ ContainerPort: int32(20170),
+ Protocol: corev1.ProtocolTCP,
+ },
+ {
+ Name: "tcp",
+ ContainerPort: int32(9000),
+ Protocol: corev1.ProtocolTCP,
+ },
+ {
+ Name: "http",
+ ContainerPort: int32(8123),
+ Protocol: corev1.ProtocolTCP,
+ },
+ {
+ Name: "internal",
+ ContainerPort: int32(9009),
+ Protocol: corev1.ProtocolTCP,
+ },
+ {
+ Name: "metrics",
+ ContainerPort: int32(8234),
+ Protocol: corev1.ProtocolTCP,
+ },
+ },
+ VolumeMounts: volMounts,
+ Resources: controller.ContainerResource(tc.Spec.TiFlash.ResourceRequirements),
+ }
+ podSpec := baseTiFlashSpec.BuildPodSpec()
+ if baseTiFlashSpec.HostNetwork() {
+ podSpec.DNSPolicy = corev1.DNSClusterFirstWithHostNet
+ env = append(env, corev1.EnvVar{
+ Name: "POD_NAME",
+ ValueFrom: &corev1.EnvVarSource{
+ FieldRef: &corev1.ObjectFieldSelector{
+ FieldPath: "metadata.name",
+ },
+ },
+ })
+ }
+ tiflashContainer.Env = util.AppendEnv(env, baseTiFlashSpec.Env())
+ podSpec.Volumes = vols
+ podSpec.SecurityContext = podSecurityContext
+ podSpec.InitContainers = initContainers
+ podSpec.Containers = []corev1.Container{tiflashContainer}
+ podSpec.Containers = append(podSpec.Containers, buildTiFlashSidecarContainers(tc)...)
+ podSpec.ServiceAccountName = tc.Spec.TiFlash.ServiceAccount
+
+ tiflashset := &apps.StatefulSet{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: setName,
+ Namespace: ns,
+ Labels: tiflashLabel.Labels(),
+ Annotations: stsAnnotations,
+ OwnerReferences: []metav1.OwnerReference{controller.GetOwnerRef(tc)},
+ },
+ Spec: apps.StatefulSetSpec{
+ Replicas: controller.Int32Ptr(tc.TiFlashStsDesiredReplicas()),
+ Selector: tiflashLabel.LabelSelector(),
+ Template: corev1.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Labels: tiflashLabel.Labels(),
+ Annotations: podAnnotations,
+ },
+ Spec: podSpec,
+ },
+ VolumeClaimTemplates: pvcs,
+ ServiceName: headlessSvcName,
+ PodManagementPolicy: apps.ParallelPodManagement,
+ UpdateStrategy: apps.StatefulSetUpdateStrategy{
+ Type: apps.RollingUpdateStatefulSetStrategyType,
+ RollingUpdate: &apps.RollingUpdateStatefulSetStrategy{
+ Partition: controller.Int32Ptr(tc.TiFlashStsDesiredReplicas()),
+ },
+ },
+ },
+ }
+ return tiflashset, nil
+}
+
+func flashVolumeClaimTemplate(storageClaims []v1alpha1.StorageClaim) ([]corev1.PersistentVolumeClaim, error) {
+ var pvcs []corev1.PersistentVolumeClaim
+ for k := range storageClaims {
+ storageRequest, err := controller.ParseStorageRequest(storageClaims[k].Resources.Requests)
+ if err != nil {
+ return nil, err
+ }
+ pvcs = append(pvcs, corev1.PersistentVolumeClaim{
+ ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("data%d", k)},
+ Spec: corev1.PersistentVolumeClaimSpec{
+ AccessModes: []corev1.PersistentVolumeAccessMode{
+ corev1.ReadWriteOnce,
+ },
+ StorageClassName: storageClaims[k].StorageClassName,
+ Resources: storageRequest,
+ },
+ })
+ }
+ return pvcs, nil
+}
+
+func getTiFlashConfigMap(tc *v1alpha1.TidbCluster) (*corev1.ConfigMap, error) {
+ config := tc.Spec.TiFlash.Config.DeepCopy()
+ if config == nil {
+ config = &v1alpha1.TiFlashConfig{}
+ }
+ setTiFlashConfigDefault(config, tc.Name, tc.Namespace)
+
+ // override CA if tls enabled
+ // if tc.IsTLSClusterEnabled() {
+ // if config.Security == nil {
+ // config.Security = &v1alpha1.TiFlashSecurityConfig{}
+ // }
+ // config.Security.CAPath = path.Join(tiflashClusterCertPath, tlsSecretRootCAKey)
+ // config.Security.CertPath = path.Join(tiflashClusterCertPath, corev1.TLSCertKey)
+ // config.Security.KeyPath = path.Join(tiflashClusterCertPath, corev1.TLSPrivateKeyKey)
+ // }
+
+ configText, err := MarshalTOML(config.CommonConfig)
+ if err != nil {
+ return nil, err
+ }
+ proxyText, err := MarshalTOML(config.ProxyConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ instanceName := tc.GetInstanceName()
+ tiflashLabel := label.New().Instance(instanceName).TiFlash().Labels()
+ cm := &corev1.ConfigMap{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: controller.TiFlashMemberName(tc.Name),
+ Namespace: tc.Namespace,
+ Labels: tiflashLabel,
+ OwnerReferences: []metav1.OwnerReference{controller.GetOwnerRef(tc)},
+ },
+ Data: map[string]string{
+ "config_templ.toml": string(configText),
+ "proxy_templ.toml": string(proxyText),
+ },
+ }
+
+ if tc.BaseTiFlashSpec().ConfigUpdateStrategy() == v1alpha1.ConfigUpdateStrategyRollingUpdate {
+ if err := AddConfigMapDigestSuffix(cm); err != nil {
+ return nil, err
+ }
+ }
+
+ return cm, nil
+}
+
+func labelTiFlash(tc *v1alpha1.TidbCluster) label.Label {
+ instanceName := tc.GetInstanceName()
+ return label.New().Instance(instanceName).TiFlash()
+}
+
+func (tfmm *tiflashMemberManager) syncTidbClusterStatus(tc *v1alpha1.TidbCluster, set *apps.StatefulSet) error {
+ if set == nil {
+ // skip if not created yet
+ return nil
+ }
+ tc.Status.TiFlash.StatefulSet = &set.Status
+ upgrading, err := tfmm.tiflashStatefulSetIsUpgradingFn(tfmm.podLister, tfmm.pdControl, set, tc)
+ if err != nil {
+ return err
+ }
+ if upgrading && tc.Status.PD.Phase != v1alpha1.UpgradePhase {
+ tc.Status.TiFlash.Phase = v1alpha1.UpgradePhase
+ } else {
+ tc.Status.TiFlash.Phase = v1alpha1.NormalPhase
+ }
+
+ previousStores := tc.Status.TiFlash.Stores
+ stores := map[string]v1alpha1.TiKVStore{}
+ tombstoneStores := map[string]v1alpha1.TiKVStore{}
+
+ pdCli := controller.GetPDClient(tfmm.pdControl, tc)
+ // This only returns Up/Down/Offline stores
+ storesInfo, err := pdCli.GetStores()
+ if err != nil {
+ tc.Status.TiFlash.Synced = false
+ return err
+ }
+
+ pattern, err := regexp.Compile(fmt.Sprintf(tiflashStoreLimitPattern, tc.Name, tc.Name, tc.Namespace))
+ if err != nil {
+ return err
+ }
+ for _, store := range storesInfo.Stores {
+ // In theory, the external tiflash can join the cluster, and the operator would only manage the internal tiflash.
+ // So we check the store owner to make sure it.
+ if store.Store != nil && !pattern.Match([]byte(store.Store.Address)) {
+ continue
+ }
+ status := tfmm.getTiFlashStore(store)
+ if status == nil {
+ continue
+ }
+ // avoid LastHeartbeatTime be overwrite by zero time when pd lost LastHeartbeatTime
+ if status.LastHeartbeatTime.IsZero() {
+ if oldStatus, ok := previousStores[status.ID]; ok {
+ klog.V(4).Infof("the pod:%s's store LastHeartbeatTime is zero,so will keep in %v", status.PodName, oldStatus.LastHeartbeatTime)
+ status.LastHeartbeatTime = oldStatus.LastHeartbeatTime
+ }
+ }
+
+ oldStore, exist := previousStores[status.ID]
+
+ status.LastTransitionTime = metav1.Now()
+ if exist && status.State == oldStore.State {
+ status.LastTransitionTime = oldStore.LastTransitionTime
+ }
+
+ stores[status.ID] = *status
+ }
+
+ //this returns all tombstone stores
+ tombstoneStoresInfo, err := pdCli.GetTombStoneStores()
+ if err != nil {
+ tc.Status.TiFlash.Synced = false
+ return err
+ }
+ for _, store := range tombstoneStoresInfo.Stores {
+ status := tfmm.getTiFlashStore(store)
+ if status == nil {
+ continue
+ }
+ tombstoneStores[status.ID] = *status
+ }
+
+ tc.Status.TiFlash.Synced = true
+ tc.Status.TiFlash.Stores = stores
+ tc.Status.TiFlash.TombstoneStores = tombstoneStores
+ tc.Status.TiFlash.Image = ""
+ c := filterContainer(set, "tiflash")
+ if c != nil {
+ tc.Status.TiFlash.Image = c.Image
+ }
+ return nil
+}
+
+func (tfmm *tiflashMemberManager) getTiFlashStore(store *pdapi.StoreInfo) *v1alpha1.TiKVStore {
+ if store.Store == nil || store.Status == nil {
+ return nil
+ }
+ storeID := fmt.Sprintf("%d", store.Store.GetId())
+ ip := strings.Split(store.Store.GetAddress(), ":")[0]
+ podName := strings.Split(ip, ".")[0]
+
+ return &v1alpha1.TiKVStore{
+ ID: storeID,
+ PodName: podName,
+ IP: ip,
+ LeaderCount: int32(store.Status.LeaderCount),
+ State: store.Store.StateName,
+ LastHeartbeatTime: metav1.Time{Time: store.Status.LastHeartbeatTS},
+ }
+}
+
+func (tfmm *tiflashMemberManager) setStoreLabelsForTiFlash(tc *v1alpha1.TidbCluster) (int, error) {
+ ns := tc.GetNamespace()
+ // for unit test
+ setCount := 0
+
+ pdCli := controller.GetPDClient(tfmm.pdControl, tc)
+ storesInfo, err := pdCli.GetStores()
+ if err != nil {
+ return setCount, err
+ }
+
+ config, err := pdCli.GetConfig()
+ if err != nil {
+ return setCount, err
+ }
+
+ locationLabels := []string(config.Replication.LocationLabels)
+ if locationLabels == nil {
+ return setCount, nil
+ }
+
+ pattern, err := regexp.Compile(fmt.Sprintf(tiflashStoreLimitPattern, tc.Name, tc.Name, tc.Namespace))
+ if err != nil {
+ return -1, err
+ }
+ for _, store := range storesInfo.Stores {
+ // In theory, the external tiflash can join the cluster, and the operator would only manage the internal tiflash.
+ // So we check the store owner to make sure it.
+ if store.Store != nil && !pattern.Match([]byte(store.Store.Address)) {
+ continue
+ }
+ status := tfmm.getTiFlashStore(store)
+ if status == nil {
+ continue
+ }
+ podName := status.PodName
+
+ pod, err := tfmm.podLister.Pods(ns).Get(podName)
+ if err != nil {
+ return setCount, err
+ }
+
+ nodeName := pod.Spec.NodeName
+ ls, err := tfmm.getNodeLabels(nodeName, locationLabels)
+ if err != nil || len(ls) == 0 {
+ klog.Warningf("node: [%s] has no node labels, skipping set store labels for Pod: [%s/%s]", nodeName, ns, podName)
+ continue
+ }
+
+ if !tfmm.storeLabelsEqualNodeLabels(store.Store.Labels, ls) {
+ set, err := pdCli.SetStoreLabels(store.Store.Id, ls)
+ if err != nil {
+ klog.Warningf("failed to set pod: [%s/%s]'s store labels: %v", ns, podName, ls)
+ continue
+ }
+ if set {
+ setCount++
+ klog.Infof("pod: [%s/%s] set labels: %v successfully", ns, podName, ls)
+ }
+ }
+ }
+
+ return setCount, nil
+}
+
+func (tfmm *tiflashMemberManager) getNodeLabels(nodeName string, storeLabels []string) (map[string]string, error) {
+ node, err := tfmm.nodeLister.Get(nodeName)
+ if err != nil {
+ return nil, err
+ }
+ labels := map[string]string{}
+ ls := node.GetLabels()
+ for _, storeLabel := range storeLabels {
+ if value, found := ls[storeLabel]; found {
+ labels[storeLabel] = value
+ continue
+ }
+
+ // TODO after pd supports storeLabel containing slash character, these codes should be deleted
+ if storeLabel == "host" {
+ if host, found := ls[corev1.LabelHostname]; found {
+ labels[storeLabel] = host
+ }
+ }
+
+ }
+ return labels, nil
+}
+
+// storeLabelsEqualNodeLabels compares store labels with node labels
+// for historic reasons, PD stores TiFlash labels as []*StoreLabel which is a key-value pair slice
+func (tfmm *tiflashMemberManager) storeLabelsEqualNodeLabels(storeLabels []*metapb.StoreLabel, nodeLabels map[string]string) bool {
+ ls := map[string]string{}
+ for _, label := range storeLabels {
+ key := label.GetKey()
+ if _, ok := nodeLabels[key]; ok {
+ val := label.GetValue()
+ ls[key] = val
+ }
+ }
+ return reflect.DeepEqual(ls, nodeLabels)
+}
+
+func tiflashStatefulSetIsUpgrading(podLister corelisters.PodLister, pdControl pdapi.PDControlInterface, set *apps.StatefulSet, tc *v1alpha1.TidbCluster) (bool, error) {
+ if statefulSetIsUpgrading(set) {
+ return true, nil
+ }
+ instanceName := tc.GetInstanceName()
+ selector, err := label.New().Instance(instanceName).TiFlash().Selector()
+ if err != nil {
+ return false, err
+ }
+ tiflashPods, err := podLister.Pods(tc.GetNamespace()).List(selector)
+ if err != nil {
+ return false, err
+ }
+ for _, pod := range tiflashPods {
+ revisionHash, exist := pod.Labels[apps.ControllerRevisionHashLabelKey]
+ if !exist {
+ return false, nil
+ }
+ if revisionHash != tc.Status.TiFlash.StatefulSet.UpdateRevision {
+ return true, nil
+ }
+ }
+
+ return false, nil
+}
+
+type FakeTiFlashMemberManager struct {
+ err error
+}
+
+func NewFakeTiFlashMemberManager() *FakeTiFlashMemberManager {
+ return &FakeTiFlashMemberManager{}
+}
+
+func (ftmm *FakeTiFlashMemberManager) SetSyncError(err error) {
+ ftmm.err = err
+}
+
+func (ftmm *FakeTiFlashMemberManager) Sync(tc *v1alpha1.TidbCluster) error {
+ if ftmm.err != nil {
+ return ftmm.err
+ }
+ if len(tc.Status.TiFlash.Stores) != 0 {
+ // simulate status update
+ tc.Status.ClusterID = string(uuid.NewUUID())
+ }
+ return nil
+}
diff --git a/pkg/manager/member/tiflash_scaler.go b/pkg/manager/member/tiflash_scaler.go
new file mode 100644
index 0000000000..0b3dff0024
--- /dev/null
+++ b/pkg/manager/member/tiflash_scaler.go
@@ -0,0 +1,93 @@
+// Copyright 2020 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 member
+
+import (
+ "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/pdapi"
+ apps "k8s.io/api/apps/v1"
+ corelisters "k8s.io/client-go/listers/core/v1"
+)
+
+type tiflashScaler struct {
+ generalScaler
+ podLister corelisters.PodLister
+}
+
+// NewTiFlashScaler returns a tiflash Scaler
+func NewTiFlashScaler(pdControl pdapi.PDControlInterface,
+ pvcLister corelisters.PersistentVolumeClaimLister,
+ pvcControl controller.PVCControlInterface,
+ podLister corelisters.PodLister) Scaler {
+ return &tiflashScaler{generalScaler{pdControl, pvcLister, pvcControl}, podLister}
+}
+
+// TODO: Finish the scaling logic
+func (tfs *tiflashScaler) Scale(tc *v1alpha1.TidbCluster, oldSet *apps.StatefulSet, newSet *apps.StatefulSet) error {
+ scaling, _, _, _ := scaleOne(oldSet, newSet)
+ if scaling > 0 {
+ return tfs.ScaleOut(tc, oldSet, newSet)
+ } else if scaling < 0 {
+ return tfs.ScaleIn(tc, oldSet, newSet)
+ }
+ // we only sync auto scaler annotations when we are finishing syncing scaling
+ return tfs.SyncAutoScalerAnn(tc, oldSet)
+}
+
+func (tfs *tiflashScaler) ScaleOut(tc *v1alpha1.TidbCluster, oldSet *apps.StatefulSet, newSet *apps.StatefulSet) error {
+
+ return nil
+}
+
+func (tfs *tiflashScaler) ScaleIn(tc *v1alpha1.TidbCluster, oldSet *apps.StatefulSet, newSet *apps.StatefulSet) error {
+
+ return nil
+}
+
+// SyncAutoScalerAnn reclaims the auto-scaling-out slots if the target pods no longer exist
+func (tfs *tiflashScaler) SyncAutoScalerAnn(tc *v1alpha1.TidbCluster, actual *apps.StatefulSet) error {
+
+ return nil
+}
+
+type fakeTiFlashScaler struct{}
+
+// NewFakeTiFlashScaler returns a fake tiflash Scaler
+func NewFakeTiFlashScaler() Scaler {
+ return &fakeTiFlashScaler{}
+}
+
+func (fsd *fakeTiFlashScaler) Scale(tc *v1alpha1.TidbCluster, oldSet *apps.StatefulSet, newSet *apps.StatefulSet) error {
+ if *newSet.Spec.Replicas > *oldSet.Spec.Replicas {
+ return fsd.ScaleOut(tc, oldSet, newSet)
+ } else if *newSet.Spec.Replicas < *oldSet.Spec.Replicas {
+ return fsd.ScaleIn(tc, oldSet, newSet)
+ }
+ return nil
+}
+
+func (fsd *fakeTiFlashScaler) ScaleOut(_ *v1alpha1.TidbCluster, oldSet *apps.StatefulSet, newSet *apps.StatefulSet) error {
+ setReplicasAndDeleteSlots(newSet, *oldSet.Spec.Replicas+1, nil)
+ return nil
+}
+
+func (fsd *fakeTiFlashScaler) ScaleIn(_ *v1alpha1.TidbCluster, oldSet *apps.StatefulSet, newSet *apps.StatefulSet) error {
+ setReplicasAndDeleteSlots(newSet, *oldSet.Spec.Replicas-1, nil)
+ return nil
+}
+
+func (fsd *fakeTiFlashScaler) SyncAutoScalerAnn(tc *v1alpha1.TidbCluster, actual *apps.StatefulSet) error {
+ return nil
+}
diff --git a/pkg/manager/member/tiflash_upgrader.go b/pkg/manager/member/tiflash_upgrader.go
new file mode 100644
index 0000000000..1d3ff7c824
--- /dev/null
+++ b/pkg/manager/member/tiflash_upgrader.go
@@ -0,0 +1,57 @@
+// Copyright 2020 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 member
+
+import (
+ "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ "github.com/pingcap/tidb-operator/pkg/pdapi"
+ apps "k8s.io/api/apps/v1"
+ corelisters "k8s.io/client-go/listers/core/v1"
+)
+
+type tiflashUpgrader struct {
+ pdControl pdapi.PDControlInterface
+ podControl controller.PodControlInterface
+ podLister corelisters.PodLister
+}
+
+// NewTiFlashUpgrader returns a tiflash Upgrader
+func NewTiFlashUpgrader(pdControl pdapi.PDControlInterface,
+ podControl controller.PodControlInterface,
+ podLister corelisters.PodLister) Upgrader {
+ return &tiflashUpgrader{
+ pdControl: pdControl,
+ podControl: podControl,
+ podLister: podLister,
+ }
+}
+
+// TODO: Finish the upgrade logic
+func (tku *tiflashUpgrader) Upgrade(tc *v1alpha1.TidbCluster, oldSet *apps.StatefulSet, newSet *apps.StatefulSet) error {
+
+ return nil
+}
+
+type fakeTiFlashUpgrader struct{}
+
+// NewFakeTiFlashUpgrader returns a fake tiflash upgrader
+func NewFakeTiFlashUpgrader() Upgrader {
+ return &fakeTiFlashUpgrader{}
+}
+
+func (tku *fakeTiFlashUpgrader) Upgrade(tc *v1alpha1.TidbCluster, _ *apps.StatefulSet, _ *apps.StatefulSet) error {
+ tc.Status.TiFlash.Phase = v1alpha1.UpgradePhase
+ return nil
+}
diff --git a/pkg/manager/member/tiflash_util.go b/pkg/manager/member/tiflash_util.go
new file mode 100644
index 0000000000..7e32223d92
--- /dev/null
+++ b/pkg/manager/member/tiflash_util.go
@@ -0,0 +1,419 @@
+// Copyright 2020 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 member
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
+ "github.com/pingcap/tidb-operator/pkg/controller"
+ corev1 "k8s.io/api/core/v1"
+)
+
+const (
+ defaultClusterLog = "/data0/logs/flash_cluster_manager.log"
+ defaultProxyLog = "/data0/logs/proxy.log"
+ defaultErrorLog = "/data0/logs/error.log"
+ defaultServerLog = "/data0/logs/server.log"
+)
+
+func buildTiFlashSidecarContainers(tc *v1alpha1.TidbCluster) []corev1.Container {
+ spec := tc.Spec.TiFlash
+ config := spec.Config.DeepCopy()
+ image := tc.HelperImage()
+ pullPolicy := tc.HelperImagePullPolicy()
+ var containers []corev1.Container
+ var resource corev1.ResourceRequirements
+ if spec.LogTailer != nil {
+ resource = controller.ContainerResource(spec.LogTailer.ResourceRequirements)
+ }
+ if config == nil {
+ config = &v1alpha1.TiFlashConfig{}
+ }
+ setTiFlashLogConfigDefault(config)
+ containers = append(containers, buildSidecarContainer("serverlog", config.CommonConfig.FlashLogger.ServerLog, image, pullPolicy, resource))
+ containers = append(containers, buildSidecarContainer("errorlog", config.CommonConfig.FlashLogger.ErrorLog, image, pullPolicy, resource))
+ containers = append(containers, buildSidecarContainer("proxylog", config.CommonConfig.Flash.FlashProxy.LogFile, image, pullPolicy, resource))
+ containers = append(containers, buildSidecarContainer("clusterlog", config.CommonConfig.Flash.FlashCluster.ClusterLog, image, pullPolicy, resource))
+ return containers
+}
+
+func buildSidecarContainer(name, path, image string,
+ pullPolicy corev1.PullPolicy,
+ resource corev1.ResourceRequirements) corev1.Container {
+ splitPath := strings.Split(path, string(os.PathSeparator))
+ // The log path should be at least /dir/base.log
+ if len(splitPath) >= 3 {
+ serverLogVolumeName := splitPath[1]
+ serverLogMountDir := "/" + serverLogVolumeName
+ return corev1.Container{
+ Name: name,
+ Image: image,
+ ImagePullPolicy: pullPolicy,
+ Resources: resource,
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: serverLogVolumeName, MountPath: serverLogMountDir},
+ },
+ Command: []string{
+ "sh",
+ "-c",
+ fmt.Sprintf("touch %s; tail -n0 -F %s;", path, path),
+ },
+ }
+ }
+ return corev1.Container{
+ Name: name,
+ Image: image,
+ ImagePullPolicy: pullPolicy,
+ Resources: resource,
+ Command: []string{
+ "sh",
+ "-c",
+ fmt.Sprintf("touch %s; tail -n0 -F %s;", path, path),
+ },
+ }
+}
+
+func setTiFlashLogConfigDefault(config *v1alpha1.TiFlashConfig) {
+ if config.CommonConfig == nil {
+ config.CommonConfig = &v1alpha1.CommonConfig{}
+ }
+ if config.CommonConfig.Flash == nil {
+ config.CommonConfig.Flash = &v1alpha1.Flash{}
+ }
+ if config.CommonConfig.Flash.FlashCluster == nil {
+ config.CommonConfig.Flash.FlashCluster = &v1alpha1.FlashCluster{}
+ }
+ if config.CommonConfig.Flash.FlashCluster.ClusterLog == "" {
+ config.CommonConfig.Flash.FlashCluster.ClusterLog = defaultClusterLog
+ }
+ if config.CommonConfig.Flash.FlashProxy == nil {
+ config.CommonConfig.Flash.FlashProxy = &v1alpha1.FlashProxy{}
+ }
+ if config.CommonConfig.Flash.FlashProxy.LogFile == "" {
+ config.CommonConfig.Flash.FlashProxy.LogFile = defaultProxyLog
+ }
+
+ if config.CommonConfig.FlashLogger == nil {
+ config.CommonConfig.FlashLogger = &v1alpha1.FlashLogger{}
+ }
+ if config.CommonConfig.FlashLogger.ErrorLog == "" {
+ config.CommonConfig.FlashLogger.ErrorLog = defaultErrorLog
+ }
+ if config.CommonConfig.FlashLogger.ServerLog == "" {
+ config.CommonConfig.FlashLogger.ServerLog = defaultServerLog
+ }
+}
+
+// setTiFlashConfigDefault sets default configs for TiFlash
+func setTiFlashConfigDefault(config *v1alpha1.TiFlashConfig, clusterName, ns string) {
+ if config.CommonConfig == nil {
+ config.CommonConfig = &v1alpha1.CommonConfig{}
+ }
+ setTiFlashCommonConfigDefault(config.CommonConfig, clusterName, ns)
+ if config.ProxyConfig == nil {
+ config.ProxyConfig = &v1alpha1.ProxyConfig{}
+ }
+ setTiFlashProxyConfigDefault(config.ProxyConfig, clusterName, ns)
+}
+
+func setTiFlashProxyConfigDefault(config *v1alpha1.ProxyConfig, clusterName, ns string) {
+ if config.LogLevel == "" {
+ config.LogLevel = "info"
+ }
+ if config.Server == nil {
+ config.Server = &v1alpha1.FlashServerConfig{}
+ }
+ if config.Server.EngineAddr == "" {
+ config.Server.EngineAddr = fmt.Sprintf("%s-POD_NUM.%s.%s.svc:3930", controller.TiFlashMemberName(clusterName), controller.TiFlashPeerMemberName(clusterName), ns)
+ }
+}
+func setTiFlashCommonConfigDefault(config *v1alpha1.CommonConfig, clusterName, ns string) {
+ if config.TmpPath == "" {
+ config.TmpPath = "/data0/tmp"
+ }
+ if config.DisplayName == "" {
+ config.DisplayName = "TiFlash"
+ }
+ if config.DefaultProfile == "" {
+ config.DefaultProfile = "default"
+ }
+ if config.Path == "" {
+ config.Path = "/data0/db"
+ }
+ if config.PathRealtimeMode == nil {
+ b := false
+ config.PathRealtimeMode = &b
+ }
+ if config.MarkCacheSize == nil {
+ var m int64 = 5368709120
+ config.MarkCacheSize = &m
+ }
+ if config.MinmaxIndexCacheSize == nil {
+ var m int64 = 5368709120
+ config.MinmaxIndexCacheSize = &m
+ }
+ if config.ListenHost == "" {
+ config.ListenHost = "0.0.0.0"
+ }
+ if config.TCPPort == nil {
+ var p int32 = 9000
+ config.TCPPort = &p
+ }
+ if config.HTTPPort == nil {
+ var p int32 = 8123
+ config.HTTPPort = &p
+ }
+ if config.InternalServerHTTPPort == nil {
+ var p int32 = 9009
+ config.InternalServerHTTPPort = &p
+ }
+ if config.Flash == nil {
+ config.Flash = &v1alpha1.Flash{}
+ }
+ setTiFlashFlashConfigDefault(config.Flash, clusterName, ns)
+ if config.FlashLogger == nil {
+ config.FlashLogger = &v1alpha1.FlashLogger{}
+ }
+ setTiFlashLoggerConfigDefault(config.FlashLogger)
+ if config.FlashApplication == nil {
+ config.FlashApplication = &v1alpha1.FlashApplication{}
+ }
+ setTiFlashApplicationConfigDefault(config.FlashApplication)
+ if config.FlashRaft == nil {
+ config.FlashRaft = &v1alpha1.FlashRaft{}
+ }
+ setTiFlashRaftConfigDefault(config.FlashRaft, clusterName, ns)
+ if config.FlashStatus == nil {
+ config.FlashStatus = &v1alpha1.FlashStatus{}
+ }
+ setTiFlashStatusConfigDefault(config.FlashStatus)
+ if config.FlashQuota == nil {
+ config.FlashQuota = &v1alpha1.FlashQuota{}
+ }
+ setTiFlashQuotasConfigDefault(config.FlashQuota)
+ if config.FlashUser == nil {
+ config.FlashUser = &v1alpha1.FlashUser{}
+ }
+ setTiFlashUsersConfigDefault(config.FlashUser)
+ if config.FlashProfile == nil {
+ config.FlashProfile = &v1alpha1.FlashProfile{}
+ }
+ setTiFlashProfilesConfigDefault(config.FlashProfile)
+}
+
+func setTiFlashFlashConfigDefault(config *v1alpha1.Flash, clusterName, ns string) {
+ if config.TiDBStatusAddr == "" {
+ config.TiDBStatusAddr = fmt.Sprintf("%s.%s.svc:10080", controller.TiDBMemberName(clusterName), ns)
+ }
+ if config.ServiceAddr == "" {
+ config.ServiceAddr = fmt.Sprintf("%s-POD_NUM.%s.%s.svc:3930", controller.TiFlashMemberName(clusterName), controller.TiFlashPeerMemberName(clusterName), ns)
+ }
+ if config.OverlapThreshold == nil {
+ o := 0.6
+ config.OverlapThreshold = &o
+ }
+ if config.CompactLogMinPeriod == nil {
+ var o int32 = 200
+ config.CompactLogMinPeriod = &o
+ }
+ if config.FlashCluster == nil {
+ config.FlashCluster = &v1alpha1.FlashCluster{}
+ }
+ setTiFlashFlashClusterConfigDefault(config.FlashCluster)
+ if config.FlashProxy == nil {
+ config.FlashProxy = &v1alpha1.FlashProxy{}
+ }
+ setTiFlashFlashProxyConfigDefault(config.FlashProxy, clusterName, ns)
+}
+
+func setTiFlashFlashProxyConfigDefault(config *v1alpha1.FlashProxy, clusterName, ns string) {
+ if config.Addr == "" {
+ config.Addr = "0.0.0.0:20170"
+ }
+ if config.AdvertiseAddr == "" {
+ config.AdvertiseAddr = fmt.Sprintf("%s-POD_NUM.%s.%s.svc:20170", controller.TiFlashMemberName(clusterName), controller.TiFlashPeerMemberName(clusterName), ns)
+ }
+ if config.DataDir == "" {
+ config.DataDir = "/data0/proxy"
+ }
+ if config.Config == "" {
+ config.Config = "/data0/proxy.toml"
+ }
+ if config.LogFile == "" {
+ config.LogFile = defaultProxyLog
+ }
+}
+
+func setTiFlashFlashClusterConfigDefault(config *v1alpha1.FlashCluster) {
+ if config.ClusterManagerPath == "" {
+ config.ClusterManagerPath = "/tiflash/flash_cluster_manager"
+ }
+ if config.ClusterLog == "" {
+ config.ClusterLog = defaultClusterLog
+ }
+ if config.RefreshInterval == nil {
+ var r int32 = 20
+ config.RefreshInterval = &r
+ }
+ if config.UpdateRuleInterval == nil {
+ var r int32 = 10
+ config.UpdateRuleInterval = &r
+ }
+ if config.MasterTTL == nil {
+ var r int32 = 60
+ config.MasterTTL = &r
+ }
+}
+
+func setTiFlashLoggerConfigDefault(config *v1alpha1.FlashLogger) {
+ if config.ErrorLog == "" {
+ config.ErrorLog = defaultErrorLog
+ }
+ if config.Size == "" {
+ config.Size = "100M"
+ }
+ if config.ServerLog == "" {
+ config.ServerLog = defaultServerLog
+ }
+ if config.Level == "" {
+ config.Level = "information"
+ }
+ if config.Count == nil {
+ var c int32 = 10
+ config.Count = &c
+ }
+}
+
+func setTiFlashApplicationConfigDefault(config *v1alpha1.FlashApplication) {
+ if config.RunAsDaemon == nil {
+ r := true
+ config.RunAsDaemon = &r
+ }
+}
+
+func setTiFlashRaftConfigDefault(config *v1alpha1.FlashRaft, clusterName, ns string) {
+ if config.PDAddr == "" {
+ config.PDAddr = fmt.Sprintf("%s.%s.svc:2379", controller.PDMemberName(clusterName), ns)
+ }
+ if config.KVStorePath == "" {
+ config.KVStorePath = "/data0/kvstore"
+ }
+ if config.StorageEngine == "" {
+ config.StorageEngine = "dt"
+ }
+}
+
+func setTiFlashStatusConfigDefault(config *v1alpha1.FlashStatus) {
+ if config.MetricsPort == nil {
+ var d int32 = 8234
+ config.MetricsPort = &d
+ }
+}
+
+func setTiFlashQuotasConfigDefault(config *v1alpha1.FlashQuota) {
+ if config.Default == nil {
+ config.Default = &v1alpha1.Quota{}
+ }
+ if config.Default.Interval == nil {
+ config.Default.Interval = &v1alpha1.Interval{}
+ }
+ if config.Default.Interval.Duration == nil {
+ var d int32 = 3600
+ config.Default.Interval.Duration = &d
+ }
+ if config.Default.Interval.Queries == nil {
+ var d int32 = 0
+ config.Default.Interval.Queries = &d
+ }
+ if config.Default.Interval.Errors == nil {
+ var d int32 = 0
+ config.Default.Interval.Errors = &d
+ }
+ if config.Default.Interval.ResultRows == nil {
+ var d int32 = 0
+ config.Default.Interval.ResultRows = &d
+ }
+ if config.Default.Interval.ReadRows == nil {
+ var d int32 = 0
+ config.Default.Interval.ReadRows = &d
+ }
+ if config.Default.Interval.ExecutionTime == nil {
+ var d int32 = 0
+ config.Default.Interval.ExecutionTime = &d
+ }
+}
+
+func setTiFlashNetworksConfigDefault(config *v1alpha1.Networks) {
+ if config.IP == "" {
+ config.IP = "::/0"
+ }
+}
+
+func setTiFlashUsersConfigDefault(config *v1alpha1.FlashUser) {
+ if config.Readonly == nil {
+ config.Readonly = &v1alpha1.User{}
+ }
+ if config.Readonly.Profile == "" {
+ config.Readonly.Profile = "readonly"
+ }
+ if config.Readonly.Quota == "" {
+ config.Readonly.Quota = "default"
+ }
+ if config.Readonly.Networks == nil {
+ config.Readonly.Networks = &v1alpha1.Networks{}
+ }
+ setTiFlashNetworksConfigDefault(config.Readonly.Networks)
+
+ if config.Default == nil {
+ config.Default = &v1alpha1.User{}
+ }
+ if config.Default.Profile == "" {
+ config.Default.Profile = "default"
+ }
+ if config.Default.Quota == "" {
+ config.Default.Quota = "default"
+ }
+ if config.Default.Networks == nil {
+ config.Default.Networks = &v1alpha1.Networks{}
+ }
+ setTiFlashNetworksConfigDefault(config.Default.Networks)
+}
+
+func setTiFlashProfilesConfigDefault(config *v1alpha1.FlashProfile) {
+ if config.Readonly == nil {
+ config.Readonly = &v1alpha1.Profile{}
+ }
+ if config.Readonly.Readonly == nil {
+ var r int32 = 1
+ config.Readonly.Readonly = &r
+ }
+ if config.Default == nil {
+ config.Default = &v1alpha1.Profile{}
+ }
+ if config.Default.MaxMemoryUsage == nil {
+ var m int64 = 10000000000
+ config.Default.MaxMemoryUsage = &m
+ }
+ if config.Default.UseUncompressedCache == nil {
+ var u int32 = 0
+ config.Default.UseUncompressedCache = &u
+ }
+ if config.Default.LoadBalancing == nil {
+ l := "random"
+ config.Default.LoadBalancing = &l
+ }
+}
diff --git a/pkg/manager/member/tiflash_util_test.go b/pkg/manager/member/tiflash_util_test.go
new file mode 100644
index 0000000000..032259be96
--- /dev/null
+++ b/pkg/manager/member/tiflash_util_test.go
@@ -0,0 +1,594 @@
+// Copyright 2020 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 member
+
+import (
+ "testing"
+
+ . "github.com/onsi/gomega"
+ "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/resource"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/utils/pointer"
+)
+
+var (
+ defaultTiFlashConfig = v1alpha1.TiFlashConfig{
+ CommonConfig: &v1alpha1.CommonConfig{
+ FlashApplication: &v1alpha1.FlashApplication{
+ RunAsDaemon: pointer.BoolPtr(true),
+ },
+ DefaultProfile: "default",
+ DisplayName: "TiFlash",
+ Flash: &v1alpha1.Flash{
+ CompactLogMinPeriod: pointer.Int32Ptr(200),
+ FlashCluster: &v1alpha1.FlashCluster{
+ ClusterManagerPath: "/tiflash/flash_cluster_manager",
+ ClusterLog: "/data0/logs/flash_cluster_manager.log",
+ MasterTTL: pointer.Int32Ptr(60),
+ RefreshInterval: pointer.Int32Ptr(20),
+ UpdateRuleInterval: pointer.Int32Ptr(10),
+ },
+ OverlapThreshold: pointer.Float64Ptr(0.6),
+ FlashProxy: &v1alpha1.FlashProxy{
+ Addr: "0.0.0.0:20170",
+ AdvertiseAddr: "test-tiflash-POD_NUM.test-tiflash-peer.test.svc:20170",
+ Config: "/data0/proxy.toml",
+ DataDir: "/data0/proxy",
+ LogFile: "/data0/logs/proxy.log",
+ },
+ ServiceAddr: "test-tiflash-POD_NUM.test-tiflash-peer.test.svc:3930",
+ TiDBStatusAddr: "test-tidb.test.svc:10080",
+ },
+ HTTPPort: pointer.Int32Ptr(8123),
+ InternalServerHTTPPort: pointer.Int32Ptr(9009),
+ ListenHost: "0.0.0.0",
+ FlashLogger: &v1alpha1.FlashLogger{
+ Count: pointer.Int32Ptr(10),
+ ErrorLog: "/data0/logs/error.log",
+ Level: "information",
+ ServerLog: "/data0/logs/server.log",
+ Size: "100M",
+ },
+ MarkCacheSize: pointer.Int64Ptr(5368709120),
+ MinmaxIndexCacheSize: pointer.Int64Ptr(5368709120),
+ Path: "/data0/db",
+ PathRealtimeMode: pointer.BoolPtr(false),
+ FlashProfile: &v1alpha1.FlashProfile{
+ Default: &v1alpha1.Profile{
+ LoadBalancing: pointer.StringPtr("random"),
+ MaxMemoryUsage: pointer.Int64Ptr(10000000000),
+ UseUncompressedCache: pointer.Int32Ptr(0),
+ },
+ Readonly: &v1alpha1.Profile{
+ Readonly: pointer.Int32Ptr(1),
+ },
+ },
+ FlashQuota: &v1alpha1.FlashQuota{
+ Default: &v1alpha1.Quota{
+ Interval: &v1alpha1.Interval{
+ Duration: pointer.Int32Ptr(3600),
+ Errors: pointer.Int32Ptr(0),
+ ExecutionTime: pointer.Int32Ptr(0),
+ Queries: pointer.Int32Ptr(0),
+ ReadRows: pointer.Int32Ptr(0),
+ ResultRows: pointer.Int32Ptr(0),
+ },
+ },
+ },
+ FlashRaft: &v1alpha1.FlashRaft{
+ KVStorePath: "/data0/kvstore",
+ PDAddr: "test-pd.test.svc:2379",
+ StorageEngine: "dt",
+ },
+ FlashStatus: &v1alpha1.FlashStatus{
+ MetricsPort: pointer.Int32Ptr(8234),
+ },
+ TCPPort: pointer.Int32Ptr(9000),
+ TmpPath: "/data0/tmp",
+ FlashUser: &v1alpha1.FlashUser{
+ Default: &v1alpha1.User{
+ Networks: &v1alpha1.Networks{
+ IP: "::/0",
+ },
+ Profile: "default",
+ Quota: "default",
+ },
+ Readonly: &v1alpha1.User{
+ Networks: &v1alpha1.Networks{
+ IP: "::/0",
+ },
+ Profile: "readonly",
+ Quota: "default",
+ },
+ },
+ },
+ ProxyConfig: &v1alpha1.ProxyConfig{
+ LogLevel: "info",
+ Server: &v1alpha1.FlashServerConfig{
+ EngineAddr: "test-tiflash-POD_NUM.test-tiflash-peer.test.svc:3930",
+ },
+ },
+ }
+ customTiFlashConfig = v1alpha1.TiFlashConfig{
+ CommonConfig: &v1alpha1.CommonConfig{
+ FlashApplication: &v1alpha1.FlashApplication{
+ RunAsDaemon: pointer.BoolPtr(false),
+ },
+ DefaultProfile: "defaul",
+ DisplayName: "TiFlah",
+ Flash: &v1alpha1.Flash{
+ CompactLogMinPeriod: pointer.Int32Ptr(100),
+ FlashCluster: &v1alpha1.FlashCluster{
+ ClusterManagerPath: "/flash_cluster_manager",
+ ClusterLog: "/data1/logs/flash_cluster_manager.log",
+ MasterTTL: pointer.Int32Ptr(50),
+ RefreshInterval: pointer.Int32Ptr(21),
+ UpdateRuleInterval: pointer.Int32Ptr(11),
+ },
+ OverlapThreshold: pointer.Float64Ptr(0.7),
+ FlashProxy: &v1alpha1.FlashProxy{
+ Addr: "0.0.0.0:20171",
+ AdvertiseAddr: "test-tiflash-POD_NUM.test-tiflash-peer.test.svc:20171",
+ Config: "/data0/proxy1.toml",
+ DataDir: "/data0/proxy1",
+ LogFile: "/data0/logs/proxy1.log",
+ },
+ ServiceAddr: "test-tiflash-POD_NUM.test-tiflash-peer.test.svc:3931",
+ TiDBStatusAddr: "test-tidb.test.svc:10081",
+ },
+ HTTPPort: pointer.Int32Ptr(8121),
+ InternalServerHTTPPort: pointer.Int32Ptr(9001),
+ ListenHost: "0.0.0.1",
+ FlashLogger: &v1alpha1.FlashLogger{
+ Count: pointer.Int32Ptr(11),
+ ErrorLog: "/data1/logs/error1.log",
+ Level: "information1",
+ ServerLog: "/data0/logs/server1.log",
+ Size: "101M",
+ },
+ MarkCacheSize: pointer.Int64Ptr(5368709121),
+ MinmaxIndexCacheSize: pointer.Int64Ptr(5368709121),
+ Path: "/data1/db",
+ PathRealtimeMode: pointer.BoolPtr(true),
+ FlashProfile: &v1alpha1.FlashProfile{
+ Default: &v1alpha1.Profile{
+ LoadBalancing: pointer.StringPtr("random1"),
+ MaxMemoryUsage: pointer.Int64Ptr(10000000001),
+ UseUncompressedCache: pointer.Int32Ptr(1),
+ },
+ Readonly: &v1alpha1.Profile{
+ Readonly: pointer.Int32Ptr(0),
+ },
+ },
+ FlashQuota: &v1alpha1.FlashQuota{
+ Default: &v1alpha1.Quota{
+ Interval: &v1alpha1.Interval{
+ Duration: pointer.Int32Ptr(3601),
+ Errors: pointer.Int32Ptr(1),
+ ExecutionTime: pointer.Int32Ptr(1),
+ Queries: pointer.Int32Ptr(1),
+ ReadRows: pointer.Int32Ptr(1),
+ ResultRows: pointer.Int32Ptr(1),
+ },
+ },
+ },
+ FlashRaft: &v1alpha1.FlashRaft{
+ KVStorePath: "/data1/kvstore",
+ PDAddr: "test-pd.test.svc:2379",
+ StorageEngine: "dt",
+ },
+ FlashStatus: &v1alpha1.FlashStatus{
+ MetricsPort: pointer.Int32Ptr(8235),
+ },
+ TCPPort: pointer.Int32Ptr(9001),
+ TmpPath: "/data1/tmp",
+ FlashUser: &v1alpha1.FlashUser{
+ Default: &v1alpha1.User{
+ Networks: &v1alpha1.Networks{
+ IP: "::/1",
+ },
+ Profile: "default1",
+ Quota: "default1",
+ },
+ Readonly: &v1alpha1.User{
+ Networks: &v1alpha1.Networks{
+ IP: "::/1",
+ },
+ Profile: "readonly1",
+ Quota: "default1",
+ },
+ },
+ },
+ ProxyConfig: &v1alpha1.ProxyConfig{
+ LogLevel: "info1",
+ Server: &v1alpha1.FlashServerConfig{
+ EngineAddr: "test-tiflash-POD_NUM.test-tiflash-peer.test.svc:3930",
+ },
+ },
+ }
+ defaultTiFlashLogConfig = v1alpha1.TiFlashConfig{
+ CommonConfig: &v1alpha1.CommonConfig{
+ Flash: &v1alpha1.Flash{
+ FlashCluster: &v1alpha1.FlashCluster{
+ ClusterLog: "/data0/logs/flash_cluster_manager.log",
+ },
+ FlashProxy: &v1alpha1.FlashProxy{
+ LogFile: "/data0/logs/proxy.log",
+ },
+ },
+ FlashLogger: &v1alpha1.FlashLogger{
+ ErrorLog: "/data0/logs/error.log",
+ ServerLog: "/data0/logs/server.log",
+ },
+ },
+ }
+ customTiFlashLogConfig = v1alpha1.TiFlashConfig{
+ CommonConfig: &v1alpha1.CommonConfig{
+ Flash: &v1alpha1.Flash{
+ FlashCluster: &v1alpha1.FlashCluster{
+ ClusterLog: "/data1/logs/flash_cluster_manager.log",
+ },
+ FlashProxy: &v1alpha1.FlashProxy{
+ LogFile: "/data1/logs/proxy.log",
+ },
+ },
+ FlashLogger: &v1alpha1.FlashLogger{
+ ErrorLog: "/data1/logs/error.log",
+ ServerLog: "/data1/logs/server.log",
+ },
+ },
+ }
+ defaultSideCarContainers = []corev1.Container{
+ {
+ Name: "serverlog",
+ Image: "busybox:1.26.2",
+ ImagePullPolicy: "",
+ Resources: corev1.ResourceRequirements{},
+ Command: []string{
+ "sh",
+ "-c",
+ "touch /data0/logs/server.log; tail -n0 -F /data0/logs/server.log;",
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: "data0", MountPath: "/data0"},
+ },
+ },
+ {
+ Name: "errorlog",
+ Image: "busybox:1.26.2",
+ ImagePullPolicy: "",
+ Resources: corev1.ResourceRequirements{},
+ Command: []string{
+ "sh",
+ "-c",
+ "touch /data0/logs/error.log; tail -n0 -F /data0/logs/error.log;",
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: "data0", MountPath: "/data0"},
+ },
+ },
+ {
+ Name: "proxylog",
+ Image: "busybox:1.26.2",
+ ImagePullPolicy: "",
+ Resources: corev1.ResourceRequirements{},
+ Command: []string{
+ "sh",
+ "-c",
+ "touch /data0/logs/proxy.log; tail -n0 -F /data0/logs/proxy.log;",
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: "data0", MountPath: "/data0"},
+ },
+ },
+ {
+ Name: "clusterlog",
+ Image: "busybox:1.26.2",
+ ImagePullPolicy: "",
+ Resources: corev1.ResourceRequirements{},
+ Command: []string{
+ "sh",
+ "-c",
+ "touch /data0/logs/flash_cluster_manager.log; tail -n0 -F /data0/logs/flash_cluster_manager.log;",
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: "data0", MountPath: "/data0"},
+ },
+ },
+ }
+ customSideCarContainers = []corev1.Container{
+ {
+ Name: "serverlog",
+ Image: "busybox:1.26.2",
+ ImagePullPolicy: "",
+ Resources: corev1.ResourceRequirements{},
+ Command: []string{
+ "sh",
+ "-c",
+ "touch /data1/logs/server.log; tail -n0 -F /data1/logs/server.log;",
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: "data1", MountPath: "/data1"},
+ },
+ },
+ {
+ Name: "errorlog",
+ Image: "busybox:1.26.2",
+ ImagePullPolicy: "",
+ Resources: corev1.ResourceRequirements{},
+ Command: []string{
+ "sh",
+ "-c",
+ "touch /data1/logs/error.log; tail -n0 -F /data1/logs/error.log;",
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: "data1", MountPath: "/data1"},
+ },
+ },
+ {
+ Name: "proxylog",
+ Image: "busybox:1.26.2",
+ ImagePullPolicy: "",
+ Resources: corev1.ResourceRequirements{},
+ Command: []string{
+ "sh",
+ "-c",
+ "touch /data1/logs/proxy.log; tail -n0 -F /data1/logs/proxy.log;",
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: "data1", MountPath: "/data1"},
+ },
+ },
+ {
+ Name: "clusterlog",
+ Image: "busybox:1.26.2",
+ ImagePullPolicy: "",
+ Resources: corev1.ResourceRequirements{},
+ Command: []string{
+ "sh",
+ "-c",
+ "touch /data1/logs/flash_cluster_manager.log; tail -n0 -F /data1/logs/flash_cluster_manager.log;",
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: "data1", MountPath: "/data1"},
+ },
+ },
+ }
+ customResourceSideCarContainers = []corev1.Container{
+ {
+ Name: "serverlog",
+ Image: "busybox:1.26.2",
+ ImagePullPolicy: "",
+ Resources: corev1.ResourceRequirements{
+ Requests: corev1.ResourceList{
+ corev1.ResourceCPU: resource.MustParse("1"),
+ corev1.ResourceMemory: resource.MustParse("2Gi"),
+ },
+ },
+ Command: []string{
+ "sh",
+ "-c",
+ "touch /data1/logs/server.log; tail -n0 -F /data1/logs/server.log;",
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: "data1", MountPath: "/data1"},
+ },
+ },
+ {
+ Name: "errorlog",
+ Image: "busybox:1.26.2",
+ ImagePullPolicy: "",
+ Resources: corev1.ResourceRequirements{
+ Requests: corev1.ResourceList{
+ corev1.ResourceCPU: resource.MustParse("1"),
+ corev1.ResourceMemory: resource.MustParse("2Gi"),
+ },
+ },
+ Command: []string{
+ "sh",
+ "-c",
+ "touch /data1/logs/error.log; tail -n0 -F /data1/logs/error.log;",
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: "data1", MountPath: "/data1"},
+ },
+ },
+ {
+ Name: "proxylog",
+ Image: "busybox:1.26.2",
+ ImagePullPolicy: "",
+ Resources: corev1.ResourceRequirements{
+ Requests: corev1.ResourceList{
+ corev1.ResourceCPU: resource.MustParse("1"),
+ corev1.ResourceMemory: resource.MustParse("2Gi"),
+ },
+ },
+ Command: []string{
+ "sh",
+ "-c",
+ "touch /data1/logs/proxy.log; tail -n0 -F /data1/logs/proxy.log;",
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: "data1", MountPath: "/data1"},
+ },
+ },
+ {
+ Name: "clusterlog",
+ Image: "busybox:1.26.2",
+ ImagePullPolicy: "",
+ Resources: corev1.ResourceRequirements{
+ Requests: corev1.ResourceList{
+ corev1.ResourceCPU: resource.MustParse("1"),
+ corev1.ResourceMemory: resource.MustParse("2Gi"),
+ },
+ },
+ Command: []string{
+ "sh",
+ "-c",
+ "touch /data1/logs/flash_cluster_manager.log; tail -n0 -F /data1/logs/flash_cluster_manager.log;",
+ },
+ VolumeMounts: []corev1.VolumeMount{
+ {Name: "data1", MountPath: "/data1"},
+ },
+ },
+ }
+)
+
+func newTidbCluster() *v1alpha1.TidbCluster {
+ return &v1alpha1.TidbCluster{
+ TypeMeta: metav1.TypeMeta{
+ Kind: "TidbCluster",
+ APIVersion: "pingcap.com/v1alpha1",
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-pd",
+ Namespace: corev1.NamespaceDefault,
+ UID: types.UID("test"),
+ },
+ Spec: v1alpha1.TidbClusterSpec{
+ PD: v1alpha1.PDSpec{
+ ComponentSpec: v1alpha1.ComponentSpec{
+ Image: "pd-test-image",
+ },
+ },
+ TiKV: v1alpha1.TiKVSpec{
+ ComponentSpec: v1alpha1.ComponentSpec{
+ Image: "tikv-test-image",
+ },
+ },
+ TiDB: v1alpha1.TiDBSpec{
+ ComponentSpec: v1alpha1.ComponentSpec{
+ Image: "tidb-test-image",
+ },
+ },
+ TiFlash: &v1alpha1.TiFlashSpec{},
+ },
+ }
+}
+
+func TestBuildTiFlashSidecarContainers(t *testing.T) {
+ g := NewGomegaWithT(t)
+
+ type testcase struct {
+ name string
+ flashConfig *v1alpha1.TiFlashConfig
+ expect []corev1.Container
+ resource bool
+ }
+
+ tests := []*testcase{
+ {
+ name: "nil config",
+ flashConfig: nil,
+ expect: defaultSideCarContainers,
+ },
+ {
+ name: "empty config",
+ flashConfig: &v1alpha1.TiFlashConfig{},
+ expect: defaultSideCarContainers,
+ },
+ {
+ name: "custom config",
+ flashConfig: &customTiFlashLogConfig,
+ expect: customSideCarContainers,
+ },
+ {
+ name: "custom resource config",
+ flashConfig: &customTiFlashLogConfig,
+ expect: customResourceSideCarContainers,
+ resource: true,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ tc := newTidbCluster()
+ tc.Spec.TiFlash.Config = test.flashConfig
+ if test.resource {
+ tc.Spec.TiFlash.LogTailer = &v1alpha1.LogTailerSpec{}
+ tc.Spec.TiFlash.LogTailer.ResourceRequirements = corev1.ResourceRequirements{
+ Requests: corev1.ResourceList{
+ corev1.ResourceCPU: resource.MustParse("1"),
+ corev1.ResourceMemory: resource.MustParse("2Gi"),
+ corev1.ResourceStorage: resource.MustParse("100Gi"),
+ },
+ }
+ }
+ cs := buildTiFlashSidecarContainers(tc)
+ g.Expect(cs).To(Equal(test.expect))
+ })
+ }
+}
+func TestSetTiFlashConfigDefault(t *testing.T) {
+ g := NewGomegaWithT(t)
+
+ type testcase struct {
+ name string
+ config v1alpha1.TiFlashConfig
+ expect v1alpha1.TiFlashConfig
+ }
+
+ tests := []*testcase{
+ {
+ name: "nil config",
+ config: v1alpha1.TiFlashConfig{},
+ expect: defaultTiFlashConfig,
+ },
+ {
+ name: "custom config",
+ config: customTiFlashConfig,
+ expect: customTiFlashConfig,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ setTiFlashConfigDefault(&test.config, "test", "test")
+ g.Expect(test.config).To(Equal(test.expect))
+ })
+ }
+}
+
+func TestSetTiFlashLogConfigDefault(t *testing.T) {
+ g := NewGomegaWithT(t)
+
+ type testcase struct {
+ name string
+ config v1alpha1.TiFlashConfig
+ expect v1alpha1.TiFlashConfig
+ }
+
+ tests := []*testcase{
+ {
+ name: "nil config",
+ config: v1alpha1.TiFlashConfig{},
+ expect: defaultTiFlashLogConfig,
+ },
+ {
+ name: "custom config",
+ config: customTiFlashLogConfig,
+ expect: customTiFlashLogConfig,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ setTiFlashLogConfigDefault(&test.config)
+ g.Expect(test.config).To(Equal(test.expect))
+ })
+ }
+}