diff --git a/deploy/crds/pravega.pravega.io_pravegaclusters_crd.yaml b/deploy/crds/pravega.pravega.io_pravegaclusters_crd.yaml index cd23954a4..d066773f4 100644 --- a/deploy/crds/pravega.pravega.io_pravegaclusters_crd.yaml +++ b/deploy/crds/pravega.pravega.io_pravegaclusters_crd.yaml @@ -2186,6 +2186,24 @@ spec: repository: type: string type: object + influxDBSecret: + description: InfluxDB Secret specifies the secret name containing + credentials and mount path volume that has to be configured + in controller and segmentstore pods + properties: + mountPath: + description: Path to the volume where the secret will be mounted + This value is considered only when the secret is provided + If this value is provided, the secret is mounted to specified + Volume else default mount path is used. + type: string + secret: + description: Secret specifies the name of Secret which needs + to be configured + type: string + required: + - secret + type: object longtermStorage: description: LongTermStorage is the configuration of Pravega's tier 2 storage. If no configuration is provided, it will assume diff --git a/doc/configuration.md b/doc/configuration.md index cdafe9c68..9a3bd2a4b 100644 --- a/doc/configuration.md +++ b/doc/configuration.md @@ -17,3 +17,4 @@ This document explains how to configure Pravega * [Configuring Service and Sts Names](service-sts-name-configuration.md) * [Enable AuthHandlers](auth-handlers.md) * [Init Containers Usage](init-containers.md) +* [Enable InfluxDB Authentication](influxdb-auth.md) diff --git a/doc/influxdb-auth.md b/doc/influxdb-auth.md new file mode 100644 index 000000000..e2bd8a512 --- /dev/null +++ b/doc/influxdb-auth.md @@ -0,0 +1,27 @@ +# Enable InfluxDB Authentication + +Operator supports passing influxdb credentials as secret. It is the recommended approach rather than passing username/password as part of Pravega options. + +Steps to configure influxdb authentication are as follows: + +1. Create a secret for basic authentication. Below is the sample yaml file + +``` +apiVersion: v1 +kind: Secret +metadata: + name: secret-basic-auth +type: kubernetes.io/basic-auth +stringData: + username: admin + password: t0p-Secret + ``` +2. Modify the Pravega manifest to include the secret name for influxdb + +``` +influxDBSecret: + name: "secret-basic-auth" + path: "/etc/secret" +``` + +3. Once Pravega is deployed, secret will be mounted in `/etc/secret` for controller and segment store pods. If the path is not mentioned, `/etc/influxdb-secret-volume` will be used as default path. diff --git a/pkg/apis/pravega/v1beta1/pravega.go b/pkg/apis/pravega/v1beta1/pravega.go index 6d7e8d51d..da0918594 100644 --- a/pkg/apis/pravega/v1beta1/pravega.go +++ b/pkg/apis/pravega/v1beta1/pravega.go @@ -65,6 +65,9 @@ const ( // DefaultSegmentStoreLimitMemory is the default memory limit for Pravega DefaultSegmentStoreLimitMemory = "2Gi" + + // DefaultInfluxDBSecretMountDir is the default mountpath of influxdb secret + DefaultInfluxDBSecretMountDir = "/etc/influxdb-secret-volume" ) // PravegaSpec defines the configuration of Pravega @@ -228,6 +231,10 @@ type PravegaSpec struct { // Details of authplugin to be copied into pravega controller AuthImplementations *AuthImplementationSpec `json:"authImplementations,omitempty"` + + // InfluxDB Secret specifies the secret name containing credentials and mount path volume + // that has to be configured in controller and segmentstore pods + InfluxDBSecret *InfluxDBSecret `json:"influxDBSecret,omitempty"` } func (s *PravegaSpec) withDefaults() (changed bool) { @@ -325,6 +332,14 @@ func (s *PravegaSpec) withDefaults() (changed bool) { changed = true } + if s.InfluxDBSecret == nil { + changed = true + s.InfluxDBSecret = &InfluxDBSecret{} + } + + if s.InfluxDBSecret.withDefaults() { + changed = true + } if s.ControllerServiceAnnotations == nil { changed = true s.ControllerServiceAnnotations = map[string]string{} @@ -389,6 +404,29 @@ func (s *SegmentStoreSecret) withDefaults() (changed bool) { return changed } +// InfluxDBSecret defines the configuration of the secret for controller and +// segmentstore +type InfluxDBSecret struct { + // Secret specifies the name of Secret which needs to be configured + Secret string `json:"secret"` + + // Path to the volume where the secret will be mounted + // This value is considered only when the secret is provided + // If this value is provided, the secret is mounted to specified Volume + // else default mount path is used. + // +optional + MountPath string `json:"mountPath"` +} + +func (s *InfluxDBSecret) withDefaults() (changed bool) { + if s.Secret != "" && s.MountPath == "" { + s.MountPath = DefaultInfluxDBSecretMountDir + changed = true + } + + return changed +} + func (s *ImageSpec) withDefaults() (changed bool) { if s.Repository == "" { changed = true diff --git a/pkg/apis/pravega/v1beta1/zz_generated.deepcopy.go b/pkg/apis/pravega/v1beta1/zz_generated.deepcopy.go index 5c2b41614..e9e7e743f 100644 --- a/pkg/apis/pravega/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/pravega/v1beta1/zz_generated.deepcopy.go @@ -226,6 +226,22 @@ func (in *ImageSpec) DeepCopy() *ImageSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InfluxDBSecret) DeepCopyInto(out *InfluxDBSecret) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfluxDBSecret. +func (in *InfluxDBSecret) DeepCopy() *InfluxDBSecret { + if in == nil { + return nil + } + out := new(InfluxDBSecret) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LongTermStorageSpec) DeepCopyInto(out *LongTermStorageSpec) { *out = *in @@ -475,6 +491,11 @@ func (in *PravegaSpec) DeepCopyInto(out *PravegaSpec) { *out = new(AuthImplementationSpec) (*in).DeepCopyInto(*out) } + if in.InfluxDBSecret != nil { + in, out := &in.InfluxDBSecret, &out.InfluxDBSecret + *out = new(InfluxDBSecret) + **out = **in + } return } diff --git a/pkg/apis/pravega/v1beta1/zz_generated_deepcopy_test.go b/pkg/apis/pravega/v1beta1/zz_generated_deepcopy_test.go index c737b018c..14825ba26 100644 --- a/pkg/apis/pravega/v1beta1/zz_generated_deepcopy_test.go +++ b/pkg/apis/pravega/v1beta1/zz_generated_deepcopy_test.go @@ -44,7 +44,10 @@ var _ = Describe("PravegaCluster DeepCopy", func() { Secret: "seg-secret", MountPath: "", }, - + InfluxDBSecret: &v1beta1.InfluxDBSecret{ + Secret: "influx-secret", + MountPath: "", + }, SegmentStoreInitContainers: []v1.Container{ v1.Container{ Name: "testing", @@ -125,6 +128,7 @@ var _ = Describe("PravegaCluster DeepCopy", func() { p2.Spec.Pravega = p1.Spec.Pravega.DeepCopy() p2.Spec.Pravega.SegmentStoreSecret = p1.Spec.Pravega.SegmentStoreSecret.DeepCopy() + p2.Spec.Pravega.InfluxDBSecret = p1.Spec.Pravega.InfluxDBSecret.DeepCopy() p2.Spec.Pravega.AuthImplementations = p1.Spec.Pravega.AuthImplementations.DeepCopy() p2.Spec.Pravega.AuthImplementations.AuthHandlers[0] = *p1.Spec.Pravega.AuthImplementations.AuthHandlers[0].DeepCopy() @@ -244,10 +248,18 @@ var _ = Describe("PravegaCluster DeepCopy", func() { p1.Spec.ExternalAccess = nil Ω(p1.Spec.ExternalAccess.DeepCopy()).Should(BeNil()) }) - It("checking for nil TLS Sttic", func() { + It("checking for nil TLS Static", func() { p1.Spec.TLS.Static = nil Ω(p1.Spec.TLS.Static.DeepCopy()).Should(BeNil()) }) + It("checking for nil InfluxDBsecret", func() { + p1.Spec.Pravega.InfluxDBSecret = nil + Ω(p1.Spec.Pravega.InfluxDBSecret.DeepCopy()).Should(BeNil()) + }) + It("checking for nil SegmentStore secret", func() { + p1.Spec.Pravega.SegmentStoreSecret = nil + Ω(p1.Spec.Pravega.SegmentStoreSecret.DeepCopy()).Should(BeNil()) + }) It("checking for nil Pravega LongTermStorage", func() { p1.Spec.Pravega.LongTermStorage = nil Ω(p1.Spec.Pravega.LongTermStorage.DeepCopy()).Should(BeNil()) diff --git a/pkg/controller/pravega/constants.go b/pkg/controller/pravega/constants.go index d33b5f65f..ae0616c46 100644 --- a/pkg/controller/pravega/constants.go +++ b/pkg/controller/pravega/constants.go @@ -28,4 +28,5 @@ const ( ssAuthVolumeName = "ss-auth-secret" controllerAuthMountDir = "/etc/controller-auth-volume" ssAuthMountDir = "/etc/ss-auth-volume" + influxDBSecretVolumeName = "influxdb-secret" ) diff --git a/pkg/controller/pravega/pravega_controller.go b/pkg/controller/pravega/pravega_controller.go index 29648a6d4..8bd99801f 100644 --- a/pkg/controller/pravega/pravega_controller.go +++ b/pkg/controller/pravega/pravega_controller.go @@ -253,6 +253,7 @@ func makeControllerPodSpec(p *api.PravegaCluster) *corev1.PodSpec { configureControllerTLSSecrets(podSpec, p) configureAuthSecrets(podSpec, p) configureControllerAuthSecrets(podSpec, p) + configureControllerInfluxDBSecrets(podSpec, p) return podSpec } @@ -276,6 +277,20 @@ func configureControllerAuthSecrets(podSpec *corev1.PodSpec, p *api.PravegaClust } } +func configureControllerInfluxDBSecrets(podSpec *corev1.PodSpec, p *api.PravegaCluster) { + if p.Spec.Pravega.InfluxDBSecret.Secret != "" { + addSecretVolumeWithMount(podSpec, p, influxDBSecretVolumeName, p.Spec.Pravega.InfluxDBSecret.Secret, + influxDBSecretVolumeName, p.Spec.Pravega.InfluxDBSecret.MountPath) + + podSpec.Containers[0].Env = []corev1.EnvVar{ + { + Name: "INFLUX_DB_SECRET_MOUNT_PATH", + Value: p.Spec.Pravega.InfluxDBSecret.MountPath, + }, + } + } +} + func addSecretVolumeWithMount(podSpec *corev1.PodSpec, p *api.PravegaCluster, volumeName string, secretName string, mountName string, mountDir string) { diff --git a/pkg/controller/pravega/pravega_controller_test.go b/pkg/controller/pravega/pravega_controller_test.go index c4b1e31ce..2c9b14fb9 100644 --- a/pkg/controller/pravega/pravega_controller_test.go +++ b/pkg/controller/pravega/pravega_controller_test.go @@ -87,6 +87,10 @@ var _ = Describe("Controller", func() { ControllerPodAnnotations: annotationsMap, SegmentStoreServiceAnnotations: annotationsMap, SegmentStorePodLabels: annotationsMap, + InfluxDBSecret: &v1beta1.InfluxDBSecret{ + Secret: "influxdb-secret", + MountPath: "", + }, Image: &v1beta1.ImageSpec{ Repository: "bar/pravega", }, @@ -181,6 +185,11 @@ var _ = Describe("Controller", func() { }) + It("should have VolumeMounts created for influxdb secret", func() { + deploy := pravega.MakeControllerPodTemplate(p) + mounthostpath := deploy.Spec.Containers[0].VolumeMounts[9].MountPath + Ω(mounthostpath).Should(Equal("/etc/influxdb-secret-volume")) + }) It("should create the service", func() { svc := pravega.MakeControllerService(p) Ω(svc.Spec.Type).To(Equal(corev1.ServiceTypeClusterIP)) diff --git a/pkg/controller/pravega/pravega_segmentstore.go b/pkg/controller/pravega/pravega_segmentstore.go index 20a616990..91504feb0 100644 --- a/pkg/controller/pravega/pravega_segmentstore.go +++ b/pkg/controller/pravega/pravega_segmentstore.go @@ -74,6 +74,7 @@ func MakeSegmentStorePodTemplate(p *api.PravegaCluster) corev1.PodTemplateSpec { func makeSegmentstorePodSpec(p *api.PravegaCluster) corev1.PodSpec { configMapName := strings.TrimSpace(p.Spec.Pravega.SegmentStoreEnvVars) secret := p.Spec.Pravega.SegmentStoreSecret + environment := []corev1.EnvFromSource{ { ConfigMapRef: &corev1.ConfigMapEnvSource{ @@ -263,6 +264,8 @@ func makeSegmentstorePodSpec(p *api.PravegaCluster) corev1.PodSpec { configureSegmentstoreAuthSecret(&podSpec, p) + configureInfluxDBSecret(&podSpec, p) + return podSpec } @@ -471,6 +474,30 @@ func configureCaBundleSecret(podSpec *corev1.PodSpec, p *api.PravegaCluster) { } } +func configureInfluxDBSecret(podSpec *corev1.PodSpec, p *api.PravegaCluster) { + if p.Spec.Pravega.InfluxDBSecret.Secret != "" { + vol := corev1.Volume{ + Name: influxDBSecretVolumeName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: p.Spec.Pravega.InfluxDBSecret.Secret, + }, + }, + } + podSpec.Volumes = append(podSpec.Volumes, vol) + + podSpec.Containers[0].VolumeMounts = append(podSpec.Containers[0].VolumeMounts, corev1.VolumeMount{ + Name: influxDBSecretVolumeName, + MountPath: p.Spec.Pravega.InfluxDBSecret.MountPath, + }) + + podSpec.Containers[0].Env = append(podSpec.Containers[0].Env, corev1.EnvVar{ + Name: "INFLUX_DB_SECRET_MOUNT_PATH", + Value: p.Spec.Pravega.InfluxDBSecret.MountPath, + }) + } +} + func MakeSegmentStoreHeadlessService(p *api.PravegaCluster) *corev1.Service { serviceport, _ := strconv.Atoi(p.Spec.Pravega.Options["pravegaservice.service.listener.port"]) return &corev1.Service{ diff --git a/pkg/controller/pravega/pravega_segmentstore_test.go b/pkg/controller/pravega/pravega_segmentstore_test.go index ed7a056ce..eff6acdd2 100644 --- a/pkg/controller/pravega/pravega_segmentstore_test.go +++ b/pkg/controller/pravega/pravega_segmentstore_test.go @@ -87,6 +87,10 @@ var _ = Describe("PravegaSegmentstore", func() { SegmentStorePodLabels: annotationsMap, SegmentStorePodAnnotations: annotationsMap, SegmentStoreEnvVars: "SEG_CONFIG_MAP", + InfluxDBSecret: &v1beta1.InfluxDBSecret{ + Secret: "influxdb-secret", + MountPath: "", + }, SegmentStoreSecret: &v1beta1.SegmentStoreSecret{ Secret: "seg-secret", MountPath: "", @@ -120,8 +124,9 @@ var _ = Describe("PravegaSegmentstore", func() { }, }, Authentication: &v1beta1.AuthenticationParameters{ - Enabled: true, - PasswordAuthSecret: "authentication-secret", + Enabled: true, + PasswordAuthSecret: "authentication-secret", + SegmentStoreTokenSecret: "segment-store-secret", }, } p.WithDefaults() @@ -187,6 +192,8 @@ var _ = Describe("PravegaSegmentstore", func() { Ω(mounthostpath3).Should(Equal("/opt/pravega/logs")) mounthostpath4 := sts.Spec.Template.Spec.Containers[0].VolumeMounts[4].MountPath Ω(mounthostpath4).Should(Equal("/opt/pravega/conf/logback.xml")) + mounthostpath5 := sts.Spec.Template.Spec.Containers[0].VolumeMounts[9].MountPath + Ω(mounthostpath5).Should(Equal("/etc/influxdb-secret-volume")) Ω(err).Should(BeNil()) }) @@ -315,11 +322,14 @@ var _ = Describe("PravegaSegmentstore", func() { Ω(strings.Contains(javaOpts, "-Dpravegaservice.service.listener.port=443")).Should(BeTrue()) Ω(err).Should(BeNil()) }) - + It("should create a stateful set with filesystem as longtermstorage", func() { + sts := pravega.MakeSegmentStoreStatefulSet(p) + mounthostpath0 := sts.Spec.Template.Spec.Containers[0].VolumeMounts[4].MountPath + Ω(mounthostpath0).Should(Equal("/mnt/tier2")) + }) It("should set external access service type to LoadBalancer", func() { Ω(p.Spec.ExternalAccess.Type).Should(Equal(corev1.ServiceTypeClusterIP)) }) - }) }) @@ -362,6 +372,10 @@ var _ = Describe("PravegaSegmentstore", func() { SegmentStoreServiceAnnotations: annotationsMap, SegmentStorePodLabels: annotationsMap, SegmentStoreExternalServiceType: corev1.ServiceTypeLoadBalancer, + SegmentStoreSecret: &v1beta1.SegmentStoreSecret{ + Secret: "seg-secret", + MountPath: "/tmp/mount", + }, Image: &v1beta1.ImageSpec{ Repository: "bar/pravega", }, diff --git a/test/e2e/resources/crd.yaml b/test/e2e/resources/crd.yaml index cf96df0b6..1e2068c73 100644 --- a/test/e2e/resources/crd.yaml +++ b/test/e2e/resources/crd.yaml @@ -2188,6 +2188,24 @@ spec: repository: type: string type: object + influxDBSecret: + description: InfluxDB Secret specifies the secret name containing + credentials and mount path volume that has to be configured + in controller and segmentstore pods + properties: + mountPath: + description: Path to the volume where the secret will be mounted + This value is considered only when the secret is provided + If this value is provided, the secret is mounted to specified + Volume else default mount path is used. + type: string + secret: + description: Secret specifies the name of Secret which needs + to be configured + type: string + required: + - secret + type: object longtermStorage: description: LongTermStorage is the configuration of Pravega's tier 2 storage. If no configuration is provided, it will assume