From 9b819afca4cb012cf0ae26872936680f3846e7ec Mon Sep 17 00:00:00 2001 From: tigerK Date: Mon, 21 Nov 2022 21:59:36 +0800 Subject: [PATCH 01/18] [feature]add serviceType functionality for standalone and cluster with annotations (#376) * [feature]add serviceType Signed-off-by: yanru.lv * [fix] lint check Signed-off-by: yanru.lv * service format Signed-off-by: yanru.lv * added as an additional service Signed-off-by: yanru.lv Signed-off-by: yanru.lv --- api/v1beta1/common_types.go | 8 ++++++ api/v1beta1/zz_generated.deepcopy.go | 27 +++++++++++++++++++ .../redis.redis.opstreelabs.in_redis.yaml | 15 +++++++++++ ...is.redis.opstreelabs.in_redisclusters.yaml | 15 +++++++++++ k8sutils/labels.go | 6 ++++- k8sutils/redis-cluster.go | 12 ++++++--- k8sutils/redis-standalone.go | 12 ++++++--- k8sutils/services.go | 8 +++--- 8 files changed, 92 insertions(+), 11 deletions(-) diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go index f4a2d7ae6..fdee1aea9 100644 --- a/api/v1beta1/common_types.go +++ b/api/v1beta1/common_types.go @@ -29,6 +29,14 @@ type KubernetesConfig struct { ExistingPasswordSecret *ExistingPasswordSecret `json:"redisSecret,omitempty"` ImagePullSecrets *[]corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` UpdateStrategy appsv1.StatefulSetUpdateStrategy `json:"updateStrategy,omitempty"` + Service *ServiceConfig `json:"service,omitempty"` +} + +// ServiceConfig define the type of service to be created and its annotations +type ServiceConfig struct { + // +kubebuilder:validation:Enum=LoadBalancer;NodePort;ClusterIP + ServiceType string `json:"serviceType,omitempty"` + ServiceAnnotations map[string]string `json:"annotations,omitempty"` } // RedisConfig defines the external configuration of Redis diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 9f30cdc7c..d263faab4 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -74,6 +74,11 @@ func (in *KubernetesConfig) DeepCopyInto(out *KubernetesConfig) { } } in.UpdateStrategy.DeepCopyInto(&out.UpdateStrategy) + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = new(ServiceConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesConfig. @@ -597,6 +602,28 @@ func (in *RedisStatus) DeepCopy() *RedisStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceConfig) DeepCopyInto(out *ServiceConfig) { + *out = *in + if in.ServiceAnnotations != nil { + in, out := &in.ServiceAnnotations, &out.ServiceAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceConfig. +func (in *ServiceConfig) DeepCopy() *ServiceConfig { + if in == nil { + return nil + } + out := new(ServiceConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Sidecar) DeepCopyInto(out *Sidecar) { *out = *in diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml index 9da37e5bb..4fcdb8b16 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml @@ -992,6 +992,21 @@ spec: to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object + service: + description: ServiceConfig define the type of service to be created + and its annotations + properties: + annotations: + additionalProperties: + type: string + type: object + serviceType: + enum: + - LoadBalancer + - NodePort + - ClusterIP + type: string + type: object updateStrategy: description: StatefulSetUpdateStrategy indicates the strategy that the StatefulSet controller will use to perform updates. diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml index b96602473..5210cc3d2 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml @@ -190,6 +190,21 @@ spec: to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object + service: + description: ServiceConfig define the type of service to be created + and its annotations + properties: + annotations: + additionalProperties: + type: string + type: object + serviceType: + enum: + - LoadBalancer + - NodePort + - ClusterIP + type: string + type: object updateStrategy: description: StatefulSetUpdateStrategy indicates the strategy that the StatefulSet controller will use to perform updates. diff --git a/k8sutils/labels.go b/k8sutils/labels.go index e0316df12..2edf8f7fa 100644 --- a/k8sutils/labels.go +++ b/k8sutils/labels.go @@ -74,7 +74,7 @@ func filterAnnotations(anots map[string]string) map[string]string { } // generateServiceAnots generates and returns service annotations -func generateServiceAnots(stsMeta metav1.ObjectMeta) map[string]string { +func generateServiceAnots(stsMeta metav1.ObjectMeta, additionalSvcAnnotations map[string]string) map[string]string { anots := map[string]string{ "redis.opstreelabs.in": "true", "redis.opstreelabs.instance": stsMeta.GetName(), @@ -84,6 +84,10 @@ func generateServiceAnots(stsMeta metav1.ObjectMeta) map[string]string { for k, v := range stsMeta.GetAnnotations() { anots[k] = v } + for k := range additionalSvcAnnotations { + anots[k] = additionalSvcAnnotations[k] + } + return filterAnnotations(anots) } diff --git a/k8sutils/redis-cluster.go b/k8sutils/redis-cluster.go index 1bd6e906e..71740a578 100644 --- a/k8sutils/redis-cluster.go +++ b/k8sutils/redis-cluster.go @@ -171,21 +171,27 @@ func (service RedisClusterService) CreateRedisClusterService(cr *redisv1beta1.Re serviceName := cr.ObjectMeta.Name + "-" + service.RedisServiceRole logger := serviceLogger(cr.Namespace, serviceName) labels := getRedisLabels(serviceName, "cluster", service.RedisServiceRole, cr.ObjectMeta.Labels) - annotations := generateServiceAnots(cr.ObjectMeta) + annotations := generateServiceAnots(cr.ObjectMeta, nil) if cr.Spec.RedisExporter != nil && cr.Spec.RedisExporter.Enabled { enableMetrics = true } objectMetaInfo := generateObjectMetaInformation(serviceName, cr.Namespace, labels, annotations) headlessObjectMetaInfo := generateObjectMetaInformation(serviceName+"-headless", cr.Namespace, labels, annotations) - err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisClusterAsOwner(cr), false, true) + additionalObjectMetaInfo := generateObjectMetaInformation(serviceName+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, cr.Spec.KubernetesConfig.Service.ServiceAnnotations)) + err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisClusterAsOwner(cr), false, true, "ClusterIP") if err != nil { logger.Error(err, "Cannot create headless service for Redis", "Setup.Type", service.RedisServiceRole) return err } - err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisClusterAsOwner(cr), enableMetrics, false) + err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisClusterAsOwner(cr), enableMetrics, false, "ClusterIP") if err != nil { logger.Error(err, "Cannot create service for Redis", "Setup.Type", service.RedisServiceRole) return err } + err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisClusterAsOwner(cr), false, false, cr.Spec.KubernetesConfig.Service.ServiceType) + if err != nil { + logger.Error(err, "Cannot create additional service for Redis", "Setup.Type", service.RedisServiceRole) + return err + } return nil } diff --git a/k8sutils/redis-standalone.go b/k8sutils/redis-standalone.go index 088d0cf38..676b01d44 100644 --- a/k8sutils/redis-standalone.go +++ b/k8sutils/redis-standalone.go @@ -12,22 +12,28 @@ var ( func CreateStandaloneService(cr *redisv1beta1.Redis) error { logger := serviceLogger(cr.Namespace, cr.ObjectMeta.Name) labels := getRedisLabels(cr.ObjectMeta.Name, "standalone", "standalone", cr.ObjectMeta.Labels) - annotations := generateServiceAnots(cr.ObjectMeta) + annotations := generateServiceAnots(cr.ObjectMeta, nil) if cr.Spec.RedisExporter != nil && cr.Spec.RedisExporter.Enabled { enableMetrics = true } objectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name, cr.Namespace, labels, annotations) headlessObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-headless", cr.Namespace, labels, annotations) - err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisAsOwner(cr), false, true) + additionalObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, cr.Spec.KubernetesConfig.Service.ServiceAnnotations)) + err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisAsOwner(cr), false, true, "ClusterIP") if err != nil { logger.Error(err, "Cannot create standalone headless service for Redis") return err } - err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisAsOwner(cr), enableMetrics, false) + err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisAsOwner(cr), enableMetrics, false, "ClusterIP") if err != nil { logger.Error(err, "Cannot create standalone service for Redis") return err } + err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisAsOwner(cr), false, false, cr.Spec.KubernetesConfig.Service.ServiceType) + if err != nil { + logger.Error(err, "Cannot create additional service for Redis") + return err + } return nil } diff --git a/k8sutils/services.go b/k8sutils/services.go index 9c1ede090..8e414a2a9 100644 --- a/k8sutils/services.go +++ b/k8sutils/services.go @@ -21,12 +21,12 @@ var ( ) // generateServiceDef generates service definition for Redis -func generateServiceDef(serviceMeta metav1.ObjectMeta, enableMetrics bool, ownerDef metav1.OwnerReference, headless bool) *corev1.Service { +func generateServiceDef(serviceMeta metav1.ObjectMeta, enableMetrics bool, ownerDef metav1.OwnerReference, headless bool, serviceType string) *corev1.Service { service := &corev1.Service{ TypeMeta: generateMetaInformation("Service", "v1"), ObjectMeta: serviceMeta, Spec: corev1.ServiceSpec{ - Type: generateServiceType("ClusterIP"), + Type: generateServiceType(serviceType), ClusterIP: "", Selector: serviceMeta.GetLabels(), Ports: []corev1.ServicePort{ @@ -120,9 +120,9 @@ func serviceLogger(namespace string, name string) logr.Logger { } // CreateOrUpdateService method will create or update Redis service -func CreateOrUpdateService(namespace string, serviceMeta metav1.ObjectMeta, ownerDef metav1.OwnerReference, enableMetrics, headless bool) error { +func CreateOrUpdateService(namespace string, serviceMeta metav1.ObjectMeta, ownerDef metav1.OwnerReference, enableMetrics, headless bool, serviceType string) error { logger := serviceLogger(namespace, serviceMeta.Name) - serviceDef := generateServiceDef(serviceMeta, enableMetrics, ownerDef, headless) + serviceDef := generateServiceDef(serviceMeta, enableMetrics, ownerDef, headless, serviceType) storedService, err := getService(namespace, serviceMeta.Name) if err != nil { if errors.IsNotFound(err) { From c4d3e9f1f8340017298b11139c5a4716d8eff71b Mon Sep 17 00:00:00 2001 From: ylck <12771335+ylck@users.noreply.github.com> Date: Thu, 24 Nov 2022 12:56:02 +0800 Subject: [PATCH 02/18] Add check PersistenceEnabled not nil (#380) Signed-off-by: ylck Signed-off-by: ylck --- k8sutils/redis-cluster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8sutils/redis-cluster.go b/k8sutils/redis-cluster.go index 71740a578..b78ecd10f 100644 --- a/k8sutils/redis-cluster.go +++ b/k8sutils/redis-cluster.go @@ -84,7 +84,7 @@ func generateRedisClusterContainerParams(cr *redisv1beta1.RedisCluster, readines if livenessProbeDef != nil { containerProp.LivenessProbe = livenessProbeDef } - if cr.Spec.Storage != nil && *cr.Spec.PersistenceEnabled { + if cr.Spec.Storage != nil && cr.Spec.PersistenceEnabled != nil && *cr.Spec.PersistenceEnabled { containerProp.PersistenceEnabled = &trueProperty } else { containerProp.PersistenceEnabled = &falseProperty From 4bd943e099a52f7fe5a9350660a391e2b81346f7 Mon Sep 17 00:00:00 2001 From: Anish Chakraborty Date: Mon, 28 Nov 2022 14:05:27 +0100 Subject: [PATCH 03/18] fix crash with go panic (#385) * fix nil pointer deref Signed-off-by: Anish C * fix nil pointer deref service type Signed-off-by: Anish C Signed-off-by: Anish C --- k8sutils/redis-cluster.go | 12 ++++++++++-- k8sutils/redis-standalone.go | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/k8sutils/redis-cluster.go b/k8sutils/redis-cluster.go index b78ecd10f..e2b1279b5 100644 --- a/k8sutils/redis-cluster.go +++ b/k8sutils/redis-cluster.go @@ -175,9 +175,13 @@ func (service RedisClusterService) CreateRedisClusterService(cr *redisv1beta1.Re if cr.Spec.RedisExporter != nil && cr.Spec.RedisExporter.Enabled { enableMetrics = true } + additionalServiceAnnotations := map[string]string{} + if cr.Spec.KubernetesConfig.Service != nil { + additionalServiceAnnotations = cr.Spec.KubernetesConfig.Service.ServiceAnnotations + } objectMetaInfo := generateObjectMetaInformation(serviceName, cr.Namespace, labels, annotations) headlessObjectMetaInfo := generateObjectMetaInformation(serviceName+"-headless", cr.Namespace, labels, annotations) - additionalObjectMetaInfo := generateObjectMetaInformation(serviceName+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, cr.Spec.KubernetesConfig.Service.ServiceAnnotations)) + additionalObjectMetaInfo := generateObjectMetaInformation(serviceName+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, additionalServiceAnnotations)) err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisClusterAsOwner(cr), false, true, "ClusterIP") if err != nil { logger.Error(err, "Cannot create headless service for Redis", "Setup.Type", service.RedisServiceRole) @@ -188,7 +192,11 @@ func (service RedisClusterService) CreateRedisClusterService(cr *redisv1beta1.Re logger.Error(err, "Cannot create service for Redis", "Setup.Type", service.RedisServiceRole) return err } - err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisClusterAsOwner(cr), false, false, cr.Spec.KubernetesConfig.Service.ServiceType) + additionalServiceType := "ClusterIP" + if cr.Spec.KubernetesConfig.Service != nil { + additionalServiceType = cr.Spec.KubernetesConfig.Service.ServiceType + } + err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisClusterAsOwner(cr), false, false, additionalServiceType) if err != nil { logger.Error(err, "Cannot create additional service for Redis", "Setup.Type", service.RedisServiceRole) return err diff --git a/k8sutils/redis-standalone.go b/k8sutils/redis-standalone.go index 676b01d44..6e82a4eb9 100644 --- a/k8sutils/redis-standalone.go +++ b/k8sutils/redis-standalone.go @@ -16,9 +16,13 @@ func CreateStandaloneService(cr *redisv1beta1.Redis) error { if cr.Spec.RedisExporter != nil && cr.Spec.RedisExporter.Enabled { enableMetrics = true } + additionalServiceAnnotations := map[string]string{} + if cr.Spec.KubernetesConfig.Service != nil { + additionalServiceAnnotations = cr.Spec.KubernetesConfig.Service.ServiceAnnotations + } objectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name, cr.Namespace, labels, annotations) headlessObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-headless", cr.Namespace, labels, annotations) - additionalObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, cr.Spec.KubernetesConfig.Service.ServiceAnnotations)) + additionalObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, additionalServiceAnnotations)) err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisAsOwner(cr), false, true, "ClusterIP") if err != nil { logger.Error(err, "Cannot create standalone headless service for Redis") @@ -29,7 +33,11 @@ func CreateStandaloneService(cr *redisv1beta1.Redis) error { logger.Error(err, "Cannot create standalone service for Redis") return err } - err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisAsOwner(cr), false, false, cr.Spec.KubernetesConfig.Service.ServiceType) + additionalServiceType := "ClusterIP" + if cr.Spec.KubernetesConfig.Service != nil { + additionalServiceType = cr.Spec.KubernetesConfig.Service.ServiceType + } + err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisAsOwner(cr), false, false, additionalServiceType) if err != nil { logger.Error(err, "Cannot create additional service for Redis") return err From 30f061982e0cc2f16581d384e68487bcf8890143 Mon Sep 17 00:00:00 2001 From: Shubham Gupta <69793468+shubham-cmyk@users.noreply.github.com> Date: Wed, 30 Nov 2022 13:18:09 +0530 Subject: [PATCH 04/18] [Development][Add] Added feature for additional volume mounts (#389) * add Common Type Signed-off-by: Shubham Gupta * add Volume Mount Signed-off-by: Shubham Gupta * Additional Volume Mount Signed-off-by: Shubham Gupta * Additional Volume Mount Signed-off-by: Shubham Gupta * add if Signed-off-by: Shubham Gupta --- api/v1beta1/common_types.go | 7 + api/v1beta1/zz_generated.deepcopy.go | 30 + .../redis.redis.opstreelabs.in_redis.yaml | 1650 +++++++++++++++++ ...is.redis.opstreelabs.in_redisclusters.yaml | 1650 +++++++++++++++++ example/volume_mount/configmap.yaml | 8 + example/volume_mount/redis-cluster.yaml | 53 + example/volume_mount/redis-standalone.yaml | 49 + k8sutils/redis-cluster.go | 10 +- k8sutils/redis-standalone.go | 10 +- k8sutils/statefulset.go | 26 +- 10 files changed, 3476 insertions(+), 17 deletions(-) create mode 100644 example/volume_mount/configmap.yaml create mode 100644 example/volume_mount/redis-cluster.yaml create mode 100644 example/volume_mount/redis-standalone.yaml diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go index fdee1aea9..44c3e54c7 100644 --- a/api/v1beta1/common_types.go +++ b/api/v1beta1/common_types.go @@ -53,6 +53,13 @@ type ExistingPasswordSecret struct { // Storage is the inteface to add pvc and pv support in redis type Storage struct { VolumeClaimTemplate corev1.PersistentVolumeClaim `json:"volumeClaimTemplate,omitempty"` + VolumeMount AdditionalVolume `json:"volumemount,omitempty"` +} + +// Additional Volume is provided by user that is mounted on the pods +type AdditionalVolume struct { + Volume []corev1.Volume `json:"volume,omitempty"` + MountPath []corev1.VolumeMount `json:"mountPath,omitempty"` } // RedisExporter interface will have the information for redis exporter related stuff diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index d263faab4..d430ef851 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -26,6 +26,35 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AdditionalVolume) DeepCopyInto(out *AdditionalVolume) { + *out = *in + if in.Volume != nil { + in, out := &in.Volume, &out.Volume + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.MountPath != nil { + in, out := &in.MountPath, &out.MountPath + *out = make([]v1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdditionalVolume. +func (in *AdditionalVolume) DeepCopy() *AdditionalVolume { + if in == nil { + return nil + } + out := new(AdditionalVolume) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExistingPasswordSecret) DeepCopyInto(out *ExistingPasswordSecret) { *out = *in @@ -659,6 +688,7 @@ func (in *Sidecar) DeepCopy() *Sidecar { func (in *Storage) DeepCopyInto(out *Storage) { *out = *in in.VolumeClaimTemplate.DeepCopyInto(&out.VolumeClaimTemplate) + in.VolumeMount.DeepCopyInto(&out.VolumeMount) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Storage. diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml index 4fcdb8b16..45e1355de 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml @@ -1873,6 +1873,1656 @@ spec: type: string type: object type: object + volumemount: + description: Additional Volume is provided by user that is mounted + on the pods + properties: + mountPath: + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the + volume should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts + are propagated from the host to container and the + other way around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's + root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. Behaves + similarly to SubPath but environment variable references + $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr and SubPath + are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + volume: + items: + description: Volume represents a named volume in a pod that + may be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS + Disk resource that is attached to a kubelet''s host + machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + partition: + description: 'The partition in the volume that you + want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the + volume partition for /dev/sda is "0" (or you can + leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the + ReadOnly property in VolumeMounts to "true". If + omitted, the default is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource + in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk + mount on the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, + Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob + storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob + disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed + data disk (only in managed availability set). + defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service + mount on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure + Storage Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the + host that shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to + key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to + the authentication secret for User, default is + empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados user name, + default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached + and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More info: + https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object + containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify the volume + in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should + populate this volume + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal + value between 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for + mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This + might be in conflict with other options that affect + the file mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair + in the Data field of the referenced ConfigMap + will be projected into the volume as a file whose + name is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the ConfigMap, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to + set permissions on this file. Must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both + octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This + might be in conflict with other options + that affect the file mode, like fsGroup, + and the result can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents + ephemeral storage that is handled by certain external + CSI drivers (Beta feature). + properties: + driver: + description: Driver is the name of the CSI driver + that handles this volume. Consult with your admin + for the correct name as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", + "xfs", "ntfs". If not provided, the empty value + is passed to the associated CSI driver which will + determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference + to the secret object containing sensitive information + to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no + secret is required. If the secret object contains + more than one secret, all secret references are + passed. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific + properties that are passed to the CSI driver. + Consult your driver's documentation for supported + values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about + the pod that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created + files by default. Must be a Optional: mode bits + used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or + a decimal value between 0 and 511. YAML accepts + both octal and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. Directories + within the path are not affected by this setting. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing the + pod field + properties: + fieldRef: + description: 'Required: Selects a field of + the pod: only annotations, labels, name + and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits used to + set permissions on this file, must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both + octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This + might be in conflict with other options + that affect the file mode, like fsGroup, + and the result can be other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' path. + Must be utf-8 encoded. The first item of + the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults to + "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory + that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should + back this directory. The default is "" which means + to use the node''s default medium. Must be an + empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'Total amount of local storage required + for this EmptyDir volume. The size limit is also + applicable for memory medium. The maximum usage + on memory medium EmptyDir would be the minimum + value between the SizeLimit specified here and + the sum of memory limits of all containers in + a pod. The default is nil which means that the + limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "Ephemeral represents a volume that is + handled by a cluster storage driver. The volume's + lifecycle is tied to the pod that defines it - it + will be created before the pod starts, and deleted + when the pod is removed. \n Use this if: a) the volume + is only needed while the pod runs, b) features of + normal volumes like restoring from snapshot or capacity + \ tracking are needed, c) the storage driver is + specified through a storage class, and d) the storage + driver supports dynamic volume provisioning through + \ a PersistentVolumeClaim (see EphemeralVolumeSource + for more information on the connection between + this volume type and PersistentVolumeClaim). \n + Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the + lifecycle of an individual pod. \n Use CSI for light-weight + local ephemeral volumes if the CSI driver is meant + to be used that way - see the documentation of the + driver for more information. \n A pod can use both + types of ephemeral volumes and persistent volumes + at the same time." + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone + PVC to provision the volume. The pod in which + this EphemeralVolumeSource is embedded will be + the owner of the PVC, i.e. the PVC will be deleted + together with the pod. The name of the PVC will + be `-` where `` + is the name from the `PodSpec.Volumes` array entry. + Pod validation will reject the pod if the concatenated + name is not valid for a PVC (for example, too + long). \n An existing PVC with that name that + is not owned by the pod will *not* be used for + the pod to avoid using an unrelated volume by + mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created + PVC is meant to be used by the pod, the PVC has + to updated with an owner reference to the pod + once the pod exists. Normally this should not + be necessary, but it may be useful when manually + reconstructing a broken cluster. \n This field + is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. \n Required, + must not be nil." + properties: + metadata: + description: May contain labels and annotations + that will be copied into the PVC when creating + it. No other fields are allowed and will be + rejected during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into + the PVC that gets created from this template. + The same fields as in a PersistentVolumeClaim + are also valid here. + properties: + accessModes: + description: 'AccessModes contains the desired + access modes the volume should have. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'This field can be used to + specify either: * An existing VolumeSnapshot + object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller + can support the specified data source, + it will create a new volume based on the + contents of the specified data source. + If the AnyVolumeDataSource feature gate + is enabled, this field will always have + the same contents as the DataSourceRef + field.' + properties: + apiGroup: + description: APIGroup is the group for + the resource being referenced. If + APIGroup is not specified, the specified + Kind must be in the core API group. + For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + dataSourceRef: + description: 'Specifies the object from + which to populate the volume with data, + if a non-empty volume is desired. This + may be any local object from a non-empty + API group (non core object) or a PersistentVolumeClaim + object. When this field is specified, + volume binding will only succeed if the + type of the specified object matches some + installed volume populator or dynamic + provisioner. This field will replace the + functionality of the DataSource field + and as such if both fields are non-empty, + they must have the same value. For backwards + compatibility, both fields (DataSource + and DataSourceRef) will be set to the + same value automatically if one of them + is empty and the other is non-empty. There + are two important differences between + DataSource and DataSourceRef: * While + DataSource only allows two specific types + of objects, DataSourceRef allows any + non-core object, as well as PersistentVolumeClaim + objects. * While DataSource ignores disallowed + values (dropping them), DataSourceRef preserves + all values, and generates an error if + a disallowed value is specified. (Alpha) + Using this field requires the AnyVolumeDataSource + feature gate to be enabled.' + properties: + apiGroup: + description: APIGroup is the group for + the resource being referenced. If + APIGroup is not specified, the specified + Kind must be in the core API group. + For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + resources: + description: 'Resources represents the minimum + resources the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to + specify resource requirements that are + lower than previous value but must still + be higher than capacity recorded in the + status field of the claim. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum + amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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-resources-containers/' + type: object + type: object + selector: + description: A label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + storageClassName: + description: 'Name of the StorageClass required + by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type + of volume is required by the claim. Value + of Filesystem is implied when not included + in claim spec. + type: string + volumeName: + description: VolumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: FC represents a Fibre Channel resource + that is attached to a kubelet's host machine and then + exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. TODO: how + do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names + (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs + and lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume + resource that is provisioned/attached using an exec + based plugin. + properties: + driver: + description: Driver is the name of the driver to + use for this volume. + type: string + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". The default + filesystem depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if + any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to + the secret object containing sensitive information + to pass to the plugin scripts. This may be empty + if no secret object is specified. If the secret + object contains more than one secret, all secrets + are passed to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached + to a kubelet's host machine. This depends on the Flocker + control service being running + properties: + datasetName: + description: Name of the dataset stored as metadata + -> name on the dataset for Flocker should be considered + as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique + identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk + resource that is attached to a kubelet''s host machine + and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + partition: + description: 'The partition in the volume that you + want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the + volume partition for /dev/sda is "0" (or you can + leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in + GCE. Used to identify the disk in GCE. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at + a particular revision. DEPRECATED: GitRepo is deprecated. + To provision a container with a git repo, mount an + EmptyDir into an InitContainer that clones the repo + using git, then mount the EmptyDir into the Pod''s + container.' + properties: + directory: + description: Target directory name. Must not contain + or start with '..'. If '.' is supplied, the volume + directory will be the git repository. Otherwise, + if specified, the volume will contain the git + repository in the subdirectory with the given + name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount + on the host that shares a pod''s lifetime. More info: + https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name + that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs + volume to be mounted with read-only permissions. + Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file + or directory on the host machine that is directly + exposed to the container. This is generally used for + system agents or other privileged things that are + allowed to see the host machine. Most containers will + NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use + host directory mounts and who can/can not mount host + directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. + If the path is a symlink, it will follow the link + to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults + to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource + that is attached to a kubelet''s host machine and + then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP + authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP + authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, + new iSCSI interface : + will be created for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI + transport. Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal + is either an IP or ip_addr:port if the port is + other than default (typically TCP ports 860 and + 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator + authentication + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The Portal is + either an IP or ip_addr:port if the port is other + than default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and + unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host + that shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export + to be mounted with read-only permissions. Defaults + to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address + of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents + a reference to a PersistentVolumeClaim in the same + namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in + VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host + machine + properties: + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume + attached and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type + to mount Must be a filesystem type supported by + the host operating system. Ex. "ext4", "xfs". + Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx + volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: Mode bits used to set permissions on + created files by default. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal + values, JSON requires decimal values for mode + bits. Directories within the path are not affected + by this setting. This might be in conflict with + other options that affect the file mode, like + fsGroup, and the result can be other mode bits + set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected + along with other supported volume types + properties: + configMap: + description: information about the configMap + data to project + properties: + items: + description: If unspecified, each key-value + pair in the Data field of the referenced + ConfigMap will be projected into the + volume as a file whose name is the key + and content is the value. If specified, + the listed keys will be projected into + the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the ConfigMap, + the volume setup will error unless it + is marked optional. Paths must be relative + and may not contain the '..' path or + start with '..'. + items: + description: Maps a string key to a + path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits + used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, + JSON requires decimal values for + mode bits. If not specified, the + volume defaultMode will be used. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of + the file to map the key to. May + not be an absolute path. May not + contain the path element '..'. + May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + downwardAPI: + description: information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects + a field of the pod: only annotations, + labels, name and namespace are + supported.' + 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 + mode: + description: 'Optional: mode bits + used to set permissions on this + file, must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, + JSON requires decimal values for + mode bits. If not specified, the + volume defaultMode will be used. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file + to be created. Must not be absolute + or contain the ''..'' path. Must + be utf-8 encoded. The first item + of the relative path must not + start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, requests.cpu and + requests.memory) are currently + supported.' + properties: + containerName: + description: 'Container name: + required for volumes, optional + for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret + data to project + properties: + items: + description: If unspecified, each key-value + pair in the Data field of the referenced + Secret will be projected into the volume + as a file whose name is the key and + content is the value. If specified, + the listed keys will be projected into + the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the Secret, + the volume setup will error unless it + is marked optional. Paths must be relative + and may not contain the '..' path or + start with '..'. + items: + description: Maps a string key to a + path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits + used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, + JSON requires decimal values for + mode bits. If not specified, the + volume defaultMode will be used. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of + the file to map the key to. May + not be an absolute path. May not + contain the path element '..'. + May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + or its key must be defined + type: boolean + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended + audience of the token. A recipient of + a token must identify itself with an + identifier specified in the audience + of the token, and otherwise should reject + the token. The audience defaults to + the identifier of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is the + requested duration of validity of the + service account token. As the token + approaches expiration, the kubelet volume + plugin will proactively rotate the service + account token. The kubelet will start + trying to rotate the token if the token + is older than 80 percent of its time + to live or if the token is older than + 24 hours.Defaults to 1 hour and must + be at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative + to the mount point of the file to project + the token into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the + host that shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default + is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte + volume to be mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple + Quobyte Registry services specified as a string + as host:port pair (multiple entries are separated + with commas) which acts as the central registry + for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume + in the Backend Used with dynamically provisioned + Quobyte volumes, value is set by the plugin + type: string + user: + description: User to map volume access to Defaults + to serivceaccount user + type: string + volume: + description: Volume is a string that references + an already created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount + on the host that shares a pod''s lifetime. More info: + https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for + RBDUser. Default is /etc/ceph/keyring. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication + secret for RBDUser. If provided overrides keyring. + Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent + volume attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Default is + "xfs". + type: string + gateway: + description: The host address of the ScaleIO API + Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection + Domain for the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret + for ScaleIO user and other sensitive information. + If this is not provided, Login operation will + fail. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a + volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created + in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should + populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal + value between 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for + mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This + might be in conflict with other options that affect + the file mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair + in the Data field of the referenced Secret will + be projected into the volume as a file whose name + is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the Secret, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to + set permissions on this file. Must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both + octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This + might be in conflict with other options + that affect the file mode, like fsGroup, + and the result can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys + must be defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace + to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use + for obtaining the StorageOS API credentials. If + not specified, default values will be attempted. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable name + of the StorageOS volume. Volume names are only + unique within a namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope + of the volume within StorageOS. If no namespace + is specified then the Pod's namespace will be + used. This allows the Kubernetes name scoping + to be mirrored within StorageOS for tighter integration. + Set VolumeName to any name to override the default + behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do + not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume + attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) + profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) + profile name. + type: string + volumePath: + description: Path that identifies vSphere volume + vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object type: object tolerations: items: diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml index 5210cc3d2..87265c658 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml @@ -2974,6 +2974,1656 @@ spec: type: string type: object type: object + volumemount: + description: Additional Volume is provided by user that is mounted + on the pods + properties: + mountPath: + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the + volume should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts + are propagated from the host to container and the + other way around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's + root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. Behaves + similarly to SubPath but environment variable references + $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr and SubPath + are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + volume: + items: + description: Volume represents a named volume in a pod that + may be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS + Disk resource that is attached to a kubelet''s host + machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + partition: + description: 'The partition in the volume that you + want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the + volume partition for /dev/sda is "0" (or you can + leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the + ReadOnly property in VolumeMounts to "true". If + omitted, the default is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource + in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk + mount on the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, + Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob + storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob + disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed + data disk (only in managed availability set). + defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service + mount on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure + Storage Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the + host that shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to + key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to + the authentication secret for User, default is + empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados user name, + default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached + and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More info: + https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object + containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify the volume + in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should + populate this volume + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal + value between 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for + mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This + might be in conflict with other options that affect + the file mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair + in the Data field of the referenced ConfigMap + will be projected into the volume as a file whose + name is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the ConfigMap, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to + set permissions on this file. Must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both + octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This + might be in conflict with other options + that affect the file mode, like fsGroup, + and the result can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents + ephemeral storage that is handled by certain external + CSI drivers (Beta feature). + properties: + driver: + description: Driver is the name of the CSI driver + that handles this volume. Consult with your admin + for the correct name as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", + "xfs", "ntfs". If not provided, the empty value + is passed to the associated CSI driver which will + determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference + to the secret object containing sensitive information + to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no + secret is required. If the secret object contains + more than one secret, all secret references are + passed. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific + properties that are passed to the CSI driver. + Consult your driver's documentation for supported + values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about + the pod that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created + files by default. Must be a Optional: mode bits + used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or + a decimal value between 0 and 511. YAML accepts + both octal and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. Directories + within the path are not affected by this setting. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing the + pod field + properties: + fieldRef: + description: 'Required: Selects a field of + the pod: only annotations, labels, name + and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits used to + set permissions on this file, must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both + octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This + might be in conflict with other options + that affect the file mode, like fsGroup, + and the result can be other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' path. + Must be utf-8 encoded. The first item of + the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults to + "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory + that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should + back this directory. The default is "" which means + to use the node''s default medium. Must be an + empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'Total amount of local storage required + for this EmptyDir volume. The size limit is also + applicable for memory medium. The maximum usage + on memory medium EmptyDir would be the minimum + value between the SizeLimit specified here and + the sum of memory limits of all containers in + a pod. The default is nil which means that the + limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "Ephemeral represents a volume that is + handled by a cluster storage driver. The volume's + lifecycle is tied to the pod that defines it - it + will be created before the pod starts, and deleted + when the pod is removed. \n Use this if: a) the volume + is only needed while the pod runs, b) features of + normal volumes like restoring from snapshot or capacity + \ tracking are needed, c) the storage driver is + specified through a storage class, and d) the storage + driver supports dynamic volume provisioning through + \ a PersistentVolumeClaim (see EphemeralVolumeSource + for more information on the connection between + this volume type and PersistentVolumeClaim). \n + Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the + lifecycle of an individual pod. \n Use CSI for light-weight + local ephemeral volumes if the CSI driver is meant + to be used that way - see the documentation of the + driver for more information. \n A pod can use both + types of ephemeral volumes and persistent volumes + at the same time." + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone + PVC to provision the volume. The pod in which + this EphemeralVolumeSource is embedded will be + the owner of the PVC, i.e. the PVC will be deleted + together with the pod. The name of the PVC will + be `-` where `` + is the name from the `PodSpec.Volumes` array entry. + Pod validation will reject the pod if the concatenated + name is not valid for a PVC (for example, too + long). \n An existing PVC with that name that + is not owned by the pod will *not* be used for + the pod to avoid using an unrelated volume by + mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created + PVC is meant to be used by the pod, the PVC has + to updated with an owner reference to the pod + once the pod exists. Normally this should not + be necessary, but it may be useful when manually + reconstructing a broken cluster. \n This field + is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. \n Required, + must not be nil." + properties: + metadata: + description: May contain labels and annotations + that will be copied into the PVC when creating + it. No other fields are allowed and will be + rejected during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into + the PVC that gets created from this template. + The same fields as in a PersistentVolumeClaim + are also valid here. + properties: + accessModes: + description: 'AccessModes contains the desired + access modes the volume should have. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'This field can be used to + specify either: * An existing VolumeSnapshot + object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller + can support the specified data source, + it will create a new volume based on the + contents of the specified data source. + If the AnyVolumeDataSource feature gate + is enabled, this field will always have + the same contents as the DataSourceRef + field.' + properties: + apiGroup: + description: APIGroup is the group for + the resource being referenced. If + APIGroup is not specified, the specified + Kind must be in the core API group. + For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + dataSourceRef: + description: 'Specifies the object from + which to populate the volume with data, + if a non-empty volume is desired. This + may be any local object from a non-empty + API group (non core object) or a PersistentVolumeClaim + object. When this field is specified, + volume binding will only succeed if the + type of the specified object matches some + installed volume populator or dynamic + provisioner. This field will replace the + functionality of the DataSource field + and as such if both fields are non-empty, + they must have the same value. For backwards + compatibility, both fields (DataSource + and DataSourceRef) will be set to the + same value automatically if one of them + is empty and the other is non-empty. There + are two important differences between + DataSource and DataSourceRef: * While + DataSource only allows two specific types + of objects, DataSourceRef allows any + non-core object, as well as PersistentVolumeClaim + objects. * While DataSource ignores disallowed + values (dropping them), DataSourceRef preserves + all values, and generates an error if + a disallowed value is specified. (Alpha) + Using this field requires the AnyVolumeDataSource + feature gate to be enabled.' + properties: + apiGroup: + description: APIGroup is the group for + the resource being referenced. If + APIGroup is not specified, the specified + Kind must be in the core API group. + For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + resources: + description: 'Resources represents the minimum + resources the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to + specify resource requirements that are + lower than previous value but must still + be higher than capacity recorded in the + status field of the claim. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum + amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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-resources-containers/' + type: object + type: object + selector: + description: A label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + storageClassName: + description: 'Name of the StorageClass required + by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type + of volume is required by the claim. Value + of Filesystem is implied when not included + in claim spec. + type: string + volumeName: + description: VolumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: FC represents a Fibre Channel resource + that is attached to a kubelet's host machine and then + exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. TODO: how + do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names + (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs + and lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume + resource that is provisioned/attached using an exec + based plugin. + properties: + driver: + description: Driver is the name of the driver to + use for this volume. + type: string + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". The default + filesystem depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if + any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to + the secret object containing sensitive information + to pass to the plugin scripts. This may be empty + if no secret object is specified. If the secret + object contains more than one secret, all secrets + are passed to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached + to a kubelet's host machine. This depends on the Flocker + control service being running + properties: + datasetName: + description: Name of the dataset stored as metadata + -> name on the dataset for Flocker should be considered + as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique + identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk + resource that is attached to a kubelet''s host machine + and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + partition: + description: 'The partition in the volume that you + want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the + volume partition for /dev/sda is "0" (or you can + leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in + GCE. Used to identify the disk in GCE. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at + a particular revision. DEPRECATED: GitRepo is deprecated. + To provision a container with a git repo, mount an + EmptyDir into an InitContainer that clones the repo + using git, then mount the EmptyDir into the Pod''s + container.' + properties: + directory: + description: Target directory name. Must not contain + or start with '..'. If '.' is supplied, the volume + directory will be the git repository. Otherwise, + if specified, the volume will contain the git + repository in the subdirectory with the given + name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount + on the host that shares a pod''s lifetime. More info: + https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name + that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs + volume to be mounted with read-only permissions. + Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file + or directory on the host machine that is directly + exposed to the container. This is generally used for + system agents or other privileged things that are + allowed to see the host machine. Most containers will + NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use + host directory mounts and who can/can not mount host + directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. + If the path is a symlink, it will follow the link + to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults + to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource + that is attached to a kubelet''s host machine and + then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP + authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP + authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, + new iSCSI interface : + will be created for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI + transport. Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal + is either an IP or ip_addr:port if the port is + other than default (typically TCP ports 860 and + 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator + authentication + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The Portal is + either an IP or ip_addr:port if the port is other + than default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and + unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host + that shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export + to be mounted with read-only permissions. Defaults + to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address + of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents + a reference to a PersistentVolumeClaim in the same + namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in + VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host + machine + properties: + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume + attached and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type + to mount Must be a filesystem type supported by + the host operating system. Ex. "ext4", "xfs". + Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx + volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: Mode bits used to set permissions on + created files by default. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal + values, JSON requires decimal values for mode + bits. Directories within the path are not affected + by this setting. This might be in conflict with + other options that affect the file mode, like + fsGroup, and the result can be other mode bits + set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected + along with other supported volume types + properties: + configMap: + description: information about the configMap + data to project + properties: + items: + description: If unspecified, each key-value + pair in the Data field of the referenced + ConfigMap will be projected into the + volume as a file whose name is the key + and content is the value. If specified, + the listed keys will be projected into + the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the ConfigMap, + the volume setup will error unless it + is marked optional. Paths must be relative + and may not contain the '..' path or + start with '..'. + items: + description: Maps a string key to a + path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits + used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, + JSON requires decimal values for + mode bits. If not specified, the + volume defaultMode will be used. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of + the file to map the key to. May + not be an absolute path. May not + contain the path element '..'. + May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + downwardAPI: + description: information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects + a field of the pod: only annotations, + labels, name and namespace are + supported.' + 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 + mode: + description: 'Optional: mode bits + used to set permissions on this + file, must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, + JSON requires decimal values for + mode bits. If not specified, the + volume defaultMode will be used. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file + to be created. Must not be absolute + or contain the ''..'' path. Must + be utf-8 encoded. The first item + of the relative path must not + start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, requests.cpu and + requests.memory) are currently + supported.' + properties: + containerName: + description: 'Container name: + required for volumes, optional + for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret + data to project + properties: + items: + description: If unspecified, each key-value + pair in the Data field of the referenced + Secret will be projected into the volume + as a file whose name is the key and + content is the value. If specified, + the listed keys will be projected into + the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the Secret, + the volume setup will error unless it + is marked optional. Paths must be relative + and may not contain the '..' path or + start with '..'. + items: + description: Maps a string key to a + path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits + used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, + JSON requires decimal values for + mode bits. If not specified, the + volume defaultMode will be used. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of + the file to map the key to. May + not be an absolute path. May not + contain the path element '..'. + May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + or its key must be defined + type: boolean + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended + audience of the token. A recipient of + a token must identify itself with an + identifier specified in the audience + of the token, and otherwise should reject + the token. The audience defaults to + the identifier of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is the + requested duration of validity of the + service account token. As the token + approaches expiration, the kubelet volume + plugin will proactively rotate the service + account token. The kubelet will start + trying to rotate the token if the token + is older than 80 percent of its time + to live or if the token is older than + 24 hours.Defaults to 1 hour and must + be at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative + to the mount point of the file to project + the token into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the + host that shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default + is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte + volume to be mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple + Quobyte Registry services specified as a string + as host:port pair (multiple entries are separated + with commas) which acts as the central registry + for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume + in the Backend Used with dynamically provisioned + Quobyte volumes, value is set by the plugin + type: string + user: + description: User to map volume access to Defaults + to serivceaccount user + type: string + volume: + description: Volume is a string that references + an already created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount + on the host that shares a pod''s lifetime. More info: + https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for + RBDUser. Default is /etc/ceph/keyring. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication + secret for RBDUser. If provided overrides keyring. + Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent + volume attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Default is + "xfs". + type: string + gateway: + description: The host address of the ScaleIO API + Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection + Domain for the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret + for ScaleIO user and other sensitive information. + If this is not provided, Login operation will + fail. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a + volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created + in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should + populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal + value between 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for + mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This + might be in conflict with other options that affect + the file mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair + in the Data field of the referenced Secret will + be projected into the volume as a file whose name + is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the Secret, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to + set permissions on this file. Must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both + octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This + might be in conflict with other options + that affect the file mode, like fsGroup, + and the result can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys + must be defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace + to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use + for obtaining the StorageOS API credentials. If + not specified, default values will be attempted. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable name + of the StorageOS volume. Volume names are only + unique within a namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope + of the volume within StorageOS. If no namespace + is specified then the Pod's namespace will be + used. This allows the Kubernetes name scoping + to be mirrored within StorageOS for tighter integration. + Set VolumeName to any name to override the default + behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do + not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume + attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) + profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) + profile name. + type: string + volumePath: + description: Path that identifies vSphere volume + vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object type: object tolerations: items: diff --git a/example/volume_mount/configmap.yaml b/example/volume_mount/configmap.yaml new file mode 100644 index 000000000..eb5501a23 --- /dev/null +++ b/example/volume_mount/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: example-configmap + labels: + app: myapplication +data: + my-key: my-value \ No newline at end of file diff --git a/example/volume_mount/redis-cluster.yaml b/example/volume_mount/redis-cluster.yaml new file mode 100644 index 000000000..1b74ee260 --- /dev/null +++ b/example/volume_mount/redis-cluster.yaml @@ -0,0 +1,53 @@ +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisCluster +metadata: + name: redis-cluster +spec: + clusterSize: 3 + clusterVersion: v7 + persistenceEnabled: true + securityContext: + runAsUser: 1000 + fsGroup: 1000 + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 101m + memory: 128Mi + limits: + cpu: 101m + memory: 128Mi + redisSecret: + name: redis-secret + key: password + # imagePullSecrets: + # - name: regcred + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + imagePullPolicy: Always + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 100m + memory: 128Mi + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + volumemount: + volume: + - name: example-config + configMap: + name: example-configmap + mount: + - mountPath: /config + name: example-config \ No newline at end of file diff --git a/example/volume_mount/redis-standalone.yaml b/example/volume_mount/redis-standalone.yaml new file mode 100644 index 000000000..6d1a59f44 --- /dev/null +++ b/example/volume_mount/redis-standalone.yaml @@ -0,0 +1,49 @@ +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: Redis +metadata: + name: redis-standalone +spec: + # redisConfig: + # additionalRedisConfig: redis-external-config + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 101m + memory: 128Mi + limits: + cpu: 101m + memory: 128Mi + # redisSecret: + # name: redis-secret + # key: password + # imagePullSecrets: + # - name: regcred + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + imagePullPolicy: Always + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 100m + memory: 128Mi + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + volumemount: + volume: + - name: example-config + configMap: + name: example-configmap + mount: + - mountPath: /config + name: example-config diff --git a/k8sutils/redis-cluster.go b/k8sutils/redis-cluster.go index e2b1279b5..1b68fca3f 100644 --- a/k8sutils/redis-cluster.go +++ b/k8sutils/redis-cluster.go @@ -53,10 +53,12 @@ func generateRedisClusterContainerParams(cr *redisv1beta1.RedisCluster, readines trueProperty := true falseProperty := false containerProp := containerParameters{ - Role: "cluster", - Image: cr.Spec.KubernetesConfig.Image, - ImagePullPolicy: cr.Spec.KubernetesConfig.ImagePullPolicy, - Resources: cr.Spec.KubernetesConfig.Resources, + Role: "cluster", + Image: cr.Spec.KubernetesConfig.Image, + ImagePullPolicy: cr.Spec.KubernetesConfig.ImagePullPolicy, + Resources: cr.Spec.KubernetesConfig.Resources, + AdditionalVolume: cr.Spec.Storage.VolumeMount.Volume, + AdditionalMountPath: cr.Spec.Storage.VolumeMount.MountPath, } if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { containerProp.EnabledPassword = &trueProperty diff --git a/k8sutils/redis-standalone.go b/k8sutils/redis-standalone.go index 6e82a4eb9..3ca2d81bd 100644 --- a/k8sutils/redis-standalone.go +++ b/k8sutils/redis-standalone.go @@ -101,10 +101,12 @@ func generateRedisStandaloneContainerParams(cr *redisv1beta1.Redis) containerPar trueProperty := true falseProperty := false containerProp := containerParameters{ - Role: "standalone", - Image: cr.Spec.KubernetesConfig.Image, - ImagePullPolicy: cr.Spec.KubernetesConfig.ImagePullPolicy, - Resources: cr.Spec.KubernetesConfig.Resources, + Role: "standalone", + Image: cr.Spec.KubernetesConfig.Image, + ImagePullPolicy: cr.Spec.KubernetesConfig.ImagePullPolicy, + Resources: cr.Spec.KubernetesConfig.Resources, + AdditionalVolume: cr.Spec.Storage.VolumeMount.Volume, + AdditionalMountPath: cr.Spec.Storage.VolumeMount.MountPath, } if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { containerProp.EnabledPassword = &trueProperty diff --git a/k8sutils/statefulset.go b/k8sutils/statefulset.go index e84813eca..c56d4853f 100644 --- a/k8sutils/statefulset.go +++ b/k8sutils/statefulset.go @@ -3,6 +3,11 @@ package k8sutils import ( "context" "fmt" + "path" + redisv1beta1 "redis-operator/api/v1beta1" + "sort" + "strconv" + "github.com/banzaicloud/k8s-objectmatcher/patch" "github.com/go-logr/logr" appsv1 "k8s.io/api/apps/v1" @@ -11,10 +16,6 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - "path" - redisv1beta1 "redis-operator/api/v1beta1" - "sort" - "strconv" ) const ( @@ -55,6 +56,8 @@ type containerParameters struct { TLSConfig *redisv1beta1.TLSConfig ReadinessProbe *redisv1beta1.Probe LivenessProbe *redisv1beta1.Probe + AdditionalVolume []corev1.Volume + AdditionalMountPath []corev1.VolumeMount } // CreateOrUpdateStateFul method will create or update Redis service @@ -193,7 +196,7 @@ func generateStatefulSetsDef(stsMeta metav1.ObjectMeta, params statefulSetParame Annotations: generateStatefulSetsAnots(stsMeta), }, Spec: corev1.PodSpec{ - Containers: generateContainerDef(stsMeta.GetName(), containerParams, params.EnableMetrics, params.ExternalConfig, sidecars), + Containers: generateContainerDef(stsMeta.GetName(), containerParams, params.EnableMetrics, params.ExternalConfig, containerParams.AdditionalMountPath, sidecars), NodeSelector: params.NodeSelector, SecurityContext: params.SecurityContext, PriorityClassName: params.PriorityClassName, @@ -214,6 +217,9 @@ func generateStatefulSetsDef(stsMeta metav1.ObjectMeta, params statefulSetParame if params.ExternalConfig != nil { statefulset.Spec.Template.Spec.Volumes = getExternalConfig(*params.ExternalConfig) } + if containerParams.AdditionalVolume != nil { + statefulset.Spec.Template.Spec.Volumes = append(statefulset.Spec.Template.Spec.Volumes, containerParams.AdditionalVolume...) + } if containerParams.TLSConfig != nil { statefulset.Spec.Template.Spec.Volumes = append(statefulset.Spec.Template.Spec.Volumes, @@ -273,7 +279,7 @@ func createPVCTemplate(stsMeta metav1.ObjectMeta, storageSpec corev1.PersistentV } // generateContainerDef generates container definition for Redis -func generateContainerDef(name string, containerParams containerParameters, enableMetrics bool, externalConfig *string, sidecars []redisv1beta1.Sidecar) []corev1.Container { +func generateContainerDef(name string, containerParams containerParameters, enableMetrics bool, externalConfig *string, mountpath []corev1.VolumeMount, sidecars []redisv1beta1.Sidecar) []corev1.Container { containerDefinition := []corev1.Container{ { Name: name, @@ -291,7 +297,7 @@ func generateContainerDef(name string, containerParams containerParameters, enab ), ReadinessProbe: getProbeInfo(containerParams.ReadinessProbe), LivenessProbe: getProbeInfo(containerParams.LivenessProbe), - VolumeMounts: getVolumeMount(name, containerParams.PersistenceEnabled, externalConfig, containerParams.TLSConfig), + VolumeMounts: getVolumeMount(name, containerParams.PersistenceEnabled, externalConfig, mountpath, containerParams.TLSConfig), }, } @@ -372,7 +378,7 @@ func enableRedisMonitoring(params containerParameters) corev1.Container { params.RedisExporterEnv, params.TLSConfig, ), - VolumeMounts: getVolumeMount("", nil, nil, params.TLSConfig), // We need/want the tls-certs but we DON'T need the PVC (if one is available) + VolumeMounts: getVolumeMount("", nil, nil, params.AdditionalMountPath, params.TLSConfig), // We need/want the tls-certs but we DON'T need the PVC (if one is available) } if params.RedisExporterResources != nil { exporterDefinition.Resources = *params.RedisExporterResources @@ -381,7 +387,7 @@ func enableRedisMonitoring(params containerParameters) corev1.Container { } // getVolumeMount gives information about persistence mount -func getVolumeMount(name string, persistenceEnabled *bool, externalConfig *string, tlsConfig *redisv1beta1.TLSConfig) []corev1.VolumeMount { +func getVolumeMount(name string, persistenceEnabled *bool, externalConfig *string, mountpath []corev1.VolumeMount, tlsConfig *redisv1beta1.TLSConfig) []corev1.VolumeMount { var VolumeMounts []corev1.VolumeMount if persistenceEnabled != nil && *persistenceEnabled { @@ -406,6 +412,8 @@ func getVolumeMount(name string, persistenceEnabled *bool, externalConfig *strin }) } + VolumeMounts = append(VolumeMounts, mountpath...) + return VolumeMounts } From be426dcf3e4efe8c5fd23a9346c53bb1fe224c79 Mon Sep 17 00:00:00 2001 From: Anish Chakraborty Date: Tue, 6 Dec 2022 13:21:13 +0100 Subject: [PATCH 05/18] export redis exporter as a container port (#393) Signed-off-by: Anish C Signed-off-by: Anish C --- k8sutils/services.go | 7 ++++--- k8sutils/statefulset.go | 7 +++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/k8sutils/services.go b/k8sutils/services.go index 8e414a2a9..4386c8924 100644 --- a/k8sutils/services.go +++ b/k8sutils/services.go @@ -12,8 +12,9 @@ import ( ) const ( - redisPort = 6379 - redisExporterPort = 9121 + redisPort = 6379 + redisExporterPort = 9121 + redisExporterPortName = "redis-exporter" ) var ( @@ -53,7 +54,7 @@ func generateServiceDef(serviceMeta metav1.ObjectMeta, enableMetrics bool, owner // enableMetricsPort will enable the metrics for Redis service func enableMetricsPort() *corev1.ServicePort { return &corev1.ServicePort{ - Name: "redis-exporter", + Name: redisExporterPortName, Port: redisExporterPort, TargetPort: intstr.FromInt(int(redisExporterPort)), Protocol: corev1.ProtocolTCP, diff --git a/k8sutils/statefulset.go b/k8sutils/statefulset.go index c56d4853f..2ff34b52f 100644 --- a/k8sutils/statefulset.go +++ b/k8sutils/statefulset.go @@ -379,6 +379,13 @@ func enableRedisMonitoring(params containerParameters) corev1.Container { params.TLSConfig, ), VolumeMounts: getVolumeMount("", nil, nil, params.AdditionalMountPath, params.TLSConfig), // We need/want the tls-certs but we DON'T need the PVC (if one is available) + Ports: []corev1.ContainerPort{ + { + Name: redisExporterPortName, + ContainerPort: redisExporterPort, + Protocol: corev1.ProtocolTCP, + }, + }, } if params.RedisExporterResources != nil { exporterDefinition.Resources = *params.RedisExporterResources From 320278569351d06e30be4e723b373a341f4c54ad Mon Sep 17 00:00:00 2001 From: tigerK Date: Mon, 19 Dec 2022 15:35:44 +0800 Subject: [PATCH 06/18] fix: invalid memory address or nil pointer dereference (#395) Fixes: #394,#383 Signed-off-by: lvyanru Signed-off-by: lvyanru --- api/v1beta1/common_types.go | 2 +- api/v1beta1/rediscluster_types.go | 4 ++-- .../bases/redis.redis.opstreelabs.in_redis.yaml | 2 +- ...redis.redis.opstreelabs.in_redisclusters.yaml | 2 +- k8sutils/redis-cluster.go | 14 ++++++++------ k8sutils/redis-standalone.go | 16 ++++++++++------ 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go index 44c3e54c7..1ba457741 100644 --- a/api/v1beta1/common_types.go +++ b/api/v1beta1/common_types.go @@ -53,7 +53,7 @@ type ExistingPasswordSecret struct { // Storage is the inteface to add pvc and pv support in redis type Storage struct { VolumeClaimTemplate corev1.PersistentVolumeClaim `json:"volumeClaimTemplate,omitempty"` - VolumeMount AdditionalVolume `json:"volumemount,omitempty"` + VolumeMount AdditionalVolume `json:"volumeMount,omitempty"` } // Additional Volume is provided by user that is mounted on the pods diff --git a/api/v1beta1/rediscluster_types.go b/api/v1beta1/rediscluster_types.go index 5f91a4963..6c2f025c5 100644 --- a/api/v1beta1/rediscluster_types.go +++ b/api/v1beta1/rediscluster_types.go @@ -89,8 +89,8 @@ type RedisPodDisruptionBudget struct { MaxUnavailable *int32 `json:"maxUnavailable,omitempty"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="ClusterSize",type=integer,JSONPath=`.spec.clusterSize`,description=Current cluster node count // +kubebuilder:printcolumn:name="LeaderReplicas",type=integer,JSONPath=`.spec.redisLeader.replicas`,description=Overridden Leader replica count // +kubebuilder:printcolumn:name="FollowerReplicas",type=integer,JSONPath=`.spec.redisFollower.replicas`,description=Overridden Follower replica count diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml index 45e1355de..3238e69aa 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml @@ -1873,7 +1873,7 @@ spec: type: string type: object type: object - volumemount: + volumeMount: description: Additional Volume is provided by user that is mounted on the pods properties: diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml index 87265c658..210445a36 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml @@ -2974,7 +2974,7 @@ spec: type: string type: object type: object - volumemount: + volumeMount: description: Additional Volume is provided by user that is mounted on the pods properties: diff --git a/k8sutils/redis-cluster.go b/k8sutils/redis-cluster.go index 1b68fca3f..a5e3092b6 100644 --- a/k8sutils/redis-cluster.go +++ b/k8sutils/redis-cluster.go @@ -53,12 +53,14 @@ func generateRedisClusterContainerParams(cr *redisv1beta1.RedisCluster, readines trueProperty := true falseProperty := false containerProp := containerParameters{ - Role: "cluster", - Image: cr.Spec.KubernetesConfig.Image, - ImagePullPolicy: cr.Spec.KubernetesConfig.ImagePullPolicy, - Resources: cr.Spec.KubernetesConfig.Resources, - AdditionalVolume: cr.Spec.Storage.VolumeMount.Volume, - AdditionalMountPath: cr.Spec.Storage.VolumeMount.MountPath, + Role: "cluster", + Image: cr.Spec.KubernetesConfig.Image, + ImagePullPolicy: cr.Spec.KubernetesConfig.ImagePullPolicy, + Resources: cr.Spec.KubernetesConfig.Resources, + } + if cr.Spec.Storage != nil { + containerProp.AdditionalVolume = cr.Spec.Storage.VolumeMount.Volume + containerProp.AdditionalMountPath = cr.Spec.Storage.VolumeMount.MountPath } if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { containerProp.EnabledPassword = &trueProperty diff --git a/k8sutils/redis-standalone.go b/k8sutils/redis-standalone.go index 3ca2d81bd..9c51f2751 100644 --- a/k8sutils/redis-standalone.go +++ b/k8sutils/redis-standalone.go @@ -101,13 +101,17 @@ func generateRedisStandaloneContainerParams(cr *redisv1beta1.Redis) containerPar trueProperty := true falseProperty := false containerProp := containerParameters{ - Role: "standalone", - Image: cr.Spec.KubernetesConfig.Image, - ImagePullPolicy: cr.Spec.KubernetesConfig.ImagePullPolicy, - Resources: cr.Spec.KubernetesConfig.Resources, - AdditionalVolume: cr.Spec.Storage.VolumeMount.Volume, - AdditionalMountPath: cr.Spec.Storage.VolumeMount.MountPath, + Role: "standalone", + Image: cr.Spec.KubernetesConfig.Image, + ImagePullPolicy: cr.Spec.KubernetesConfig.ImagePullPolicy, + Resources: cr.Spec.KubernetesConfig.Resources, } + + if cr.Spec.Storage != nil { + containerProp.AdditionalVolume = cr.Spec.Storage.VolumeMount.Volume + containerProp.AdditionalMountPath = cr.Spec.Storage.VolumeMount.MountPath + } + if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { containerProp.EnabledPassword = &trueProperty containerProp.SecretName = cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name From 0e975e11c37dab8c040ef53291a0efd06b61a486 Mon Sep 17 00:00:00 2001 From: jiuker <2818723467@qq.com> Date: Wed, 28 Dec 2022 16:23:44 +0800 Subject: [PATCH 07/18] When cr annotation update,sts annotations will not updated! (#398) * When cr annotation update,sts annotations will not updated! Signed-off-by: jiuker * only the spec can be equal Signed-off-by: jiuker * only the spec can be equal Signed-off-by: jiuker * Update CHANGELOG.md Signed-off-by: jiuker Signed-off-by: jiuker Co-authored-by: jiuker --- CHANGELOG.md | 1 + k8sutils/statefulset.go | 110 ++++++++++++++++++++-------------------- 2 files changed, 57 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a77877022..fdf2139d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ #### :beetle: Bug Fixes - Fixed multiple follower logic for redis cluster +- Fixed CR's annotations updated,sts annotations will not updated #### :tada: Features diff --git a/k8sutils/statefulset.go b/k8sutils/statefulset.go index 2ff34b52f..7635ace88 100644 --- a/k8sutils/statefulset.go +++ b/k8sutils/statefulset.go @@ -99,69 +99,71 @@ func patchStatefulSet(storedStateful *appsv1.StatefulSet, newStateful *appsv1.St } if !patchResult.IsEmpty() { logger.Info("Changes in statefulset Detected, Updating...", "patch", string(patchResult.Patch)) - // Field is immutable therefore we MUST keep it as is. - if !apiequality.Semantic.DeepEqual(newStateful.Spec.VolumeClaimTemplates, storedStateful.Spec.VolumeClaimTemplates) { - // resize pvc - // 1.Get the data already stored internally - // 2.Get the desired data - // 3.Start querying the pvc list when you find data inconsistencies - // 3.1 Comparison using real pvc capacity and desired data - // 3.1.1 Update if you find inconsistencies - // 3.2 Writing successful updates to internal - // 4. Set to old VolumeClaimTemplates to update.Prevent update error reporting - // 5. Set to old annotations to update - annotations := storedStateful.Annotations - if annotations == nil { - annotations = map[string]string{ - "storageCapacity": "0", - } - } - storedCapacity, _ := strconv.ParseInt(annotations["storageCapacity"], 0, 64) - if len(newStateful.Spec.VolumeClaimTemplates) != 0 { - stateCapacity := newStateful.Spec.VolumeClaimTemplates[0].Spec.Resources.Requests.Storage().Value() - if storedCapacity != stateCapacity { - listOpt := metav1.ListOptions{ - LabelSelector: labels.FormatLabels( - map[string]string{ - "app": storedStateful.Name, - "app.kubernetes.io/component": "redis", - "app.kubernetes.io/name": storedStateful.Name, - }, - ), + if len(newStateful.Spec.VolumeClaimTemplates) >= 1 && len(newStateful.Spec.VolumeClaimTemplates) == len(storedStateful.Spec.VolumeClaimTemplates) { + // Field is immutable therefore we MUST keep it as is. + if !apiequality.Semantic.DeepEqual(newStateful.Spec.VolumeClaimTemplates[0].Spec, storedStateful.Spec.VolumeClaimTemplates[0].Spec) { + // resize pvc + // 1.Get the data already stored internally + // 2.Get the desired data + // 3.Start querying the pvc list when you find data inconsistencies + // 3.1 Comparison using real pvc capacity and desired data + // 3.1.1 Update if you find inconsistencies + // 3.2 Writing successful updates to internal + // 4. Set to old VolumeClaimTemplates to update.Prevent update error reporting + // 5. Set to old annotations to update + annotations := storedStateful.Annotations + if annotations == nil { + annotations = map[string]string{ + "storageCapacity": "0", } - pvcs, err := generateK8sClient().CoreV1().PersistentVolumeClaims(storedStateful.Namespace).List(context.Background(), listOpt) - if err != nil { - return err - } - updateFailed := false - realUpdate := false - for _, pvc := range pvcs.Items { - realCapacity := pvc.Spec.Resources.Requests.Storage().Value() - if realCapacity != stateCapacity { - realUpdate = true - pvc.Spec.Resources.Requests = newStateful.Spec.VolumeClaimTemplates[0].Spec.Resources.Requests - _, err = generateK8sClient().CoreV1().PersistentVolumeClaims(storedStateful.Namespace).Update(context.Background(), &pvc, metav1.UpdateOptions{}) - if err != nil { - if !updateFailed { - updateFailed = true + } + storedCapacity, _ := strconv.ParseInt(annotations["storageCapacity"], 0, 64) + if len(newStateful.Spec.VolumeClaimTemplates) != 0 { + stateCapacity := newStateful.Spec.VolumeClaimTemplates[0].Spec.Resources.Requests.Storage().Value() + if storedCapacity != stateCapacity { + listOpt := metav1.ListOptions{ + LabelSelector: labels.FormatLabels( + map[string]string{ + "app": storedStateful.Name, + "app.kubernetes.io/component": "redis", + "app.kubernetes.io/name": storedStateful.Name, + }, + ), + } + pvcs, err := generateK8sClient().CoreV1().PersistentVolumeClaims(storedStateful.Namespace).List(context.Background(), listOpt) + if err != nil { + return err + } + updateFailed := false + realUpdate := false + for _, pvc := range pvcs.Items { + realCapacity := pvc.Spec.Resources.Requests.Storage().Value() + if realCapacity != stateCapacity { + realUpdate = true + pvc.Spec.Resources.Requests = newStateful.Spec.VolumeClaimTemplates[0].Spec.Resources.Requests + _, err = generateK8sClient().CoreV1().PersistentVolumeClaims(storedStateful.Namespace).Update(context.Background(), &pvc, metav1.UpdateOptions{}) + if err != nil { + if !updateFailed { + updateFailed = true + } + logger.Error(fmt.Errorf("redis:%s resize pvc failed:%s", storedStateful.Name, err.Error()), "") } - logger.Error(fmt.Errorf("redis:%s resize pvc failed:%s", storedStateful.Name, err.Error()), "") } } - } - if !updateFailed && len(pvcs.Items) != 0 { - annotations["storageCapacity"] = fmt.Sprintf("%d", stateCapacity) - storedStateful.Annotations = annotations - if realUpdate { - logger.Info(fmt.Sprintf("redis:%s resize pvc from %d to %d", storedStateful.Name, storedCapacity, stateCapacity)) - } else { - logger.Info(fmt.Sprintf("redis:%s resize noting,just set annotations", storedStateful.Name)) + if !updateFailed && len(pvcs.Items) != 0 { + annotations["storageCapacity"] = fmt.Sprintf("%d", stateCapacity) + storedStateful.Annotations = annotations + if realUpdate { + logger.Info(fmt.Sprintf("redis:%s resize pvc from %d to %d", storedStateful.Name, storedCapacity, stateCapacity)) + } else { + logger.Info(fmt.Sprintf("redis:%s resize noting,just set annotations", storedStateful.Name)) + } } } } } + newStateful.Annotations["storageCapacity"] = storedStateful.Annotations["storageCapacity"] // set stored.volumeClaimTemplates - newStateful.Annotations = storedStateful.Annotations newStateful.Spec.VolumeClaimTemplates = storedStateful.Spec.VolumeClaimTemplates } From 4e37f66802b495bba4f579fac236fe80ddcd0981 Mon Sep 17 00:00:00 2001 From: Shubham Gupta <69793468+shubham-cmyk@users.noreply.github.com> Date: Mon, 16 Jan 2023 17:10:34 +0530 Subject: [PATCH 08/18] Add Label Selector to pod anti affinity (#407) * pod anti affinity * update * update 2 --- example/affinity/clusterd.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/example/affinity/clusterd.yaml b/example/affinity/clusterd.yaml index 3d9de9372..535a98fde 100644 --- a/example/affinity/clusterd.yaml +++ b/example/affinity/clusterd.yaml @@ -26,6 +26,7 @@ spec: operator: In values: - redis-cluster-leader + - redis-cluster-follower topologyKey: "kubernetes.io/hostname" redisFollower: affinity: @@ -37,6 +38,7 @@ spec: operator: In values: - redis-cluster-follower + - redis-cluster-leader topologyKey: "kubernetes.io/hostname" storage: volumeClaimTemplate: From cbcd2ca28130390f13a8f80c8c5b52fd47990b28 Mon Sep 17 00:00:00 2001 From: Abhishek Dubey Date: Mon, 16 Jan 2023 22:09:47 +0530 Subject: [PATCH 09/18] [Development][Add] Added nodeSelector and tolerations for cluster (#410) * Added node selector and tolerations for leader and follower Signed-off-by: iamabhishek-dubey * Added example for node selector and tolerations Signed-off-by: iamabhishek-dubey --- .azure-pipelines/pipeline.yaml | 2 +- Makefile | 2 +- api/v1beta1/rediscluster_types.go | 10 +- api/v1beta1/zz_generated.deepcopy.go | 54 +++++--- ...is.redis.opstreelabs.in_redisclusters.yaml | 131 ++++++++++++------ example/eks-cluster.yaml | 1 + example/node-selector/clusterd.yaml | 32 +++++ example/node-selector/standalone.yaml | 25 ++++ example/tolerations/clusterd.yaml | 38 +++++ example/tolerations/standalone.yaml | 28 ++++ k8sutils/redis-cluster.go | 16 ++- 11 files changed, 267 insertions(+), 72 deletions(-) create mode 100644 example/node-selector/clusterd.yaml create mode 100644 example/node-selector/standalone.yaml create mode 100644 example/tolerations/clusterd.yaml create mode 100644 example/tolerations/standalone.yaml diff --git a/.azure-pipelines/pipeline.yaml b/.azure-pipelines/pipeline.yaml index 49e122bc5..632975297 100644 --- a/.azure-pipelines/pipeline.yaml +++ b/.azure-pipelines/pipeline.yaml @@ -24,5 +24,5 @@ extends: QuayImageName: opstree/redis-operator GithubImageName: ot-container-kit/redis-operator/redis-operator BuildDocs: false - AppVersion: "v0.13.0" + AppVersion: "v0.14.0" GolangVersion: "1.17" diff --git a/Makefile b/Makefile index 9b906c919..e087eb3bc 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # Current Operator version -VERSION ?= 0.13.0 +VERSION ?= 0.14.0 # Default bundle image tag BUNDLE_IMG ?= controller-bundle:$(VERSION) # Options for 'bundle-build' diff --git a/api/v1beta1/rediscluster_types.go b/api/v1beta1/rediscluster_types.go index 6c2f025c5..489c16712 100644 --- a/api/v1beta1/rediscluster_types.go +++ b/api/v1beta1/rediscluster_types.go @@ -33,10 +33,8 @@ type RedisClusterSpec struct { RedisFollower RedisFollower `json:"redisFollower,omitempty"` RedisExporter *RedisExporter `json:"redisExporter,omitempty"` Storage *Storage `json:"storage,omitempty"` - NodeSelector map[string]string `json:"nodeSelector,omitempty"` SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` PriorityClassName string `json:"priorityClassName,omitempty"` - Tolerations *[]corev1.Toleration `json:"tolerations,omitempty"` Resources *corev1.ResourceRequirements `json:"resources,omitempty"` TLS *TLSConfig `json:"TLS,omitempty"` Sidecars *[]Sidecar `json:"sidecars,omitempty"` @@ -63,7 +61,9 @@ type RedisLeader struct { // +kubebuilder:default:={initialDelaySeconds: 1, timeoutSeconds: 1, periodSeconds: 10, successThreshold: 1, failureThreshold:3} ReadinessProbe *Probe `json:"readinessProbe,omitempty" protobuf:"bytes,11,opt,name=readinessProbe"` // +kubebuilder:default:={initialDelaySeconds: 1, timeoutSeconds: 1, periodSeconds: 10, successThreshold: 1, failureThreshold:3} - LivenessProbe *Probe `json:"livenessProbe,omitempty" protobuf:"bytes,11,opt,name=livenessProbe"` + LivenessProbe *Probe `json:"livenessProbe,omitempty" protobuf:"bytes,11,opt,name=livenessProbe"` + Tolerations *[]corev1.Toleration `json:"tolerations,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` } // RedisFollower interface will have the redis follower configuration @@ -75,7 +75,9 @@ type RedisFollower struct { // +kubebuilder:default:={initialDelaySeconds: 1, timeoutSeconds: 1, periodSeconds: 10, successThreshold: 1, failureThreshold:3} ReadinessProbe *Probe `json:"readinessProbe,omitempty" protobuf:"bytes,11,opt,name=readinessProbe"` // +kubebuilder:default:={initialDelaySeconds: 1, timeoutSeconds: 1, periodSeconds: 10, successThreshold: 1, failureThreshold:3} - LivenessProbe *Probe `json:"livenessProbe,omitempty" protobuf:"bytes,11,opt,name=livenessProbe"` + LivenessProbe *Probe `json:"livenessProbe,omitempty" protobuf:"bytes,11,opt,name=livenessProbe"` + Tolerations *[]corev1.Toleration `json:"tolerations,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` } // RedisClusterStatus defines the observed state of RedisCluster diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index d430ef851..461c03e35 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -247,29 +247,11 @@ func (in *RedisClusterSpec) DeepCopyInto(out *RedisClusterSpec) { *out = new(Storage) (*in).DeepCopyInto(*out) } - if in.NodeSelector != nil { - in, out := &in.NodeSelector, &out.NodeSelector - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } if in.SecurityContext != nil { in, out := &in.SecurityContext, &out.SecurityContext *out = new(v1.PodSecurityContext) (*in).DeepCopyInto(*out) } - if in.Tolerations != nil { - in, out := &in.Tolerations, &out.Tolerations - *out = new([]v1.Toleration) - if **in != nil { - in, out := *in, *out - *out = make([]v1.Toleration, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - } if in.Resources != nil { in, out := &in.Resources, &out.Resources *out = new(v1.ResourceRequirements) @@ -412,6 +394,24 @@ func (in *RedisFollower) DeepCopyInto(out *RedisFollower) { *out = new(Probe) **out = **in } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = new([]v1.Toleration) + if **in != nil { + in, out := *in, *out + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisFollower. @@ -457,6 +457,24 @@ func (in *RedisLeader) DeepCopyInto(out *RedisLeader) { *out = new(Probe) **out = **in } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = new([]v1.Toleration) + if **in != nil { + in, out := *in, *out + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisLeader. diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml index 210445a36..bb3a74454 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml @@ -230,10 +230,6 @@ spec: required: - image type: object - nodeSelector: - additionalProperties: - type: string - type: object persistenceEnabled: type: boolean priorityClassName: @@ -1306,6 +1302,10 @@ spec: minimum: 1 type: integer type: object + nodeSelector: + additionalProperties: + type: string + type: object pdb: description: RedisPodDisruptionBudget configure a PodDisruptionBudget on the resource (leader/follower) @@ -1364,6 +1364,46 @@ spec: replicas: format: int32 type: integer + tolerations: + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array type: object redisLeader: default: @@ -2281,6 +2321,10 @@ spec: minimum: 1 type: integer type: object + nodeSelector: + additionalProperties: + type: string + type: object pdb: description: RedisPodDisruptionBudget configure a PodDisruptionBudget on the resource (leader/follower) @@ -2339,6 +2383,46 @@ spec: replicas: format: int32 type: integer + tolerations: + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array type: object resources: description: ResourceRequirements describes the compute resource requirements. @@ -4625,45 +4709,6 @@ spec: type: array type: object type: object - tolerations: - items: - description: The pod this Toleration is attached to tolerates any - taint that matches the triple using the matching - operator . - properties: - effect: - description: Effect indicates the taint effect to match. Empty - means match all taint effects. When specified, allowed values - are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: Key is the taint key that the toleration applies - to. Empty means match all taint keys. If the key is empty, - operator must be Exists; this combination means to match all - values and all keys. - type: string - operator: - description: Operator represents a key's relationship to the - value. Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod - can tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period of time - the toleration (which must be of effect NoExecute, otherwise - this field is ignored) tolerates the taint. By default, it - is not set, which means tolerate the taint forever (do not - evict). Zero and negative values will be treated as 0 (evict - immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. - type: string - type: object - type: array required: - clusterSize - kubernetesConfig diff --git a/example/eks-cluster.yaml b/example/eks-cluster.yaml index fa5aecdf9..c2481d46c 100644 --- a/example/eks-cluster.yaml +++ b/example/eks-cluster.yaml @@ -19,5 +19,6 @@ addons: - name: vpc-cni - name: coredns - name: kube-proxy + - name: ebs-csi-driver iam: withOIDC: true diff --git a/example/node-selector/clusterd.yaml b/example/node-selector/clusterd.yaml new file mode 100644 index 000000000..df8520ca8 --- /dev/null +++ b/example/node-selector/clusterd.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisCluster +metadata: + name: redis-cluster +spec: + clusterSize: 3 + clusterVersion: v7 + securityContext: + runAsUser: 1000 + fsGroup: 1000 + persistenceEnabled: true + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + redisLeader: + nodeSelector: + kubernetes.io/os: linux + redisFollower: + nodeSelector: + kubernetes.io/os: linux + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi diff --git a/example/node-selector/standalone.yaml b/example/node-selector/standalone.yaml new file mode 100644 index 000000000..72596236b --- /dev/null +++ b/example/node-selector/standalone.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: Redis +metadata: + name: redis-standalone +spec: + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 1000 + fsGroup: 1000 + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + nodeSelector: + kubernetes.io/os: linux diff --git a/example/tolerations/clusterd.yaml b/example/tolerations/clusterd.yaml new file mode 100644 index 000000000..77fe6a068 --- /dev/null +++ b/example/tolerations/clusterd.yaml @@ -0,0 +1,38 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisCluster +metadata: + name: redis-cluster +spec: + clusterSize: 3 + clusterVersion: v7 + securityContext: + runAsUser: 1000 + fsGroup: 1000 + persistenceEnabled: true + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + redisLeader: + tolerations: + - key: "key1" + operator: "Equal" + value: "value1" + effect: "NoSchedule" + redisFollower: + tolerations: + - key: "key1" + operator: "Equal" + value: "value1" + effect: "NoSchedule" + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi diff --git a/example/tolerations/standalone.yaml b/example/tolerations/standalone.yaml new file mode 100644 index 000000000..378409fa9 --- /dev/null +++ b/example/tolerations/standalone.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: Redis +metadata: + name: redis-standalone +spec: + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 1000 + fsGroup: 1000 + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + tolerations: + - key: "key1" + operator: "Equal" + value: "value1" + effect: "NoSchedule" diff --git a/k8sutils/redis-cluster.go b/k8sutils/redis-cluster.go index a5e3092b6..2ba427436 100644 --- a/k8sutils/redis-cluster.go +++ b/k8sutils/redis-cluster.go @@ -13,6 +13,8 @@ type RedisClusterSTS struct { Affinity *corev1.Affinity `json:"affinity,omitempty"` ReadinessProbe *redisv1beta1.Probe LivenessProbe *redisv1beta1.Probe + NodeSelector map[string]string + Tolerations *[]corev1.Toleration } // RedisClusterService is a interface to call Redis Service function @@ -21,15 +23,15 @@ type RedisClusterService struct { } // generateRedisClusterParams generates Redis cluster information -func generateRedisClusterParams(cr *redisv1beta1.RedisCluster, replicas int32, externalConfig *string, affinity *corev1.Affinity) statefulSetParameters { +func generateRedisClusterParams(cr *redisv1beta1.RedisCluster, replicas int32, externalConfig *string, params RedisClusterSTS) statefulSetParameters { res := statefulSetParameters{ Metadata: cr.ObjectMeta, Replicas: &replicas, - NodeSelector: cr.Spec.NodeSelector, + NodeSelector: params.NodeSelector, SecurityContext: cr.Spec.SecurityContext, PriorityClassName: cr.Spec.PriorityClassName, - Affinity: affinity, - Tolerations: cr.Spec.Tolerations, + Affinity: params.Affinity, + Tolerations: params.Tolerations, ServiceAccountName: cr.Spec.ServiceAccountName, UpdateStrategy: cr.Spec.KubernetesConfig.UpdateStrategy, } @@ -105,6 +107,8 @@ func CreateRedisLeader(cr *redisv1beta1.RedisCluster) error { prop := RedisClusterSTS{ RedisStateFulType: "leader", Affinity: cr.Spec.RedisLeader.Affinity, + NodeSelector: cr.Spec.RedisLeader.NodeSelector, + Tolerations: cr.Spec.RedisLeader.Tolerations, ReadinessProbe: cr.Spec.RedisLeader.ReadinessProbe, LivenessProbe: cr.Spec.RedisLeader.LivenessProbe, } @@ -119,6 +123,8 @@ func CreateRedisFollower(cr *redisv1beta1.RedisCluster) error { prop := RedisClusterSTS{ RedisStateFulType: "follower", Affinity: cr.Spec.RedisFollower.Affinity, + NodeSelector: cr.Spec.RedisFollower.NodeSelector, + Tolerations: cr.Spec.RedisFollower.Tolerations, ReadinessProbe: cr.Spec.RedisFollower.ReadinessProbe, LivenessProbe: cr.Spec.RedisFollower.LivenessProbe, } @@ -158,7 +164,7 @@ func (service RedisClusterSTS) CreateRedisClusterSetup(cr *redisv1beta1.RedisClu err := CreateOrUpdateStateFul( cr.Namespace, objectMetaInfo, - generateRedisClusterParams(cr, service.getReplicaCount(cr), service.ExternalConfig, service.Affinity), + generateRedisClusterParams(cr, service.getReplicaCount(cr), service.ExternalConfig, service), redisClusterAsOwner(cr), generateRedisClusterContainerParams(cr, service.ReadinessProbe, service.LivenessProbe), cr.Spec.Sidecars, From 8762e50069d8946992bf49e377b084e8c3b2553f Mon Sep 17 00:00:00 2001 From: Michael Primeaux Date: Mon, 16 Jan 2023 11:45:03 -0600 Subject: [PATCH 10/18] Fixes issue with arm64 support. (#404) * Addresses issue [#390](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/391) by adding support for multi-architecture container images. * Removed --platform due to linting rule constraints. * Removed echo statements due to lint rules. Signed-off-by: Michael Primeaux --- Dockerfile | 18 +++++++++++++++--- Makefile | 7 +++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7f924e863..c643513b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,13 @@ # Build the manager binary -ARG ARCH="amd64" FROM golang:1.17 as builder +ARG BUILDOS +ARG BUILDPLATFORM +ARG BUILDARCH +ARG BUILDVARIANT +ARG TARGETPLATFORM +ARG TARGETOS +ARG TARGETARCH +ARG TARGETVARIANT WORKDIR /workspace # Copy the Go Modules manifests @@ -17,11 +24,16 @@ COPY controllers/ controllers/ COPY k8sutils/ k8sutils/ # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=${ARCH} GO111MODULE=on go build -a -o manager main.go +ARG LDFLAGS="-s -w" +ENV GOOS=$TARGETOS +ENV GOARCH=$TARGETARCH +ENV CGO_ENABLED=0 + +RUN GO111MODULE=on go build -ldflags "${LDFLAGS}" -a -o manager main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details -FROM gcr.io/distroless/static:nonroot-${ARCH} +FROM gcr.io/distroless/static:nonroot LABEL maintainer="The Opstree Opensource " WORKDIR / COPY --from=builder /workspace/manager . diff --git a/Makefile b/Makefile index e087eb3bc..28b9a94a2 100644 --- a/Makefile +++ b/Makefile @@ -73,9 +73,12 @@ vet: generate: controller-gen $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." +docker-create: + docker buildx create --platform "linux/amd64,linux/arm64" --use + # Build the docker image docker-build: - docker build -t ${IMG} . + docker buildx build --platform="linux/arm64,linux/amd64" -t ${IMG} . # Push the docker image docker-push: @@ -116,4 +119,4 @@ bundle: manifests kustomize # Build the bundle image. .PHONY: bundle-build bundle-build: - docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . + docker buildx build --platform="linux/arm64,linux/amd64" -f bundle.Dockerfile -t $(BUNDLE_IMG) . From 77b42752e267d3a894c38d2b6926428648364907 Mon Sep 17 00:00:00 2001 From: Abhishek Dubey Date: Tue, 17 Jan 2023 15:15:15 +0530 Subject: [PATCH 11/18] [Development][Add] Added recreation logic for statefulset (#411) * Added code for recreate statefulsets * Added logic for standalone and cluster recreate * Added push command for buildx * Added example for recreation of sts Signed-off-by: iamabhishek-dubey --- Makefile | 2 +- example/eks-cluster.yaml | 4 ++- example/recreate-statefulset/clusterd.yaml | 29 +++++++++++++++++ example/recreate-statefulset/standalone.yaml | 26 +++++++++++++++ k8sutils/redis-cluster.go | 3 ++ k8sutils/redis-standalone.go | 3 ++ k8sutils/statefulset.go | 34 ++++++++++++++------ 7 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 example/recreate-statefulset/clusterd.yaml create mode 100644 example/recreate-statefulset/standalone.yaml diff --git a/Makefile b/Makefile index 28b9a94a2..986346ca3 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ docker-build: # Push the docker image docker-push: - docker push ${IMG} + docker buildx build --push --platform="linux/arm64,linux/amd64" -t ${IMG} . # Download controller-gen locally if necessary CONTROLLER_GEN = $(shell pwd)/bin/controller-gen diff --git a/example/eks-cluster.yaml b/example/eks-cluster.yaml index c2481d46c..4474537e3 100644 --- a/example/eks-cluster.yaml +++ b/example/eks-cluster.yaml @@ -19,6 +19,8 @@ addons: - name: vpc-cni - name: coredns - name: kube-proxy - - name: ebs-csi-driver + - name: aws-ebs-csi-driver + attachPolicyARNs: + - arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy iam: withOIDC: true diff --git a/example/recreate-statefulset/clusterd.yaml b/example/recreate-statefulset/clusterd.yaml new file mode 100644 index 000000000..f944f8b2c --- /dev/null +++ b/example/recreate-statefulset/clusterd.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisCluster +metadata: + name: redis-cluster + annotations: + redis.opstreelabs.in/recreate-statefulset: "true" +spec: + clusterSize: 3 + clusterVersion: v7 + securityContext: + runAsUser: 1000 + fsGroup: 1000 + persistenceEnabled: true + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + priorityClassName: priority-100 diff --git a/example/recreate-statefulset/standalone.yaml b/example/recreate-statefulset/standalone.yaml new file mode 100644 index 000000000..03b62796f --- /dev/null +++ b/example/recreate-statefulset/standalone.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: Redis +metadata: + name: redis-standalone + annotations: + redis.opstreelabs.in/recreate-statefulset: "true" +spec: + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 1000 + fsGroup: 1000 + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + priorityClassName: system-cluster-critical diff --git a/k8sutils/redis-cluster.go b/k8sutils/redis-cluster.go index 2ba427436..2a5047525 100644 --- a/k8sutils/redis-cluster.go +++ b/k8sutils/redis-cluster.go @@ -47,6 +47,9 @@ func generateRedisClusterParams(cr *redisv1beta1.RedisCluster, replicas int32, e if externalConfig != nil { res.ExternalConfig = externalConfig } + if _, found := cr.ObjectMeta.GetAnnotations()["redis.opstreelabs.in/recreate-statefulset"]; found { + res.RecreateStatefulSet = true + } return res } diff --git a/k8sutils/redis-standalone.go b/k8sutils/redis-standalone.go index 9c51f2751..9b9c02336 100644 --- a/k8sutils/redis-standalone.go +++ b/k8sutils/redis-standalone.go @@ -93,6 +93,9 @@ func generateRedisStandaloneParams(cr *redisv1beta1.Redis) statefulSetParameters if cr.Spec.ServiceAccountName != nil { res.ServiceAccountName = cr.Spec.ServiceAccountName } + if _, found := cr.ObjectMeta.GetAnnotations()["redis.opstreelabs.in/recreate-statefulset"]; found { + res.RecreateStatefulSet = true + } return res } diff --git a/k8sutils/statefulset.go b/k8sutils/statefulset.go index 7635ace88..0cdfa8c70 100644 --- a/k8sutils/statefulset.go +++ b/k8sutils/statefulset.go @@ -7,13 +7,15 @@ import ( redisv1beta1 "redis-operator/api/v1beta1" "sort" "strconv" + "strings" "github.com/banzaicloud/k8s-objectmatcher/patch" "github.com/go-logr/logr" + "github.com/pkg/errors" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" ) @@ -37,6 +39,7 @@ type statefulSetParameters struct { ExternalConfig *string ServiceAccountName *string UpdateStrategy appsv1.StatefulSetUpdateStrategy + RecreateStatefulSet bool } // containerParameters will define container input params @@ -70,16 +73,16 @@ func CreateOrUpdateStateFul(namespace string, stsMeta metav1.ObjectMeta, params logger.Error(err, "Unable to patch redis statefulset with comparison object") return err } - if errors.IsNotFound(err) { + if apierrors.IsNotFound(err) { return createStatefulSet(namespace, statefulSetDef) } return err } - return patchStatefulSet(storedStateful, statefulSetDef, namespace) + return patchStatefulSet(storedStateful, statefulSetDef, namespace, params.RecreateStatefulSet) } // patchStateFulSet will patch Redis Kubernetes StateFulSet -func patchStatefulSet(storedStateful *appsv1.StatefulSet, newStateful *appsv1.StatefulSet, namespace string) error { +func patchStatefulSet(storedStateful *appsv1.StatefulSet, newStateful *appsv1.StatefulSet, namespace string, recreateStateFulSet bool) error { logger := statefulSetLogger(namespace, storedStateful.Name) // We want to try and keep this atomic as possible. @@ -176,7 +179,7 @@ func patchStatefulSet(storedStateful *appsv1.StatefulSet, newStateful *appsv1.St logger.Error(err, "Unable to patch redis statefulset with comparison object") return err } - return updateStatefulSet(namespace, newStateful) + return updateStatefulSet(namespace, newStateful, recreateStateFulSet) } logger.Info("Reconciliation Complete, no Changes required.") return nil @@ -520,15 +523,28 @@ func createStatefulSet(namespace string, stateful *appsv1.StatefulSet) error { } // updateStatefulSet is a method to update statefulset in Kubernetes -func updateStatefulSet(namespace string, stateful *appsv1.StatefulSet) error { +func updateStatefulSet(namespace string, stateful *appsv1.StatefulSet, recreateStateFulSet bool) error { logger := statefulSetLogger(namespace, stateful.Name) - // logger.Info(fmt.Sprintf("Setting Statefulset to the following: %s", stateful)) _, err := generateK8sClient().AppsV1().StatefulSets(namespace).Update(context.TODO(), stateful, metav1.UpdateOptions{}) + if recreateStateFulSet { + sErr, ok := err.(*apierrors.StatusError) + if ok && sErr.ErrStatus.Code == 422 && sErr.ErrStatus.Reason == metav1.StatusReasonInvalid { + failMsg := make([]string, len(sErr.ErrStatus.Details.Causes)) + for messageCount, cause := range sErr.ErrStatus.Details.Causes { + failMsg[messageCount] = cause.Message + } + logger.Info("recreating StatefulSet because the update operation wasn't possible", "reason", strings.Join(failMsg, ", ")) + propagationPolicy := metav1.DeletePropagationForeground + if err := generateK8sClient().AppsV1().StatefulSets(namespace).Delete(context.TODO(), stateful.GetName(), metav1.DeleteOptions{PropagationPolicy: &propagationPolicy}); err != nil { + return errors.Wrap(err, "failed to delete StatefulSet to avoid forbidden action") + } + } + } if err != nil { - logger.Error(err, "Redis stateful update failed") + logger.Error(err, "Redis statefulset update failed") return err } - logger.Info("Redis stateful successfully updated ") + logger.Info("Redis statefulset successfully updated ") return nil } From cd4661e17295d5e958af002d6ccf2aba99b7a116 Mon Sep 17 00:00:00 2001 From: Shubham Gupta <69793468+shubham-cmyk@users.noreply.github.com> Date: Mon, 6 Feb 2023 12:16:13 +0530 Subject: [PATCH 12/18] [Feature] : Add Replication Mode to the Redis Operator (#417) * add Api Support * api + controller * subsentinel * add Finalizers * fix naming convention * fix name convention * create master slave replication * remove extra CRD * add redis replication to reconcile loop * fix naming * fix error and logs * add example * add TLS to redis replication * remove additional field * examples --- api/v1beta1/redisreplication_types.go | 60 + api/v1beta1/zz_generated.deepcopy.go | 169 + ...edis.opstreelabs.in_redisreplications.yaml | 3587 +++++++++++++++++ controllers/redisreplication_controller.go | 102 + example/additional_config/replication.yaml | 26 + example/affinity/replication.yaml | 34 + example/external_service/replication-svc.yaml | 21 + example/external_service/replication.yaml | 24 + example/node-selector/replication.yaml | 26 + example/password_protected/replication.yaml | 27 + example/password_protected/secret.yaml | 8 + example/private_registry/replication.yaml | 26 + example/probes/replication.yaml | 36 + example/recreate-statefulset/replication.yaml | 27 + example/redis-replication.yaml | 60 + example/redis_monitoring/replication.yaml | 30 + example/tls_enabled/redis-replication.yaml | 45 + example/volume_mount/redis-replication.yaml | 50 + go.mod | 2 +- k8sutils/finalizer.go | 78 +- k8sutils/labels.go | 12 + k8sutils/redis-replication.go | 144 + k8sutils/redis.go | 148 + k8sutils/secrets.go | 48 + main.go | 8 + 25 files changed, 4795 insertions(+), 3 deletions(-) create mode 100644 api/v1beta1/redisreplication_types.go create mode 100644 config/crd/bases/redis.redis.opstreelabs.in_redisreplications.yaml create mode 100644 controllers/redisreplication_controller.go create mode 100644 example/additional_config/replication.yaml create mode 100644 example/affinity/replication.yaml create mode 100644 example/external_service/replication-svc.yaml create mode 100644 example/external_service/replication.yaml create mode 100644 example/node-selector/replication.yaml create mode 100644 example/password_protected/replication.yaml create mode 100644 example/password_protected/secret.yaml create mode 100644 example/private_registry/replication.yaml create mode 100644 example/probes/replication.yaml create mode 100644 example/recreate-statefulset/replication.yaml create mode 100644 example/redis-replication.yaml create mode 100644 example/redis_monitoring/replication.yaml create mode 100644 example/tls_enabled/redis-replication.yaml create mode 100644 example/volume_mount/redis-replication.yaml create mode 100644 k8sutils/redis-replication.go diff --git a/api/v1beta1/redisreplication_types.go b/api/v1beta1/redisreplication_types.go new file mode 100644 index 000000000..ca4719ea0 --- /dev/null +++ b/api/v1beta1/redisreplication_types.go @@ -0,0 +1,60 @@ +package v1beta1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type RedisReplicationSpec struct { + Size *int32 `json:"clusterSize"` + KubernetesConfig KubernetesConfig `json:"kubernetesConfig"` + RedisExporter *RedisExporter `json:"redisExporter,omitempty"` + RedisConfig *RedisConfig `json:"redisConfig,omitempty"` + Storage *Storage `json:"storage,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` + PriorityClassName string `json:"priorityClassName,omitempty"` + Affinity *corev1.Affinity `json:"affinity,omitempty"` + Tolerations *[]corev1.Toleration `json:"tolerations,omitempty"` + TLS *TLSConfig `json:"TLS,omitempty"` + // +kubebuilder:default:={initialDelaySeconds: 1, timeoutSeconds: 1, periodSeconds: 10, successThreshold: 1, failureThreshold:3} + ReadinessProbe *Probe `json:"readinessProbe,omitempty" protobuf:"bytes,11,opt,name=readinessProbe"` + // +kubebuilder:default:={initialDelaySeconds: 1, timeoutSeconds: 1, periodSeconds: 10, successThreshold: 1, failureThreshold:3} + LivenessProbe *Probe `json:"livenessProbe,omitempty" protobuf:"bytes,11,opt,name=livenessProbe"` + Sidecars *[]Sidecar `json:"sidecars,omitempty"` + ServiceAccountName *string `json:"serviceAccountName,omitempty"` +} + +func (cr *RedisReplicationSpec) GetReplicationCounts(t string) int32 { + replica := cr.Size + return *replica +} + +// RedisStatus defines the observed state of Redis +type RedisReplicationStatus struct { +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// Redis is the Schema for the redis API +type RedisReplication struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec RedisReplicationSpec `json:"spec"` + Status RedisReplicationStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// RedisList contains a list of Redis +type RedisReplicationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Redis `json:"items"` +} + +func init() { + SchemeBuilder.Register(&RedisReplication{}, &RedisReplicationList{}) +} diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 461c03e35..5cf190b2f 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -544,6 +544,175 @@ func (in *RedisPodDisruptionBudget) DeepCopy() *RedisPodDisruptionBudget { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RedisReplication) DeepCopyInto(out *RedisReplication) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisReplication. +func (in *RedisReplication) DeepCopy() *RedisReplication { + if in == nil { + return nil + } + out := new(RedisReplication) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RedisReplication) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RedisReplicationList) DeepCopyInto(out *RedisReplicationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Redis, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisReplicationList. +func (in *RedisReplicationList) DeepCopy() *RedisReplicationList { + if in == nil { + return nil + } + out := new(RedisReplicationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RedisReplicationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RedisReplicationSpec) DeepCopyInto(out *RedisReplicationSpec) { + *out = *in + if in.Size != nil { + in, out := &in.Size, &out.Size + *out = new(int32) + **out = **in + } + in.KubernetesConfig.DeepCopyInto(&out.KubernetesConfig) + if in.RedisExporter != nil { + in, out := &in.RedisExporter, &out.RedisExporter + *out = new(RedisExporter) + (*in).DeepCopyInto(*out) + } + if in.RedisConfig != nil { + in, out := &in.RedisConfig, &out.RedisConfig + *out = new(RedisConfig) + (*in).DeepCopyInto(*out) + } + if in.Storage != nil { + in, out := &in.Storage, &out.Storage + *out = new(Storage) + (*in).DeepCopyInto(*out) + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = new([]v1.Toleration) + if **in != nil { + in, out := *in, *out + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSConfig) + (*in).DeepCopyInto(*out) + } + if in.ReadinessProbe != nil { + in, out := &in.ReadinessProbe, &out.ReadinessProbe + *out = new(Probe) + **out = **in + } + if in.LivenessProbe != nil { + in, out := &in.LivenessProbe, &out.LivenessProbe + *out = new(Probe) + **out = **in + } + if in.Sidecars != nil { + in, out := &in.Sidecars, &out.Sidecars + *out = new([]Sidecar) + if **in != nil { + in, out := *in, *out + *out = make([]Sidecar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + } + if in.ServiceAccountName != nil { + in, out := &in.ServiceAccountName, &out.ServiceAccountName + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisReplicationSpec. +func (in *RedisReplicationSpec) DeepCopy() *RedisReplicationSpec { + if in == nil { + return nil + } + out := new(RedisReplicationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RedisReplicationStatus) DeepCopyInto(out *RedisReplicationStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisReplicationStatus. +func (in *RedisReplicationStatus) DeepCopy() *RedisReplicationStatus { + if in == nil { + return nil + } + out := new(RedisReplicationStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RedisSpec) DeepCopyInto(out *RedisSpec) { *out = *in diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redisreplications.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redisreplications.yaml new file mode 100644 index 000000000..f278e76a2 --- /dev/null +++ b/config/crd/bases/redis.redis.opstreelabs.in_redisreplications.yaml @@ -0,0 +1,3587 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + creationTimestamp: null + name: redisreplications.redis.redis.opstreelabs.in +spec: + group: redis.redis.opstreelabs.in + names: + kind: RedisReplication + listKind: RedisReplicationList + plural: redisreplications + singular: redisreplication + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: Redis is the Schema for the redis API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + TLS: + description: TLS Configuration for redis instances + properties: + ca: + type: string + cert: + type: string + key: + type: string + secret: + description: Reference to secret which contains the certificates + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This might be + in conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced Secret will be projected into the + volume as a file whose name is the key and content is the + value. If specified, the listed keys will be projected into + the specified paths, and unlisted keys will not be present. + If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' path + or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to set permissions + on this file. Must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, the + volume defaultMode will be used. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + required: + - secret + type: object + affinity: + description: Affinity is a group of affinity scheduling rules. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node matches + the corresponding matchExpressions; the node(s) with the + highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects (i.e. + is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node has + pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may + not try to eventually evict the pod from its node. When + there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms + must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of + pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. null selector + and null or empty namespaces list means "this pod's + namespace". An empty selector ({}) matches all namespaces. + This field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. null or + empty namespaces list and null namespaceSelector means + "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of + any node on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the anti-affinity expressions specified + by this field, but it may choose a node that violates one + or more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node has + pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its + node. When there are multiple elements, the lists of nodes + corresponding to each podAffinityTerm are intersected, i.e. + all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of + pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. null selector + and null or empty namespaces list means "this pod's + namespace". An empty selector ({}) matches all namespaces. + This field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. null or + empty namespaces list and null namespaceSelector means + "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of + any node on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + clusterSize: + format: int32 + type: integer + kubernetesConfig: + description: KubernetesConfig will be the JSON struct for Basic Redis + Config + properties: + image: + type: string + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + imagePullSecrets: + items: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + type: array + redisSecret: + description: ExistingPasswordSecret is the struct to access the + existing secret + properties: + key: + type: string + name: + type: string + type: object + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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-resources-containers/' + type: object + type: object + service: + description: ServiceConfig define the type of service to be created + and its annotations + properties: + annotations: + additionalProperties: + type: string + type: object + serviceType: + enum: + - LoadBalancer + - NodePort + - ClusterIP + type: string + type: object + updateStrategy: + description: StatefulSetUpdateStrategy indicates the strategy + that the StatefulSet controller will use to perform updates. + It includes any additional parameters necessary to perform the + update for the indicated strategy. + properties: + rollingUpdate: + description: RollingUpdate is used to communicate parameters + when Type is RollingUpdateStatefulSetStrategyType. + properties: + partition: + description: Partition indicates the ordinal at which + the StatefulSet should be partitioned. Default value + is 0. + format: int32 + type: integer + type: object + type: + description: Type indicates the type of the StatefulSetUpdateStrategy. + Default is RollingUpdate. + type: string + type: object + required: + - image + type: object + livenessProbe: + default: + failureThreshold: 3 + initialDelaySeconds: 1 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + description: Probe is a interface for ReadinessProbe and LivenessProbe + properties: + failureThreshold: + default: 3 + format: int32 + minimum: 1 + type: integer + initialDelaySeconds: + default: 1 + format: int32 + minimum: 1 + type: integer + periodSeconds: + default: 10 + format: int32 + minimum: 1 + type: integer + successThreshold: + default: 1 + format: int32 + minimum: 1 + type: integer + timeoutSeconds: + default: 1 + format: int32 + minimum: 1 + type: integer + type: object + nodeSelector: + additionalProperties: + type: string + type: object + priorityClassName: + type: string + readinessProbe: + default: + failureThreshold: 3 + initialDelaySeconds: 1 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + description: Probe is a interface for ReadinessProbe and LivenessProbe + properties: + failureThreshold: + default: 3 + format: int32 + minimum: 1 + type: integer + initialDelaySeconds: + default: 1 + format: int32 + minimum: 1 + type: integer + periodSeconds: + default: 10 + format: int32 + minimum: 1 + type: integer + successThreshold: + default: 1 + format: int32 + minimum: 1 + type: integer + timeoutSeconds: + default: 1 + format: int32 + minimum: 1 + type: integer + type: object + redisConfig: + description: RedisConfig defines the external configuration of Redis + properties: + additionalRedisConfig: + type: string + type: object + redisExporter: + description: RedisExporter interface will have the information for + redis exporter related stuff + properties: + enabled: + type: boolean + env: + 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 previously 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. Double $$ are reduced to a single + $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless + of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of 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 + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + 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: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + 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 + TODO: Add other useful fields. apiVersion, kind, + uid?' + 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 + image: + type: string + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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-resources-containers/' + type: object + type: object + required: + - image + type: object + securityContext: + description: PodSecurityContext holds pod-level security attributes + and common container settings. Some fields are also present in container.securityContext. Field + values of container.securityContext take precedence over field values + of PodSecurityContext. + properties: + fsGroup: + description: "A special supplemental group that applies to all + containers in a pod. Some volume types allow the Kubelet to + change the ownership of that volume to be owned by the pod: + \n 1. The owning GID will be the FSGroup 2. The setgid bit is + set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- \n If unset, + the Kubelet will not modify the ownership and permissions of + any volume. Note that this field cannot be set when spec.os.name + is windows." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. This field will only apply to volume types which + support fsGroup based ownership(and permissions). It will have + no effect on ephemeral volume types such as: secret, configmaps + and emptydir. Valid values are "OnRootMismatch" and "Always". + If not specified, "Always" is used. Note that this field cannot + be set when spec.os.name is windows.' + type: string + runAsGroup: + description: The GID to run the entrypoint of the container process. + Uses runtime default if unset. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail to start + the container if it does. If unset or false, no such validation + will be performed. May also be set in SecurityContext. If set + in both SecurityContext and PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. Note that this field cannot + be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers in this + pod. Note that this field cannot be set when spec.os.name is + windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must be + preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a profile + defined in a file on the node should be used. RuntimeDefault + - the container runtime default profile should be used. + Unconfined - no profile should be applied." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process run + in each container, in addition to the container's primary GID. If + unspecified, no groups will be added to any container. Note + that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used for + the pod. Pods with unsupported sysctls (by the container runtime) + might fail to launch. Note that this field cannot be set when + spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named by + the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is alpha-level + and will only be honored by components that enable the WindowsHostProcessContainers + feature flag. Setting this field without the feature flag + will result in errors when validating the Pod. All of a + Pod's containers must have the same effective HostProcess + value (it is not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in PodSecurityContext. + If set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: string + type: object + type: object + serviceAccountName: + type: string + sidecars: + items: + description: Sidecar for each Redis pods + properties: + env: + 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 previously 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. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) + syntax: i.e. "$$(VAR_NAME)" will produce the string + literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists + or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of 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 + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + 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: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + 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 + TODO: Add other useful fields. apiVersion, kind, + uid?' + 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 + image: + type: string + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + name: + type: string + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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-resources-containers/' + type: object + type: object + required: + - image + - name + type: object + type: array + storage: + description: Storage is the inteface to add pvc and pv support in + redis + properties: + volumeClaimTemplate: + description: PersistentVolumeClaim is a user's request for and + claim to a persistent volume + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this + representation of an object. Servers should convert recognized + schemas to the latest internal value, and may reject unrecognized + values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST + resource this object represents. Servers may infer this + from the endpoint the client submits requests to. Cannot + be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: 'Spec defines the desired characteristics of + a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + accessModes: + description: 'AccessModes contains the desired access + modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'This field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) If the provisioner + or an external controller can support the specified + data source, it will create a new volume based on the + contents of the specified data source. If the AnyVolumeDataSource + feature gate is enabled, this field will always have + the same contents as the DataSourceRef field.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + dataSourceRef: + description: 'Specifies the object from which to populate + the volume with data, if a non-empty volume is desired. + This may be any local object from a non-empty API group + (non core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only + succeed if the type of the specified object matches + some installed volume populator or dynamic provisioner. + This field will replace the functionality of the DataSource + field and as such if both fields are non-empty, they + must have the same value. For backwards compatibility, + both fields (DataSource and DataSourceRef) will be set + to the same value automatically if one of them is empty + and the other is non-empty. There are two important + differences between DataSource and DataSourceRef: * + While DataSource only allows two specific types of objects, + DataSourceRef allows any non-core object, as well + as PersistentVolumeClaim objects. * While DataSource + ignores disallowed values (dropping them), DataSourceRef preserves + all values, and generates an error if a disallowed value + is specified. (Alpha) Using this field requires the + AnyVolumeDataSource feature gate to be enabled.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + resources: + description: 'Resources represents the minimum resources + the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to specify resource + requirements that are lower than previous value but + must still be higher than capacity recorded in the status + field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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-resources-containers/' + type: object + type: object + selector: + description: A label query over volumes to consider for + binding. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + storageClassName: + description: 'Name of the StorageClass required by the + claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume is + required by the claim. Value of Filesystem is implied + when not included in claim spec. + type: string + volumeName: + description: VolumeName is the binding reference to the + PersistentVolume backing this claim. + type: string + type: object + status: + description: 'Status represents the current information/status + of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + accessModes: + description: 'AccessModes contains the actual access modes + the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + allocatedResources: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: The storage resource within AllocatedResources + tracks the capacity allocated to a PVC. It may be larger + than the actual capacity when a volume expansion operation + is requested. For storage quota, the larger value from + allocatedResources and PVC.spec.resources is used. If + allocatedResources is not set, PVC.spec.resources alone + is used for quota calculation. If a volume expansion + capacity request is lowered, allocatedResources is only + lowered if there are no expansion operations in progress + and if the actual volume capacity is equal or lower + than the requested capacity. This is an alpha field + and requires enabling RecoverVolumeExpansionFailure + feature. + type: object + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Represents the actual resources of the underlying + volume. + type: object + conditions: + description: Current Condition of persistent volume claim. + If underlying persistent volume is being resized then + the Condition will be set to 'ResizeStarted'. + items: + description: PersistentVolumeClaimCondition contails + details about state of pvc + properties: + lastProbeTime: + description: Last time we probed the condition. + format: date-time + type: string + lastTransitionTime: + description: Last time the condition transitioned + from one status to another. + format: date-time + type: string + message: + description: Human-readable message indicating details + about last transition. + type: string + reason: + description: Unique, this should be a short, machine + understandable string that gives the reason for + condition's last transition. If it reports "ResizeStarted" + that means the underlying persistent volume is + being resized. + type: string + status: + type: string + type: + description: PersistentVolumeClaimConditionType + is a valid value of PersistentVolumeClaimCondition.Type + type: string + required: + - status + - type + type: object + type: array + phase: + description: Phase represents the current phase of PersistentVolumeClaim. + type: string + resizeStatus: + description: ResizeStatus stores status of resize operation. + ResizeStatus is not set by default but when expansion + is complete resizeStatus is set to empty string by resize + controller or kubelet. This is an alpha field and requires + enabling RecoverVolumeExpansionFailure feature. + type: string + type: object + type: object + volumeMount: + description: Additional Volume is provided by user that is mounted + on the pods + properties: + mountPath: + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the + volume should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts + are propagated from the host to container and the + other way around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's + root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. Behaves + similarly to SubPath but environment variable references + $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr and SubPath + are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + volume: + items: + description: Volume represents a named volume in a pod that + may be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS + Disk resource that is attached to a kubelet''s host + machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + partition: + description: 'The partition in the volume that you + want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the + volume partition for /dev/sda is "0" (or you can + leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the + ReadOnly property in VolumeMounts to "true". If + omitted, the default is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource + in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk + mount on the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, + Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob + storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob + disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed + data disk (only in managed availability set). + defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service + mount on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure + Storage Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the + host that shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to + key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to + the authentication secret for User, default is + empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados user name, + default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached + and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More info: + https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object + containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify the volume + in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should + populate this volume + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal + value between 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for + mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This + might be in conflict with other options that affect + the file mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair + in the Data field of the referenced ConfigMap + will be projected into the volume as a file whose + name is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the ConfigMap, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to + set permissions on this file. Must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both + octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This + might be in conflict with other options + that affect the file mode, like fsGroup, + and the result can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents + ephemeral storage that is handled by certain external + CSI drivers (Beta feature). + properties: + driver: + description: Driver is the name of the CSI driver + that handles this volume. Consult with your admin + for the correct name as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", + "xfs", "ntfs". If not provided, the empty value + is passed to the associated CSI driver which will + determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference + to the secret object containing sensitive information + to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no + secret is required. If the secret object contains + more than one secret, all secret references are + passed. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific + properties that are passed to the CSI driver. + Consult your driver's documentation for supported + values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about + the pod that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created + files by default. Must be a Optional: mode bits + used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or + a decimal value between 0 and 511. YAML accepts + both octal and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. Directories + within the path are not affected by this setting. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing the + pod field + properties: + fieldRef: + description: 'Required: Selects a field of + the pod: only annotations, labels, name + and namespace are supported.' + 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 + mode: + description: 'Optional: mode bits used to + set permissions on this file, must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both + octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This + might be in conflict with other options + that affect the file mode, like fsGroup, + and the result can be other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' path. + Must be utf-8 encoded. The first item of + the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults to + "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory + that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should + back this directory. The default is "" which means + to use the node''s default medium. Must be an + empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'Total amount of local storage required + for this EmptyDir volume. The size limit is also + applicable for memory medium. The maximum usage + on memory medium EmptyDir would be the minimum + value between the SizeLimit specified here and + the sum of memory limits of all containers in + a pod. The default is nil which means that the + limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "Ephemeral represents a volume that is + handled by a cluster storage driver. The volume's + lifecycle is tied to the pod that defines it - it + will be created before the pod starts, and deleted + when the pod is removed. \n Use this if: a) the volume + is only needed while the pod runs, b) features of + normal volumes like restoring from snapshot or capacity + \ tracking are needed, c) the storage driver is + specified through a storage class, and d) the storage + driver supports dynamic volume provisioning through + \ a PersistentVolumeClaim (see EphemeralVolumeSource + for more information on the connection between + this volume type and PersistentVolumeClaim). \n + Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the + lifecycle of an individual pod. \n Use CSI for light-weight + local ephemeral volumes if the CSI driver is meant + to be used that way - see the documentation of the + driver for more information. \n A pod can use both + types of ephemeral volumes and persistent volumes + at the same time." + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone + PVC to provision the volume. The pod in which + this EphemeralVolumeSource is embedded will be + the owner of the PVC, i.e. the PVC will be deleted + together with the pod. The name of the PVC will + be `-` where `` + is the name from the `PodSpec.Volumes` array entry. + Pod validation will reject the pod if the concatenated + name is not valid for a PVC (for example, too + long). \n An existing PVC with that name that + is not owned by the pod will *not* be used for + the pod to avoid using an unrelated volume by + mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created + PVC is meant to be used by the pod, the PVC has + to updated with an owner reference to the pod + once the pod exists. Normally this should not + be necessary, but it may be useful when manually + reconstructing a broken cluster. \n This field + is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. \n Required, + must not be nil." + properties: + metadata: + description: May contain labels and annotations + that will be copied into the PVC when creating + it. No other fields are allowed and will be + rejected during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into + the PVC that gets created from this template. + The same fields as in a PersistentVolumeClaim + are also valid here. + properties: + accessModes: + description: 'AccessModes contains the desired + access modes the volume should have. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'This field can be used to + specify either: * An existing VolumeSnapshot + object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller + can support the specified data source, + it will create a new volume based on the + contents of the specified data source. + If the AnyVolumeDataSource feature gate + is enabled, this field will always have + the same contents as the DataSourceRef + field.' + properties: + apiGroup: + description: APIGroup is the group for + the resource being referenced. If + APIGroup is not specified, the specified + Kind must be in the core API group. + For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + dataSourceRef: + description: 'Specifies the object from + which to populate the volume with data, + if a non-empty volume is desired. This + may be any local object from a non-empty + API group (non core object) or a PersistentVolumeClaim + object. When this field is specified, + volume binding will only succeed if the + type of the specified object matches some + installed volume populator or dynamic + provisioner. This field will replace the + functionality of the DataSource field + and as such if both fields are non-empty, + they must have the same value. For backwards + compatibility, both fields (DataSource + and DataSourceRef) will be set to the + same value automatically if one of them + is empty and the other is non-empty. There + are two important differences between + DataSource and DataSourceRef: * While + DataSource only allows two specific types + of objects, DataSourceRef allows any + non-core object, as well as PersistentVolumeClaim + objects. * While DataSource ignores disallowed + values (dropping them), DataSourceRef preserves + all values, and generates an error if + a disallowed value is specified. (Alpha) + Using this field requires the AnyVolumeDataSource + feature gate to be enabled.' + properties: + apiGroup: + description: APIGroup is the group for + the resource being referenced. If + APIGroup is not specified, the specified + Kind must be in the core API group. + For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + resources: + description: 'Resources represents the minimum + resources the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to + specify resource requirements that are + lower than previous value but must still + be higher than capacity recorded in the + status field of the claim. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum + amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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-resources-containers/' + type: object + type: object + selector: + description: A label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + storageClassName: + description: 'Name of the StorageClass required + by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type + of volume is required by the claim. Value + of Filesystem is implied when not included + in claim spec. + type: string + volumeName: + description: VolumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: FC represents a Fibre Channel resource + that is attached to a kubelet's host machine and then + exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. TODO: how + do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names + (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs + and lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume + resource that is provisioned/attached using an exec + based plugin. + properties: + driver: + description: Driver is the name of the driver to + use for this volume. + type: string + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". The default + filesystem depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if + any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to + the secret object containing sensitive information + to pass to the plugin scripts. This may be empty + if no secret object is specified. If the secret + object contains more than one secret, all secrets + are passed to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached + to a kubelet's host machine. This depends on the Flocker + control service being running + properties: + datasetName: + description: Name of the dataset stored as metadata + -> name on the dataset for Flocker should be considered + as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique + identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk + resource that is attached to a kubelet''s host machine + and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + partition: + description: 'The partition in the volume that you + want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, + you specify the partition as "1". Similarly, the + volume partition for /dev/sda is "0" (or you can + leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in + GCE. Used to identify the disk in GCE. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at + a particular revision. DEPRECATED: GitRepo is deprecated. + To provision a container with a git repo, mount an + EmptyDir into an InitContainer that clones the repo + using git, then mount the EmptyDir into the Pod''s + container.' + properties: + directory: + description: Target directory name. Must not contain + or start with '..'. If '.' is supplied, the volume + directory will be the git repository. Otherwise, + if specified, the volume will contain the git + repository in the subdirectory with the given + name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount + on the host that shares a pod''s lifetime. More info: + https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name + that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs + volume to be mounted with read-only permissions. + Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file + or directory on the host machine that is directly + exposed to the container. This is generally used for + system agents or other privileged things that are + allowed to see the host machine. Most containers will + NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use + host directory mounts and who can/can not mount host + directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. + If the path is a symlink, it will follow the link + to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults + to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource + that is attached to a kubelet''s host machine and + then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP + authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP + authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, + new iSCSI interface : + will be created for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI + transport. Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal + is either an IP or ip_addr:port if the port is + other than default (typically TCP ports 860 and + 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator + authentication + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The Portal is + either an IP or ip_addr:port if the port is other + than default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and + unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host + that shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export + to be mounted with read-only permissions. Defaults + to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address + of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents + a reference to a PersistentVolumeClaim in the same + namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in + VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host + machine + properties: + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume + attached and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type + to mount Must be a filesystem type supported by + the host operating system. Ex. "ext4", "xfs". + Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx + volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: Mode bits used to set permissions on + created files by default. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal + values, JSON requires decimal values for mode + bits. Directories within the path are not affected + by this setting. This might be in conflict with + other options that affect the file mode, like + fsGroup, and the result can be other mode bits + set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected + along with other supported volume types + properties: + configMap: + description: information about the configMap + data to project + properties: + items: + description: If unspecified, each key-value + pair in the Data field of the referenced + ConfigMap will be projected into the + volume as a file whose name is the key + and content is the value. If specified, + the listed keys will be projected into + the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the ConfigMap, + the volume setup will error unless it + is marked optional. Paths must be relative + and may not contain the '..' path or + start with '..'. + items: + description: Maps a string key to a + path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits + used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, + JSON requires decimal values for + mode bits. If not specified, the + volume defaultMode will be used. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of + the file to map the key to. May + not be an absolute path. May not + contain the path element '..'. + May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + downwardAPI: + description: information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects + a field of the pod: only annotations, + labels, name and namespace are + supported.' + 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 + mode: + description: 'Optional: mode bits + used to set permissions on this + file, must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, + JSON requires decimal values for + mode bits. If not specified, the + volume defaultMode will be used. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file + to be created. Must not be absolute + or contain the ''..'' path. Must + be utf-8 encoded. The first item + of the relative path must not + start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, requests.cpu and + requests.memory) are currently + supported.' + properties: + containerName: + description: 'Container name: + required for volumes, optional + for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret + data to project + properties: + items: + description: If unspecified, each key-value + pair in the Data field of the referenced + Secret will be projected into the volume + as a file whose name is the key and + content is the value. If specified, + the listed keys will be projected into + the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the Secret, + the volume setup will error unless it + is marked optional. Paths must be relative + and may not contain the '..' path or + start with '..'. + items: + description: Maps a string key to a + path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits + used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, + JSON requires decimal values for + mode bits. If not specified, the + volume defaultMode will be used. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of + the file to map the key to. May + not be an absolute path. May not + contain the path element '..'. + May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + or its key must be defined + type: boolean + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended + audience of the token. A recipient of + a token must identify itself with an + identifier specified in the audience + of the token, and otherwise should reject + the token. The audience defaults to + the identifier of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is the + requested duration of validity of the + service account token. As the token + approaches expiration, the kubelet volume + plugin will proactively rotate the service + account token. The kubelet will start + trying to rotate the token if the token + is older than 80 percent of its time + to live or if the token is older than + 24 hours.Defaults to 1 hour and must + be at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative + to the mount point of the file to project + the token into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the + host that shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default + is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte + volume to be mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple + Quobyte Registry services specified as a string + as host:port pair (multiple entries are separated + with commas) which acts as the central registry + for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume + in the Backend Used with dynamically provisioned + Quobyte volumes, value is set by the plugin + type: string + user: + description: User to map volume access to Defaults + to serivceaccount user + type: string + volume: + description: Volume is a string that references + an already created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount + on the host that shares a pod''s lifetime. More info: + https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for + RBDUser. Default is /etc/ceph/keyring. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication + secret for RBDUser. If provided overrides keyring. + Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent + volume attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Default is + "xfs". + type: string + gateway: + description: The host address of the ScaleIO API + Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection + Domain for the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret + for ScaleIO user and other sensitive information. + If this is not provided, Login operation will + fail. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a + volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created + in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should + populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal + value between 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal values for + mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This + might be in conflict with other options that affect + the file mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair + in the Data field of the referenced Secret will + be projected into the volume as a file whose name + is the key and content is the value. If specified, + the listed keys will be projected into the specified + paths, and unlisted keys will not be present. + If a key is specified which is not present in + the Secret, the volume setup will error unless + it is marked optional. Paths must be relative + and may not contain the '..' path or start with + '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to + set permissions on this file. Must be an + octal value between 0000 and 0777 or a decimal + value between 0 and 511. YAML accepts both + octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This + might be in conflict with other options + that affect the file mode, like fsGroup, + and the result can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys + must be defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace + to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use + for obtaining the StorageOS API credentials. If + not specified, default values will be attempted. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable name + of the StorageOS volume. Volume names are only + unique within a namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope + of the volume within StorageOS. If no namespace + is specified then the Pod's namespace will be + used. This allows the Kubernetes name scoping + to be mirrored within StorageOS for tighter integration. + Set VolumeName to any name to override the default + behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do + not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume + attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a + filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) + profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) + profile name. + type: string + volumePath: + description: Path that identifies vSphere volume + vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + type: object + tolerations: + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match all + values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the + value. Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod + can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time + the toleration (which must be of effect NoExecute, otherwise + this field is ignored) tolerates the taint. By default, it + is not set, which means tolerate the taint forever (do not + evict). Zero and negative values will be treated as 0 (evict + immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + required: + - clusterSize + - kubernetesConfig + type: object + status: + description: RedisStatus defines the observed state of Redis + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/controllers/redisreplication_controller.go b/controllers/redisreplication_controller.go new file mode 100644 index 000000000..155274a44 --- /dev/null +++ b/controllers/redisreplication_controller.go @@ -0,0 +1,102 @@ +package controllers + +import ( + "context" + "strconv" + "time" + + "redis-operator/k8sutils" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + redisv1beta1 "redis-operator/api/v1beta1" +) + +// RedisReplicationReconciler reconciles a RedisReplication object +type RedisReplicationReconciler struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme +} + +func (r *RedisReplicationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + reqLogger := r.Log.WithValues("Request.Namespace", req.Namespace, "Request.Name", req.Name) + reqLogger.Info("Reconciling opstree redis replication controller") + instance := &redisv1beta1.RedisReplication{} + + err := r.Client.Get(context.TODO(), req.NamespacedName, instance) + if err != nil { + if errors.IsNotFound(err) { + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + if _, found := instance.ObjectMeta.GetAnnotations()["redisreplication.opstreelabs.in/skip-reconcile"]; found { + reqLogger.Info("Found annotations redisreplication.opstreelabs.in/skip-reconcile, so skipping reconcile") + return ctrl.Result{RequeueAfter: time.Second * 10}, nil + } + + leaderReplicas := int32(1) + followerReplicas := instance.Spec.GetReplicationCounts("replication") - leaderReplicas + totalReplicas := leaderReplicas + followerReplicas + + if err := k8sutils.HandleRedisReplicationFinalizer(instance, r.Client); err != nil { + return ctrl.Result{}, err + } + + if err := k8sutils.AddRedisReplicationFinalizer(instance, r.Client); err != nil { + return ctrl.Result{}, err + } + + err = k8sutils.CreateReplicationRedis(instance) + if err != nil { + return ctrl.Result{}, err + } + err = k8sutils.CreateReplicationService(instance) + if err != nil { + return ctrl.Result{}, err + } + + // Set Pod distruptiuon Budget Later + + redisReplicationInfo, err := k8sutils.GetStatefulSet(instance.Namespace, instance.ObjectMeta.Name) + if err != nil { + return ctrl.Result{RequeueAfter: time.Second * 60}, err + } + + // Check that the Leader and Follower are ready in redis replication + if int32(redisReplicationInfo.Status.ReadyReplicas) != totalReplicas { + reqLogger.Info("Redis leader and follower nodes are not ready yet", "Ready.Replicas", strconv.Itoa(int(redisReplicationInfo.Status.ReadyReplicas)), "Expected.Replicas", totalReplicas) + return ctrl.Result{RequeueAfter: time.Second * 60}, nil + } + + reqLogger.Info("Creating redis replication by executing replication creation commands", "Replication.Ready", strconv.Itoa(int(redisReplicationInfo.Status.ReadyReplicas))) + + if len(k8sutils.GetRedisNodesByRole(instance, "master")) > int(leaderReplicas) { + + masterNodes := k8sutils.GetRedisNodesByRole(instance, "master") + slaveNodes := k8sutils.GetRedisNodesByRole(instance, "slave") + err := k8sutils.CreateMasterSlaveReplication(instance, masterNodes, slaveNodes) + if err != nil { + return ctrl.Result{RequeueAfter: time.Second * 60}, err + } + + } + + reqLogger.Info("Will reconcile redis operator in again 10 seconds") + return ctrl.Result{RequeueAfter: time.Second * 10}, nil + +} + +// SetupWithManager sets up the controller with the Manager. +func (r *RedisReplicationReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&redisv1beta1.RedisReplication{}). + Complete(r) +} diff --git a/example/additional_config/replication.yaml b/example/additional_config/replication.yaml new file mode 100644 index 000000000..5bfd37d9d --- /dev/null +++ b/example/additional_config/replication.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisReplication +metadata: + name: redis-replication +spec: + clusterSize: 3 + redisConfig: + additionalRedisConfig: redis-external-config + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 1000 + fsGroup: 1000 + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 diff --git a/example/affinity/replication.yaml b/example/affinity/replication.yaml new file mode 100644 index 000000000..7efc207ed --- /dev/null +++ b/example/affinity/replication.yaml @@ -0,0 +1,34 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisReplication +metadata: + name: redis-replication +spec: + clusterSize: 3 + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 1000 + fsGroup: 1000 + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - redis-replication + topologyKey: "kubernetes.io/hostname" diff --git a/example/external_service/replication-svc.yaml b/example/external_service/replication-svc.yaml new file mode 100644 index 000000000..5e412de80 --- /dev/null +++ b/example/external_service/replication-svc.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: redis-replication + redis_setup_type: replication + role: replication + name: redis-replication +spec: + ports: + - name: redis-client + port: 6379 + protocol: TCP + targetPort: 6379 + selector: + app: redis-replication + redis_setup_type: replication + role: replication +# type: LoadBalancer + type: NodePort diff --git a/example/external_service/replication.yaml b/example/external_service/replication.yaml new file mode 100644 index 000000000..4f84f2467 --- /dev/null +++ b/example/external_service/replication.yaml @@ -0,0 +1,24 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisReplication +metadata: + name: redis-replication +spec: + clusterSize: 3 + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 1000 + fsGroup: 1000 + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 diff --git a/example/node-selector/replication.yaml b/example/node-selector/replication.yaml new file mode 100644 index 000000000..991de4243 --- /dev/null +++ b/example/node-selector/replication.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisReplication +metadata: + name: redis-replication +spec: + clusterSize: 3 + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 1000 + fsGroup: 1000 + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + nodeSelector: + kubernetes.io/os: linux diff --git a/example/password_protected/replication.yaml b/example/password_protected/replication.yaml new file mode 100644 index 000000000..ef3311d61 --- /dev/null +++ b/example/password_protected/replication.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisReplication +metadata: + name: redis-replication +spec: + clusterSize: 3 + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + redisSecret: + name: redis-secret + key: password + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + securityContext: + runAsUser: 1000 + fsGroup: 1000 diff --git a/example/password_protected/secret.yaml b/example/password_protected/secret.yaml new file mode 100644 index 000000000..2a91c0d81 --- /dev/null +++ b/example/password_protected/secret.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: redis-secret +data: + password: T3BzdHJlZUAxMjM0Cg== +type: Opaque \ No newline at end of file diff --git a/example/private_registry/replication.yaml b/example/private_registry/replication.yaml new file mode 100644 index 000000000..c33ca9d1a --- /dev/null +++ b/example/private_registry/replication.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisReplication +metadata: + name: redis-replication +spec: + clusterSize: 3 + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + imagePullSecrets: + - name: regcred + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + securityContext: + runAsUser: 1000 + fsGroup: 1000 diff --git a/example/probes/replication.yaml b/example/probes/replication.yaml new file mode 100644 index 000000000..02513b28c --- /dev/null +++ b/example/probes/replication.yaml @@ -0,0 +1,36 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisReplication +metadata: + name: redis-replication +spec: + clusterSize: 3 + securityContext: + runAsUser: 1000 + fsGroup: 1000 + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + readinessProbe: + failureThreshold: 5 + initialDelaySeconds: 15 + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 5 + livenessProbe: + failureThreshold: 5 + initialDelaySeconds: 15 + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 5 diff --git a/example/recreate-statefulset/replication.yaml b/example/recreate-statefulset/replication.yaml new file mode 100644 index 000000000..bdeb9db87 --- /dev/null +++ b/example/recreate-statefulset/replication.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisReplication +metadata: + name: redis-replication + annotations: + redis.opstreelabs.in/recreate-statefulset: "true" +spec: + clusterSize: 3 + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 1000 + fsGroup: 1000 + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + priorityClassName: system-cluster-critical diff --git a/example/redis-replication.yaml b/example/redis-replication.yaml new file mode 100644 index 000000000..5170612f9 --- /dev/null +++ b/example/redis-replication.yaml @@ -0,0 +1,60 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisReplication +metadata: + name: redis-replication +spec: + clusterSize: 3 + securityContext: + runAsUser: 1000 + fsGroup: 1000 + # redisConfig: + # additionalRedisConfig: redis-external-config + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 101m + memory: 128Mi + limits: + cpu: 101m + memory: 128Mi + # redisSecret: + # name: redis-secret + # key: password + # imagePullSecrets: + # - name: regcred + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + imagePullPolicy: Always + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 100m + memory: 128Mi +# Environment Variables for Redis Exporter + # env: + # - name: REDIS_EXPORTER_INCL_SYSTEM_METRICS + # value: "true" + # - name: UI_PROPERTIES_FILE_NAME + # valueFrom: + # configMapKeyRef: + # name: game-demo + # key: ui_properties_file_name + # - name: SECRET_USERNAME + # valueFrom: + # secretKeyRef: + # name: mysecret + # key: username + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi \ No newline at end of file diff --git a/example/redis_monitoring/replication.yaml b/example/redis_monitoring/replication.yaml new file mode 100644 index 000000000..bf3602ae2 --- /dev/null +++ b/example/redis_monitoring/replication.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisReplication +metadata: + name: redis-replication +# annotations: +# redis.opstreelabs.in/skip-reconcile: "true" +spec: + clusterSize: 3 + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + redisExporter: + enabled: true + image: quay.io/opstree/redis-exporter:v1.44.0 + imagePullPolicy: Always + # env: + # - name: REDIS_EXPORTER_INCL_SYSTEM_METRICS + # value: "true" + securityContext: + runAsUser: 1000 + fsGroup: 1000 diff --git a/example/tls_enabled/redis-replication.yaml b/example/tls_enabled/redis-replication.yaml new file mode 100644 index 000000000..ac8b34490 --- /dev/null +++ b/example/tls_enabled/redis-replication.yaml @@ -0,0 +1,45 @@ +# Examples of creating a valid TLS certificate can be found at ./config/certmanager via Cert-manager +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisReplication +metadata: + name: redis-replication +spec: + clusterSize: 3 + TLS: + ca: ca.key + cert: tls.crt + key: tls.key + secret: + secretName: redis-tls-cert + securityContext: + runAsUser: 1000 + fsGroup: 1000 + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 101m + memory: 128Mi + limits: + cpu: 101m + memory: 128Mi + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + imagePullPolicy: Always + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 100m + memory: 128Mi + storage: + volumeClaimTemplate: + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi diff --git a/example/volume_mount/redis-replication.yaml b/example/volume_mount/redis-replication.yaml new file mode 100644 index 000000000..58adea03a --- /dev/null +++ b/example/volume_mount/redis-replication.yaml @@ -0,0 +1,50 @@ +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisReplication +metadata: + name: redis-replication +spec: + clusterSize: 3 + # redisConfig: + # additionalRedisConfig: redis-external-config + kubernetesConfig: + image: quay.io/opstree/redis:v7.0.5 + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 101m + memory: 128Mi + limits: + cpu: 101m + memory: 128Mi + # redisSecret: + # name: redis-secret + # key: password + # imagePullSecrets: + # - name: regcred + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:v1.44.0 + imagePullPolicy: Always + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 100m + memory: 128Mi + storage: + volumeClaimTemplate: + spec: + # storageClassName: standard + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + volumemount: + volume: + - name: example-config + configMap: + name: example-configmap + mount: + - mountPath: /config + name: example-config diff --git a/go.mod b/go.mod index 2931eb770..021a64691 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/go-redis/redis v6.15.9+incompatible github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.17.0 + github.com/pkg/errors v0.9.1 k8s.io/api v0.23.0 k8s.io/apimachinery v0.23.0 k8s.io/client-go v0.23.0 @@ -44,7 +45,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.11.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.28.0 // indirect diff --git a/k8sutils/finalizer.go b/k8sutils/finalizer.go index 43bb3b927..58d6d5dcf 100644 --- a/k8sutils/finalizer.go +++ b/k8sutils/finalizer.go @@ -13,8 +13,9 @@ import ( ) const ( - RedisFinalizer string = "redisFinalizer" - RedisClusterFinalizer string = "redisClusterFinalizer" + RedisFinalizer string = "redisFinalizer" + RedisClusterFinalizer string = "redisClusterFinalizer" + RedisReplicationFinalizer string = "redisReplicationFinalizer" ) // finalizeLogger will generate logging interface @@ -71,6 +72,30 @@ func HandleRedisClusterFinalizer(cr *redisv1beta1.RedisCluster, cl client.Client return nil } +// Handle RedisReplicationFinalizer finalize resource if instance is marked to be deleted +func HandleRedisReplicationFinalizer(cr *redisv1beta1.RedisReplication, cl client.Client) error { + logger := finalizerLogger(cr.Namespace, RedisReplicationFinalizer) + if cr.GetDeletionTimestamp() != nil { + if controllerutil.ContainsFinalizer(cr, RedisReplicationFinalizer) { + if err := finalizeRedisReplicationServices(cr); err != nil { + return err + } + if err := finalizeRedisReplicationPVC(cr); err != nil { + return err + } + if err := finalizeRedisReplicationStatefulSets(cr); err != nil { + return err + } + controllerutil.RemoveFinalizer(cr, RedisReplicationFinalizer) + if err := cl.Update(context.TODO(), cr); err != nil { + logger.Error(err, "Could not remove finalizer "+RedisReplicationFinalizer) + return err + } + } + } + return nil +} + // AddRedisFinalizer add finalizer for graceful deletion func AddRedisFinalizer(cr *redisv1beta1.Redis, cl client.Client) error { if !controllerutil.ContainsFinalizer(cr, RedisFinalizer) { @@ -89,6 +114,15 @@ func AddRedisClusterFinalizer(cr *redisv1beta1.RedisCluster, cl client.Client) e return nil } +// AddRedisReplicationFinalizer add finalizer for graceful deletion +func AddRedisReplicationFinalizer(cr *redisv1beta1.RedisReplication, cl client.Client) error { + if !controllerutil.ContainsFinalizer(cr, RedisReplicationFinalizer) { + controllerutil.AddFinalizer(cr, RedisReplicationFinalizer) + return cl.Update(context.TODO(), cr) + } + return nil +} + // finalizeRedisServices delete Services func finalizeRedisServices(cr *redisv1beta1.Redis) error { logger := finalizerLogger(cr.Namespace, RedisFinalizer) @@ -117,6 +151,20 @@ func finalizeRedisClusterServices(cr *redisv1beta1.RedisCluster) error { return nil } +// finalize RedisReplicationServices delete Services +func finalizeRedisReplicationServices(cr *redisv1beta1.RedisReplication) error { + logger := finalizerLogger(cr.Namespace, RedisReplicationFinalizer) + serviceName, headlessServiceName := cr.Name, cr.Name+"-headless" + for _, svc := range []string{serviceName, headlessServiceName} { + err := generateK8sClient().CoreV1().Services(cr.Namespace).Delete(context.TODO(), svc, metav1.DeleteOptions{}) + if err != nil && !errors.IsNotFound(err) { + logger.Error(err, "Could not delete service "+svc) + return err + } + } + return nil +} + // finalizeRedisPVC delete PVC func finalizeRedisPVC(cr *redisv1beta1.Redis) error { logger := finalizerLogger(cr.Namespace, RedisFinalizer) @@ -145,6 +193,21 @@ func finalizeRedisClusterPVC(cr *redisv1beta1.RedisCluster) error { return nil } +// finalizeRedisReplicationPVC delete PVCs +func finalizeRedisReplicationPVC(cr *redisv1beta1.RedisReplication) error { + logger := finalizerLogger(cr.Namespace, RedisReplicationFinalizer) + for i := 0; i < int(cr.Spec.GetReplicationCounts("replication")); i++ { + PVCName := cr.Name + "-" + "replication" + "-" + cr.Name + "-" + "replication" + "-" + strconv.Itoa(i) + err := generateK8sClient().CoreV1().PersistentVolumeClaims(cr.Namespace).Delete(context.TODO(), PVCName, metav1.DeleteOptions{}) + if err != nil && !errors.IsNotFound(err) { + logger.Error(err, "Could not delete Persistent Volume Claim "+PVCName) + return err + } + } + + return nil +} + // finalizeRedisStatefulSet delete statefulset for Redis func finalizeRedisStatefulSet(cr *redisv1beta1.Redis) error { logger := finalizerLogger(cr.Namespace, RedisFinalizer) @@ -168,3 +231,14 @@ func finalizeRedisClusterStatefulSets(cr *redisv1beta1.RedisCluster) error { } return nil } + +// finalizeRedisReplicationStatefulSets delete statefulset for Redis Replication +func finalizeRedisReplicationStatefulSets(cr *redisv1beta1.RedisReplication) error { + logger := finalizerLogger(cr.Namespace, RedisReplicationFinalizer) + err := generateK8sClient().AppsV1().StatefulSets(cr.Namespace).Delete(context.TODO(), cr.Name, metav1.DeleteOptions{}) + if err != nil && !errors.IsNotFound(err) { + logger.Error(err, "Could not delete StatefulSets "+cr.Name) + return err + } + return nil +} diff --git a/k8sutils/labels.go b/k8sutils/labels.go index 2edf8f7fa..fb006269a 100644 --- a/k8sutils/labels.go +++ b/k8sutils/labels.go @@ -53,6 +53,18 @@ func redisClusterAsOwner(cr *redisv1beta1.RedisCluster) metav1.OwnerReference { } } +// redisReplicationAsOwner generates and returns object refernece +func redisReplicationAsOwner(cr *redisv1beta1.RedisReplication) metav1.OwnerReference { + trueVar := true + return metav1.OwnerReference{ + APIVersion: cr.APIVersion, + Kind: cr.Kind, + Name: cr.Name, + UID: cr.UID, + Controller: &trueVar, + } +} + // generateStatefulSetsAnots generates and returns statefulsets annotations func generateStatefulSetsAnots(stsMeta metav1.ObjectMeta) map[string]string { anots := map[string]string{ diff --git a/k8sutils/redis-replication.go b/k8sutils/redis-replication.go new file mode 100644 index 000000000..d46da8783 --- /dev/null +++ b/k8sutils/redis-replication.go @@ -0,0 +1,144 @@ +package k8sutils + +import ( + redisv1beta1 "redis-operator/api/v1beta1" +) + +// CreateReplicationService method will create replication service for Redis +func CreateReplicationService(cr *redisv1beta1.RedisReplication) error { + logger := serviceLogger(cr.Namespace, cr.ObjectMeta.Name) + labels := getRedisLabels(cr.ObjectMeta.Name, "replication", "replication", cr.ObjectMeta.Labels) + annotations := generateServiceAnots(cr.ObjectMeta, nil) + if cr.Spec.RedisExporter != nil && cr.Spec.RedisExporter.Enabled { + enableMetrics = true + } + additionalServiceAnnotations := map[string]string{} + if cr.Spec.KubernetesConfig.Service != nil { + additionalServiceAnnotations = cr.Spec.KubernetesConfig.Service.ServiceAnnotations + } + objectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name, cr.Namespace, labels, annotations) + headlessObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-headless", cr.Namespace, labels, annotations) + additionalObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, additionalServiceAnnotations)) + err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisReplicationAsOwner(cr), false, true, "ClusterIP") + if err != nil { + logger.Error(err, "Cannot create replication headless service for Redis") + return err + } + err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisReplicationAsOwner(cr), enableMetrics, false, "ClusterIP") + if err != nil { + logger.Error(err, "Cannot create replication service for Redis") + return err + } + additionalServiceType := "ClusterIP" + if cr.Spec.KubernetesConfig.Service != nil { + additionalServiceType = cr.Spec.KubernetesConfig.Service.ServiceType + } + err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisReplicationAsOwner(cr), false, false, additionalServiceType) + if err != nil { + logger.Error(err, "Cannot create additional service for Redis Replication") + return err + } + return nil +} + +// CreateReplicationRedis will create a replication redis setup +func CreateReplicationRedis(cr *redisv1beta1.RedisReplication) error { + stateFulName := cr.ObjectMeta.Name + logger := statefulSetLogger(cr.Namespace, cr.ObjectMeta.Name) + labels := getRedisLabels(cr.ObjectMeta.Name, "replication", "replication", cr.ObjectMeta.Labels) + annotations := generateStatefulSetsAnots(cr.ObjectMeta) + objectMetaInfo := generateObjectMetaInformation(stateFulName, cr.Namespace, labels, annotations) + err := CreateOrUpdateStateFul(cr.Namespace, + objectMetaInfo, + generateRedisReplicationParams(cr), + redisReplicationAsOwner(cr), + generateRedisReplicationContainerParams(cr), + cr.Spec.Sidecars, + ) + if err != nil { + logger.Error(err, "Cannot create replication statefulset for Redis") + return err + } + return nil +} + +func generateRedisReplicationParams(cr *redisv1beta1.RedisReplication) statefulSetParameters { + replicas := cr.Spec.GetReplicationCounts("Replication") + res := statefulSetParameters{ + Replicas: &replicas, + NodeSelector: cr.Spec.NodeSelector, + SecurityContext: cr.Spec.SecurityContext, + PriorityClassName: cr.Spec.PriorityClassName, + Affinity: cr.Spec.Affinity, + Tolerations: cr.Spec.Tolerations, + UpdateStrategy: cr.Spec.KubernetesConfig.UpdateStrategy, + } + if cr.Spec.KubernetesConfig.ImagePullSecrets != nil { + res.ImagePullSecrets = cr.Spec.KubernetesConfig.ImagePullSecrets + } + if cr.Spec.Storage != nil { + res.PersistentVolumeClaim = cr.Spec.Storage.VolumeClaimTemplate + } + if cr.Spec.RedisConfig != nil { + res.ExternalConfig = cr.Spec.RedisConfig.AdditionalRedisConfig + } + if cr.Spec.RedisExporter != nil { + res.EnableMetrics = cr.Spec.RedisExporter.Enabled + + } + if cr.Spec.ServiceAccountName != nil { + res.ServiceAccountName = cr.Spec.ServiceAccountName + } + return res +} + +// generateRedisReplicationContainerParams generates Redis container information +func generateRedisReplicationContainerParams(cr *redisv1beta1.RedisReplication) containerParameters { + trueProperty := true + falseProperty := false + containerProp := containerParameters{ + Role: "replication", + Image: cr.Spec.KubernetesConfig.Image, + ImagePullPolicy: cr.Spec.KubernetesConfig.ImagePullPolicy, + Resources: cr.Spec.KubernetesConfig.Resources, + } + + if cr.Spec.Storage != nil { + containerProp.AdditionalVolume = cr.Spec.Storage.VolumeMount.Volume + containerProp.AdditionalMountPath = cr.Spec.Storage.VolumeMount.MountPath + } + + if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { + containerProp.EnabledPassword = &trueProperty + containerProp.SecretName = cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name + containerProp.SecretKey = cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key + } else { + containerProp.EnabledPassword = &falseProperty + } + if cr.Spec.RedisExporter != nil { + containerProp.RedisExporterImage = cr.Spec.RedisExporter.Image + containerProp.RedisExporterImagePullPolicy = cr.Spec.RedisExporter.ImagePullPolicy + + if cr.Spec.RedisExporter.Resources != nil { + containerProp.RedisExporterResources = cr.Spec.RedisExporter.Resources + } + + if cr.Spec.RedisExporter.EnvVars != nil { + containerProp.RedisExporterEnv = cr.Spec.RedisExporter.EnvVars + } + + } + if cr.Spec.ReadinessProbe != nil { + containerProp.ReadinessProbe = cr.Spec.ReadinessProbe + } + if cr.Spec.LivenessProbe != nil { + containerProp.LivenessProbe = cr.Spec.LivenessProbe + } + if cr.Spec.Storage != nil { + containerProp.PersistenceEnabled = &trueProperty + } + if cr.Spec.TLS != nil { + containerProp.TLSConfig = cr.Spec.TLS + } + return containerProp +} diff --git a/k8sutils/redis.go b/k8sutils/redis.go index 8235b6bc8..f6af1bbe4 100644 --- a/k8sutils/redis.go +++ b/k8sutils/redis.go @@ -415,3 +415,151 @@ func generateRedisManagerLogger(namespace, name string) logr.Logger { reqLogger := log.WithValues("Request.RedisManager.Namespace", namespace, "Request.RedisManager.Name", name) return reqLogger } + +// configureRedisClient will configure the Redis Client +func configureRedisReplicationClient(cr *redisv1beta1.RedisReplication, podName string) *redis.Client { + logger := generateRedisManagerLogger(cr.Namespace, cr.ObjectMeta.Name) + redisInfo := RedisDetails{ + PodName: podName, + Namespace: cr.Namespace, + } + var client *redis.Client + + if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { + pass, err := getRedisPassword(cr.Namespace, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) + if err != nil { + logger.Error(err, "Error in getting redis password") + } + client = redis.NewClient(&redis.Options{ + Addr: getRedisServerIP(redisInfo) + ":6379", + Password: pass, + DB: 0, + TLSConfig: getRedisReplicationTLSConfig(cr, redisInfo), + }) + } else { + client = redis.NewClient(&redis.Options{ + Addr: getRedisServerIP(redisInfo) + ":6379", + Password: "", + DB: 0, + TLSConfig: getRedisReplicationTLSConfig(cr, redisInfo), + }) + } + return client +} + +// Get Redis nodes by it's role i.e. master, slave and sentinel +func GetRedisNodesByRole(cr *redisv1beta1.RedisReplication, redisRole string) []string { + logger := generateRedisManagerLogger(cr.Namespace, cr.ObjectMeta.Name) + statefulset, err := GetStatefulSet(cr.Namespace, cr.Name) + if err != nil { + logger.Error(err, "Failed to Get the Statefulset of the", "custom resource", cr.Name, "in namespace", cr.Namespace) + } + + var pods []string + replicas := cr.Spec.GetReplicationCounts("replication") + + for i := 0; i < int(replicas); i++ { + + podName := statefulset.Name + "-" + strconv.Itoa(i) + podRole := checkRedisServerRole(cr, podName) + if podRole == redisRole { + pods = append(pods, podName) + } + } + + return pods +} + +// Check the Redis Server Role i.e. master, slave and sentinel +func checkRedisServerRole(cr *redisv1beta1.RedisReplication, podName string) string { + logger := generateRedisManagerLogger(cr.Namespace, cr.ObjectMeta.Name) + + redisClient := configureRedisReplicationClient(cr, podName) + defer redisClient.Close() + info, err := redisClient.Info("replication").Result() + if err != nil { + logger.Error(err, "Failed to Get the role Info of the", "redis pod", podName) + } + + lines := strings.Split(info, "\r\n") + role := "" + for _, line := range lines { + if strings.HasPrefix(line, "role:") { + role = strings.TrimPrefix(line, "role:") + break + } + } + + return role + +} + +// checkAttachedSlave would return redis pod name which has slave +func checkAttachedSlave(cr *redisv1beta1.RedisReplication, masterPods []string) string { + logger := generateRedisManagerLogger(cr.Namespace, cr.ObjectMeta.Name) + + for _, podName := range masterPods { + + connected_slaves := "" + redisClient := configureRedisReplicationClient(cr, podName) + defer redisClient.Close() + info, err := redisClient.Info("replication").Result() + if err != nil { + logger.Error(err, "Failed to Get the connected slaves Info of the", "redis pod", podName) + } + + lines := strings.Split(info, "\r\n") + + for _, line := range lines { + if strings.HasPrefix(line, "connected_slaves:") { + connected_slaves = strings.TrimPrefix(line, "connected_slaves:") + break + } + } + + nums, _ := strconv.Atoi(connected_slaves) + if nums > 0 { + return podName + } + + } + + return "" + +} + +func CreateMasterSlaveReplication(cr *redisv1beta1.RedisReplication, masterPods []string, slavePods []string) error { + logger := generateRedisManagerLogger(cr.Namespace, cr.ObjectMeta.Name) + + var realMasterPod string + realMasterPod = checkAttachedSlave(cr, masterPods) + + if len(slavePods) < 1 { + realMasterPod = masterPods[0] + logger.Info("No Master Node Found with attached slave promoting the following pod to master", "pod", masterPods[0]) + } + + logger.Info("Redis Master Node is set to", "pod", realMasterPod) + realMasterInfo := RedisDetails{ + PodName: realMasterPod, + Namespace: cr.Namespace, + } + + realMasterPodIP := getRedisServerIP(realMasterInfo) + + for i := 0; i < len(masterPods); i++ { + if masterPods[i] != realMasterPod { + + redisClient := configureRedisReplicationClient(cr, masterPods[i]) + defer redisClient.Close() + log.Info("Setting the", "pod", masterPods[i], "to slave of", realMasterPod) + err := redisClient.SlaveOf(realMasterPodIP, "6379").Err() + if err != nil { + logger.Error(err, "Failed to set", "pod", masterPods[i], "to slave of", realMasterPod) + return err + } + } + } + + return nil +} diff --git a/k8sutils/secrets.go b/k8sutils/secrets.go index 840d157a2..a8dfda3c2 100644 --- a/k8sutils/secrets.go +++ b/k8sutils/secrets.go @@ -82,3 +82,51 @@ func getRedisTLSConfig(cr *redisv1beta1.RedisCluster, redisInfo RedisDetails) *t } return nil } + +func getRedisReplicationTLSConfig(cr *redisv1beta1.RedisReplication, redisInfo RedisDetails) *tls.Config { + if cr.Spec.TLS != nil { + reqLogger := log.WithValues("Request.Namespace", cr.Namespace, "Request.Name", cr.ObjectMeta.Name) + secretName, err := generateK8sClient().CoreV1().Secrets(cr.Namespace).Get(context.TODO(), cr.Spec.TLS.Secret.SecretName, metav1.GetOptions{}) + if err != nil { + reqLogger.Error(err, "Failed in getting TLS secret for redis") + } + + var ( + tlsClientCert []byte + tlsClientKey []byte + tlsCaCertificate []byte + tlsCaCertificates *x509.CertPool + tlsClientCertificates []tls.Certificate + ) + for key, value := range secretName.Data { + if key == cr.Spec.TLS.CaKeyFile || key == "ca.crt" { + tlsCaCertificate = value + } else if key == cr.Spec.TLS.CertKeyFile || key == "tls.crt" { + tlsClientCert = value + } else if key == cr.Spec.TLS.KeyFile || key == "tls.key" { + tlsClientKey = value + } + } + + cert, err := tls.X509KeyPair(tlsClientCert, tlsClientKey) + if err != nil { + reqLogger.Error(err, "Couldn't load TLS client key pair") + } + tlsClientCertificates = append(tlsClientCertificates, cert) + + tlsCaCertificates = x509.NewCertPool() + ok := tlsCaCertificates.AppendCertsFromPEM(tlsCaCertificate) + if !ok { + reqLogger.Info("Failed to load CA Certificates from Secret") + } + + return &tls.Config{ + Certificates: tlsClientCertificates, + ServerName: redisInfo.PodName, + RootCAs: tlsCaCertificates, + MinVersion: 2, + ClientAuth: 0, + } + } + return nil +} diff --git a/main.go b/main.go index 9e0856d23..5b6557538 100644 --- a/main.go +++ b/main.go @@ -100,6 +100,14 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "RedisCluster") os.Exit(1) } + if err = (&controllers.RedisReplicationReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("RedisReplication"), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "RedisReplication") + os.Exit(1) + } // +kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("health", healthz.Ping); err != nil { From 34ad273f98e89c9955a4040c96b2ac778eedd28f Mon Sep 17 00:00:00 2001 From: Shubham Gupta <69793468+shubham-cmyk@users.noreply.github.com> Date: Mon, 13 Feb 2023 12:26:43 +0530 Subject: [PATCH 13/18] Fixed Redis Replicate Cache bug (#424) --- api/v1beta1/redisreplication_types.go | 2 +- api/v1beta1/zz_generated.deepcopy.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/v1beta1/redisreplication_types.go b/api/v1beta1/redisreplication_types.go index ca4719ea0..df9ea159c 100644 --- a/api/v1beta1/redisreplication_types.go +++ b/api/v1beta1/redisreplication_types.go @@ -52,7 +52,7 @@ type RedisReplication struct { type RedisReplicationList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []Redis `json:"items"` + Items []RedisReplication `json:"items"` } func init() { diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 5cf190b2f..9013455e6 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -578,7 +578,7 @@ func (in *RedisReplicationList) DeepCopyInto(out *RedisReplicationList) { in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]Redis, len(*in)) + *out = make([]RedisReplication, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } From e86884ead1005484bdb10fb30caf8f8acac2f89b Mon Sep 17 00:00:00 2001 From: Shubham Gupta <69793468+shubham-cmyk@users.noreply.github.com> Date: Mon, 13 Feb 2023 23:45:44 +0530 Subject: [PATCH 14/18] [Feature] Add Redis Sentinel Support (#408) * commit * sentinel Major Changes * example addition * example addition * add Env Variable * add Env variable * add Env Variable in sentinel * fix Issue from the int env Variable * name correction * add field for replication * fix : comments, api add : getMaster IP * add : Dynamic Client , fix : getMasterIP function * change Example * fix : bug * fixed : requirement * change : image name --- api/v1beta1/redissentinel_types.go | 78 + api/v1beta1/zz_generated.deepcopy.go | 184 ++ ...s.redis.opstreelabs.in_redissentinels.yaml | 1537 +++++++++++++++++ config/manager/kustomization.yaml | 10 +- controllers/redissentinel_controller.go | 69 + example/redis-sentinel.yaml | 22 + example/redis_sentinel/sentinel.yaml | 22 + k8sutils/client.go | 15 + k8sutils/finalizer.go | 59 + k8sutils/labels.go | 12 + k8sutils/redis-sentinel.go | 266 +++ k8sutils/statefulset.go | 13 +- main.go | 9 + 13 files changed, 2291 insertions(+), 5 deletions(-) create mode 100644 api/v1beta1/redissentinel_types.go create mode 100644 config/crd/bases/redis.redis.opstreelabs.in_redissentinels.yaml create mode 100644 controllers/redissentinel_controller.go create mode 100644 example/redis-sentinel.yaml create mode 100644 example/redis_sentinel/sentinel.yaml create mode 100644 k8sutils/redis-sentinel.go diff --git a/api/v1beta1/redissentinel_types.go b/api/v1beta1/redissentinel_types.go new file mode 100644 index 000000000..e87c8d728 --- /dev/null +++ b/api/v1beta1/redissentinel_types.go @@ -0,0 +1,78 @@ +package v1beta1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type RedisSentinelSpec struct { + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Default=3 + // +kubebuilder:validation:Not=2 + Size *int32 `json:"clusterSize"` + KubernetesConfig KubernetesConfig `json:"kubernetesConfig"` + RedisSentinelConfig *RedisSentinelConfig `json:"redisSentinelConfig,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` + PriorityClassName string `json:"priorityClassName,omitempty"` + Affinity *corev1.Affinity `json:"affinity,omitempty"` + Tolerations *[]corev1.Toleration `json:"tolerations,omitempty"` + TLS *TLSConfig `json:"TLS,omitempty"` + PodDisruptionBudget *RedisPodDisruptionBudget `json:"pdb,omitempty"` + // +kubebuilder:default:={initialDelaySeconds: 1, timeoutSeconds: 1, periodSeconds: 10, successThreshold: 1, failureThreshold:3} + ReadinessProbe *Probe `json:"readinessProbe,omitempty" protobuf:"bytes,11,opt,name=readinessProbe"` + // +kubebuilder:default:={initialDelaySeconds: 1, timeoutSeconds: 1, periodSeconds: 10, successThreshold: 1, failureThreshold:3} + LivenessProbe *Probe `json:"livenessProbe,omitempty" protobuf:"bytes,11,opt,name=livenessProbe"` + Sidecars *[]Sidecar `json:"sidecars,omitempty"` + ServiceAccountName *string `json:"serviceAccountName,omitempty"` +} + +func (cr *RedisSentinelSpec) GetSentinelCounts(t string) int32 { + replica := cr.Size + return *replica +} + +type RedisSentinelConfig struct { + AdditionalSentinelConfig *string `json:"additionalSentinelConfig,omitempty"` + RedisReplicationName string `json:"redisReplicationName"` + // +kubebuilder:default:=myMaster + MasterGroupName string `json:"masterGroupName,omitempty"` + // +kubebuilder:default:="6379" + RedisPort string `json:"redisPort,omitempty"` + // +kubebuilder:default:="2" + Quorum string `json:"quorum,omitempty"` + // +kubebuilder:default:="1" + ParallelSyncs string `json:"parallelSyncs,omitempty"` + // +kubebuilder:default:="180000" + FailoverTimeout string `json:"failoverTimeout,omitempty"` + // +kubebuilder:default:="30000" + DownAfterMilliseconds string `json:"downAfterMilliseconds,omitempty"` +} + +type RedisSentinelStatus struct { +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// Redis is the Schema for the redis API +type RedisSentinel struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec RedisSentinelSpec `json:"spec"` + Status RedisSentinelStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// RedisList contains a list of Redis +type RedisSentinelList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []RedisSentinel `json:"items"` +} + +func init() { + SchemeBuilder.Register(&RedisSentinel{}, &RedisSentinelList{}) +} diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 9013455e6..bd797c98f 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -713,6 +713,190 @@ func (in *RedisReplicationStatus) DeepCopy() *RedisReplicationStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RedisSentinel) DeepCopyInto(out *RedisSentinel) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisSentinel. +func (in *RedisSentinel) DeepCopy() *RedisSentinel { + if in == nil { + return nil + } + out := new(RedisSentinel) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RedisSentinel) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RedisSentinelConfig) DeepCopyInto(out *RedisSentinelConfig) { + *out = *in + if in.AdditionalSentinelConfig != nil { + in, out := &in.AdditionalSentinelConfig, &out.AdditionalSentinelConfig + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisSentinelConfig. +func (in *RedisSentinelConfig) DeepCopy() *RedisSentinelConfig { + if in == nil { + return nil + } + out := new(RedisSentinelConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RedisSentinelList) DeepCopyInto(out *RedisSentinelList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]RedisSentinel, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisSentinelList. +func (in *RedisSentinelList) DeepCopy() *RedisSentinelList { + if in == nil { + return nil + } + out := new(RedisSentinelList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RedisSentinelList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RedisSentinelSpec) DeepCopyInto(out *RedisSentinelSpec) { + *out = *in + if in.Size != nil { + in, out := &in.Size, &out.Size + *out = new(int32) + **out = **in + } + in.KubernetesConfig.DeepCopyInto(&out.KubernetesConfig) + if in.RedisSentinelConfig != nil { + in, out := &in.RedisSentinelConfig, &out.RedisSentinelConfig + *out = new(RedisSentinelConfig) + (*in).DeepCopyInto(*out) + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = new([]v1.Toleration) + if **in != nil { + in, out := *in, *out + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSConfig) + (*in).DeepCopyInto(*out) + } + if in.PodDisruptionBudget != nil { + in, out := &in.PodDisruptionBudget, &out.PodDisruptionBudget + *out = new(RedisPodDisruptionBudget) + (*in).DeepCopyInto(*out) + } + if in.ReadinessProbe != nil { + in, out := &in.ReadinessProbe, &out.ReadinessProbe + *out = new(Probe) + **out = **in + } + if in.LivenessProbe != nil { + in, out := &in.LivenessProbe, &out.LivenessProbe + *out = new(Probe) + **out = **in + } + if in.Sidecars != nil { + in, out := &in.Sidecars, &out.Sidecars + *out = new([]Sidecar) + if **in != nil { + in, out := *in, *out + *out = make([]Sidecar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + } + if in.ServiceAccountName != nil { + in, out := &in.ServiceAccountName, &out.ServiceAccountName + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisSentinelSpec. +func (in *RedisSentinelSpec) DeepCopy() *RedisSentinelSpec { + if in == nil { + return nil + } + out := new(RedisSentinelSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RedisSentinelStatus) DeepCopyInto(out *RedisSentinelStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedisSentinelStatus. +func (in *RedisSentinelStatus) DeepCopy() *RedisSentinelStatus { + if in == nil { + return nil + } + out := new(RedisSentinelStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RedisSpec) DeepCopyInto(out *RedisSpec) { *out = *in diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redissentinels.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redissentinels.yaml new file mode 100644 index 000000000..77b4d6f17 --- /dev/null +++ b/config/crd/bases/redis.redis.opstreelabs.in_redissentinels.yaml @@ -0,0 +1,1537 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + creationTimestamp: null + name: redissentinels.redis.redis.opstreelabs.in +spec: + group: redis.redis.opstreelabs.in + names: + kind: RedisSentinel + listKind: RedisSentinelList + plural: redissentinels + singular: redissentinel + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: Redis is the Schema for the redis API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + TLS: + description: TLS Configuration for redis instances + properties: + ca: + type: string + cert: + type: string + key: + type: string + secret: + description: Reference to secret which contains the certificates + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This might be + in conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced Secret will be projected into the + volume as a file whose name is the key and content is the + value. If specified, the listed keys will be projected into + the specified paths, and unlisted keys will not be present. + If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' path + or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to set permissions + on this file. Must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, the + volume defaultMode will be used. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + required: + - secret + type: object + affinity: + description: Affinity is a group of affinity scheduling rules. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node matches + the corresponding matchExpressions; the node(s) with the + highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects (i.e. + is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node has + pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may + not try to eventually evict the pod from its node. When + there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms + must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of + pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. null selector + and null or empty namespaces list means "this pod's + namespace". An empty selector ({}) matches all namespaces. + This field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. null or + empty namespaces list and null namespaceSelector means + "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of + any node on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the anti-affinity expressions specified + by this field, but it may choose a node that violates one + or more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node has + pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its + node. When there are multiple elements, the lists of nodes + corresponding to each podAffinityTerm are intersected, i.e. + all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of + pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. null selector + and null or empty namespaces list means "this pod's + namespace". An empty selector ({}) matches all namespaces. + This field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. null or + empty namespaces list and null namespaceSelector means + "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of + any node on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + clusterSize: + format: int32 + minimum: 1 + type: integer + kubernetesConfig: + description: KubernetesConfig will be the JSON struct for Basic Redis + Config + properties: + image: + type: string + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + imagePullSecrets: + items: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + type: array + redisSecret: + description: ExistingPasswordSecret is the struct to access the + existing secret + properties: + key: + type: string + name: + type: string + type: object + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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-resources-containers/' + type: object + type: object + service: + description: ServiceConfig define the type of service to be created + and its annotations + properties: + annotations: + additionalProperties: + type: string + type: object + serviceType: + enum: + - LoadBalancer + - NodePort + - ClusterIP + type: string + type: object + updateStrategy: + description: StatefulSetUpdateStrategy indicates the strategy + that the StatefulSet controller will use to perform updates. + It includes any additional parameters necessary to perform the + update for the indicated strategy. + properties: + rollingUpdate: + description: RollingUpdate is used to communicate parameters + when Type is RollingUpdateStatefulSetStrategyType. + properties: + partition: + description: Partition indicates the ordinal at which + the StatefulSet should be partitioned. Default value + is 0. + format: int32 + type: integer + type: object + type: + description: Type indicates the type of the StatefulSetUpdateStrategy. + Default is RollingUpdate. + type: string + type: object + required: + - image + type: object + livenessProbe: + default: + failureThreshold: 3 + initialDelaySeconds: 1 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + description: Probe is a interface for ReadinessProbe and LivenessProbe + properties: + failureThreshold: + default: 3 + format: int32 + minimum: 1 + type: integer + initialDelaySeconds: + default: 1 + format: int32 + minimum: 1 + type: integer + periodSeconds: + default: 10 + format: int32 + minimum: 1 + type: integer + successThreshold: + default: 1 + format: int32 + minimum: 1 + type: integer + timeoutSeconds: + default: 1 + format: int32 + minimum: 1 + type: integer + type: object + nodeSelector: + additionalProperties: + type: string + type: object + pdb: + description: RedisPodDisruptionBudget configure a PodDisruptionBudget + on the resource (leader/follower) + properties: + enabled: + type: boolean + maxUnavailable: + format: int32 + type: integer + minAvailable: + format: int32 + type: integer + type: object + priorityClassName: + type: string + readinessProbe: + default: + failureThreshold: 3 + initialDelaySeconds: 1 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + description: Probe is a interface for ReadinessProbe and LivenessProbe + properties: + failureThreshold: + default: 3 + format: int32 + minimum: 1 + type: integer + initialDelaySeconds: + default: 1 + format: int32 + minimum: 1 + type: integer + periodSeconds: + default: 10 + format: int32 + minimum: 1 + type: integer + successThreshold: + default: 1 + format: int32 + minimum: 1 + type: integer + timeoutSeconds: + default: 1 + format: int32 + minimum: 1 + type: integer + type: object + redisSentinelConfig: + properties: + additionalSentinelConfig: + type: string + downAfterMilliseconds: + default: "30000" + type: string + failoverTimeout: + default: "180000" + type: string + masterGroupName: + default: myMaster + type: string + parallelSyncs: + default: "1" + type: string + quorum: + default: "2" + type: string + redisPort: + default: "6379" + type: string + redisReplicationName: + type: string + required: + - redisReplicationName + type: object + securityContext: + description: PodSecurityContext holds pod-level security attributes + and common container settings. Some fields are also present in container.securityContext. Field + values of container.securityContext take precedence over field values + of PodSecurityContext. + properties: + fsGroup: + description: "A special supplemental group that applies to all + containers in a pod. Some volume types allow the Kubelet to + change the ownership of that volume to be owned by the pod: + \n 1. The owning GID will be the FSGroup 2. The setgid bit is + set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- \n If unset, + the Kubelet will not modify the ownership and permissions of + any volume. Note that this field cannot be set when spec.os.name + is windows." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. This field will only apply to volume types which + support fsGroup based ownership(and permissions). It will have + no effect on ephemeral volume types such as: secret, configmaps + and emptydir. Valid values are "OnRootMismatch" and "Always". + If not specified, "Always" is used. Note that this field cannot + be set when spec.os.name is windows.' + type: string + runAsGroup: + description: The GID to run the entrypoint of the container process. + Uses runtime default if unset. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail to start + the container if it does. If unset or false, no such validation + will be performed. May also be set in SecurityContext. If set + in both SecurityContext and PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. Note that this field cannot + be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers in this + pod. Note that this field cannot be set when spec.os.name is + windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must be + preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a profile + defined in a file on the node should be used. RuntimeDefault + - the container runtime default profile should be used. + Unconfined - no profile should be applied." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process run + in each container, in addition to the container's primary GID. If + unspecified, no groups will be added to any container. Note + that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used for + the pod. Pods with unsupported sysctls (by the container runtime) + might fail to launch. Note that this field cannot be set when + spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named by + the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is alpha-level + and will only be honored by components that enable the WindowsHostProcessContainers + feature flag. Setting this field without the feature flag + will result in errors when validating the Pod. All of a + Pod's containers must have the same effective HostProcess + value (it is not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in PodSecurityContext. + If set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: string + type: object + type: object + serviceAccountName: + type: string + sidecars: + items: + description: Sidecar for each Redis pods + properties: + env: + 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 previously 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. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) + syntax: i.e. "$$(VAR_NAME)" will produce the string + literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists + or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of 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 + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + 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: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + 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 + TODO: Add other useful fields. apiVersion, kind, + uid?' + 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 + image: + type: string + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + name: + type: string + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + 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-resources-containers/' + type: object + type: object + required: + - image + - name + type: object + type: array + tolerations: + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match all + values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the + value. Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod + can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time + the toleration (which must be of effect NoExecute, otherwise + this field is ignored) tolerates the taint. By default, it + is not set, which means tolerate the taint forever (do not + evict). Zero and negative values will be treated as 0 (evict + immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + required: + - clusterSize + - kubernetesConfig + type: object + status: + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 2bcd3eeaa..05c896cef 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -5,6 +5,12 @@ generatorOptions: disableNameSuffixHash: true configMapGenerator: -- name: manager-config - files: +- files: - controller_manager_config.yaml + name: manager-config +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: +- name: controller + newName: shubham192001/redis-operator + newTag: v0.13.0 diff --git a/controllers/redissentinel_controller.go b/controllers/redissentinel_controller.go new file mode 100644 index 000000000..c47dd63ab --- /dev/null +++ b/controllers/redissentinel_controller.go @@ -0,0 +1,69 @@ +package controllers + +import ( + "context" + "redis-operator/k8sutils" + "time" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + redisv1beta1 "redis-operator/api/v1beta1" +) + +// RedisSentinelReconciler reconciles a RedisSentinel object +type RedisSentinelReconciler struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme +} + +// Reconcile is part of the main kubernetes reconciliation loop which aims +func (r *RedisSentinelReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + reqLogger := r.Log.WithValues("Request.Namespace", req.Namespace, "Request.Name", req.Name) + reqLogger.Info("Reconciling opstree redis controller") + instance := &redisv1beta1.RedisSentinel{} + + err := r.Client.Get(context.TODO(), req.NamespacedName, instance) + if err != nil { + if errors.IsNotFound(err) { + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + // Get total Sentinel Replicas + // sentinelReplicas := instance.Spec.GetSentinelCounts("sentinel") + + if err := k8sutils.HandleRedisSentinelFinalizer(instance, r.Client); err != nil { + return ctrl.Result{RequeueAfter: time.Second * 60}, err + } + + if err := k8sutils.AddRedisSentinelFinalizer(instance, r.Client); err != nil { + return ctrl.Result{RequeueAfter: time.Second * 60}, err + } + + // Create Redis Sentinel + err = k8sutils.CreateRedisSentinel(instance) + if err != nil { + return ctrl.Result{}, err + } + + // Create the Service for Redis Sentinel + err = k8sutils.CreateRedisSentinelService(instance) + if err != nil { + return ctrl.Result{}, err + } + + reqLogger.Info("Will reconcile redis operator in again 10 seconds") + return ctrl.Result{RequeueAfter: time.Second * 10}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *RedisSentinelReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&redisv1beta1.RedisSentinel{}). + Complete(r) +} diff --git a/example/redis-sentinel.yaml b/example/redis-sentinel.yaml new file mode 100644 index 000000000..4eec84b51 --- /dev/null +++ b/example/redis-sentinel.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisSentinel +metadata: + name: redis-sentinel +spec: + clusterSize: 3 + securityContext: + runAsUser: 1000 + fsGroup: 1000 + redisSentinelConfig: + redisReplicationName : redis-replication + kubernetesConfig: + image: quay.io/opstree/redis-sentinel:v7.0.7 + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 101m + memory: 128Mi + limits: + cpu: 101m + memory: 128Mi diff --git a/example/redis_sentinel/sentinel.yaml b/example/redis_sentinel/sentinel.yaml new file mode 100644 index 000000000..4eec84b51 --- /dev/null +++ b/example/redis_sentinel/sentinel.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisSentinel +metadata: + name: redis-sentinel +spec: + clusterSize: 3 + securityContext: + runAsUser: 1000 + fsGroup: 1000 + redisSentinelConfig: + redisReplicationName : redis-replication + kubernetesConfig: + image: quay.io/opstree/redis-sentinel:v7.0.7 + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 101m + memory: 128Mi + limits: + cpu: 101m + memory: 128Mi diff --git a/k8sutils/client.go b/k8sutils/client.go index 1c5ce2a26..54078ab7e 100644 --- a/k8sutils/client.go +++ b/k8sutils/client.go @@ -1,6 +1,8 @@ package k8sutils import ( + // custom "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -19,6 +21,19 @@ func generateK8sClient() *kubernetes.Clientset { return clientset } +// generateK8sClient create Dynamic client for kubernetes +func generateK8sDynamicClient() dynamic.Interface { + config, err := generateK8sConfig() + if err != nil { + panic(err.Error()) + } + dynamicClientset, err := dynamic.NewForConfig(config) + if err != nil { + panic(err.Error()) + } + return dynamicClientset +} + // generateK8sConfig will load the kube config file func generateK8sConfig() (*rest.Config, error) { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() diff --git a/k8sutils/finalizer.go b/k8sutils/finalizer.go index 58d6d5dcf..f26669de1 100644 --- a/k8sutils/finalizer.go +++ b/k8sutils/finalizer.go @@ -16,6 +16,7 @@ const ( RedisFinalizer string = "redisFinalizer" RedisClusterFinalizer string = "redisClusterFinalizer" RedisReplicationFinalizer string = "redisReplicationFinalizer" + RedisSentinelFinalizer string = "redisSentinelFinalizer" ) // finalizeLogger will generate logging interface @@ -96,6 +97,30 @@ func HandleRedisReplicationFinalizer(cr *redisv1beta1.RedisReplication, cl clien return nil } +// HandleRedisSentinelFinalizer finalize resource if instance is marked to be deleted +func HandleRedisSentinelFinalizer(cr *redisv1beta1.RedisSentinel, cl client.Client) error { + logger := finalizerLogger(cr.Namespace, RedisSentinelFinalizer) + if cr.GetDeletionTimestamp() != nil { + if controllerutil.ContainsFinalizer(cr, RedisSentinelFinalizer) { + if err := finalizeRedisSentinelServices(cr); err != nil { + return err + } + if err := finalizeRedisSentinelPVC(cr); err != nil { + return err + } + if err := finalizeRedisSentinelStatefulSets(cr); err != nil { + return err + } + controllerutil.RemoveFinalizer(cr, RedisSentinelFinalizer) + if err := cl.Update(context.TODO(), cr); err != nil { + logger.Error(err, "Could not remove finalizer "+RedisSentinelFinalizer) + return err + } + } + } + return nil +} + // AddRedisFinalizer add finalizer for graceful deletion func AddRedisFinalizer(cr *redisv1beta1.Redis, cl client.Client) error { if !controllerutil.ContainsFinalizer(cr, RedisFinalizer) { @@ -123,6 +148,15 @@ func AddRedisReplicationFinalizer(cr *redisv1beta1.RedisReplication, cl client.C return nil } +// AddRedisSentinelFinalizer add finalizer for graceful deletion +func AddRedisSentinelFinalizer(cr *redisv1beta1.RedisSentinel, cl client.Client) error { + if !controllerutil.ContainsFinalizer(cr, RedisSentinelFinalizer) { + controllerutil.AddFinalizer(cr, RedisSentinelFinalizer) + return cl.Update(context.TODO(), cr) + } + return nil +} + // finalizeRedisServices delete Services func finalizeRedisServices(cr *redisv1beta1.Redis) error { logger := finalizerLogger(cr.Namespace, RedisFinalizer) @@ -165,6 +199,21 @@ func finalizeRedisReplicationServices(cr *redisv1beta1.RedisReplication) error { return nil } +// finalizeRedisSentinelServices delete Services +func finalizeRedisSentinelServices(cr *redisv1beta1.RedisSentinel) error { + logger := finalizerLogger(cr.Namespace, RedisSentinelFinalizer) + serviceName, headlessServiceName := cr.Name, cr.Name+"-headless" + for _, svc := range []string{serviceName, headlessServiceName} { + err := generateK8sClient().CoreV1().Services(cr.Namespace).Delete(context.TODO(), svc, metav1.DeleteOptions{}) + if err != nil && !errors.IsNotFound(err) { + logger.Error(err, "Could not delete service "+svc) + return err + } + } + return nil + +} + // finalizeRedisPVC delete PVC func finalizeRedisPVC(cr *redisv1beta1.Redis) error { logger := finalizerLogger(cr.Namespace, RedisFinalizer) @@ -208,6 +257,11 @@ func finalizeRedisReplicationPVC(cr *redisv1beta1.RedisReplication) error { return nil } +func finalizeRedisSentinelPVC(cr *redisv1beta1.RedisSentinel) error { + + return nil +} + // finalizeRedisStatefulSet delete statefulset for Redis func finalizeRedisStatefulSet(cr *redisv1beta1.Redis) error { logger := finalizerLogger(cr.Namespace, RedisFinalizer) @@ -242,3 +296,8 @@ func finalizeRedisReplicationStatefulSets(cr *redisv1beta1.RedisReplication) err } return nil } + +func finalizeRedisSentinelStatefulSets(cr *redisv1beta1.RedisSentinel) error { + + return nil +} diff --git a/k8sutils/labels.go b/k8sutils/labels.go index fb006269a..9b19a87e6 100644 --- a/k8sutils/labels.go +++ b/k8sutils/labels.go @@ -65,6 +65,18 @@ func redisReplicationAsOwner(cr *redisv1beta1.RedisReplication) metav1.OwnerRefe } } +// RedisSentinelAsOwner generates and returns object refernece +func redisSentinelAsOwner(cr *redisv1beta1.RedisSentinel) metav1.OwnerReference { + trueVar := true + return metav1.OwnerReference{ + APIVersion: cr.APIVersion, + Kind: cr.Kind, + Name: cr.Name, + UID: cr.UID, + Controller: &trueVar, + } +} + // generateStatefulSetsAnots generates and returns statefulsets annotations func generateStatefulSetsAnots(stsMeta metav1.ObjectMeta) map[string]string { anots := map[string]string{ diff --git a/k8sutils/redis-sentinel.go b/k8sutils/redis-sentinel.go new file mode 100644 index 000000000..e52abfb95 --- /dev/null +++ b/k8sutils/redis-sentinel.go @@ -0,0 +1,266 @@ +package k8sutils + +import ( + "context" + "encoding/json" + redisv1beta1 "redis-operator/api/v1beta1" + + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// RedisSentinelSTS is a interface to call Redis Statefulset function +type RedisSentinelSTS struct { + RedisStateFulType string + ExternalConfig *string + Affinity *corev1.Affinity `json:"affinity,omitempty"` + ReadinessProbe *redisv1beta1.Probe + LivenessProbe *redisv1beta1.Probe +} + +// RedisSentinelService is a interface to call Redis Service function +type RedisSentinelService struct { + RedisServiceRole string +} + +type RedisReplicationObject struct { + RedisReplication *redisv1beta1.RedisReplication +} + +// Redis Sentinel Create the Redis Sentinel Setup +func CreateRedisSentinel(cr *redisv1beta1.RedisSentinel) error { + prop := RedisSentinelSTS{ + RedisStateFulType: "sentinel", + Affinity: cr.Spec.Affinity, + ReadinessProbe: cr.Spec.ReadinessProbe, + LivenessProbe: cr.Spec.LivenessProbe, + } + + if cr.Spec.RedisSentinelConfig.AdditionalSentinelConfig != nil { + prop.ExternalConfig = cr.Spec.RedisSentinelConfig.AdditionalSentinelConfig + } + + return prop.CreateRedisSentinelSetup(cr) + +} + +// Create RedisSentinel Service +func CreateRedisSentinelService(cr *redisv1beta1.RedisSentinel) error { + + prop := RedisSentinelService{ + RedisServiceRole: "sentinel", + } + return prop.CreateRedisSentinelService(cr) +} + +// Create Redis Sentinel Cluster Setup +func (service RedisSentinelSTS) CreateRedisSentinelSetup(cr *redisv1beta1.RedisSentinel) error { + + stateFulName := cr.ObjectMeta.Name + logger := statefulSetLogger(cr.Namespace, stateFulName) + labels := getRedisLabels(stateFulName, "cluster", service.RedisStateFulType, cr.ObjectMeta.Labels) + annotations := generateStatefulSetsAnots(cr.ObjectMeta) + objectMetaInfo := generateObjectMetaInformation(stateFulName, cr.Namespace, labels, annotations) + err := CreateOrUpdateStateFul( + cr.Namespace, + objectMetaInfo, + generateRedisSentinelParams(cr, service.getSentinelCount(cr), service.ExternalConfig, service.Affinity), + redisSentinelAsOwner(cr), + generateRedisSentinelContainerParams(cr, service.ReadinessProbe, service.LivenessProbe), + cr.Spec.Sidecars, + ) + + if err != nil { + logger.Error(err, "Cannot create Sentinel statefulset for Redis") + return err + } + return nil +} + +// Create Redis Sentile Params for the statefulset +func generateRedisSentinelParams(cr *redisv1beta1.RedisSentinel, replicas int32, externalConfig *string, affinity *corev1.Affinity) statefulSetParameters { + + res := statefulSetParameters{ + Metadata: cr.ObjectMeta, + Replicas: &replicas, + NodeSelector: cr.Spec.NodeSelector, + SecurityContext: cr.Spec.SecurityContext, + PriorityClassName: cr.Spec.PriorityClassName, + Affinity: affinity, + Tolerations: cr.Spec.Tolerations, + ServiceAccountName: cr.Spec.ServiceAccountName, + UpdateStrategy: cr.Spec.KubernetesConfig.UpdateStrategy, + } + + if cr.Spec.KubernetesConfig.ImagePullSecrets != nil { + res.ImagePullSecrets = cr.Spec.KubernetesConfig.ImagePullSecrets + } + if externalConfig != nil { + res.ExternalConfig = externalConfig + } + + return res +} + +// Create Redis Sentinel Statefulset Container Params +func generateRedisSentinelContainerParams(cr *redisv1beta1.RedisSentinel, readinessProbeDef *redisv1beta1.Probe, livenessProbeDef *redisv1beta1.Probe) containerParameters { + + trueProperty := true + falseProperty := false + containerProp := containerParameters{ + Role: "sentinel", + Image: cr.Spec.KubernetesConfig.Image, + ImagePullPolicy: cr.Spec.KubernetesConfig.ImagePullPolicy, + Resources: cr.Spec.KubernetesConfig.Resources, + AdditionalEnvVariable: getSentinelEnvVariable(cr), + } + if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { + containerProp.EnabledPassword = &trueProperty + containerProp.SecretName = cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name + containerProp.SecretKey = cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key + } else { + containerProp.EnabledPassword = &falseProperty + } + if readinessProbeDef != nil { + containerProp.ReadinessProbe = readinessProbeDef + } + if livenessProbeDef != nil { + containerProp.LivenessProbe = livenessProbeDef + } + if cr.Spec.TLS != nil { + containerProp.TLSConfig = cr.Spec.TLS + } + + return containerProp + +} + +// Get the Count of the Sentinel +func (service RedisSentinelSTS) getSentinelCount(cr *redisv1beta1.RedisSentinel) int32 { + return cr.Spec.GetSentinelCounts(service.RedisStateFulType) +} + +// Create the Service for redis sentinel +func (service RedisSentinelService) CreateRedisSentinelService(cr *redisv1beta1.RedisSentinel) error { + serviceName := cr.ObjectMeta.Name + "-" + service.RedisServiceRole + logger := serviceLogger(cr.Namespace, serviceName) + labels := getRedisLabels(serviceName, "cluster", service.RedisServiceRole, cr.ObjectMeta.Labels) + annotations := generateServiceAnots(cr.ObjectMeta, nil) + + additionalServiceAnnotations := map[string]string{} + if cr.Spec.KubernetesConfig.Service != nil { + additionalServiceAnnotations = cr.Spec.KubernetesConfig.Service.ServiceAnnotations + } + + objectMetaInfo := generateObjectMetaInformation(serviceName, cr.Namespace, labels, annotations) + headlessObjectMetaInfo := generateObjectMetaInformation(serviceName+"-headless", cr.Namespace, labels, annotations) + additionalObjectMetaInfo := generateObjectMetaInformation(serviceName+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, additionalServiceAnnotations)) + + err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisSentinelAsOwner(cr), false, true, "ClusterIP") + if err != nil { + logger.Error(err, "Cannot create headless service for Redis", "Setup.Type", service.RedisServiceRole) + return err + } + err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisSentinelAsOwner(cr), enableMetrics, false, "ClusterIP") + if err != nil { + logger.Error(err, "Cannot create service for Redis", "Setup.Type", service.RedisServiceRole) + return err + } + + additionalServiceType := "ClusterIP" + if cr.Spec.KubernetesConfig.Service != nil { + additionalServiceType = cr.Spec.KubernetesConfig.Service.ServiceType + } + err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisSentinelAsOwner(cr), false, false, additionalServiceType) + if err != nil { + logger.Error(err, "Cannot create additional service for Redis", "Setup.Type", service.RedisServiceRole) + return err + } + return nil + +} + +func getSentinelEnvVariable(cr *redisv1beta1.RedisSentinel) *[]corev1.EnvVar { + + envVar := &[]corev1.EnvVar{ + { + Name: "MASTER_GROUP_NAME", + Value: cr.Spec.RedisSentinelConfig.MasterGroupName, + }, + { + Name: "IP", + Value: getRedisReplicationMasterIP(cr), + }, + { + Name: "PORT", + Value: cr.Spec.RedisSentinelConfig.RedisPort, + }, + { + Name: "QUORUM", + Value: cr.Spec.RedisSentinelConfig.Quorum, + }, + { + Name: "DOWN_AFTER_MILLISECONDS", + Value: cr.Spec.RedisSentinelConfig.DownAfterMilliseconds, + }, + { + Name: "PARALLEL_SYNCS", + Value: cr.Spec.RedisSentinelConfig.ParallelSyncs, + }, + { + Name: "FAILOVER_TIMEOUT", + Value: cr.Spec.RedisSentinelConfig.FailoverTimeout, + }, + } + + return envVar + +} + +func getRedisReplicationMasterIP(cr *redisv1beta1.RedisSentinel) string { + logger := generateRedisManagerLogger(cr.Namespace, cr.ObjectMeta.Name) + + replicationName := cr.Spec.RedisSentinelConfig.RedisReplicationName + replicationNamespace := cr.Namespace + + var replicationInstance redisv1beta1.RedisReplication + + // Get Request on Dynamic Client + customObject, err := generateK8sDynamicClient().Resource(schema.GroupVersionResource{ + Group: "redis.redis.opstreelabs.in", + Version: "v1beta1", + Resource: "redisreplications", + }).Namespace(replicationNamespace).Get(context.TODO(), replicationName, v1.GetOptions{}) + + if err != nil { + logger.Error(err, "Failed to Execute Get Request", "replication name", replicationName, "namespace", replicationNamespace) + return "" + } else { + logger.Info("Successfully Execute the Get Request", "replication name", replicationName, "namespace", replicationNamespace) + } + + // Marshal CustomObject to JSON + replicationJSON, err := customObject.MarshalJSON() + if err != nil { + logger.Error(err, "Failed To Load JSON") + return "" + } + + // Unmarshal The JSON on Object + if err := json.Unmarshal(replicationJSON, &replicationInstance); err != nil { + logger.Error(err, "Failed To Unmarshal JSON over the Object") + return "" + } + + masterPods := GetRedisNodesByRole(&replicationInstance, "master") + realMasterPod := checkAttachedSlave(&replicationInstance, masterPods) + realMasterInfo := RedisDetails{ + PodName: realMasterPod, + Namespace: replicationNamespace, + } + + realMasterPodIP := getRedisServerIP(realMasterInfo) + return realMasterPodIP + +} diff --git a/k8sutils/statefulset.go b/k8sutils/statefulset.go index 0cdfa8c70..280ef3c82 100644 --- a/k8sutils/statefulset.go +++ b/k8sutils/statefulset.go @@ -59,6 +59,7 @@ type containerParameters struct { TLSConfig *redisv1beta1.TLSConfig ReadinessProbe *redisv1beta1.Probe LivenessProbe *redisv1beta1.Probe + AdditionalEnvVariable *[]corev1.EnvVar AdditionalVolume []corev1.Volume AdditionalMountPath []corev1.VolumeMount } @@ -326,6 +327,12 @@ func generateContainerDef(name string, containerParams containerParameters, enab } containerDefinition = append(containerDefinition, container) } + + if containerParams.AdditionalEnvVariable != nil { + containerDefinition[0].Env = append(containerDefinition[0].Env, *containerParams.AdditionalEnvVariable...) + + } + return containerDefinition } @@ -449,7 +456,7 @@ func getProbeInfo(probe *redisv1beta1.Probe) *corev1.Probe { } // getEnvironmentVariables returns all the required Environment Variables -func getEnvironmentVariables(role string, enabledMetric bool, enabledPassword *bool, secretName *string, secretKey *string, persistenceEnabled *bool, extraEnv *[]corev1.EnvVar, tlsConfig *redisv1beta1.TLSConfig) []corev1.EnvVar { +func getEnvironmentVariables(role string, enabledMetric bool, enabledPassword *bool, secretName *string, secretKey *string, persistenceEnabled *bool, exporterEnvVar *[]corev1.EnvVar, tlsConfig *redisv1beta1.TLSConfig) []corev1.EnvVar { envVars := []corev1.EnvVar{ {Name: "SERVER_MODE", Value: role}, {Name: "SETUP_MODE", Value: role}, @@ -501,8 +508,8 @@ func getEnvironmentVariables(role string, enabledMetric bool, enabledPassword *b envVars = append(envVars, corev1.EnvVar{Name: "PERSISTENCE_ENABLED", Value: "true"}) } - if extraEnv != nil { - envVars = append(envVars, *extraEnv...) + if exporterEnvVar != nil { + envVars = append(envVars, *exporterEnvVar...) } sort.SliceStable(envVars, func(i, j int) bool { return envVars[i].Name < envVars[j].Name diff --git a/main.go b/main.go index 5b6557538..44c9b5bc8 100644 --- a/main.go +++ b/main.go @@ -108,6 +108,15 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "RedisReplication") os.Exit(1) } + + if err = (&controllers.RedisSentinelReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("RedisSentinel"), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "RedisSentinel") + os.Exit(1) + } // +kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("health", healthz.Ping); err != nil { From 418c7f6498a0a8a953a8d0efa41aed04b5b5d914 Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Thu, 9 Jun 2022 13:35:49 -0400 Subject: [PATCH 15/18] Adds CODEOWNERS for our internal branch --- CODEOWNERS | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 000000000..7228f769f --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,7 @@ +# This lists file patterns and associated owners. Last match wins. + +# Assign everything to controlplane by default +* @@team-eng-platform-dataservices + +# Require at least one approval from an owner +OverallCheck(1) From 4ad95848d67ee2dd54c13dcb81c740bc48bff872 Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Thu, 9 Jun 2022 15:46:42 -0400 Subject: [PATCH 16/18] Bumps prometheus/client_golang to address vuln --- go.mod | 2 ++ go.sum | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 021a64691..cf34e7ac0 100644 --- a/go.mod +++ b/go.mod @@ -76,3 +76,5 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.2.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) + +replace github.com/prometheus/client_golang v1.11.0 => github.com/prometheus/client_golang v1.11.1 diff --git a/go.sum b/go.sum index 62a6ce859..f6f0c1c59 100644 --- a/go.sum +++ b/go.sum @@ -407,8 +407,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= From 8f083d51ecadf07c4faf538e312409bc9f282224 Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Tue, 13 Sep 2022 12:52:11 -0400 Subject: [PATCH 17/18] Handle nil probe This handles the case where the probes are not, this will use the defaults. This fix should be temp. until the upstream fix is in place https://github.com/OT-CONTAINER-KIT/redis-operator/pull/323 --- k8sutils/statefulset.go | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/k8sutils/statefulset.go b/k8sutils/statefulset.go index 280ef3c82..da0ebdb72 100644 --- a/k8sutils/statefulset.go +++ b/k8sutils/statefulset.go @@ -438,21 +438,27 @@ func getVolumeMount(name string, persistenceEnabled *bool, externalConfig *strin // getProbeInfo generate probe for Redis StatefulSet func getProbeInfo(probe *redisv1beta1.Probe) *corev1.Probe { - return &corev1.Probe{ - InitialDelaySeconds: probe.InitialDelaySeconds, - PeriodSeconds: probe.PeriodSeconds, - FailureThreshold: probe.FailureThreshold, - TimeoutSeconds: probe.TimeoutSeconds, - SuccessThreshold: probe.SuccessThreshold, - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{ - "bash", - "/usr/bin/healthcheck.sh", - }, + var p *corev1.Probe + if probe != nil { + p = &corev1.Probe{ + InitialDelaySeconds: probe.InitialDelaySeconds, + PeriodSeconds: probe.PeriodSeconds, + FailureThreshold: probe.FailureThreshold, + TimeoutSeconds: probe.TimeoutSeconds, + SuccessThreshold: probe.SuccessThreshold, + } + } else { + p = &corev1.Probe{} + } + p.ProbeHandler = corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{ + "bash", + "/usr/bin/healthcheck.sh", }, }, } + return p } // getEnvironmentVariables returns all the required Environment Variables From 2ea8fcaf61b322186f8a0a2c4e7bcb310f55ea2d Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Tue, 7 Mar 2023 16:14:55 -0500 Subject: [PATCH 18/18] Revert "Handle nil probe" This reverts commit 8f083d51ecadf07c4faf538e312409bc9f282224. --- k8sutils/statefulset.go | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/k8sutils/statefulset.go b/k8sutils/statefulset.go index da0ebdb72..280ef3c82 100644 --- a/k8sutils/statefulset.go +++ b/k8sutils/statefulset.go @@ -438,27 +438,21 @@ func getVolumeMount(name string, persistenceEnabled *bool, externalConfig *strin // getProbeInfo generate probe for Redis StatefulSet func getProbeInfo(probe *redisv1beta1.Probe) *corev1.Probe { - var p *corev1.Probe - if probe != nil { - p = &corev1.Probe{ - InitialDelaySeconds: probe.InitialDelaySeconds, - PeriodSeconds: probe.PeriodSeconds, - FailureThreshold: probe.FailureThreshold, - TimeoutSeconds: probe.TimeoutSeconds, - SuccessThreshold: probe.SuccessThreshold, - } - } else { - p = &corev1.Probe{} - } - p.ProbeHandler = corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{ - "bash", - "/usr/bin/healthcheck.sh", + return &corev1.Probe{ + InitialDelaySeconds: probe.InitialDelaySeconds, + PeriodSeconds: probe.PeriodSeconds, + FailureThreshold: probe.FailureThreshold, + TimeoutSeconds: probe.TimeoutSeconds, + SuccessThreshold: probe.SuccessThreshold, + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{ + "bash", + "/usr/bin/healthcheck.sh", + }, }, }, } - return p } // getEnvironmentVariables returns all the required Environment Variables