diff --git a/api/v1/aerospikecluster_validating_webhook.go b/api/v1/aerospikecluster_validating_webhook.go index fd8dcb34e..1e060cd16 100644 --- a/api/v1/aerospikecluster_validating_webhook.go +++ b/api/v1/aerospikecluster_validating_webhook.go @@ -223,7 +223,7 @@ func (c *AerospikeCluster) validate(aslog logr.Logger) error { return err } - if err := validateRequiredFileStorageForFeatureConf( + if err := validateRequiredFileStorageForAerospikeConfig( rack.AerospikeConfig, &rack.Storage, ); err != nil { return err @@ -1797,12 +1797,15 @@ func validateRequiredFileStorageForMetadata( return nil } -func validateRequiredFileStorageForFeatureConf( +func validateRequiredFileStorageForAerospikeConfig( configSpec AerospikeConfigSpec, storage *AerospikeStorageSpec, ) error { - // TODO Add validation for feature key file. featureKeyFilePaths := getFeatureKeyFilePaths(configSpec) nonCAPaths, caPaths := getTLSFilePaths(configSpec) + defaultPassFilePath := GetDefaultPasswordFilePath(&configSpec) + + // TODO: What if default password file is given via Secret Manager? + // How operator will access that file? Should we allow that? var allPaths []string @@ -1818,16 +1821,34 @@ func validateRequiredFileStorageForFeatureConf( } } + if defaultPassFilePath != nil { + if !isSecretManagerPath(*defaultPassFilePath) { + allPaths = append(allPaths, *defaultPassFilePath) + } else { + return fmt.Errorf("default-password-file path doesn't support Secret Manager, path %s", *defaultPassFilePath) + } + } + // CA cert related fields are not supported with Secret Manager, so check their mount volume allPaths = append(allPaths, caPaths...) for _, path := range allPaths { - if !storage.isVolumePresentForAerospikePath(filepath.Dir(path)) { + volume := storage.GetVolumeForAerospikePath(filepath.Dir(path)) + if volume == nil { return fmt.Errorf( - "feature-key-file paths or tls paths are not mounted - create an entry for '%v' in 'storage.volumes'", + "feature-key-file paths or tls paths or default-password-file path "+ + "are not mounted - create an entry for '%s' in 'storage.volumes'", path, ) } + + if defaultPassFilePath != nil && + (path == *defaultPassFilePath && volume.Source.Secret == nil) { + return fmt.Errorf( + "default-password-file path %s volume source should be secret in storage config, volume %v", + path, volume, + ) + } } return nil diff --git a/api/v1/storage.go b/api/v1/storage.go index e28ceba1d..074385228 100644 --- a/api/v1/storage.go +++ b/api/v1/storage.go @@ -203,12 +203,6 @@ func (s *AerospikeStorageSpec) getAerospikeStorageList(onlyPV bool) ( return blockStorageDeviceList, fileStorageList, nil } -// isVolumePresentForAerospikePath checks if configuration has a volume defined for given path for Aerospike server -// container. -func (s *AerospikeStorageSpec) isVolumePresentForAerospikePath(path string) bool { - return s.GetVolumeForAerospikePath(path) != nil -} - // GetVolumeForAerospikePath returns volume defined for given path for Aerospike server container. func (s *AerospikeStorageSpec) GetVolumeForAerospikePath(path string) *VolumeSpec { var matchedVolume *VolumeSpec diff --git a/api/v1/utils.go b/api/v1/utils.go index d17cc9f58..0d3fa1b96 100644 --- a/api/v1/utils.go +++ b/api/v1/utils.go @@ -53,6 +53,10 @@ const ( confKeyXdr = "xdr" confKeyXdrDlogPath = "xdr-digestlog-path" + // Security keys. + confKeySecurity = "security" + confKeySecurityDefaultPasswordFile = "default-password-file" + // Service section keys. confKeyService = "service" confKeyWorkDirectory = "work-directory" @@ -505,3 +509,30 @@ func getContainerNames(containers []v1.Container) []string { func GetBool(boolPtr *bool) bool { return ptr.Deref(boolPtr, false) } + +// GetDefaultPasswordFilePath returns the default-password-fille path if configured. +func GetDefaultPasswordFilePath(aerospikeConfigSpec *AerospikeConfigSpec) *string { + aerospikeConfig := aerospikeConfigSpec.Value + + // Get security config. + securityConfTmp, ok := aerospikeConfig[confKeySecurity] + if !ok { + return nil + } + + securityConf, ok := securityConfTmp.(map[string]interface{}) + if !ok { + // Should never happen. + return nil + } + + // Get password file. + passFileTmp, ok := securityConf[confKeySecurityDefaultPasswordFile] + if !ok { + return nil + } + + passFile := passFileTmp.(string) + + return &passFile +} diff --git a/config/samples/all_flash_cluster_cr.yaml b/config/samples/all_flash_cluster_cr.yaml index 9ee6cee6c..fd357098a 100644 --- a/config/samples/all_flash_cluster_cr.yaml +++ b/config/samples/all_flash_cluster_cr.yaml @@ -12,7 +12,7 @@ metadata: spec: size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 storage: filesystemVolumePolicy: diff --git a/config/samples/dim_nostorage_cluster_cr.yaml b/config/samples/dim_nostorage_cluster_cr.yaml index 50b18b353..0f3f544d8 100644 --- a/config/samples/dim_nostorage_cluster_cr.yaml +++ b/config/samples/dim_nostorage_cluster_cr.yaml @@ -5,7 +5,7 @@ metadata: namespace: aerospike spec: size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 podSpec: multiPodPerHost: true diff --git a/config/samples/dim_nostorage_cluster_skip_validation_cr.yaml b/config/samples/dim_nostorage_cluster_skip_validation_cr.yaml index 0ef553842..58a154e14 100644 --- a/config/samples/dim_nostorage_cluster_skip_validation_cr.yaml +++ b/config/samples/dim_nostorage_cluster_skip_validation_cr.yaml @@ -20,7 +20,7 @@ metadata: spec: size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 podSpec: multiPodPerHost: true diff --git a/config/samples/hdd_dim_storage_cluster_cr.yaml b/config/samples/hdd_dim_storage_cluster_cr.yaml index 96be94226..9493c4fc5 100644 --- a/config/samples/hdd_dim_storage_cluster_cr.yaml +++ b/config/samples/hdd_dim_storage_cluster_cr.yaml @@ -6,7 +6,7 @@ metadata: spec: size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 storage: filesystemVolumePolicy: diff --git a/config/samples/host_network_cluster_cr.yaml b/config/samples/host_network_cluster_cr.yaml index f513e030d..d7848da4c 100644 --- a/config/samples/host_network_cluster_cr.yaml +++ b/config/samples/host_network_cluster_cr.yaml @@ -6,7 +6,7 @@ metadata: spec: size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 storage: filesystemVolumePolicy: diff --git a/config/samples/ldap_cluster_cr.yaml b/config/samples/ldap_cluster_cr.yaml index 0e707cad9..3ccb12505 100644 --- a/config/samples/ldap_cluster_cr.yaml +++ b/config/samples/ldap_cluster_cr.yaml @@ -14,7 +14,7 @@ metadata: namespace: aerospike spec: size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 podSpec: multiPodPerHost: true diff --git a/config/samples/pmem_cluster_cr.yaml b/config/samples/pmem_cluster_cr.yaml index 48395e43b..62b34f54d 100644 --- a/config/samples/pmem_cluster_cr.yaml +++ b/config/samples/pmem_cluster_cr.yaml @@ -6,7 +6,7 @@ metadata: spec: size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 storage: filesystemVolumePolicy: diff --git a/config/samples/podspec_cr.yaml b/config/samples/podspec_cr.yaml index 93591b4e3..8c1ed966f 100644 --- a/config/samples/podspec_cr.yaml +++ b/config/samples/podspec_cr.yaml @@ -6,7 +6,7 @@ metadata: spec: size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 storage: filesystemVolumePolicy: diff --git a/config/samples/rack_enabled_cluster_cr.yaml b/config/samples/rack_enabled_cluster_cr.yaml index ca2038a2d..16444b474 100644 --- a/config/samples/rack_enabled_cluster_cr.yaml +++ b/config/samples/rack_enabled_cluster_cr.yaml @@ -6,7 +6,7 @@ metadata: spec: size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 rackConfig: namespaces: - test diff --git a/config/samples/sc_mode_cluster_cr.yaml b/config/samples/sc_mode_cluster_cr.yaml index 52ab65d6d..8b2b1e914 100644 --- a/config/samples/sc_mode_cluster_cr.yaml +++ b/config/samples/sc_mode_cluster_cr.yaml @@ -6,7 +6,7 @@ metadata: spec: size: 4 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 rosterNodeBlockList: - 1A0 diff --git a/config/samples/secrets/password.conf b/config/samples/secrets/password.conf new file mode 100644 index 000000000..f2b40b22f --- /dev/null +++ b/config/samples/secrets/password.conf @@ -0,0 +1 @@ +admin12345 \ No newline at end of file diff --git a/config/samples/shadow_device_cluster_cr.yaml b/config/samples/shadow_device_cluster_cr.yaml index 68456b905..160e30cb0 100644 --- a/config/samples/shadow_device_cluster_cr.yaml +++ b/config/samples/shadow_device_cluster_cr.yaml @@ -7,7 +7,7 @@ metadata: spec: # Add fields here size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 storage: filesystemVolumePolicy: diff --git a/config/samples/shadow_file_cluster_cr.yaml b/config/samples/shadow_file_cluster_cr.yaml index af53079a1..0d8571b4a 100644 --- a/config/samples/shadow_file_cluster_cr.yaml +++ b/config/samples/shadow_file_cluster_cr.yaml @@ -7,7 +7,7 @@ metadata: spec: # Add fields here size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 storage: filesystemVolumePolicy: diff --git a/config/samples/ssd_storage_cluster_cr.yaml b/config/samples/ssd_storage_cluster_cr.yaml index 585e5a4a9..7e10c726c 100644 --- a/config/samples/ssd_storage_cluster_cr.yaml +++ b/config/samples/ssd_storage_cluster_cr.yaml @@ -6,7 +6,7 @@ metadata: spec: size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 storage: filesystemVolumePolicy: diff --git a/config/samples/tls_cluster_cr.yaml b/config/samples/tls_cluster_cr.yaml index c9c5f90fe..b7e65cfd1 100644 --- a/config/samples/tls_cluster_cr.yaml +++ b/config/samples/tls_cluster_cr.yaml @@ -6,7 +6,7 @@ metadata: spec: size: 4 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 storage: filesystemVolumePolicy: diff --git a/config/samples/xdr_dst_cluster_cr.yaml b/config/samples/xdr_dst_cluster_cr.yaml index 3fddcf46e..afa08f51d 100644 --- a/config/samples/xdr_dst_cluster_cr.yaml +++ b/config/samples/xdr_dst_cluster_cr.yaml @@ -6,7 +6,7 @@ metadata: spec: size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 storage: filesystemVolumePolicy: diff --git a/config/samples/xdr_src_cluster_cr.yaml b/config/samples/xdr_src_cluster_cr.yaml index a38122efd..6d3dc936d 100644 --- a/config/samples/xdr_src_cluster_cr.yaml +++ b/config/samples/xdr_src_cluster_cr.yaml @@ -6,7 +6,7 @@ metadata: spec: size: 2 - image: aerospike/aerospike-server-enterprise:7.0.0.0 + image: aerospike/aerospike-server-enterprise:7.1.0.0 storage: filesystemVolumePolicy: diff --git a/controllers/access_control.go b/controllers/access_control.go index 926821880..6c7042e4a 100644 --- a/controllers/access_control.go +++ b/controllers/access_control.go @@ -75,7 +75,7 @@ func AerospikeAdminCredentials( if currentState.AerospikeAccessControl == nil { // We haven't yet set up access control. Use default password. - return asdbv1.AdminUsername, asdbv1.DefaultAdminPassword, nil + return asdbv1.AdminUsername, passwordProvider.GetDefaultPassword(desiredState), nil } adminUserSpec, ok := asdbv1.GetUsersFromSpec(currentState)[asdbv1.AdminUsername] @@ -396,6 +396,9 @@ type AerospikeUserPasswordProvider interface { Get(username string, userSpec *asdbv1.AerospikeUserSpec) ( string, error, ) + + // GetDefaultPassword returns the default password for cluster using AerospikeClusterSpec. + GetDefaultPassword(spec *asdbv1.AerospikeClusterSpec) string } // aerospikeAccessControlReconcileCmd commands needed to Reconcile a single access control entry, diff --git a/controllers/client_policy.go b/controllers/client_policy.go index 01a85f0da..0e0ebf09c 100644 --- a/controllers/client_policy.go +++ b/controllers/client_policy.go @@ -53,6 +53,55 @@ func (pp fromSecretPasswordProvider) Get( return string(passBytes), nil } +// GetDefaultPassword returns the default password for cluster using AerospikeClusterSpec. +func (pp fromSecretPasswordProvider) GetDefaultPassword(spec *asdbv1.AerospikeClusterSpec) string { + defaultPasswordFilePath := asdbv1.GetDefaultPasswordFilePath(spec.AerospikeConfig) + + // No default password file specified. Give default password. + if defaultPasswordFilePath == nil { + return asdbv1.DefaultAdminPassword + } + + // Default password file specified. Get the secret name from the volume + volume := spec.Storage.GetVolumeForAerospikePath(*defaultPasswordFilePath) + secretName := volume.Source.Secret.SecretName + + // Get the password from the secret. + passwordFileName := filepath.Base(*defaultPasswordFilePath) + + password, err := pp.getPasswordFromSecret(secretName, passwordFileName) + if err != nil { + pkgLog.Error(err, "Failed to get password from secret") + + return asdbv1.DefaultAdminPassword + } + + return password +} + +// GetPasswordFromSecret returns the password from the secret. +func (pp fromSecretPasswordProvider) getPasswordFromSecret( + secretName string, passFileName string, +) (string, error) { + secretNamespcedName := types.NamespacedName{Name: secretName, Namespace: pp.namespace} + secret := &corev1.Secret{} + + err := (*pp.k8sClient).Get(context.TODO(), secretNamespcedName, secret) + if err != nil { + return "", fmt.Errorf("failed to get secret %s: %v", secretNamespcedName, err) + } + + passBytes, ok := secret.Data[passFileName] + if !ok { + return "", fmt.Errorf( + "failed to get password file in secret %s, fileName %s", + secretNamespcedName, passFileName, + ) + } + + return string(passBytes), nil +} + func (r *SingleClusterReconciler) getPasswordProvider() fromSecretPasswordProvider { return fromSecretPasswordProvider{ k8sClient: &r.Client, namespace: r.aeroCluster.Namespace, diff --git a/helm-charts/aerospike-cluster/README.md b/helm-charts/aerospike-cluster/README.md index 2b32d62c4..52d051aa7 100644 --- a/helm-charts/aerospike-cluster/README.md +++ b/helm-charts/aerospike-cluster/README.md @@ -48,7 +48,7 @@ helm install aerospike ./aerospike-cluster/ \ | ---------- | ----------- | --------- | | `replicas` | Aerospike cluster size | `3` | | `image.repository` | Aerospike server container image repository | `aerospike/aerospike-server-enterprise` | -| `image.tag` | Aerospike server container image tag | `7.0.0.0` | +| `image.tag` | Aerospike server container image tag | `7.1.0.0` | | `imagePullSecrets` | Secrets containing credentials to pull Aerospike container image from a private registry | `{}` (nil) | | `customLabels` | Custom labels to add on the aerospikecluster resource | `{}` (nil) | | `aerospikeAccessControl` | Aerospike access control configuration. Define users and roles to be created on the cluster. | `{}` (nil) | diff --git a/helm-charts/aerospike-cluster/templates/aerospike-cluster-cr.yaml b/helm-charts/aerospike-cluster/templates/aerospike-cluster-cr.yaml index 9f814f0a4..5ba0dc431 100644 --- a/helm-charts/aerospike-cluster/templates/aerospike-cluster-cr.yaml +++ b/helm-charts/aerospike-cluster/templates/aerospike-cluster-cr.yaml @@ -16,7 +16,7 @@ spec: size: {{ .Values.replicas | default 3 }} # Aerospike server docker image - image: {{ .Values.image.repository | default "aerospike/aerospike-server-enterprise" }}:{{ .Values.image.tag | default "7.0.0.0" }} + image: {{ .Values.image.repository | default "aerospike/aerospike-server-enterprise" }}:{{ .Values.image.tag | default "7.1.0.0" }} # Aerospike access control configuration {{- with .Values.aerospikeAccessControl }} diff --git a/helm-charts/aerospike-cluster/values.yaml b/helm-charts/aerospike-cluster/values.yaml index 9c322c3f9..cb9774064 100644 --- a/helm-charts/aerospike-cluster/values.yaml +++ b/helm-charts/aerospike-cluster/values.yaml @@ -8,7 +8,7 @@ replicas: 3 ## Aerospike server docker image image: repository: aerospike/aerospike-server-enterprise - tag: 7.0.0.0 + tag: 7.1.0.0 ## In case the above image is pulled from a registry that requires ## authentication, a secret containining credentials can be added diff --git a/pkg/configschema/schemas b/pkg/configschema/schemas index b3f2db0a4..b98c65e83 160000 --- a/pkg/configschema/schemas +++ b/pkg/configschema/schemas @@ -1 +1 @@ -Subproject commit b3f2db0a48f09d2a963024934f3681afaaaf52bb +Subproject commit b98c65e83ef7267f35847d94ada4ba3c76cb48e2 diff --git a/test/access_control_test.go b/test/access_control_test.go index 668b73aaf..6ca87bf74 100644 --- a/test/access_control_test.go +++ b/test/access_control_test.go @@ -8,10 +8,12 @@ import ( "math/rand" "reflect" "strings" + "time" "github.com/go-logr/logr" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" @@ -57,6 +59,7 @@ var aerospikeConfigWithSecurityWithQuota = &asdbv1.AerospikeConfigSpec{ var _ = Describe( "AccessControl", func() { + ctx := goctx.TODO() Context( "AccessControl", func() { @@ -1329,8 +1332,6 @@ var _ = Describe( Context( "When cluster is not deployed", func() { - ctx := goctx.Background() - clusterName := "ac-invalid" clusterNamespacedName := getNamespacedName( clusterName, namespace, @@ -1624,8 +1625,6 @@ var _ = Describe( ) Context( "When cluster is deployed", func() { - ctx := goctx.Background() - It( "SecurityEnable: should enable security in running cluster", func() { @@ -2144,6 +2143,100 @@ var _ = Describe( ) }, ) + + Context("Using default-password-file", func() { + var clusterNamespacedName = getNamespacedName( + "default-password-file", namespace, + ) + + It("Should fail if volume is not present for default-password-file", func() { + aeroCluster := createDummyAerospikeCluster(clusterNamespacedName, 4) + racks := getDummyRackConf(1, 2) + aeroCluster.Spec.RackConfig.Racks = racks + aeroCluster.Spec.RackConfig.Namespaces = []string{"test"} + aeroCluster.Spec.AerospikeConfig.Value["security"] = map[string]interface{}{ + "default-password-file": "randompath", + } + + err := k8sClient.Create(ctx, aeroCluster) + Expect(err).To(HaveOccurred()) + }) + + It("Should fail if volume source is not secret for default-password-file", func() { + aeroCluster := createDummyAerospikeCluster(clusterNamespacedName, 4) + racks := getDummyRackConf(1, 2) + aeroCluster.Spec.RackConfig.Racks = racks + aeroCluster.Spec.RackConfig.Namespaces = []string{"test"} + aeroCluster.Spec.AerospikeConfig.Value["security"] = map[string]interface{}{ + "default-password-file": "/etc/aerospike/defaultpass/password.conf", + } + aeroCluster.Spec.Storage.Volumes = append(aeroCluster.Spec.Storage.Volumes, asdbv1.VolumeSpec{ + Name: "defaultpass", + Source: asdbv1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + Aerospike: &asdbv1.AerospikeServerVolumeAttachment{ + Path: "/etc/aerospike/defaultpass", + }, + }) + + err := k8sClient.Create(ctx, aeroCluster) + Expect(err).To(HaveOccurred()) + }) + + It("Should use default-password-file when configured", func() { + By("Creating cluster") + + aeroCluster := createDummyAerospikeCluster(clusterNamespacedName, 4) + racks := getDummyRackConf(1, 2) + aeroCluster.Spec.RackConfig.Racks = racks + aeroCluster.Spec.RackConfig.Namespaces = []string{"test"} + // This file is already added in the storage volume backed by the secret. + aeroCluster.Spec.AerospikeConfig.Value["security"] = map[string]interface{}{ + "default-password-file": "/etc/aerospike/secret/password.conf", + } + + err := k8sClient.Create(ctx, aeroCluster) + Expect(err).ToNot(HaveOccurred()) + + By("Cluster is not ready, connect with cluster using default password") + + // Get default password from secret. + secretNamespcedName := types.NamespacedName{ + Name: aerospikeSecretName, + Namespace: aeroCluster.Namespace, + } + passFileName := "password.conf" + pass, err := getPasswordFromSecret(k8sClient, secretNamespcedName, passFileName) + Expect(err).ToNot(HaveOccurred()) + + // Cluster is not yet ready. Therefore, it should be using default password + Eventually(func() error { + clientPolicy := getClientPolicy(aeroCluster, k8sClient) + clientPolicy.Password = pass + + client, cerr := getClientWithPolicy( + pkgLog, aeroCluster, k8sClient, clientPolicy) + if cerr != nil { + return cerr + } + + if !client.IsConnected() { + return fmt.Errorf("Not connected") + } + + pkgLog.Info("Connected to cluster") + + return nil + }, 3*time.Minute).ShouldNot(HaveOccurred()) + + By("Try scaleup") + err = scaleUpClusterTest( + k8sClient, ctx, clusterNamespacedName, 1, + ) + Expect(err).ToNot(HaveOccurred()) + }) + }) }, ) diff --git a/test/batch_restart_pods_test.go b/test/batch_restart_pods_test.go index 55aee6460..5a9342c79 100644 --- a/test/batch_restart_pods_test.go +++ b/test/batch_restart_pods_test.go @@ -20,9 +20,8 @@ import ( const batchClusterName = "batch-restart" var ( - unavailableImage = fmt.Sprintf("%s:%s", baseImage, "7.0.0.99") - availableImage1 = fmt.Sprintf("%s:%s", baseImage, "7.0.0.0_1") - availableImage2 = fmt.Sprintf("%s:%s", baseImage, "7.0.0.0_2") + unavailableImage = fmt.Sprintf("%s:%s", baseImage, "7.1.0.99") + availableImage1 = nextImage ) func percent(val string) *intstr.IntOrString { @@ -429,7 +428,7 @@ func BatchUpgrade(ctx goctx.Context, clusterNamespacedName types.NamespacedName) aeroCluster, err = getCluster(k8sClient, ctx, clusterNamespacedName) Expect(err).ToNot(HaveOccurred()) aeroCluster.Spec.RackConfig.RollingUpdateBatchSize = count(1) - aeroCluster.Spec.Image = availableImage2 + aeroCluster.Spec.Image = latestImage err = k8sClient.Update(ctx, aeroCluster) Expect(err).ToNot(HaveOccurred()) diff --git a/test/cluster_helper.go b/test/cluster_helper.go index 49a52b655..0e6ac343d 100644 --- a/test/cluster_helper.go +++ b/test/cluster_helper.go @@ -31,11 +31,13 @@ import ( const ( baseImage = "aerospike/aerospike-server-enterprise" - prevServerVersion = "6.4.0.0" - pre6Version = "5.7.0.17" - version6 = "6.0.0.5" - latestServerVersion = "7.0.0.0" + nextServerVersion = "7.1.0.0_1" + latestServerVersion = "7.1.0.0" invalidVersion = "3.0.0.4" + + post6Version = "7.0.0.0" + pre6Version = "5.7.0.17" + version6 = "6.0.0.5" ) var ( @@ -43,11 +45,14 @@ var ( cascadeDeleteFalse = false cascadeDeleteTrue = true logger = logr.Discard() - prevImage = fmt.Sprintf("%s:%s", baseImage, prevServerVersion) + nextImage = fmt.Sprintf("%s:%s", baseImage, nextServerVersion) latestImage = fmt.Sprintf("%s:%s", baseImage, latestServerVersion) - version6Image = fmt.Sprintf("%s:%s", baseImage, version6) invalidImage = fmt.Sprintf("%s:%s", baseImage, invalidVersion) - pre6Image = fmt.Sprintf("%s:%s", baseImage, pre6Version) + + // Storage wipe test + post6Image = fmt.Sprintf("%s:%s", baseImage, post6Version) + version6Image = fmt.Sprintf("%s:%s", baseImage, version6) + pre6Image = fmt.Sprintf("%s:%s", baseImage, pre6Version) ) func rollingRestartClusterByEnablingTLS( @@ -63,7 +68,7 @@ func rollingRestartClusterByEnablingTLS( aeroCluster.Spec.OperatorClientCertSpec = &asdbv1.AerospikeOperatorClientCertSpec{ AerospikeOperatorCertSource: asdbv1.AerospikeOperatorCertSource{ SecretCertSource: &asdbv1.AerospikeSecretCertSource{ - SecretName: tlsSecretName, + SecretName: aerospikeSecretName, CaCertsFilename: "cacert.pem", ClientCertFilename: "svc_cluster_chain.pem", ClientKeyFilename: "svc_key.pem", @@ -862,7 +867,7 @@ func createAerospikeClusterPost460( OperatorClientCertSpec: &asdbv1.AerospikeOperatorClientCertSpec{ AerospikeOperatorCertSource: asdbv1.AerospikeOperatorCertSource{ SecretCertSource: &asdbv1.AerospikeSecretCertSource{ - SecretName: tlsSecretName, + SecretName: aerospikeSecretName, CaCertsFilename: "cacert.pem", ClientCertFilename: "svc_cluster_chain.pem", ClientKeyFilename: "svc_key.pem", @@ -925,7 +930,7 @@ func createAerospikeClusterPost560( OperatorClientCertSpec: &asdbv1.AerospikeOperatorClientCertSpec{ AerospikeOperatorCertSource: asdbv1.AerospikeOperatorCertSource{ SecretCertSource: &asdbv1.AerospikeSecretCertSource{ - SecretName: tlsSecretName, + SecretName: aerospikeSecretName, CaCertsFilename: "cacert.pem", ClientCertFilename: "svc_cluster_chain.pem", ClientKeyFilename: "svc_key.pem", @@ -1310,7 +1315,7 @@ func createBasicTLSCluster( OperatorClientCertSpec: &asdbv1.AerospikeOperatorClientCertSpec{ AerospikeOperatorCertSource: asdbv1.AerospikeOperatorCertSource{ SecretCertSource: &asdbv1.AerospikeSecretCertSource{ - SecretName: tlsSecretName, + SecretName: aerospikeSecretName, CaCertsFilename: "cacert.pem", ClientCertFilename: "svc_cluster_chain.pem", ClientKeyFilename: "svc_key.pem", @@ -1465,7 +1470,7 @@ func aerospikeClusterCreateUpdate( ctx goctx.Context, ) error { return aerospikeClusterCreateUpdateWithTO( - k8sClient, desired, ctx, retryInterval, getTimeout(1), + k8sClient, desired, ctx, retryInterval, getTimeout(desired.Spec.Size), ) } @@ -1533,7 +1538,7 @@ func getStorageVolumeForSecret() asdbv1.VolumeSpec { Name: aerospikeConfigSecret, Source: asdbv1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: tlsSecretName, + SecretName: aerospikeSecretName, }, }, Aerospike: &asdbv1.AerospikeServerVolumeAttachment{ diff --git a/test/cluster_test.go b/test/cluster_test.go index 84da35a98..85a3d8146 100644 --- a/test/cluster_test.go +++ b/test/cluster_test.go @@ -254,8 +254,7 @@ func clusterWithMaxIgnorablePod(ctx goctx.Context) { Eventually(func() error { aeroCluster, err = getCluster(k8sClient, ctx, clusterNamespacedName) Expect(err).ToNot(HaveOccurred()) - newImage := baseImage + ":7.0.0.0_2" - aeroCluster.Spec.Image = newImage + aeroCluster.Spec.Image = nextImage // As pod is in pending state, CR object will be won't reach the final phase. // So expectedPhases can be InProgress or Completed return updateClusterWithExpectedPhases(k8sClient, ctx, aeroCluster, expectedPhases) @@ -1032,7 +1031,7 @@ func UpdateClusterTest(ctx goctx.Context) { // TODO: How to check if it is checking cluster stability before killing node // dont change image, it upgrade, check old version err = upgradeClusterTest( - k8sClient, ctx, clusterNamespacedName, prevImage, + k8sClient, ctx, clusterNamespacedName, nextImage, ) Expect(err).ToNot(HaveOccurred()) }, @@ -1198,7 +1197,7 @@ func UpdateClusterTest(ctx goctx.Context) { aeroCluster.Spec.OperatorClientCertSpec = &asdbv1.AerospikeOperatorClientCertSpec{ AerospikeOperatorCertSource: asdbv1.AerospikeOperatorCertSource{ SecretCertSource: &asdbv1.AerospikeSecretCertSource{ - SecretName: tlsSecretName, + SecretName: aerospikeSecretName, CaCertsFilename: "cacert.pem", ClientCertFilename: "svc_cluster_chain.pem", ClientKeyFilename: "svc_key.pem", diff --git a/test/dynamic_config_test.go b/test/dynamic_config_test.go index 9e5adc727..36150e194 100644 --- a/test/dynamic_config_test.go +++ b/test/dynamic_config_test.go @@ -13,6 +13,7 @@ import ( "golang.org/x/net/context" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" @@ -31,16 +32,15 @@ type podID struct { const clName = "dynamic-config-test" -var clusterNamespacedName = getNamespacedName( - clName, namespace, -) - var configWithMaxDefaultVal = mapset.NewSet("info-max-ms", "flush-max-ms") var _ = Describe( "DynamicConfig", func() { ctx := goctx.Background() + var clusterNamespacedName = getNamespacedName( + clName, namespace, + ) Context( "When doing valid operations", func() { @@ -587,7 +587,8 @@ var _ = Describe( validateServerRestart(ctx, aeroCluster, podPIDMap, false) By("Verify XDR Context configs dynamically") - err = validateXDRContextDynamically(ctx, flatServer, flatSpec, aeroCluster, dynamic) + err = validateXDRContextDynamically(clusterNamespacedName, + ctx, flatServer, flatSpec, aeroCluster, dynamic) Expect(err).ToNot(HaveOccurred()) aeroCluster, err = getCluster( @@ -849,7 +850,7 @@ func validateSecurityContextDynamically( return updateCluster(k8sClient, ctx, aeroCluster) } -func validateXDRContextDynamically( +func validateXDRContextDynamically(clusterNamespacedName types.NamespacedName, ctx goctx.Context, flatServer, flatSpec *asconfig.Conf, aeroCluster *asdbv1.AerospikeCluster, dynamic mapset.Set[string], ) error { diff --git a/test/k8snode_block_list_test.go b/test/k8snode_block_list_test.go index 0bee2e41c..a298944e8 100644 --- a/test/k8snode_block_list_test.go +++ b/test/k8snode_block_list_test.go @@ -122,7 +122,7 @@ var _ = Describe( aeroCluster, err = getCluster(k8sClient, ctx, clusterNamespacedName) Expect(err).ToNot(HaveOccurred()) aeroCluster.Spec.K8sNodeBlockList = []string{oldK8sNode} - aeroCluster.Spec.Image = availableImage2 + aeroCluster.Spec.Image = availableImage1 err = updateCluster(k8sClient, ctx, aeroCluster) Expect(err).ToNot(HaveOccurred()) diff --git a/test/large_reconcile_test.go b/test/large_reconcile_test.go index 244a880aa..07075b644 100644 --- a/test/large_reconcile_test.go +++ b/test/large_reconcile_test.go @@ -139,7 +139,7 @@ var _ = Describe( ) Expect(err).ToNot(HaveOccurred()) - err = UpdateClusterImage(aeroCluster, prevImage) + err = UpdateClusterImage(aeroCluster, nextImage) Expect(err).ToNot(HaveOccurred()) err = k8sClient.Update(goctx.TODO(), aeroCluster) Expect(err).ToNot(HaveOccurred()) @@ -159,7 +159,7 @@ var _ = Describe( // Only 1 pod need upgrade err = waitForClusterUpgrade( k8sClient, aeroCluster, int(aeroCluster.Spec.Size), - prevImage, retryInterval, getTimeout(4), + nextImage, retryInterval, getTimeout(4), ) Expect(err).ToNot(HaveOccurred()) diff --git a/test/multicluster_test.go b/test/multicluster_test.go index 1aec249fa..8728f837a 100644 --- a/test/multicluster_test.go +++ b/test/multicluster_test.go @@ -227,7 +227,7 @@ func validateLifecycleOperationInRackCluster( By("Upgrade/Downgrade") // don't change image, it upgrades, check old version err = upgradeClusterTest( - k8sClient, ctx, clusterNamespacedName, prevImage, + k8sClient, ctx, clusterNamespacedName, nextImage, ) Expect(err).ToNot(HaveOccurred()) diff --git a/test/rack_enabled_cluster_test.go b/test/rack_enabled_cluster_test.go index b9c32c8f1..bf3cb656d 100644 --- a/test/rack_enabled_cluster_test.go +++ b/test/rack_enabled_cluster_test.go @@ -132,7 +132,7 @@ var _ = Describe( // don't change image, it upgrade, check old version err = upgradeClusterTest( - k8sClient, ctx, clusterNamespacedName, prevImage, + k8sClient, ctx, clusterNamespacedName, nextImage, ) Expect(err).ToNot(HaveOccurred()) diff --git a/test/storage_init_test.go b/test/storage_init_test.go index 5cb21b8d0..fbff805ac 100644 --- a/test/storage_init_test.go +++ b/test/storage_init_test.go @@ -212,7 +212,7 @@ var _ = Describe( Expect(err).ToNot(HaveOccurred()) By("Forcing a rolling restart, volumes should still have data") - err = UpdateClusterImage(aeroCluster, prevImage) + err = UpdateClusterImage(aeroCluster, nextImage) Expect(err).ToNot(HaveOccurred()) err = aerospikeClusterCreateUpdate( k8sClient, aeroCluster, ctx, @@ -231,7 +231,7 @@ var _ = Describe( By("Recreating. Older volumes will still be around and reused") aeroCluster = getStorageInitAerospikeCluster( clusterNamespacedName, storageConfig, racks, - prevImage, + nextImage, ) aeroCluster.Spec.PodSpec = podSpec err = aerospikeClusterCreateUpdate( diff --git a/test/storage_wipe_test.go b/test/storage_wipe_test.go index 24a604340..02217aee3 100644 --- a/test/storage_wipe_test.go +++ b/test/storage_wipe_test.go @@ -74,7 +74,7 @@ var _ = Describe( } aeroCluster := getStorageWipeAerospikeCluster( clusterNamespacedName, storageConfig, racks, - latestImage, getAerospikeClusterConfig(), + post6Image, getAerospikeClusterConfig(), ) aeroCluster.Spec.PodSpec = podSpec @@ -107,7 +107,7 @@ var _ = Describe( By( fmt.Sprintf( "Downgrading image from %s to %s - volumes should not be wiped", - latestImage, version6, + post6Image, version6, ), ) err = UpdateClusterImage(aeroCluster, version6Image) diff --git a/test/strong_consistency_test.go b/test/strong_consistency_test.go index 982a2bea3..f88373360 100644 --- a/test/strong_consistency_test.go +++ b/test/strong_consistency_test.go @@ -194,7 +194,7 @@ var _ = Describe("SCMode", func() { By("Upgrade/Downgrade") // don't change image, it upgrades err = upgradeClusterTest( - k8sClient, ctx, clusterNamespacedName, prevImage, + k8sClient, ctx, clusterNamespacedName, nextImage, ) Expect(err).ToNot(HaveOccurred()) @@ -366,7 +366,7 @@ func validateLifecycleOperationInSCCluster( By("Upgrade/Downgrade") // don't change image, it upgrades, check old version err = upgradeClusterTest( - k8sClient, ctx, clusterNamespacedName, prevImage, + k8sClient, ctx, clusterNamespacedName, nextImage, ) Expect(err).ToNot(HaveOccurred()) diff --git a/test/test_client.go b/test/test_client.go index 72b3eb6e6..7ea76bdbd 100644 --- a/test/test_client.go +++ b/test/test_client.go @@ -60,6 +60,13 @@ func (pp FromSecretPasswordProvider) Get( return string(passbyte), nil } +// This function is not used in the test code. This is just a dummy implementation. +// Tests do not make client call till cluster is up and reconciled. +// DefaultPassword only comes into play when the cluster is being created. +func (pp FromSecretPasswordProvider) GetDefaultPassword(_ *asdbv1.AerospikeClusterSpec) string { + return asdbv1.DefaultAdminPassword +} + func getPasswordProvider( aeroCluster *asdbv1.AerospikeCluster, k8sClient client.Client, ) FromSecretPasswordProvider { @@ -71,6 +78,18 @@ func getPasswordProvider( func getClient( log logr.Logger, aeroCluster *asdbv1.AerospikeCluster, k8sClient client.Client, +) (*as.Client, error) { + // Create policy using status, status has current connection info + policy := getClientPolicy( + aeroCluster, k8sClient, + ) + + return getClientWithPolicy(log, aeroCluster, k8sClient, policy) +} + +func getClientWithPolicy( + log logr.Logger, aeroCluster *asdbv1.AerospikeCluster, + k8sClient client.Client, policy *as.ClientPolicy, ) (*as.Client, error) { conns, err := newAllHostConn(log, aeroCluster, k8sClient) if err != nil { @@ -87,10 +106,7 @@ func getClient( }, ) } - // Create policy using status, status has current connection info - policy := getClientPolicy( - aeroCluster, k8sClient, - ) + aeroClient, err := as.NewClientWithPolicyAndHost( policy, hosts..., ) @@ -299,13 +315,17 @@ func appendCACertFromFileOrPath( if err != nil { return err } + if !d.IsDir() { var caData []byte + if caData, err = os.ReadFile(path); err != nil { return err } + serverPool.AppendCertsFromPEM(caData) } + return nil }, ) diff --git a/test/utils.go b/test/utils.go index d43469c29..e3c5bf5c5 100644 --- a/test/utils.go +++ b/test/utils.go @@ -44,7 +44,7 @@ var cacertSecrets map[string][]byte const secretDir = "../config/samples/secrets" //nolint:gosec // for testing const cacertSecretDir = "../config/samples/secrets/cacerts" //nolint:gosec // for testing -const tlsSecretName = "aerospike-secret" +const aerospikeSecretName = "aerospike-secret" const tlsCacertSecretName = "aerospike-cacert-secret" //nolint:gosec // for testing const authSecretName = "auth-secret" const authSecretNameForUpdate = "auth-update" @@ -191,7 +191,7 @@ func createConfigSecret( // Create configSecret s := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: tlsSecretName, + Name: aerospikeSecretName, Namespace: namespace, Labels: labels, }, @@ -795,3 +795,24 @@ func getAerospikeConfigFromNode(log logr.Logger, k8sClient client.Client, ctx go return confs[configContext].(lib.Stats), nil } + +func getPasswordFromSecret(k8sClient client.Client, + secretNamespcedName types.NamespacedName, passFileName string, +) (string, error) { + secret := &corev1.Secret{} + + err := k8sClient.Get(goctx.TODO(), secretNamespcedName, secret) + if err != nil { + return "", fmt.Errorf("failed to get secret %s: %v", secretNamespcedName, err) + } + + passBytes, ok := secret.Data[passFileName] + if !ok { + return "", fmt.Errorf( + "failed to get password file in secret %s, fileName %s", + secretNamespcedName, passFileName, + ) + } + + return string(passBytes), nil +}