diff --git a/docs/api-references/docs.html b/docs/api-references/docs.html
index 31780b56a00..a89f209fa5f 100644
--- a/docs/api-references/docs.html
+++ b/docs/api-references/docs.html
@@ -2606,6 +2606,34 @@
ComponentSpec
Optional: Defaults to cluster-level setting
+
+
+env
+
+
+[]Kubernetes core/v1.EnvVar
+
+
+ |
+
+(Optional)
+ List of environment variables to set in the container, like
+v1.Container.Env.
+Note that following env names cannot be used and may be overrided by
+tidb-operator built envs.
+- NAMESPACE
+- TZ
+- SERVICE_NAME
+- PEER_SERVICE_NAME
+- HEADLESS_SERVICE_NAME
+- SET_NAME
+- HOSTNAME
+- CLUSTER_NAME
+- POD_NAME
+- BINLOG_ENABLED
+- SLOW_LOG_FILE
+ |
+
ConfigUpdateStrategy
diff --git a/manifests/crd.yaml b/manifests/crd.yaml
index fc7f0e89930..f30ef00f709 100644
--- a/manifests/crd.yaml
+++ b/manifests/crd.yaml
@@ -1700,6 +1700,102 @@ spec:
cluster-level updateStrategy if present Optional: Defaults to
cluster-level setting'
type: string
+ env:
+ description: List of environment variables to set in the container,
+ like v1.Container.Env. Note that following env names cannot be
+ used and may be overrided by tidb-operator built envs. - NAMESPACE
+ - TZ - SERVICE_NAME - PEER_SERVICE_NAME - HEADLESS_SERVICE_NAME
+ - SET_NAME - HOSTNAME - CLUSTER_NAME - POD_NAME - BINLOG_ENABLED
+ - SLOW_LOG_FILE
+ items:
+ description: EnvVar represents an environment variable present
+ in a Container.
+ properties:
+ name:
+ description: Name of the environment variable. Must be a C_IDENTIFIER.
+ type: string
+ value:
+ description: 'Variable references $(VAR_NAME) are expanded
+ using the previous defined environment variables in the
+ container and any service environment variables. If a variable
+ cannot be resolved, the reference in the input string will
+ be unchanged. The $(VAR_NAME) syntax can be escaped with
+ a double $$, ie: $$(VAR_NAME). Escaped references will never
+ be expanded, regardless of whether the variable exists or
+ not. Defaults to "".'
+ type: string
+ valueFrom:
+ description: EnvVarSource represents a source for the value
+ of an EnvVar.
+ properties:
+ configMapKeyRef:
+ description: Selects a key from a ConfigMap.
+ properties:
+ key:
+ description: The key to select.
+ type: string
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ optional:
+ description: Specify whether the ConfigMap or its
+ key must be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ fieldRef:
+ description: ObjectFieldSelector selects an APIVersioned
+ field of an object.
+ properties:
+ apiVersion:
+ description: Version of the schema the FieldPath is
+ written in terms of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to select in the specified
+ API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ resourceFieldRef:
+ description: ResourceFieldSelector represents container
+ resources (cpu, memory) and their output format
+ properties:
+ containerName:
+ description: 'Container name: required for volumes,
+ optional for env vars'
+ type: string
+ divisor: {}
+ resource:
+ description: 'Required: resource to select'
+ type: string
+ required:
+ - resource
+ type: object
+ secretKeyRef:
+ description: SecretKeySelector selects a key of a Secret.
+ properties:
+ key:
+ description: The key of the secret to select from. Must
+ be a valid secret key.
+ type: string
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ optional:
+ description: Specify whether the Secret or its key
+ must be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ type: object
+ required:
+ - name
+ type: object
+ type: array
hostNetwork:
description: 'Whether Hostnetwork of the component is enabled. Override
the cluster-level setting if present Optional: Defaults to cluster-level
@@ -2558,6 +2654,102 @@ spec:
cluster-level updateStrategy if present Optional: Defaults to
cluster-level setting'
type: string
+ env:
+ description: List of environment variables to set in the container,
+ like v1.Container.Env. Note that following env names cannot be
+ used and may be overrided by tidb-operator built envs. - NAMESPACE
+ - TZ - SERVICE_NAME - PEER_SERVICE_NAME - HEADLESS_SERVICE_NAME
+ - SET_NAME - HOSTNAME - CLUSTER_NAME - POD_NAME - BINLOG_ENABLED
+ - SLOW_LOG_FILE
+ items:
+ description: EnvVar represents an environment variable present
+ in a Container.
+ properties:
+ name:
+ description: Name of the environment variable. Must be a C_IDENTIFIER.
+ type: string
+ value:
+ description: 'Variable references $(VAR_NAME) are expanded
+ using the previous defined environment variables in the
+ container and any service environment variables. If a variable
+ cannot be resolved, the reference in the input string will
+ be unchanged. The $(VAR_NAME) syntax can be escaped with
+ a double $$, ie: $$(VAR_NAME). Escaped references will never
+ be expanded, regardless of whether the variable exists or
+ not. Defaults to "".'
+ type: string
+ valueFrom:
+ description: EnvVarSource represents a source for the value
+ of an EnvVar.
+ properties:
+ configMapKeyRef:
+ description: Selects a key from a ConfigMap.
+ properties:
+ key:
+ description: The key to select.
+ type: string
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ optional:
+ description: Specify whether the ConfigMap or its
+ key must be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ fieldRef:
+ description: ObjectFieldSelector selects an APIVersioned
+ field of an object.
+ properties:
+ apiVersion:
+ description: Version of the schema the FieldPath is
+ written in terms of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to select in the specified
+ API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ resourceFieldRef:
+ description: ResourceFieldSelector represents container
+ resources (cpu, memory) and their output format
+ properties:
+ containerName:
+ description: 'Container name: required for volumes,
+ optional for env vars'
+ type: string
+ divisor: {}
+ resource:
+ description: 'Required: resource to select'
+ type: string
+ required:
+ - resource
+ type: object
+ secretKeyRef:
+ description: SecretKeySelector selects a key of a Secret.
+ properties:
+ key:
+ description: The key of the secret to select from. Must
+ be a valid secret key.
+ type: string
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ optional:
+ description: Specify whether the Secret or its key
+ must be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ type: object
+ required:
+ - name
+ type: object
+ type: array
hostNetwork:
description: 'Whether Hostnetwork of the component is enabled. Override
the cluster-level setting if present Optional: Defaults to cluster-level
@@ -3887,6 +4079,102 @@ spec:
description: 'Add --advertise-address to TiDB''s startup parameters
Optional: Defaults to false'
type: boolean
+ env:
+ description: List of environment variables to set in the container,
+ like v1.Container.Env. Note that following env names cannot be
+ used and may be overrided by tidb-operator built envs. - NAMESPACE
+ - TZ - SERVICE_NAME - PEER_SERVICE_NAME - HEADLESS_SERVICE_NAME
+ - SET_NAME - HOSTNAME - CLUSTER_NAME - POD_NAME - BINLOG_ENABLED
+ - SLOW_LOG_FILE
+ items:
+ description: EnvVar represents an environment variable present
+ in a Container.
+ properties:
+ name:
+ description: Name of the environment variable. Must be a C_IDENTIFIER.
+ type: string
+ value:
+ description: 'Variable references $(VAR_NAME) are expanded
+ using the previous defined environment variables in the
+ container and any service environment variables. If a variable
+ cannot be resolved, the reference in the input string will
+ be unchanged. The $(VAR_NAME) syntax can be escaped with
+ a double $$, ie: $$(VAR_NAME). Escaped references will never
+ be expanded, regardless of whether the variable exists or
+ not. Defaults to "".'
+ type: string
+ valueFrom:
+ description: EnvVarSource represents a source for the value
+ of an EnvVar.
+ properties:
+ configMapKeyRef:
+ description: Selects a key from a ConfigMap.
+ properties:
+ key:
+ description: The key to select.
+ type: string
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ optional:
+ description: Specify whether the ConfigMap or its
+ key must be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ fieldRef:
+ description: ObjectFieldSelector selects an APIVersioned
+ field of an object.
+ properties:
+ apiVersion:
+ description: Version of the schema the FieldPath is
+ written in terms of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to select in the specified
+ API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ resourceFieldRef:
+ description: ResourceFieldSelector represents container
+ resources (cpu, memory) and their output format
+ properties:
+ containerName:
+ description: 'Container name: required for volumes,
+ optional for env vars'
+ type: string
+ divisor: {}
+ resource:
+ description: 'Required: resource to select'
+ type: string
+ required:
+ - resource
+ type: object
+ secretKeyRef:
+ description: SecretKeySelector selects a key of a Secret.
+ properties:
+ key:
+ description: The key of the secret to select from. Must
+ be a valid secret key.
+ type: string
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ optional:
+ description: Specify whether the Secret or its key
+ must be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ type: object
+ required:
+ - name
+ type: object
+ type: array
hostNetwork:
description: 'Whether Hostnetwork of the component is enabled. Override
the cluster-level setting if present Optional: Defaults to cluster-level
@@ -5938,6 +6226,102 @@ spec:
cluster-level updateStrategy if present Optional: Defaults to
cluster-level setting'
type: string
+ env:
+ description: List of environment variables to set in the container,
+ like v1.Container.Env. Note that following env names cannot be
+ used and may be overrided by tidb-operator built envs. - NAMESPACE
+ - TZ - SERVICE_NAME - PEER_SERVICE_NAME - HEADLESS_SERVICE_NAME
+ - SET_NAME - HOSTNAME - CLUSTER_NAME - POD_NAME - BINLOG_ENABLED
+ - SLOW_LOG_FILE
+ items:
+ description: EnvVar represents an environment variable present
+ in a Container.
+ properties:
+ name:
+ description: Name of the environment variable. Must be a C_IDENTIFIER.
+ type: string
+ value:
+ description: 'Variable references $(VAR_NAME) are expanded
+ using the previous defined environment variables in the
+ container and any service environment variables. If a variable
+ cannot be resolved, the reference in the input string will
+ be unchanged. The $(VAR_NAME) syntax can be escaped with
+ a double $$, ie: $$(VAR_NAME). Escaped references will never
+ be expanded, regardless of whether the variable exists or
+ not. Defaults to "".'
+ type: string
+ valueFrom:
+ description: EnvVarSource represents a source for the value
+ of an EnvVar.
+ properties:
+ configMapKeyRef:
+ description: Selects a key from a ConfigMap.
+ properties:
+ key:
+ description: The key to select.
+ type: string
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ optional:
+ description: Specify whether the ConfigMap or its
+ key must be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ fieldRef:
+ description: ObjectFieldSelector selects an APIVersioned
+ field of an object.
+ properties:
+ apiVersion:
+ description: Version of the schema the FieldPath is
+ written in terms of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to select in the specified
+ API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ resourceFieldRef:
+ description: ResourceFieldSelector represents container
+ resources (cpu, memory) and their output format
+ properties:
+ containerName:
+ description: 'Container name: required for volumes,
+ optional for env vars'
+ type: string
+ divisor: {}
+ resource:
+ description: 'Required: resource to select'
+ type: string
+ required:
+ - resource
+ type: object
+ secretKeyRef:
+ description: SecretKeySelector selects a key of a Secret.
+ properties:
+ key:
+ description: The key of the secret to select from. Must
+ be a valid secret key.
+ type: string
+ name:
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ optional:
+ description: Specify whether the Secret or its key
+ must be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ type: object
+ required:
+ - name
+ type: object
+ type: array
hostNetwork:
description: 'Whether Hostnetwork of the component is enabled. Override
the cluster-level setting if present Optional: Defaults to cluster-level
diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go
index fb1dda6dfae..ecdab749b9d 100644
--- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go
+++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go
@@ -1027,11 +1027,24 @@ func schema_pkg_apis_pingcap_v1alpha1_ComponentSpec(ref common.ReferenceCallback
Format: "",
},
},
+ "env": {
+ SchemaProps: spec.SchemaProps{
+ Description: "List of environment variables to set in the container, like v1.Container.Env. Note that following env names cannot be used and may be overrided by tidb-operator built envs. - NAMESPACE - TZ - SERVICE_NAME - PEER_SERVICE_NAME - HEADLESS_SERVICE_NAME - SET_NAME - HOSTNAME - CLUSTER_NAME - POD_NAME - BINLOG_ENABLED - SLOW_LOG_FILE",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Ref: ref("k8s.io/api/core/v1.EnvVar"),
+ },
+ },
+ },
+ },
+ },
},
},
},
Dependencies: []string{
- "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
+ "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"},
}
}
@@ -2290,6 +2303,19 @@ func schema_pkg_apis_pingcap_v1alpha1_PDSpec(ref common.ReferenceCallback) commo
Format: "",
},
},
+ "env": {
+ SchemaProps: spec.SchemaProps{
+ Description: "List of environment variables to set in the container, like v1.Container.Env. Note that following env names cannot be used and may be overrided by tidb-operator built envs. - NAMESPACE - TZ - SERVICE_NAME - PEER_SERVICE_NAME - HEADLESS_SERVICE_NAME - SET_NAME - HOSTNAME - CLUSTER_NAME - POD_NAME - BINLOG_ENABLED - SLOW_LOG_FILE",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Ref: ref("k8s.io/api/core/v1.EnvVar"),
+ },
+ },
+ },
+ },
+ },
"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/",
@@ -2356,7 +2382,7 @@ func schema_pkg_apis_pingcap_v1alpha1_PDSpec(ref common.ReferenceCallback) commo
},
},
Dependencies: []string{
- "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.PDConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.ServiceSpec", "k8s.io/api/core/v1.Affinity", "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.PDConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.ServiceSpec", "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"},
}
}
@@ -2743,6 +2769,19 @@ func schema_pkg_apis_pingcap_v1alpha1_PumpSpec(ref common.ReferenceCallback) com
Format: "",
},
},
+ "env": {
+ SchemaProps: spec.SchemaProps{
+ Description: "List of environment variables to set in the container, like v1.Container.Env. Note that following env names cannot be used and may be overrided by tidb-operator built envs. - NAMESPACE - TZ - SERVICE_NAME - PEER_SERVICE_NAME - HEADLESS_SERVICE_NAME - SET_NAME - HOSTNAME - CLUSTER_NAME - POD_NAME - BINLOG_ENABLED - SLOW_LOG_FILE",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Ref: ref("k8s.io/api/core/v1.EnvVar"),
+ },
+ },
+ },
+ },
+ },
"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/",
@@ -2811,7 +2850,7 @@ func schema_pkg_apis_pingcap_v1alpha1_PumpSpec(ref common.ReferenceCallback) com
},
},
Dependencies: []string{
- "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/apimachinery/pkg/api/resource.Quantity"},
+ "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"},
}
}
@@ -3780,6 +3819,19 @@ func schema_pkg_apis_pingcap_v1alpha1_TiDBSpec(ref common.ReferenceCallback) com
Format: "",
},
},
+ "env": {
+ SchemaProps: spec.SchemaProps{
+ Description: "List of environment variables to set in the container, like v1.Container.Env. Note that following env names cannot be used and may be overrided by tidb-operator built envs. - NAMESPACE - TZ - SERVICE_NAME - PEER_SERVICE_NAME - HEADLESS_SERVICE_NAME - SET_NAME - HOSTNAME - CLUSTER_NAME - POD_NAME - BINLOG_ENABLED - SLOW_LOG_FILE",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Ref: ref("k8s.io/api/core/v1.EnvVar"),
+ },
+ },
+ },
+ },
+ },
"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/",
@@ -3893,7 +3945,7 @@ func schema_pkg_apis_pingcap_v1alpha1_TiDBSpec(ref common.ReferenceCallback) com
},
},
Dependencies: []string{
- "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBServiceSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBSlowLogTailerSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBTLSClient", "k8s.io/api/core/v1.Affinity", "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.TiDBConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBServiceSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBSlowLogTailerSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBTLSClient", "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"},
}
}
@@ -5640,6 +5692,19 @@ func schema_pkg_apis_pingcap_v1alpha1_TiKVSpec(ref common.ReferenceCallback) com
Format: "",
},
},
+ "env": {
+ SchemaProps: spec.SchemaProps{
+ Description: "List of environment variables to set in the container, like v1.Container.Env. Note that following env names cannot be used and may be overrided by tidb-operator built envs. - NAMESPACE - TZ - SERVICE_NAME - PEER_SERVICE_NAME - HEADLESS_SERVICE_NAME - SET_NAME - HOSTNAME - CLUSTER_NAME - POD_NAME - BINLOG_ENABLED - SLOW_LOG_FILE",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Ref: ref("k8s.io/api/core/v1.EnvVar"),
+ },
+ },
+ },
+ },
+ },
"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/",
@@ -5721,7 +5786,7 @@ func schema_pkg_apis_pingcap_v1alpha1_TiKVSpec(ref common.ReferenceCallback) com
},
},
Dependencies: []string{
- "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiKVConfig", "k8s.io/api/core/v1.Affinity", "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.TiKVConfig", "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_component.go b/pkg/apis/pingcap/v1alpha1/tidbcluster_component.go
index 8bd0c4e37dd..f8d6d626379 100644
--- a/pkg/apis/pingcap/v1alpha1/tidbcluster_component.go
+++ b/pkg/apis/pingcap/v1alpha1/tidbcluster_component.go
@@ -36,6 +36,7 @@ type ComponentAccessor interface {
DnsPolicy() corev1.DNSPolicy
ConfigUpdateStrategy() ConfigUpdateStrategy
BuildPodSpec() corev1.PodSpec
+ Env() []corev1.EnvVar
}
type componentAccessorImpl struct {
@@ -160,6 +161,10 @@ func (a *componentAccessorImpl) BuildPodSpec() corev1.PodSpec {
return spec
}
+func (a *componentAccessorImpl) Env() []corev1.EnvVar {
+ return a.ComponentSpec.Env
+}
+
// BaseTiDBSpec returns the base spec of TiDB servers
func (tc *TidbCluster) BaseTiDBSpec() ComponentAccessor {
return &componentAccessorImpl{&tc.Spec, &tc.Spec.TiDB.ComponentSpec}
diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go
index 5992a2b4179..008769a6d88 100644
--- a/pkg/apis/pingcap/v1alpha1/types.go
+++ b/pkg/apis/pingcap/v1alpha1/types.go
@@ -455,6 +455,24 @@ type ComponentSpec struct {
// Optional: Defaults to cluster-level setting
// +optional
ConfigUpdateStrategy *ConfigUpdateStrategy `json:"configUpdateStrategy,omitempty"`
+
+ // List of environment variables to set in the container, like
+ // v1.Container.Env.
+ // Note that following env names cannot be used and may be overrided by
+ // tidb-operator built envs.
+ // - NAMESPACE
+ // - TZ
+ // - SERVICE_NAME
+ // - PEER_SERVICE_NAME
+ // - HEADLESS_SERVICE_NAME
+ // - SET_NAME
+ // - HOSTNAME
+ // - CLUSTER_NAME
+ // - POD_NAME
+ // - BINLOG_ENABLED
+ // - SLOW_LOG_FILE
+ // +optional
+ Env []corev1.EnvVar `json:"env,omitempty"`
}
// +k8s:openapi-gen=true
diff --git a/pkg/apis/pingcap/v1alpha1/validation/validation.go b/pkg/apis/pingcap/v1alpha1/validation/validation.go
index 39855db7a20..48391b9aa29 100644
--- a/pkg/apis/pingcap/v1alpha1/validation/validation.go
+++ b/pkg/apis/pingcap/v1alpha1/validation/validation.go
@@ -20,7 +20,9 @@ import (
"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
"github.com/pingcap/tidb-operator/pkg/label"
+ corev1 "k8s.io/api/core/v1"
apivalidation "k8s.io/apimachinery/pkg/api/validation"
+ "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
)
@@ -31,10 +33,151 @@ func ValidateTidbCluster(tc *v1alpha1.TidbCluster) field.ErrorList {
// validate metadata
fldPath := field.NewPath("metadata")
// validate metadata/annotations
- allErrs = append(allErrs, apivalidation.ValidateAnnotations(tc.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
+ allErrs = append(allErrs, validateAnnotations(tc.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
+ // validate spec
+ allErrs = append(allErrs, validateTiDBClusterSpec(&tc.Spec, field.NewPath("spec"))...)
+ return allErrs
+}
+
+func validateAnnotations(anns map[string]string, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ allErrs = append(allErrs, apivalidation.ValidateAnnotations(anns, fldPath)...)
for _, key := range []string{label.AnnPDDeleteSlots, label.AnnTiDBDeleteSlots, label.AnnTiKVDeleteSlots} {
- allErrs = append(allErrs, validateDeleteSlots(tc.ObjectMeta.Annotations, key, fldPath.Child("annotations", key))...)
+ allErrs = append(allErrs, validateDeleteSlots(anns, key, fldPath.Child(key))...)
+ }
+ return allErrs
+}
+
+func validateTiDBClusterSpec(spec *v1alpha1.TidbClusterSpec, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ allErrs = append(allErrs, validatePDSpec(&spec.PD, fldPath.Child("pd"))...)
+ allErrs = append(allErrs, validateTiKVSpec(&spec.TiKV, fldPath.Child("tikv"))...)
+ allErrs = append(allErrs, validateTiDBSpec(&spec.TiDB, fldPath.Child("tidb"))...)
+ if spec.Pump != nil {
+ allErrs = append(allErrs, validatePumpSpec(spec.Pump, fldPath.Child("pump"))...)
+ }
+ return allErrs
+}
+
+func validatePDSpec(spec *v1alpha1.PDSpec, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ allErrs = append(allErrs, validateComponentSpec(&spec.ComponentSpec, fldPath)...)
+ return allErrs
+}
+
+func validateTiKVSpec(spec *v1alpha1.TiKVSpec, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ allErrs = append(allErrs, validateComponentSpec(&spec.ComponentSpec, fldPath)...)
+ return allErrs
+}
+
+func validateTiDBSpec(spec *v1alpha1.TiDBSpec, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ allErrs = append(allErrs, validateComponentSpec(&spec.ComponentSpec, fldPath)...)
+ return allErrs
+}
+
+func validatePumpSpec(spec *v1alpha1.PumpSpec, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ allErrs = append(allErrs, validateComponentSpec(&spec.ComponentSpec, fldPath)...)
+ return allErrs
+}
+
+func validateComponentSpec(spec *v1alpha1.ComponentSpec, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ // TODO validate other fields
+ allErrs = append(allErrs, validateEnv(spec.Env, fldPath.Child("env"))...)
+ return allErrs
+}
+
+// validateEnv validates env vars
+func validateEnv(vars []corev1.EnvVar, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+
+ for i, ev := range vars {
+ idxPath := fldPath.Index(i)
+ if len(ev.Name) == 0 {
+ allErrs = append(allErrs, field.Required(idxPath.Child("name"), ""))
+ } else {
+ for _, msg := range validation.IsEnvVarName(ev.Name) {
+ allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), ev.Name, msg))
+ }
+ }
+ allErrs = append(allErrs, validateEnvVarValueFrom(ev, idxPath.Child("valueFrom"))...)
+ }
+ return allErrs
+}
+
+func validateEnvVarValueFrom(ev corev1.EnvVar, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+
+ if ev.ValueFrom == nil {
+ return allErrs
+ }
+
+ numSources := 0
+
+ if ev.ValueFrom.FieldRef != nil {
+ numSources++
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("fieldRef"), "", "fieldRef is not supported"))
+ }
+ if ev.ValueFrom.ResourceFieldRef != nil {
+ numSources++
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceFieldRef"), "", "resourceFieldRef is not supported"))
+ }
+ if ev.ValueFrom.ConfigMapKeyRef != nil {
+ numSources++
+ allErrs = append(allErrs, validateConfigMapKeySelector(ev.ValueFrom.ConfigMapKeyRef, fldPath.Child("configMapKeyRef"))...)
+ }
+ if ev.ValueFrom.SecretKeyRef != nil {
+ numSources++
+ allErrs = append(allErrs, validateSecretKeySelector(ev.ValueFrom.SecretKeyRef, fldPath.Child("secretKeyRef"))...)
+ }
+
+ if numSources == 0 {
+ allErrs = append(allErrs, field.Invalid(fldPath, "", "must specify one of: `configMapKeyRef` or `secretKeyRef`"))
+ } else if len(ev.Value) != 0 {
+ if numSources != 0 {
+ allErrs = append(allErrs, field.Invalid(fldPath, "", "may not be specified when `value` is not empty"))
+ }
+ } else if numSources > 1 {
+ allErrs = append(allErrs, field.Invalid(fldPath, "", "may not have more than one field specified at a time"))
+ }
+
+ return allErrs
+}
+
+func validateConfigMapKeySelector(s *corev1.ConfigMapKeySelector, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+
+ for _, msg := range apivalidation.NameIsDNSSubdomain(s.Name, false) {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), s.Name, msg))
+ }
+ if len(s.Key) == 0 {
+ allErrs = append(allErrs, field.Required(fldPath.Child("key"), ""))
+ } else {
+ for _, msg := range validation.IsConfigMapKey(s.Key) {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("key"), s.Key, msg))
+ }
+ }
+
+ return allErrs
+}
+
+func validateSecretKeySelector(s *corev1.SecretKeySelector, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+
+ for _, msg := range apivalidation.NameIsDNSSubdomain(s.Name, false) {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), s.Name, msg))
+ }
+ if len(s.Key) == 0 {
+ allErrs = append(allErrs, field.Required(fldPath.Child("key"), ""))
+ } else {
+ for _, msg := range validation.IsConfigMapKey(s.Key) {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("key"), s.Key, msg))
+ }
}
+
return allErrs
}
diff --git a/pkg/apis/pingcap/v1alpha1/validation/validation_test.go b/pkg/apis/pingcap/v1alpha1/validation/validation_test.go
index fccae350145..1324d7bbecd 100644
--- a/pkg/apis/pingcap/v1alpha1/validation/validation_test.go
+++ b/pkg/apis/pingcap/v1alpha1/validation/validation_test.go
@@ -23,8 +23,7 @@ import (
"k8s.io/apimachinery/pkg/util/validation/field"
)
-// TODO: more UTs
-func TestValidateDeletedSlots(t *testing.T) {
+func TestValidateAnnotations(t *testing.T) {
successCases := []struct {
name string
tc v1alpha1.TidbCluster
@@ -83,7 +82,7 @@ func TestValidateDeletedSlots(t *testing.T) {
}
for _, v := range successCases {
- if errs := ValidateTidbCluster(&v.tc); len(errs) != 0 {
+ if errs := validateAnnotations(v.tc.ObjectMeta.Annotations, field.NewPath("metadata", "annotations")); len(errs) != 0 {
t.Errorf("[%s]: unexpected error: %v", v.name, errs)
}
}
@@ -160,7 +159,7 @@ func TestValidateDeletedSlots(t *testing.T) {
}
for _, v := range errorCases {
- errs := ValidateTidbCluster(&v.tc)
+ errs := validateAnnotations(v.tc.ObjectMeta.Annotations, field.NewPath("metadata", "annotations"))
if len(errs) != len(v.errs) {
t.Errorf("[%s]: expected %d failures, got %d failures: %v", v.name, len(v.errs), len(errs), errs)
continue
diff --git a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go
index 6bc97a3328c..b24b675f3ac 100644
--- a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go
@@ -494,6 +494,13 @@ func (in *ComponentSpec) DeepCopyInto(out *ComponentSpec) {
*out = new(ConfigUpdateStrategy)
**out = **in
}
+ if in.Env != nil {
+ in, out := &in.Env, &out.Env
+ *out = make([]v1.EnvVar, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
return
}
diff --git a/pkg/manager/member/pd_member_manager.go b/pkg/manager/member/pd_member_manager.go
index 12cc2f256cb..75dd9cd46be 100644
--- a/pkg/manager/member/pd_member_manager.go
+++ b/pkg/manager/member/pd_member_manager.go
@@ -601,7 +601,7 @@ func getNewPDSetForTidbCluster(tc *v1alpha1.TidbCluster, cm *corev1.ConfigMap) (
},
})
}
- pdContainer.Env = env
+ pdContainer.Env = util.MergeEnv(basePDSpec.Env(), env)
podSpec.Volumes = vols
podSpec.Containers = []corev1.Container{pdContainer}
diff --git a/pkg/manager/member/pd_member_manager_test.go b/pkg/manager/member/pd_member_manager_test.go
index 829e7791e14..b6147de14bc 100644
--- a/pkg/manager/member/pd_member_manager_test.go
+++ b/pkg/manager/member/pd_member_manager_test.go
@@ -918,6 +918,20 @@ func testAnnotations(t *testing.T, annotations map[string]string) func(sts *apps
}
}
+func testPDContainerEnv(t *testing.T, env []corev1.EnvVar) func(sts *apps.StatefulSet) {
+ return func(sts *apps.StatefulSet) {
+ got := []corev1.EnvVar{}
+ for _, c := range sts.Spec.Template.Spec.Containers {
+ if c.Name == v1alpha1.PDMemberType.String() {
+ got = c.Env
+ }
+ }
+ if diff := cmp.Diff(env, got); diff != "" {
+ t.Errorf("unexpected (-want, +got): %s", diff)
+ }
+ }
+}
+
func TestGetNewPDSetForTidbCluster(t *testing.T) {
enable := true
tests := []struct {
@@ -1035,6 +1049,74 @@ func TestGetNewPDSetForTidbCluster(t *testing.T) {
}))
},
},
+ {
+ name: "set custom env",
+ tc: v1alpha1.TidbCluster{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "tc",
+ Namespace: "ns",
+ },
+ Spec: v1alpha1.TidbClusterSpec{
+ PD: v1alpha1.PDSpec{
+ ComponentSpec: v1alpha1.ComponentSpec{
+ Env: []corev1.EnvVar{
+ {
+ Name: "DASHBOARD_SESSION_SECRET",
+ ValueFrom: &corev1.EnvVarSource{
+ SecretKeyRef: &corev1.SecretKeySelector{
+ LocalObjectReference: corev1.LocalObjectReference{
+ Name: "dashboard-session-secret",
+ },
+ Key: "encryption_key",
+ },
+ },
+ },
+ {
+ Name: "TZ",
+ Value: "ignored",
+ },
+ },
+ },
+ },
+ },
+ },
+ testSts: testPDContainerEnv(t, []corev1.EnvVar{
+ {
+ Name: "DASHBOARD_SESSION_SECRET",
+ ValueFrom: &corev1.EnvVarSource{
+ SecretKeyRef: &corev1.SecretKeySelector{
+ LocalObjectReference: corev1.LocalObjectReference{
+ Name: "dashboard-session-secret",
+ },
+ Key: "encryption_key",
+ },
+ },
+ },
+ {
+ Name: "NAMESPACE",
+ ValueFrom: &corev1.EnvVarSource{
+ FieldRef: &corev1.ObjectFieldSelector{
+ FieldPath: "metadata.namespace",
+ },
+ },
+ },
+ {
+ Name: "PEER_SERVICE_NAME",
+ Value: "tc-pd-peer",
+ },
+ {
+ Name: "SERVICE_NAME",
+ Value: "tc-pd",
+ },
+ {
+ Name: "SET_NAME",
+ Value: "tc-pd",
+ },
+ {
+ Name: "TZ",
+ },
+ }),
+ },
// TODO add more tests
}
diff --git a/pkg/manager/member/pump_member_manager.go b/pkg/manager/member/pump_member_manager.go
index 5899c4e8789..995012aa9c9 100644
--- a/pkg/manager/member/pump_member_manager.go
+++ b/pkg/manager/member/pump_member_manager.go
@@ -344,7 +344,7 @@ func getNewPumpStatefulSet(tc *v1alpha1.TidbCluster, cm *corev1.ConfigMap) (*app
ContainerPort: 8250,
}},
Resources: controller.ContainerResource(tc.Spec.Pump.ResourceRequirements),
- Env: envs,
+ Env: util.MergeEnv(spec.Env(), envs),
VolumeMounts: volumeMounts,
},
}
diff --git a/pkg/manager/member/tidb_member_manager.go b/pkg/manager/member/tidb_member_manager.go
index 3f9da819616..8c89511078d 100644
--- a/pkg/manager/member/tidb_member_manager.go
+++ b/pkg/manager/member/tidb_member_manager.go
@@ -646,7 +646,7 @@ func getNewTiDBSetForTidbCluster(tc *v1alpha1.TidbCluster, cm *corev1.ConfigMap)
},
VolumeMounts: volMounts,
Resources: controller.ContainerResource(tc.Spec.TiDB.ResourceRequirements),
- Env: envs,
+ Env: util.MergeEnv(baseTiDBSpec.Env(), envs),
ReadinessProbe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
diff --git a/pkg/manager/member/tikv_member_manager.go b/pkg/manager/member/tikv_member_manager.go
index 56ca06c9a2b..519ae3fa350 100644
--- a/pkg/manager/member/tikv_member_manager.go
+++ b/pkg/manager/member/tikv_member_manager.go
@@ -443,7 +443,7 @@ func getNewTiKVSetForTidbCluster(tc *v1alpha1.TidbCluster, cm *corev1.ConfigMap)
},
})
}
- tikvContainer.Env = env
+ tikvContainer.Env = util.MergeEnv(baseTiKVSpec.Env(), env)
podSpec.Volumes = vols
podSpec.SecurityContext = podSecurityContext
podSpec.InitContainers = initContainers
diff --git a/pkg/monitor/monitor/util.go b/pkg/monitor/monitor/util.go
index 55851b5e7d0..b213526af7d 100644
--- a/pkg/monitor/monitor/util.go
+++ b/pkg/monitor/monitor/util.go
@@ -16,13 +16,13 @@ package monitor
import (
"encoding/json"
"fmt"
- "github.com/pingcap/tidb-operator/pkg/util"
"sort"
"strconv"
"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/util"
"github.com/prometheus/prometheus/config"
apps "k8s.io/api/apps/v1"
core "k8s.io/api/core/v1"
@@ -514,7 +514,7 @@ func getMonitorGrafanaContainer(secret *core.Secret, monitor *v1alpha1.TidbMonit
if monitor.Spec.Grafana.ImagePullPolicy != nil {
c.ImagePullPolicy = *monitor.Spec.Grafana.ImagePullPolicy
}
- c.Env = sortEnvByName(c.Env)
+ sort.Sort(util.SortEnvByName(c.Env))
return c
}
@@ -791,27 +791,3 @@ func getMonitorPVC(monitor *v1alpha1.TidbMonitor) *core.PersistentVolumeClaim {
},
}
}
-
-// sortEnvByName in order to avoid syncing same template into different results
-func sortEnvByName(envlist []core.EnvVar) []core.EnvVar {
- if envlist == nil || len(envlist) < 1 {
- return envlist
- }
- var wrappers EnvListWrapper
- wrappers = envlist
- sort.Sort(wrappers)
- return wrappers
-}
-
-type EnvListWrapper []core.EnvVar
-
-func (e EnvListWrapper) Len() int {
- return len(e)
-}
-func (e EnvListWrapper) Swap(i, j int) {
- e[i], e[j] = e[j], e[i]
-}
-
-func (e EnvListWrapper) Less(i, j int) bool {
- return e[i].Name < e[j].Name
-}
diff --git a/pkg/util/util.go b/pkg/util/util.go
index bc02d7ad8db..2d4d1f777e1 100644
--- a/pkg/util/util.go
+++ b/pkg/util/util.go
@@ -16,6 +16,7 @@ package util
import (
"encoding/json"
"fmt"
+ "sort"
"strconv"
"strings"
@@ -180,3 +181,34 @@ func ClusterTLSSecretName(tcName, component string) string {
func TiDBClientTLSSecretName(tcName string) string {
return fmt.Sprintf("%s-tidb-client-secret", tcName)
}
+
+// SortEnvByName implements sort.Interface to sort env list by name.
+type SortEnvByName []corev1.EnvVar
+
+func (e SortEnvByName) Len() int {
+ return len(e)
+}
+func (e SortEnvByName) Swap(i, j int) {
+ e[i], e[j] = e[j], e[i]
+}
+
+func (e SortEnvByName) Less(i, j int) bool {
+ return e[i].Name < e[j].Name
+}
+
+// MergeEnv merges env in `b` to `a` and overrides env that has the same name.
+func MergeEnv(a []corev1.EnvVar, b []corev1.EnvVar) []corev1.EnvVar {
+ tmpEnv := make(map[string]corev1.EnvVar)
+ for _, e := range a {
+ tmpEnv[e.Name] = e
+ }
+ for _, e := range b {
+ tmpEnv[e.Name] = e
+ }
+ c := make([]corev1.EnvVar, 0)
+ for _, e := range tmpEnv {
+ c = append(c, e)
+ }
+ sort.Sort(SortEnvByName(c))
+ return c
+}
diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go
index cccbec683b0..17572c49b87 100644
--- a/pkg/util/utils_test.go
+++ b/pkg/util/utils_test.go
@@ -16,9 +16,11 @@ package util
import (
"testing"
+ "github.com/google/go-cmp/cmp"
. "github.com/onsi/gomega"
"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
"github.com/pingcap/tidb-operator/pkg/label"
+ corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
)
@@ -125,3 +127,59 @@ func TestGetPodOrdinals(t *testing.T) {
})
}
}
+
+func TestMergeEnv(t *testing.T) {
+ tests := []struct {
+ name string
+ a []corev1.EnvVar
+ b []corev1.EnvVar
+ want []corev1.EnvVar
+ }{
+ {
+ name: "b overrides a with the same name",
+ a: []corev1.EnvVar{
+ {
+ Name: "foo",
+ Value: "bar",
+ },
+ {
+ Name: "xxx",
+ Value: "xxx",
+ },
+ },
+ b: []corev1.EnvVar{
+ {
+ Name: "foo",
+ Value: "barbar",
+ },
+ {
+ Name: "new",
+ Value: "bar",
+ },
+ },
+ want: []corev1.EnvVar{
+ {
+ Name: "foo",
+ Value: "barbar",
+ },
+ {
+ Name: "new",
+ Value: "bar",
+ },
+ {
+ Name: "xxx",
+ Value: "xxx",
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := MergeEnv(tt.a, tt.b)
+ if diff := cmp.Diff(tt.want, got); diff != "" {
+ t.Errorf("unwant (-want, +got): %s", diff)
+ }
+ })
+ }
+}