From 98ce31ab4a38f1e777dbcf523ae6d29bd1e0e963 Mon Sep 17 00:00:00 2001 From: Sergei Monakhov Date: Fri, 12 Apr 2024 21:26:50 +0300 Subject: [PATCH 1/7] refactoring for certs and data volume --- internal/controller/factory/statefulset.go | 382 ++++++++++++--------- 1 file changed, 223 insertions(+), 159 deletions(-) diff --git a/internal/controller/factory/statefulset.go b/internal/controller/factory/statefulset.go index f9447901..8737aca2 100644 --- a/internal/controller/factory/statefulset.go +++ b/internal/controller/factory/statefulset.go @@ -19,6 +19,7 @@ package factory import ( "context" "fmt" + "path/filepath" "slices" appsv1 "k8s.io/api/apps/v1" @@ -32,8 +33,58 @@ import ( etcdaenixiov1alpha1 "github.com/aenix-io/etcd-operator/api/v1alpha1" ) -const ( - etcdContainerName = "etcd" +type certConfigurator interface { + Name() string + MountPath() string + CrtFilePath() string + KeyFilePath() string +} + +type certificate struct { + certName string + mountPath string + crtFile string + keyFile string +} + +type certificates map[string]certConfigurator + +type etcdConfigurator interface { + ContainerName() string + VolumeName() string + MountPath() string + DirName() string + DataPath() string +} + +type etcd struct { + containerName string + volumeName string + mountPath string + dirName string +} + +var ( + config = etcd{ + containerName: "etcd", + volumeName: "data", + mountPath: "/var/run/etcd", + dirName: "default.etcd", + } + + etcdConfig etcdConfigurator = config + + etcdCertificates = certificates{ + "peer-trusted-ca-certificate": getCertificateConfig("peer-trusted-ca-certificate", "/etc/etcd/pki/peer/ca", "ca.crt", ""), + "peer-certificate": getCertificateConfig("peer-certificate", "/etc/etcd/pki/peer/cert", "tls.crt", "tls.key"), + "server-certificate": getCertificateConfig("server-certificate", "/etc/etcd/pki/server/cert", "tls.crt", "tls.key"), + "client-trusted-ca-certificate": getCertificateConfig("client-trusted-ca-certificate", "/etc/etcd/pki/client/ca", "ca.crt", ""), + } + + peerTrustedCACertificate = etcdCertificates["peer-trusted-ca-certificate"] + peerCertificate = etcdCertificates["peer-certificate"] + serverCertificate = etcdCertificates["server-certificate"] + clientTrustedCACertificate = etcdCertificates["client-trusted-ca-certificate"] ) func CreateOrUpdateStatefulSet( @@ -114,10 +165,9 @@ func CreateOrUpdateStatefulSet( } func generateVolumes(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.Volume { - volumes := []corev1.Volume{} + volumesMap := make(map[string]corev1.Volume) var dataVolumeSource corev1.VolumeSource - if cluster.Spec.Storage.EmptyDir != nil { dataVolumeSource = corev1.VolumeSource{EmptyDir: cluster.Spec.Storage.EmptyDir} } else { @@ -127,137 +177,103 @@ func generateVolumes(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.Volume { }, } } - - dataVolumeIdx := slices.IndexFunc(cluster.Spec.PodTemplate.Spec.Volumes, func(volume corev1.Volume) bool { - return volume.Name == "data" - }) - if dataVolumeIdx == -1 { - dataVolumeIdx = len(cluster.Spec.PodTemplate.Spec.Volumes) - volumes = append( - volumes, - corev1.Volume{}, - ) - } - - volumes[dataVolumeIdx] = corev1.Volume{ - Name: "data", + volumesMap[etcdConfig.VolumeName()] = corev1.Volume{ + Name: etcdConfig.VolumeName(), VolumeSource: dataVolumeSource, } - if cluster.Spec.Security != nil && cluster.Spec.Security.TLS.PeerSecret != "" { - volumes = append(volumes, - []corev1.Volume{ - { - Name: "peer-trusted-ca-certificate", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: cluster.Spec.Security.TLS.PeerTrustedCASecret, - }, + if cluster.Spec.Security != nil { + addSecretVolume := func(name, secretName string) { + volumesMap[name] = corev1.Volume{ + Name: name, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: secretName, }, }, - { - Name: "peer-certificate", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: cluster.Spec.Security.TLS.PeerSecret, - }, - }, - }, - }...) - } + } + } - if cluster.Spec.Security != nil && cluster.Spec.Security.TLS.ServerSecret != "" { - volumes = append(volumes, - []corev1.Volume{ - { - Name: "server-certificate", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: cluster.Spec.Security.TLS.ServerSecret, - }, - }, - }, - }...) + if cluster.Spec.Security.TLS.PeerSecret != "" { + addSecretVolume(peerTrustedCACertificate.Name(), cluster.Spec.Security.TLS.PeerTrustedCASecret) + addSecretVolume(peerCertificate.Name(), cluster.Spec.Security.TLS.PeerSecret) + } + if cluster.Spec.Security.TLS.ServerSecret != "" { + addSecretVolume(serverCertificate.Name(), cluster.Spec.Security.TLS.ServerSecret) + } + if cluster.Spec.Security.TLS.ClientSecret != "" { + addSecretVolume(clientTrustedCACertificate.Name(), cluster.Spec.Security.TLS.ClientTrustedCASecret) + } } - if cluster.Spec.Security != nil && cluster.Spec.Security.TLS.ClientSecret != "" { - volumes = append(volumes, - []corev1.Volume{ - { - Name: "client-trusted-ca-certificate", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: cluster.Spec.Security.TLS.ClientTrustedCASecret, - }, - }, - }, - }...) + var volumes []corev1.Volume + for _, volume := range volumesMap { + volumes = append(volumes, volume) } return volumes - } func generateVolumeMounts(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.VolumeMount { - volumeMounts := []corev1.VolumeMount{} - for _, c := range cluster.Spec.PodTemplate.Spec.Containers { - if c.Name == etcdContainerName { + security := cluster.Spec.Security + if security != nil { + if security.TLS.PeerSecret != "" { + volumeMounts = append(volumeMounts, []corev1.VolumeMount{ + { + Name: peerTrustedCACertificate.Name(), + ReadOnly: true, + MountPath: peerTrustedCACertificate.MountPath(), + }, + { + Name: peerCertificate.Name(), + ReadOnly: true, + MountPath: peerCertificate.MountPath(), + }, + }...) + } - volumeMounts = c.VolumeMounts + if security.TLS.ServerSecret != "" { + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: serverCertificate.Name(), + ReadOnly: true, + MountPath: serverCertificate.MountPath(), + }) + } - mountIdx := slices.IndexFunc(volumeMounts, func(mount corev1.VolumeMount) bool { - return mount.Name == "data" + if security.TLS.ClientSecret != "" { + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: clientTrustedCACertificate.Name(), + ReadOnly: true, + MountPath: clientTrustedCACertificate.MountPath(), }) - if mountIdx == -1 { + } + } + + // Iterate over containers to find the etcd container + for _, c := range cluster.Spec.PodTemplate.Spec.Containers { + if c.Name == etcdConfig.ContainerName() { + // Look for existing dataVolumeName volume mount + found := false + for i := range volumeMounts { + if volumeMounts[i].Name == etcdConfig.VolumeName() { + volumeMounts[i].ReadOnly = false + volumeMounts[i].MountPath = etcdConfig.MountPath() + found = true + break + } + } + // If dataVolumeName volume mount not found, add it + if !found { volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: "data", + Name: etcdConfig.VolumeName(), ReadOnly: false, - MountPath: "/var/run/etcd", + MountPath: etcdConfig.MountPath(), }) - } else { - volumeMounts[mountIdx].ReadOnly = false - volumeMounts[mountIdx].MountPath = "/var/run/etcd" } + break // No need to continue loop after finding etcd container } - - } - - if cluster.Spec.Security != nil && cluster.Spec.Security.TLS.PeerSecret != "" { - volumeMounts = append(volumeMounts, []corev1.VolumeMount{ - { - Name: "peer-trusted-ca-certificate", - ReadOnly: true, - MountPath: "/etc/etcd/pki/peer/ca", - }, - { - Name: "peer-certificate", - ReadOnly: true, - MountPath: "/etc/etcd/pki/peer/cert", - }, - }...) - } - - if cluster.Spec.Security != nil && cluster.Spec.Security.TLS.ServerSecret != "" { - volumeMounts = append(volumeMounts, []corev1.VolumeMount{ - { - Name: "server-certificate", - ReadOnly: true, - MountPath: "/etc/etcd/pki/server/cert", - }, - }...) - } - - if cluster.Spec.Security != nil && cluster.Spec.Security.TLS.ClientSecret != "" { - - volumeMounts = append(volumeMounts, []corev1.VolumeMount{ - { - Name: "client-trusted-ca-certificate", - ReadOnly: true, - MountPath: "/etc/etcd/pki/client/ca", - }, - }...) } return volumeMounts @@ -272,62 +288,58 @@ func generateEtcdCommand() []string { func generateEtcdArgs(cluster *etcdaenixiov1alpha1.EtcdCluster) []string { args := []string{} - for name, value := range cluster.Spec.Options { - flag := "--" + name - if len(value) == 0 { - args = append(args, flag) - - continue - } - - args = append(args, fmt.Sprintf("%s=%s", flag, value)) + // Helper function to check TLS settings presence + hasTLS := func(secret string) bool { + return cluster.Spec.Security != nil && secret != "" } - peerTlsSettings := []string{"--peer-auto-tls"} + // Determine client protocol (http or https) + clientProtocol := "http" + if hasTLS(cluster.Spec.Security.TLS.ServerSecret) { + clientProtocol = "https" + } - if cluster.Spec.Security != nil && cluster.Spec.Security.TLS.PeerSecret != "" { - peerTlsSettings = []string{ - "--peer-trusted-ca-file=/etc/etcd/pki/peer/ca/ca.crt", - "--peer-cert-file=/etc/etcd/pki/peer/cert/tls.crt", - "--peer-key-file=/etc/etcd/pki/peer/cert/tls.key", + args = append(args, + "--name=$(POD_NAME)", + "--listen-metrics-urls=http://0.0.0.0:2381", + "--listen-peer-urls=https://0.0.0.0:2380", + "--data-dir="+etcdConfig.DataPath(), + ) + + // Determine listen client URLs + listenClientURL := fmt.Sprintf("%s://0.0.0.0:2379", clientProtocol) + args = append(args, "--listen-client-urls="+listenClientURL) + + // Determine advertise client URLs and initial advertise peer URLs + advertiseClientURL := fmt.Sprintf("%s://$(POD_NAME).%s.$(POD_NAMESPACE).svc:2379", clientProtocol, cluster.Name) + initialAdvertisePeerURL := fmt.Sprintf("https://$(POD_NAME).%s.$(POD_NAMESPACE).svc:2380", cluster.Name) + args = append(args, + "--advertise-client-urls="+advertiseClientURL, + "--initial-advertise-peer-urls="+initialAdvertisePeerURL, + ) + + // Append TLS settings if enabled + if hasTLS(cluster.Spec.Security.TLS.PeerSecret) { + args = append(args, + "--peer-trusted-ca-file="+peerTrustedCACertificate.CrtFilePath(), + "--peer-cert-file="+peerCertificate.CrtFilePath(), + "--peer-key-file="+peerCertificate.KeyFilePath(), "--peer-client-cert-auth", - } + ) } - - serverTlsSettings := []string{} - serverProtocol := "http" - - if cluster.Spec.Security != nil && cluster.Spec.Security.TLS.ServerSecret != "" { - serverTlsSettings = []string{ - "--cert-file=/etc/etcd/pki/server/cert/tls.crt", - "--key-file=/etc/etcd/pki/server/cert/tls.key", - } - serverProtocol = "https" + if hasTLS(cluster.Spec.Security.TLS.ServerSecret) { + args = append(args, + "--cert-file="+serverCertificate.CrtFilePath(), + "--key-file="+serverCertificate.KeyFilePath(), + ) } - - clientTlsSettings := []string{} - - if cluster.Spec.Security != nil && cluster.Spec.Security.TLS.ClientSecret != "" { - clientTlsSettings = []string{ - "--trusted-ca-file=/etc/etcd/pki/client/ca/ca.crt", + if hasTLS(cluster.Spec.Security.TLS.ClientSecret) { + args = append(args, + "--trusted-ca-file="+clientTrustedCACertificate.CrtFilePath(), "--client-cert-auth", - } + ) } - args = append(args, []string{ - "--name=$(POD_NAME)", - "--listen-metrics-urls=http://0.0.0.0:2381", - "--listen-peer-urls=https://0.0.0.0:2380", - fmt.Sprintf("--listen-client-urls=%s://0.0.0.0:2379", serverProtocol), - fmt.Sprintf("--initial-advertise-peer-urls=https://$(POD_NAME).%s.$(POD_NAMESPACE).svc:2380", cluster.Name), - "--data-dir=/var/run/etcd/default.etcd", - fmt.Sprintf("--advertise-client-urls=%s://$(POD_NAME).%s.$(POD_NAMESPACE).svc:2379", serverProtocol, cluster.Name), - }...) - - args = append(args, peerTlsSettings...) - args = append(args, serverTlsSettings...) - args = append(args, clientTlsSettings...) - return args } @@ -353,7 +365,7 @@ func generateContainers(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.Conta containers := make([]corev1.Container, 0, len(cluster.Spec.PodTemplate.Spec.Containers)) for _, c := range cluster.Spec.PodTemplate.Spec.Containers { - if c.Name == etcdContainerName { + if c.Name == etcdConfig.ContainerName() { c.Command = generateEtcdCommand() c.Args = generateEtcdArgs(cluster) c.Ports = mergePorts(c.Ports, []corev1.ContainerPort{ @@ -482,3 +494,55 @@ func mergeWithDefaultProbe(probe *corev1.Probe, defaultProbe corev1.Probe) *core func hasProbeHandlerAction(probe corev1.Probe) bool { return probe.HTTPGet != nil || probe.TCPSocket != nil || probe.Exec != nil || probe.GRPC != nil } + +func (c certificate) Name() string { + return c.certName +} + +func (c certificate) MountPath() string { + return c.mountPath +} + +func (c certificate) CrtFilePath() string { + return c.buildFilePath(c.crtFile) +} + +func (c certificate) KeyFilePath() string { + if c.keyFile == "" { + return "" + } + return c.buildFilePath(c.keyFile) +} + +func (c certificate) buildFilePath(fileName string) string { + return filepath.Join(c.mountPath, fileName) +} + +func getCertificateConfig(name, mountPath, crtFile, keyFile string) certificate { + return certificate{ + certName: name, + mountPath: mountPath, + crtFile: crtFile, + keyFile: keyFile, + } +} + +func (e etcd) ContainerName() string { + return e.containerName +} + +func (e etcd) VolumeName() string { + return e.volumeName +} + +func (e etcd) MountPath() string { + return e.mountPath +} + +func (e etcd) DirName() string { + return e.dirName +} + +func (e etcd) DataPath() string { + return filepath.Join(e.mountPath, e.dirName) +} From 268f5504cd9934e6ad5585ffa29102d13fba4026 Mon Sep 17 00:00:00 2001 From: Sergei Monakhov Date: Sat, 13 Apr 2024 18:50:02 +0300 Subject: [PATCH 2/7] more refactoring and fixed tests --- internal/controller/factory/statefulset.go | 406 +++++++++++++-------- 1 file changed, 250 insertions(+), 156 deletions(-) diff --git a/internal/controller/factory/statefulset.go b/internal/controller/factory/statefulset.go index 8737aca2..598511ab 100644 --- a/internal/controller/factory/statefulset.go +++ b/internal/controller/factory/statefulset.go @@ -75,10 +75,14 @@ var ( etcdConfig etcdConfigurator = config etcdCertificates = certificates{ - "peer-trusted-ca-certificate": getCertificateConfig("peer-trusted-ca-certificate", "/etc/etcd/pki/peer/ca", "ca.crt", ""), - "peer-certificate": getCertificateConfig("peer-certificate", "/etc/etcd/pki/peer/cert", "tls.crt", "tls.key"), - "server-certificate": getCertificateConfig("server-certificate", "/etc/etcd/pki/server/cert", "tls.crt", "tls.key"), - "client-trusted-ca-certificate": getCertificateConfig("client-trusted-ca-certificate", "/etc/etcd/pki/client/ca", "ca.crt", ""), + "peer-trusted-ca-certificate": getCertificateConfig("peer-trusted-ca-certificate", + "/etc/etcd/pki/peer/ca", "ca.crt", ""), + "peer-certificate": getCertificateConfig("peer-certificate", + "/etc/etcd/pki/peer/cert", "tls.crt", "tls.key"), + "server-certificate": getCertificateConfig("server-certificate", + "/etc/etcd/pki/server/cert", "tls.crt", "tls.key"), + "client-trusted-ca-certificate": getCertificateConfig("client-trusted-ca-certificate", + "/etc/etcd/pki/client/ca", "ca.crt", ""), } peerTrustedCACertificate = etcdCertificates["peer-trusted-ca-certificate"] @@ -165,48 +169,18 @@ func CreateOrUpdateStatefulSet( } func generateVolumes(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.Volume { - volumesMap := make(map[string]corev1.Volume) + volumes := []corev1.Volume{} + dataVolume := generateDataVolume(cluster) - var dataVolumeSource corev1.VolumeSource - if cluster.Spec.Storage.EmptyDir != nil { - dataVolumeSource = corev1.VolumeSource{EmptyDir: cluster.Spec.Storage.EmptyDir} - } else { - dataVolumeSource = corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: GetPVCName(cluster), - }, - } - } - volumesMap[etcdConfig.VolumeName()] = corev1.Volume{ - Name: etcdConfig.VolumeName(), - VolumeSource: dataVolumeSource, - } + secretVolumes := generateSecretVolumes(cluster) - if cluster.Spec.Security != nil { - addSecretVolume := func(name, secretName string) { - volumesMap[name] = corev1.Volume{ - Name: name, - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: secretName, - }, - }, - } - } - - if cluster.Spec.Security.TLS.PeerSecret != "" { - addSecretVolume(peerTrustedCACertificate.Name(), cluster.Spec.Security.TLS.PeerTrustedCASecret) - addSecretVolume(peerCertificate.Name(), cluster.Spec.Security.TLS.PeerSecret) - } - if cluster.Spec.Security.TLS.ServerSecret != "" { - addSecretVolume(serverCertificate.Name(), cluster.Spec.Security.TLS.ServerSecret) - } - if cluster.Spec.Security.TLS.ClientSecret != "" { - addSecretVolume(clientTrustedCACertificate.Name(), cluster.Spec.Security.TLS.ClientTrustedCASecret) - } + volumesMap := map[string]corev1.Volume{ + etcdConfig.VolumeName(): dataVolume, + } + for name, volume := range secretVolumes { + volumesMap[name] = volume } - var volumes []corev1.Volume for _, volume := range volumesMap { volumes = append(volumes, volume) } @@ -214,67 +188,12 @@ func generateVolumes(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.Volume { return volumes } -func generateVolumeMounts(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.VolumeMount { - volumeMounts := []corev1.VolumeMount{} - - security := cluster.Spec.Security - if security != nil { - if security.TLS.PeerSecret != "" { - volumeMounts = append(volumeMounts, []corev1.VolumeMount{ - { - Name: peerTrustedCACertificate.Name(), - ReadOnly: true, - MountPath: peerTrustedCACertificate.MountPath(), - }, - { - Name: peerCertificate.Name(), - ReadOnly: true, - MountPath: peerCertificate.MountPath(), - }, - }...) - } - - if security.TLS.ServerSecret != "" { - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: serverCertificate.Name(), - ReadOnly: true, - MountPath: serverCertificate.MountPath(), - }) - } - - if security.TLS.ClientSecret != "" { - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: clientTrustedCACertificate.Name(), - ReadOnly: true, - MountPath: clientTrustedCACertificate.MountPath(), - }) - } - } - - // Iterate over containers to find the etcd container - for _, c := range cluster.Spec.PodTemplate.Spec.Containers { - if c.Name == etcdConfig.ContainerName() { - // Look for existing dataVolumeName volume mount - found := false - for i := range volumeMounts { - if volumeMounts[i].Name == etcdConfig.VolumeName() { - volumeMounts[i].ReadOnly = false - volumeMounts[i].MountPath = etcdConfig.MountPath() - found = true - break - } - } - // If dataVolumeName volume mount not found, add it - if !found { - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: etcdConfig.VolumeName(), - ReadOnly: false, - MountPath: etcdConfig.MountPath(), - }) - } - break // No need to continue loop after finding etcd container - } - } +func generateVolumeMounts( + cluster *etcdaenixiov1alpha1.EtcdCluster, + dataVolumeMount []corev1.VolumeMount, +) []corev1.VolumeMount { + tlsVolumeMounts := generateTLSSecretVolumeMounts(cluster) + volumeMounts := mergeVolumeMounts(tlsVolumeMounts, dataVolumeMount) return volumeMounts } @@ -286,58 +205,15 @@ func generateEtcdCommand() []string { } func generateEtcdArgs(cluster *etcdaenixiov1alpha1.EtcdCluster) []string { - args := []string{} - - // Helper function to check TLS settings presence - hasTLS := func(secret string) bool { - return cluster.Spec.Security != nil && secret != "" - } - - // Determine client protocol (http or https) - clientProtocol := "http" - if hasTLS(cluster.Spec.Security.TLS.ServerSecret) { - clientProtocol = "https" - } - - args = append(args, - "--name=$(POD_NAME)", - "--listen-metrics-urls=http://0.0.0.0:2381", - "--listen-peer-urls=https://0.0.0.0:2380", - "--data-dir="+etcdConfig.DataPath(), - ) - - // Determine listen client URLs - listenClientURL := fmt.Sprintf("%s://0.0.0.0:2379", clientProtocol) - args = append(args, "--listen-client-urls="+listenClientURL) + args := baseEtcdFlags() - // Determine advertise client URLs and initial advertise peer URLs - advertiseClientURL := fmt.Sprintf("%s://$(POD_NAME).%s.$(POD_NAMESPACE).svc:2379", clientProtocol, cluster.Name) - initialAdvertisePeerURL := fmt.Sprintf("https://$(POD_NAME).%s.$(POD_NAMESPACE).svc:2380", cluster.Name) - args = append(args, - "--advertise-client-urls="+advertiseClientURL, - "--initial-advertise-peer-urls="+initialAdvertisePeerURL, - ) - - // Append TLS settings if enabled - if hasTLS(cluster.Spec.Security.TLS.PeerSecret) { - args = append(args, - "--peer-trusted-ca-file="+peerTrustedCACertificate.CrtFilePath(), - "--peer-cert-file="+peerCertificate.CrtFilePath(), - "--peer-key-file="+peerCertificate.KeyFilePath(), - "--peer-client-cert-auth", - ) - } - if hasTLS(cluster.Spec.Security.TLS.ServerSecret) { - args = append(args, - "--cert-file="+serverCertificate.CrtFilePath(), - "--key-file="+serverCertificate.KeyFilePath(), - ) - } - if hasTLS(cluster.Spec.Security.TLS.ClientSecret) { - args = append(args, - "--trusted-ca-file="+clientTrustedCACertificate.CrtFilePath(), - "--client-cert-auth", - ) + // Append TLS flags + args = append(args, etcdTLSFlags(cluster)...) + // Append URLs flags + args = append(args, etcdURLsFlags(cluster)...) + // Append extra flags + if cluster.Spec.Options != nil { + args = append(args, etcdExtraFlags(cluster.Spec.Options)...) } return args @@ -374,7 +250,8 @@ func generateContainers(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.Conta }) clusterStateConfigMapName := GetClusterStateConfigMapName(cluster) envIdx := slices.IndexFunc(c.EnvFrom, func(env corev1.EnvFromSource) bool { - return env.ConfigMapRef != nil && env.ConfigMapRef.LocalObjectReference.Name == clusterStateConfigMapName + return env.ConfigMapRef != nil && + env.ConfigMapRef.LocalObjectReference.Name == clusterStateConfigMapName }) if envIdx == -1 { c.EnvFrom = append(c.EnvFrom, corev1.EnvFromSource{ @@ -389,7 +266,10 @@ func generateContainers(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.Conta c.LivenessProbe = getLivenessProbe(c.LivenessProbe) c.ReadinessProbe = getReadinessProbe(c.ReadinessProbe) c.Env = mergeEnvs(c.Env, podEnv) - c.VolumeMounts = generateVolumeMounts(cluster) + + dataVolumeMounts := updateOrAddDataVolumeMount(c.VolumeMounts, etcdConfig.VolumeName(), + etcdConfig.MountPath(), false) + c.VolumeMounts = generateVolumeMounts(cluster, dataVolumeMounts) } containers = append(containers, c) @@ -546,3 +426,217 @@ func (e etcd) DirName() string { func (e etcd) DataPath() string { return filepath.Join(e.mountPath, e.dirName) } + +func hasTLSConfigInSpec(cluster *etcdaenixiov1alpha1.EtcdCluster, secretType string) bool { + if cluster.Spec.Security == nil { + return false + } + + switch secretType { + case "PeerSecret": + return cluster.Spec.Security.TLS.PeerSecret != "" + case "ServerSecret": + return cluster.Spec.Security.TLS.ServerSecret != "" + case "ClientSecret": + return cluster.Spec.Security.TLS.ClientSecret != "" + default: + return false + } +} + +func baseEtcdFlags() []string { + return []string{ + "--name=$(POD_NAME)", + "--listen-metrics-urls=http://0.0.0.0:2381", + "--listen-peer-urls=https://0.0.0.0:2380", + "--data-dir=" + etcdConfig.DataPath(), + } +} + +func etcdTLSFlags(cluster *etcdaenixiov1alpha1.EtcdCluster) []string { + args := []string{} + + if hasTLSConfigInSpec(cluster, "PeerSecret") { + args = append(args, + "--peer-trusted-ca-file="+peerTrustedCACertificate.CrtFilePath(), + "--peer-cert-file="+peerCertificate.CrtFilePath(), + "--peer-key-file="+peerCertificate.KeyFilePath(), + "--peer-client-cert-auth", + ) + } + if hasTLSConfigInSpec(cluster, "ServerSecret") { + args = append(args, + "--cert-file="+serverCertificate.CrtFilePath(), + "--key-file="+serverCertificate.KeyFilePath(), + ) + } + if hasTLSConfigInSpec(cluster, "ClientSecret") { + args = append(args, + "--trusted-ca-file="+clientTrustedCACertificate.CrtFilePath(), + "--client-cert-auth", + ) + } + return args +} + +func etcdURLsFlags(cluster *etcdaenixiov1alpha1.EtcdCluster) []string { + args := []string{} + + clientProtocol := "http" + if hasTLSConfigInSpec(cluster, "ServerSecret") { + clientProtocol = "https" + } + + listenClientURL := fmt.Sprintf("%s://0.0.0.0:2379", clientProtocol) + advertiseClientURL := fmt.Sprintf("%s://$(POD_NAME).%s.$(POD_NAMESPACE).svc:2379", + clientProtocol, cluster.Name) + initialAdvertisePeerURL := fmt.Sprintf("https://$(POD_NAME).%s.$(POD_NAMESPACE).svc:2380", cluster.Name) + + args = append(args, "--listen-client-urls="+listenClientURL) + args = append(args, "--advertise-client-urls="+advertiseClientURL) + args = append(args, "--initial-advertise-peer-urls="+initialAdvertisePeerURL) + + return args +} + +func etcdExtraFlags(options map[string]string) []string { + args := []string{} + + for name, value := range options { + flag := "--" + name + if len(value) == 0 { + args = append(args, flag) + } else { + args = append(args, fmt.Sprintf("%s=%s", flag, value)) + } + } + return args +} + +func generateSecretVolumes(cluster *etcdaenixiov1alpha1.EtcdCluster) map[string]corev1.Volume { + volumesMap := make(map[string]corev1.Volume) + + addSecretVolume := func(name, secretName string) { + volumesMap[name] = createSecretVolume(name, secretName) + } + + if cluster.Spec.Security != nil { + if cluster.Spec.Security.TLS.PeerSecret != "" { + addSecretVolume(peerTrustedCACertificate.Name(), cluster.Spec.Security.TLS.PeerTrustedCASecret) + addSecretVolume(peerCertificate.Name(), cluster.Spec.Security.TLS.PeerSecret) + } + if cluster.Spec.Security.TLS.ServerSecret != "" { + addSecretVolume(serverCertificate.Name(), cluster.Spec.Security.TLS.ServerSecret) + } + if cluster.Spec.Security.TLS.ClientSecret != "" { + addSecretVolume(clientTrustedCACertificate.Name(), cluster.Spec.Security.TLS.ClientTrustedCASecret) + } + } + + return volumesMap +} + +func createSecretVolume(name, secretName string) corev1.Volume { + return corev1.Volume{ + Name: name, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: secretName, + }, + }, + } +} + +func generateDataVolume(cluster *etcdaenixiov1alpha1.EtcdCluster) corev1.Volume { + var dataVolumeSource corev1.VolumeSource + + if cluster.Spec.Storage.EmptyDir != nil { + dataVolumeSource = corev1.VolumeSource{EmptyDir: cluster.Spec.Storage.EmptyDir} + } else { + dataVolumeSource = corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: GetPVCName(cluster), + }, + } + } + + return corev1.Volume{ + Name: etcdConfig.VolumeName(), + VolumeSource: dataVolumeSource, + } +} + +func updateOrAddDataVolumeMount(volumeMounts []corev1.VolumeMount, + volumeName, mountPath string, readOnly bool) []corev1.VolumeMount { + found := false + + for i := range volumeMounts { + if volumeMounts[i].Name == volumeName { + volumeMounts[i].ReadOnly = readOnly + volumeMounts[i].MountPath = mountPath + found = true + break + } + } + + if !found { + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: volumeName, + ReadOnly: readOnly, + MountPath: mountPath, + }) + } + + return volumeMounts +} + +func generateTLSSecretVolumeMounts(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.VolumeMount { + volumeMounts := []corev1.VolumeMount{} + + if hasTLSConfigInSpec(cluster, "") { + return volumeMounts + } + + if hasTLSConfigInSpec(cluster, "PeerSecret") { + volumeMounts = append(volumeMounts, []corev1.VolumeMount{ + { + Name: peerTrustedCACertificate.Name(), + ReadOnly: true, + MountPath: peerTrustedCACertificate.MountPath(), + }, + { + Name: peerCertificate.Name(), + ReadOnly: true, + MountPath: peerCertificate.MountPath(), + }, + }...) + } + + if hasTLSConfigInSpec(cluster, "ServerSecret") { + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: serverCertificate.Name(), + ReadOnly: true, + MountPath: serverCertificate.MountPath(), + }) + } + + if hasTLSConfigInSpec(cluster, "ClientSecret") { + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: clientTrustedCACertificate.Name(), + ReadOnly: true, + MountPath: clientTrustedCACertificate.MountPath(), + }) + } + + return volumeMounts +} + +func mergeVolumeMounts(lists ...[]corev1.VolumeMount) []corev1.VolumeMount { + var volumeMounts []corev1.VolumeMount + + for _, list := range lists { + volumeMounts = append(volumeMounts, list...) + } + + return volumeMounts +} From 542632d1f877996f35af4d75a0e06118a2b0c4a6 Mon Sep 17 00:00:00 2001 From: Sergei Monakhov Date: Sat, 13 Apr 2024 22:48:21 +0300 Subject: [PATCH 3/7] remove interface for etcd, move setting to const, update generateVolumes func --- internal/controller/factory/statefulset.go | 71 +++++----------------- 1 file changed, 14 insertions(+), 57 deletions(-) diff --git a/internal/controller/factory/statefulset.go b/internal/controller/factory/statefulset.go index 598511ab..6bba5525 100644 --- a/internal/controller/factory/statefulset.go +++ b/internal/controller/factory/statefulset.go @@ -49,31 +49,14 @@ type certificate struct { type certificates map[string]certConfigurator -type etcdConfigurator interface { - ContainerName() string - VolumeName() string - MountPath() string - DirName() string - DataPath() string -} - -type etcd struct { - containerName string - volumeName string - mountPath string - dirName string -} +const ( + etcdContainerName = "etcd" + etcdVolumeName = "data" + etcdDataMountPath = "/var/run/etcd" + ectdDataDirName = "default.etcd" +) var ( - config = etcd{ - containerName: "etcd", - volumeName: "data", - mountPath: "/var/run/etcd", - dirName: "default.etcd", - } - - etcdConfig etcdConfigurator = config - etcdCertificates = certificates{ "peer-trusted-ca-certificate": getCertificateConfig("peer-trusted-ca-certificate", "/etc/etcd/pki/peer/ca", "ca.crt", ""), @@ -170,18 +153,12 @@ func CreateOrUpdateStatefulSet( func generateVolumes(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.Volume { volumes := []corev1.Volume{} + dataVolume := generateDataVolume(cluster) + volumes = append(volumes, dataVolume) secretVolumes := generateSecretVolumes(cluster) - - volumesMap := map[string]corev1.Volume{ - etcdConfig.VolumeName(): dataVolume, - } - for name, volume := range secretVolumes { - volumesMap[name] = volume - } - - for _, volume := range volumesMap { + for _, volume := range secretVolumes { volumes = append(volumes, volume) } @@ -241,7 +218,7 @@ func generateContainers(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.Conta containers := make([]corev1.Container, 0, len(cluster.Spec.PodTemplate.Spec.Containers)) for _, c := range cluster.Spec.PodTemplate.Spec.Containers { - if c.Name == etcdConfig.ContainerName() { + if c.Name == etcdContainerName { c.Command = generateEtcdCommand() c.Args = generateEtcdArgs(cluster) c.Ports = mergePorts(c.Ports, []corev1.ContainerPort{ @@ -267,8 +244,8 @@ func generateContainers(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.Conta c.ReadinessProbe = getReadinessProbe(c.ReadinessProbe) c.Env = mergeEnvs(c.Env, podEnv) - dataVolumeMounts := updateOrAddDataVolumeMount(c.VolumeMounts, etcdConfig.VolumeName(), - etcdConfig.MountPath(), false) + dataVolumeMounts := updateOrAddDataVolumeMount(c.VolumeMounts, etcdVolumeName, + etcdDataMountPath, false) c.VolumeMounts = generateVolumeMounts(cluster, dataVolumeMounts) } @@ -407,26 +384,6 @@ func getCertificateConfig(name, mountPath, crtFile, keyFile string) certificate } } -func (e etcd) ContainerName() string { - return e.containerName -} - -func (e etcd) VolumeName() string { - return e.volumeName -} - -func (e etcd) MountPath() string { - return e.mountPath -} - -func (e etcd) DirName() string { - return e.dirName -} - -func (e etcd) DataPath() string { - return filepath.Join(e.mountPath, e.dirName) -} - func hasTLSConfigInSpec(cluster *etcdaenixiov1alpha1.EtcdCluster, secretType string) bool { if cluster.Spec.Security == nil { return false @@ -449,7 +406,7 @@ func baseEtcdFlags() []string { "--name=$(POD_NAME)", "--listen-metrics-urls=http://0.0.0.0:2381", "--listen-peer-urls=https://0.0.0.0:2380", - "--data-dir=" + etcdConfig.DataPath(), + "--data-dir=" + ectdDataDirName, } } @@ -561,7 +518,7 @@ func generateDataVolume(cluster *etcdaenixiov1alpha1.EtcdCluster) corev1.Volume } return corev1.Volume{ - Name: etcdConfig.VolumeName(), + Name: etcdVolumeName, VolumeSource: dataVolumeSource, } } From 64c2272785de72eb0033d969949cf35a5b98b62f Mon Sep 17 00:00:00 2001 From: Sergei Monakhov Date: Sun, 14 Apr 2024 17:23:26 +0300 Subject: [PATCH 4/7] update generateVolumeMounts function --- internal/controller/factory/statefulset.go | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/internal/controller/factory/statefulset.go b/internal/controller/factory/statefulset.go index 6bba5525..5ca608ca 100644 --- a/internal/controller/factory/statefulset.go +++ b/internal/controller/factory/statefulset.go @@ -167,8 +167,9 @@ func generateVolumes(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.Volume { func generateVolumeMounts( cluster *etcdaenixiov1alpha1.EtcdCluster, - dataVolumeMount []corev1.VolumeMount, + containerVolumeMounts []corev1.VolumeMount, ) []corev1.VolumeMount { + dataVolumeMount := updateOrAddEtcdDataVolumeMount(containerVolumeMounts) tlsVolumeMounts := generateTLSSecretVolumeMounts(cluster) volumeMounts := mergeVolumeMounts(tlsVolumeMounts, dataVolumeMount) @@ -243,10 +244,7 @@ func generateContainers(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.Conta c.LivenessProbe = getLivenessProbe(c.LivenessProbe) c.ReadinessProbe = getReadinessProbe(c.ReadinessProbe) c.Env = mergeEnvs(c.Env, podEnv) - - dataVolumeMounts := updateOrAddDataVolumeMount(c.VolumeMounts, etcdVolumeName, - etcdDataMountPath, false) - c.VolumeMounts = generateVolumeMounts(cluster, dataVolumeMounts) + c.VolumeMounts = generateVolumeMounts(cluster, c.VolumeMounts) } containers = append(containers, c) @@ -523,14 +521,14 @@ func generateDataVolume(cluster *etcdaenixiov1alpha1.EtcdCluster) corev1.Volume } } -func updateOrAddDataVolumeMount(volumeMounts []corev1.VolumeMount, - volumeName, mountPath string, readOnly bool) []corev1.VolumeMount { +func updateOrAddEtcdDataVolumeMount(volumeMounts []corev1.VolumeMount) []corev1.VolumeMount { + readOnly := false found := false for i := range volumeMounts { - if volumeMounts[i].Name == volumeName { + if volumeMounts[i].Name == etcdVolumeName { volumeMounts[i].ReadOnly = readOnly - volumeMounts[i].MountPath = mountPath + volumeMounts[i].MountPath = etcdDataMountPath found = true break } @@ -538,9 +536,9 @@ func updateOrAddDataVolumeMount(volumeMounts []corev1.VolumeMount, if !found { volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: volumeName, + Name: etcdVolumeName, ReadOnly: readOnly, - MountPath: mountPath, + MountPath: etcdDataMountPath, }) } From e01d139a4754c9bea3baaa3ccfc0949929c5592f Mon Sep 17 00:00:00 2001 From: Sergei Monakhov Date: Sun, 14 Apr 2024 18:52:19 +0300 Subject: [PATCH 5/7] fix logic for peer-tls --- internal/controller/factory/statefulset.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/controller/factory/statefulset.go b/internal/controller/factory/statefulset.go index 5ca608ca..8c46aaf4 100644 --- a/internal/controller/factory/statefulset.go +++ b/internal/controller/factory/statefulset.go @@ -418,6 +418,10 @@ func etcdTLSFlags(cluster *etcdaenixiov1alpha1.EtcdCluster) []string { "--peer-key-file="+peerCertificate.KeyFilePath(), "--peer-client-cert-auth", ) + } else { + args = append(args, + "--peer-auto-tls", + ) } if hasTLSConfigInSpec(cluster, "ServerSecret") { args = append(args, From cbe83d7f74b8ff04889973b9bd3207581135cf34 Mon Sep 17 00:00:00 2001 From: Sergei Monakhov Date: Sun, 14 Apr 2024 21:24:30 +0300 Subject: [PATCH 6/7] add tests for args --- internal/controller/factory/statefulset.go | 5 ++- .../controller/factory/statefulset_test.go | 36 ++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/internal/controller/factory/statefulset.go b/internal/controller/factory/statefulset.go index 8c46aaf4..4d0a5a49 100644 --- a/internal/controller/factory/statefulset.go +++ b/internal/controller/factory/statefulset.go @@ -72,6 +72,8 @@ var ( peerCertificate = etcdCertificates["peer-certificate"] serverCertificate = etcdCertificates["server-certificate"] clientTrustedCACertificate = etcdCertificates["client-trusted-ca-certificate"] + + etcdDataFullPath = filepath.Join(etcdDataMountPath, ectdDataDirName) ) func CreateOrUpdateStatefulSet( @@ -404,7 +406,7 @@ func baseEtcdFlags() []string { "--name=$(POD_NAME)", "--listen-metrics-urls=http://0.0.0.0:2381", "--listen-peer-urls=https://0.0.0.0:2380", - "--data-dir=" + ectdDataDirName, + "--data-dir=" + etcdDataFullPath, } } @@ -435,6 +437,7 @@ func etcdTLSFlags(cluster *etcdaenixiov1alpha1.EtcdCluster) []string { "--client-cert-auth", ) } + return args } diff --git a/internal/controller/factory/statefulset_test.go b/internal/controller/factory/statefulset_test.go index 724692a8..ce28fa5d 100644 --- a/internal/controller/factory/statefulset_test.go +++ b/internal/controller/factory/statefulset_test.go @@ -687,7 +687,7 @@ var _ = Describe("CreateOrUpdateStatefulSet handler", func() { } } }) - It("should generate security volumes mounts", func() { + It("should generate security volumes mounts and cert args", func() { localCluster := etcdCluster.DeepCopy() localCluster.Spec.Security = &etcdaenixiov1alpha1.SecuritySpec{ TLS: etcdaenixiov1alpha1.TLSSpec{ @@ -700,6 +700,7 @@ var _ = Describe("CreateOrUpdateStatefulSet handler", func() { } containers := generateContainers(localCluster) + args := generateEtcdArgs(localCluster) Expect(containers[0].VolumeMounts).To(ContainElement(corev1.VolumeMount{ Name: "peer-trusted-ca-certificate", @@ -721,6 +722,39 @@ var _ = Describe("CreateOrUpdateStatefulSet handler", func() { MountPath: "/etc/etcd/pki/client/ca", ReadOnly: true, })) + + Expect(args).To(ContainElements([]string{ + "--name=$(POD_NAME)", + "--listen-metrics-urls=http://0.0.0.0:2381", + "--listen-peer-urls=https://0.0.0.0:2380", + "--data-dir=/var/run/etcd/default.etcd", + "--listen-client-urls=https://0.0.0.0:2379", + "--advertise-client-urls=https://$(POD_NAME).test-resource.$(POD_NAMESPACE).svc:2379", + "--initial-advertise-peer-urls=https://$(POD_NAME).test-resource.$(POD_NAMESPACE).svc:2380", + "--peer-trusted-ca-file=/etc/etcd/pki/peer/ca/ca.crt", + "--peer-cert-file=/etc/etcd/pki/peer/cert/tls.crt", + "--peer-key-file=/etc/etcd/pki/peer/cert/tls.key", + "--peer-client-cert-auth", + "--cert-file=/etc/etcd/pki/server/cert/tls.crt", + "--key-file=/etc/etcd/pki/server/cert/tls.key", + "--trusted-ca-file=/etc/etcd/pki/client/ca/ca.crt", + "--client-cert-auth", + })) + }) + It("should generate correct default args", func() { + localCluster := etcdCluster.DeepCopy() + args := generateEtcdArgs(localCluster) + + Expect(args).To(ContainElements([]string{ + "--name=$(POD_NAME)", + "--listen-metrics-urls=http://0.0.0.0:2381", + "--listen-peer-urls=https://0.0.0.0:2380", + "--data-dir=/var/run/etcd/default.etcd", + "--peer-auto-tls", + "--listen-client-urls=http://0.0.0.0:2379", + "--advertise-client-urls=http://$(POD_NAME).test-resource.$(POD_NAMESPACE).svc:2379", + "--initial-advertise-peer-urls=https://$(POD_NAME).test-resource.$(POD_NAMESPACE).svc:2380", + })) }) }) From dcc29c17fb073f5edcfeaa2a88d6e7c2f8bd8227 Mon Sep 17 00:00:00 2001 From: Sergei Monakhov Date: Sun, 14 Apr 2024 23:07:49 +0300 Subject: [PATCH 7/7] add comments for functions --- internal/controller/factory/statefulset.go | 42 +++++++++++++++------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/internal/controller/factory/statefulset.go b/internal/controller/factory/statefulset.go index 4d0a5a49..cf4002e3 100644 --- a/internal/controller/factory/statefulset.go +++ b/internal/controller/factory/statefulset.go @@ -109,6 +109,7 @@ func CreateOrUpdateStatefulSet( } volumes := generateVolumes(cluster) + containers := generateContainers(cluster) statefulSet := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ @@ -126,7 +127,7 @@ func CreateOrUpdateStatefulSet( Template: corev1.PodTemplateSpec{ ObjectMeta: podMetadata, Spec: corev1.PodSpec{ - Containers: generateContainers(cluster), + Containers: containers, ImagePullSecrets: cluster.Spec.PodTemplate.Spec.ImagePullSecrets, Affinity: cluster.Spec.PodTemplate.Spec.Affinity, NodeSelector: cluster.Spec.PodTemplate.Spec.NodeSelector, @@ -352,18 +353,22 @@ func hasProbeHandlerAction(probe corev1.Probe) bool { return probe.HTTPGet != nil || probe.TCPSocket != nil || probe.Exec != nil || probe.GRPC != nil } +// Name returns the name of the certificate. func (c certificate) Name() string { return c.certName } +// MountPath returns the mount path associated with the certificate. func (c certificate) MountPath() string { return c.mountPath } +// CrtFilePath returns the file path of the certificate's CRT file. func (c certificate) CrtFilePath() string { return c.buildFilePath(c.crtFile) } +// KeyFilePath returns the file path of the certificate's key file, if available. func (c certificate) KeyFilePath() string { if c.keyFile == "" { return "" @@ -371,10 +376,12 @@ func (c certificate) KeyFilePath() string { return c.buildFilePath(c.keyFile) } +// buildFilePath constructs the full file path using the certificate's mount path. func (c certificate) buildFilePath(fileName string) string { return filepath.Join(c.mountPath, fileName) } +// getCertificateConfig returns a certificate configuration object with the provided details. func getCertificateConfig(name, mountPath, crtFile, keyFile string) certificate { return certificate{ certName: name, @@ -384,6 +391,7 @@ func getCertificateConfig(name, mountPath, crtFile, keyFile string) certificate } } +// hasTLSConfigInSpec checks if a specific type of TLS secret is configured in the cluster specification. func hasTLSConfigInSpec(cluster *etcdaenixiov1alpha1.EtcdCluster, secretType string) bool { if cluster.Spec.Security == nil { return false @@ -401,6 +409,7 @@ func hasTLSConfigInSpec(cluster *etcdaenixiov1alpha1.EtcdCluster, secretType str } } +// baseEtcdFlags returns a list of base flags for configuring etcd. func baseEtcdFlags() []string { return []string{ "--name=$(POD_NAME)", @@ -410,6 +419,7 @@ func baseEtcdFlags() []string { } } +// etcdTLSFlags generates TLS-related flags for etcd based on the cluster's TLS configuration. func etcdTLSFlags(cluster *etcdaenixiov1alpha1.EtcdCluster) []string { args := []string{} @@ -441,6 +451,7 @@ func etcdTLSFlags(cluster *etcdaenixiov1alpha1.EtcdCluster) []string { return args } +// etcdURLsFlags generates URL-related flags for etcd depending on the TLS cluster configuration. func etcdURLsFlags(cluster *etcdaenixiov1alpha1.EtcdCluster) []string { args := []string{} @@ -461,6 +472,7 @@ func etcdURLsFlags(cluster *etcdaenixiov1alpha1.EtcdCluster) []string { return args } +// etcdExtraFlags generates additional flags for etcd based on the provided in spec options. func etcdExtraFlags(options map[string]string) []string { args := []string{} @@ -475,6 +487,7 @@ func etcdExtraFlags(options map[string]string) []string { return args } +// generateSecretVolumes generates secret volumes based on TLS configuration in the etcd cluster spec. func generateSecretVolumes(cluster *etcdaenixiov1alpha1.EtcdCluster) map[string]corev1.Volume { volumesMap := make(map[string]corev1.Volume) @@ -498,6 +511,7 @@ func generateSecretVolumes(cluster *etcdaenixiov1alpha1.EtcdCluster) map[string] return volumesMap } +// createSecretVolume return a volume object for a given secret. func createSecretVolume(name, secretName string) corev1.Volume { return corev1.Volume{ Name: name, @@ -509,6 +523,7 @@ func createSecretVolume(name, secretName string) corev1.Volume { } } +// generateDataVolume generates the etcd data volume. func generateDataVolume(cluster *etcdaenixiov1alpha1.EtcdCluster) corev1.Volume { var dataVolumeSource corev1.VolumeSource @@ -528,20 +543,21 @@ func generateDataVolume(cluster *etcdaenixiov1alpha1.EtcdCluster) corev1.Volume } } +// updateOrAddEtcdDataVolumeMount updates or adds the volume mount for etcd data volume. func updateOrAddEtcdDataVolumeMount(volumeMounts []corev1.VolumeMount) []corev1.VolumeMount { readOnly := false - found := false - - for i := range volumeMounts { - if volumeMounts[i].Name == etcdVolumeName { - volumeMounts[i].ReadOnly = readOnly - volumeMounts[i].MountPath = etcdDataMountPath - found = true - break - } - } - if !found { + // Check if the volume mount with etcdVolumeName already exists + idx := slices.IndexFunc(volumeMounts, func(vm corev1.VolumeMount) bool { + return vm.Name == etcdVolumeName + }) + + if idx >= 0 { + // Volume mount with etcdVolumeName found, update it + volumeMounts[idx].ReadOnly = readOnly + volumeMounts[idx].MountPath = etcdDataMountPath + } else { + // Volume mount with etcdVolumeName not found, append new volume mount volumeMounts = append(volumeMounts, corev1.VolumeMount{ Name: etcdVolumeName, ReadOnly: readOnly, @@ -552,6 +568,7 @@ func updateOrAddEtcdDataVolumeMount(volumeMounts []corev1.VolumeMount) []corev1. return volumeMounts } +// generateTLSSecretVolumeMounts generates volume mounts for TLS secrets. func generateTLSSecretVolumeMounts(cluster *etcdaenixiov1alpha1.EtcdCluster) []corev1.VolumeMount { volumeMounts := []corev1.VolumeMount{} @@ -593,6 +610,7 @@ func generateTLSSecretVolumeMounts(cluster *etcdaenixiov1alpha1.EtcdCluster) []c return volumeMounts } +// mergeVolumeMounts merges multiple lists of volume mounts into a single list. func mergeVolumeMounts(lists ...[]corev1.VolumeMount) []corev1.VolumeMount { var volumeMounts []corev1.VolumeMount