Skip to content

Commit

Permalink
fix: added support of opensource redis_exporter support for password …
Browse files Browse the repository at this point in the history
…update (#58)

* fix: added support of opensource redis_exporter support for password update

* test: added unit test

* fix: fix set volumes when enable tls
  • Loading branch information
chideat authored Aug 29, 2024
1 parent 9b19015 commit 673ef42
Show file tree
Hide file tree
Showing 4 changed files with 565 additions and 43 deletions.
56 changes: 38 additions & 18 deletions internal/builder/clusterbuilder/statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const (
// Volume
RedisStorageVolumeName = "redis-data"
RedisTempVolumeName = "temp"
RedisExporterTempVolumeName = "exporter-temp"
RedisOperatorPasswordVolumeName = "operator-password"
ConfigVolumeName = "conf"
RedisTLSVolumeName = "redis-tls"
Expand Down Expand Up @@ -332,14 +333,6 @@ func persistentClaim(cluster *redisv1alpha1.DistributedRedisCluster, labels map[
func redisServerContainer(cluster *redisv1alpha1.DistributedRedisCluster, u *user.User, envs []corev1.EnvVar, index int) corev1.Container {
shutdownArgs := []string{"sh", "-c", "/opt/redis-tools cluster shutdown &> /proc/1/fd/1"}
startArgs := []string{"sh", "/opt/run.sh"}
if cluster.Spec.EnableActiveRedis {
startArgs = append(startArgs,
"--loadmodule", "/modules/activeredis.so",
"service_id", fmt.Sprintf("%d", *cluster.Spec.ServiceID),
"service_uid", string(cluster.UID),
"shard_id", fmt.Sprintf("%d", index),
)
}

container := corev1.Container{
Env: envs,
Expand Down Expand Up @@ -480,12 +473,20 @@ func buildContainers(cluster *redisv1alpha1.DistributedRedisCluster, user *user.
}

func redisExporterContainer(cluster *redisv1alpha1.DistributedRedisCluster, user *user.User) corev1.Container {
const DefaultPasswordFile = "/tmp/passwords.json"
entrypoint := fmt.Sprintf(`
if [ -f /account/password ]; then
echo "{\"${REDIS_ADDR}\": \"$(cat /account/password)\"}" > %s
fi
/redis_exporter --web.listen-address=:%d --web.telemetry-path=%s %s`,
DefaultPasswordFile,
PrometheusExporterPortNumber,
PrometheusExporterTelemetryPath,
strings.Join(cluster.Spec.Monitor.Args, " "),
)
container := corev1.Container{
Name: ExporterContainerName,
Args: append([]string{
fmt.Sprintf("--web.listen-address=:%v", PrometheusExporterPortNumber),
fmt.Sprintf("--web.telemetry-path=%v", PrometheusExporterTelemetryPath),
}, cluster.Spec.Monitor.Args...),
Name: ExporterContainerName,
Command: []string{"/bin/sh", "-c", entrypoint},
Image: cluster.Spec.Monitor.Image,
ImagePullPolicy: builder.GetPullPolicy(cluster.Spec.Monitor.ImagePullPolicy, cluster.Spec.ImagePullPolicy),
Ports: []corev1.ContainerPort{
Expand All @@ -509,12 +510,19 @@ func redisExporterContainer(cluster *redisv1alpha1.DistributedRedisCluster, user
corev1.EnvVar{Name: OperatorUsername, Value: user.Name},
corev1.EnvVar{Name: OperatorSecretName, Value: user.GetPassword().GetSecretName()},
corev1.EnvVar{Name: "REDIS_USER", Value: name},
corev1.EnvVar{Name: "REDIS_PASSWORD_FILE", Value: DefaultPasswordFile},
)

container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{
Name: RedisOperatorPasswordVolumeName,
MountPath: OperatorPasswordVolumeMountPath,
})
container.VolumeMounts = append(container.VolumeMounts,
corev1.VolumeMount{
Name: RedisOperatorPasswordVolumeName,
MountPath: OperatorPasswordVolumeMountPath,
},
corev1.VolumeMount{
Name: RedisExporterTempVolumeName,
MountPath: RedisTmpVolumeMountPath,
},
)
}

if cluster.Spec.EnableTLS {
Expand Down Expand Up @@ -546,7 +554,7 @@ func redisExporterContainer(cluster *redisv1alpha1.DistributedRedisCluster, user
Value: fmt.Sprintf("rediss://%s:%d", config.LocalInjectName, DefaultRedisServerPort),
},
}...)
} else if cluster.Spec.IPFamilyPrefer == corev1.IPv6Protocol {
} else {
container.Env = append(container.Env, []corev1.EnvVar{
{Name: "REDIS_ADDR",
Value: fmt.Sprintf("redis://%s:%d", config.LocalInjectName, DefaultRedisServerPort)},
Expand Down Expand Up @@ -671,6 +679,18 @@ func redisVolumes(cluster *redisv1alpha1.DistributedRedisCluster, user *user.Use
})
}

if cluster.Spec.Monitor != nil {
volumes = append(volumes, corev1.Volume{
Name: RedisExporterTempVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{
Medium: corev1.StorageMediumMemory,
SizeLimit: resource.NewQuantity(1<<20, resource.BinarySI), //1Mi
},
},
})
}

if cluster.Spec.EnableTLS {
volumes = append(volumes, corev1.Volume{
Name: RedisTLSVolumeName,
Expand Down
256 changes: 256 additions & 0 deletions internal/builder/clusterbuilder/statefulset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
package clusterbuilder

import (
"testing"

redisv1alpha1 "github.com/alauda/redis-operator/api/cluster/v1alpha1"
"github.com/alauda/redis-operator/pkg/types/user"

"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
)

func TestRedisExporterContainer(t *testing.T) {
tests := []struct {
name string
cluster *redisv1alpha1.DistributedRedisCluster
user *user.User
expected corev1.Container
}{
{
name: "Basic test",
cluster: &redisv1alpha1.DistributedRedisCluster{
Spec: redisv1alpha1.DistributedRedisClusterSpec{
Monitor: &redisv1alpha1.Monitor{
Image: "redis-exporter:latest",
Env: []corev1.EnvVar{
{Name: "ENV_VAR", Value: "value"},
},
Resources: &corev1.ResourceRequirements{
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("200Mi"),
},
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("200Mi"),
},
},
},
},
},
user: &user.User{
Name: "default",
Password: &user.Password{
SecretName: "secret-name",
},
},
expected: corev1.Container{
Name: ExporterContainerName,
Image: "redis-exporter:latest",
ImagePullPolicy: corev1.PullAlways,
Ports: []corev1.ContainerPort{
{
Name: "prom-http",
Protocol: corev1.ProtocolTCP,
ContainerPort: PrometheusExporterPortNumber,
},
},
Env: []corev1.EnvVar{
{Name: "ENV_VAR", Value: "value"},
{Name: OperatorUsername, Value: "default"},
{Name: OperatorSecretName, Value: "secret-name"},
{Name: "REDIS_USER", Value: ""},
{Name: "REDIS_PASSWORD_FILE", Value: "/tmp/passwords.json"},
{Name: "REDIS_ADDR", Value: "redis://local.inject:6379"},
},
Resources: corev1.ResourceRequirements{
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("200Mi"),
},
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("200Mi"),
},
},
VolumeMounts: []corev1.VolumeMount{
{Name: RedisOperatorPasswordVolumeName, MountPath: OperatorPasswordVolumeMountPath},
{Name: RedisExporterTempVolumeName, MountPath: RedisTmpVolumeMountPath},
},
},
},
{
name: "test with tls enabled",
cluster: &redisv1alpha1.DistributedRedisCluster{
Spec: redisv1alpha1.DistributedRedisClusterSpec{
EnableTLS: true,
Monitor: &redisv1alpha1.Monitor{
Image: "redis-exporter:latest",
Env: []corev1.EnvVar{
{Name: "ENV_VAR", Value: "value"},
},
Resources: &corev1.ResourceRequirements{
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("200Mi"),
},
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("200Mi"),
},
},
},
},
},
user: &user.User{
Name: "default",
Password: &user.Password{
SecretName: "secret-name",
},
},
expected: corev1.Container{
Name: ExporterContainerName,
Image: "redis-exporter:latest",
ImagePullPolicy: corev1.PullAlways,
Ports: []corev1.ContainerPort{
{
Name: "prom-http",
Protocol: corev1.ProtocolTCP,
ContainerPort: PrometheusExporterPortNumber,
},
},
Env: []corev1.EnvVar{
{Name: "ENV_VAR", Value: "value"},
{Name: OperatorUsername, Value: "default"},
{Name: OperatorSecretName, Value: "secret-name"},
{Name: "REDIS_USER", Value: ""},
{Name: "REDIS_PASSWORD_FILE", Value: "/tmp/passwords.json"},
{Name: "REDIS_EXPORTER_TLS_CLIENT_KEY_FILE", Value: "/tls/tls.key"},
{Name: "REDIS_EXPORTER_TLS_CLIENT_CERT_FILE", Value: "/tls/tls.crt"},
{Name: "REDIS_EXPORTER_TLS_CA_CERT_FILE", Value: "/tls/ca.crt"},
{Name: "REDIS_EXPORTER_SKIP_TLS_VERIFICATION", Value: "true"},
{Name: "REDIS_ADDR", Value: "rediss://local.inject:6379"},
},
Resources: corev1.ResourceRequirements{
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("200Mi"),
},
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("200Mi"),
},
},
VolumeMounts: []corev1.VolumeMount{
{Name: RedisOperatorPasswordVolumeName, MountPath: OperatorPasswordVolumeMountPath},
{Name: RedisExporterTempVolumeName, MountPath: RedisTmpVolumeMountPath},
{Name: RedisTLSVolumeName, MountPath: TLSVolumeMountPath},
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
container := redisExporterContainer(tt.cluster, tt.user)
assert.Equal(t, tt.expected.Name, container.Name)
assert.Equal(t, tt.expected.Image, container.Image)
assert.Equal(t, tt.expected.ImagePullPolicy, container.ImagePullPolicy)
assert.Equal(t, tt.expected.Ports, container.Ports)
assert.ElementsMatch(t, tt.expected.Env, container.Env)
assert.Equal(t, tt.expected.Resources, container.Resources)
assert.ElementsMatch(t, tt.expected.VolumeMounts, container.VolumeMounts)
})
}
}

func TestVolumeMounts(t *testing.T) {
tests := []struct {
name string
cluster *redisv1alpha1.DistributedRedisCluster
user *user.User
expected []corev1.VolumeMount
}{
{
name: "Basic test",
cluster: &redisv1alpha1.DistributedRedisCluster{
Spec: redisv1alpha1.DistributedRedisClusterSpec{},
},
user: &user.User{
Password: &user.Password{},
},
expected: []corev1.VolumeMount{
{Name: ConfigVolumeName, MountPath: ConfigVolumeMountPath},
{Name: RedisStorageVolumeName, MountPath: StorageVolumeMountPath},
{Name: RedisOptVolumeName, MountPath: RedisOptVolumeMountPath},
{Name: RedisTempVolumeName, MountPath: RedisTmpVolumeMountPath},
},
},
{
name: "With password",
cluster: &redisv1alpha1.DistributedRedisCluster{
Spec: redisv1alpha1.DistributedRedisClusterSpec{},
},
user: &user.User{
Password: &user.Password{
SecretName: "secret-name",
},
},
expected: []corev1.VolumeMount{
{Name: ConfigVolumeName, MountPath: ConfigVolumeMountPath},
{Name: RedisStorageVolumeName, MountPath: StorageVolumeMountPath},
{Name: RedisOptVolumeName, MountPath: RedisOptVolumeMountPath},
{Name: RedisTempVolumeName, MountPath: RedisTmpVolumeMountPath},
{Name: RedisOperatorPasswordVolumeName, MountPath: OperatorPasswordVolumeMountPath},
},
},
{
name: "With TLS",
cluster: &redisv1alpha1.DistributedRedisCluster{
Spec: redisv1alpha1.DistributedRedisClusterSpec{
EnableTLS: true,
},
},
user: &user.User{
Password: &user.Password{},
},
expected: []corev1.VolumeMount{
{Name: ConfigVolumeName, MountPath: ConfigVolumeMountPath},
{Name: RedisStorageVolumeName, MountPath: StorageVolumeMountPath},
{Name: RedisOptVolumeName, MountPath: RedisOptVolumeMountPath},
{Name: RedisTempVolumeName, MountPath: RedisTmpVolumeMountPath},
{Name: RedisTLSVolumeName, MountPath: TLSVolumeMountPath},
},
},
{
name: "With password and TLS",
cluster: &redisv1alpha1.DistributedRedisCluster{
Spec: redisv1alpha1.DistributedRedisClusterSpec{
EnableTLS: true,
},
},
user: &user.User{
Password: &user.Password{
SecretName: "secret-name",
},
},
expected: []corev1.VolumeMount{
{Name: ConfigVolumeName, MountPath: ConfigVolumeMountPath},
{Name: RedisStorageVolumeName, MountPath: StorageVolumeMountPath},
{Name: RedisOptVolumeName, MountPath: RedisOptVolumeMountPath},
{Name: RedisTempVolumeName, MountPath: RedisTmpVolumeMountPath},
{Name: RedisOperatorPasswordVolumeName, MountPath: OperatorPasswordVolumeMountPath},
{Name: RedisTLSVolumeName, MountPath: TLSVolumeMountPath},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
volumeMounts := volumeMounts(tt.cluster, tt.user)
assert.ElementsMatch(t, tt.expected, volumeMounts)
})
}
}
Loading

0 comments on commit 673ef42

Please sign in to comment.