Repository: FoundationDB/fdb-kubernetes-operator
Pull request #1152 (2022-04-01 03:45:19)

Add support for ssd-rocksdb-v1

foundationdb-ci: 
### AWS CodeBuild CI Report for Linux CentOS 7

* CodeBuild project: fdb-kubernetes-operator-pr
* Commit ID: d9d2cb4e3e7283bc192ce567b4dd4648601b47a7
* Result: FAILED
* Error: `Error while executing command: make -C tests -kj prOperator. Reason: exit status 2`
* [Build Logs](https://d1e0l78xbkh3xa.cloudfront.net/build-logs/fdb-kubernetes-operator/fdb-kubernetes-operator-pr/49121b68-3f15-44f8-99cb-3e807f850c56.gz) (available for 30 days)

foundationdb-ci: 
### AWS CodeBuild CI Report for Linux CentOS 7

* CodeBuild project: fdb-kubernetes-operator-pr
* Commit ID: e6abcc02edc3bc02c95f28c26a154a5246a5ff9c
* Result: FAILED
* Error: `Error while executing command: make -C tests -kj prOperator. Reason: exit status 2`
* [Build Logs](https://d1e0l78xbkh3xa.cloudfront.net/build-logs/fdb-kubernetes-operator/fdb-kubernetes-operator-pr/9bdd486d-048a-40c7-a398-206eac8b9db0.gz) (available for 30 days)

foundationdb-ci: 
### AWS CodeBuild CI Report for Linux CentOS 7

* CodeBuild project: fdb-kubernetes-operator-pr
* Commit ID: d878f8e6bf998d01fefb06b903f17a4a5f4f7319
* Result: FAILED
* Error: `Error while executing command: make -C tests -kj prOperator. Reason: exit status 2`
* [Build Logs](https://d1e0l78xbkh3xa.cloudfront.net/build-logs/fdb-kubernetes-operator/fdb-kubernetes-operator-pr/60342e50-5387-4a42-b729-fb03b1f4208c.gz) (available for 30 days)

foundationdb-ci: 
### AWS CodeBuild CI Report for Linux CentOS 7

* CodeBuild project: fdb-kubernetes-operator-pr
* Commit ID: ffa1fce1e9e5e98ec188ba8d73afe4a59b8cd764
* Result: FAILED
* Error: `Error while executing command: make -C tests -kj prOperator. Reason: exit status 2`
* [Build Logs](https://d1e0l78xbkh3xa.cloudfront.net/build-logs/fdb-kubernetes-operator/fdb-kubernetes-operator-pr/fba6d8da-171b-4770-9514-978378043418.gz) (available for 30 days)

foundationdb-ci: 
### AWS CodeBuild CI Report for Linux CentOS 7

* CodeBuild project: fdb-kubernetes-operator-pr
* Commit ID: 8927f50b293b1aee1b3451b811f9dc773917c432
* Result: FAILED
* Error: `Error while executing command: make -C tests -kj prOperator. Reason: exit status 2`
* [Build Logs](https://d1e0l78xbkh3xa.cloudfront.net/build-logs/fdb-kubernetes-operator/fdb-kubernetes-operator-pr/f9d6973c-e69b-4bf2-a010-6b3a46a0d464.gz) (available for 30 days)


Patch:
diff --git a/api/v1beta2/foundationdb_database_configuration.go b/api/v1beta2/foundationdb_database_configuration.go
index 328f2f67..239b8b35 100644
--- a/api/v1beta2/foundationdb_database_configuration.go
+++ b/api/v1beta2/foundationdb_database_configuration.go
@@ -39,7 +39,7 @@ type DatabaseConfiguration struct {
 
 	// StorageEngine defines the storage engine the database uses.
 	// +kubebuilder:validation:Optional
-	// +kubebuilder:validation:Enum=ssd;ssd-1;ssd-2;memory;memory-1;memory-2;ssd-redwood-1-experimental;ssd-rocksdb-experimental;memory-radixtree-beta;custom
+	// +kubebuilder:validation:Enum=ssd;ssd-1;ssd-2;memory;memory-1;memory-2;ssd-redwood-1-experimental;ssd-rocksdb-experimental;ssd-rocksdb-v1;memory-radixtree-beta;custom
 	// +kubebuilder:default:=ssd-2
 	StorageEngine StorageEngine `json:"storage_engine,omitempty"`
 
@@ -684,6 +684,10 @@ const (
 	StorageEngineMemory StorageEngine = "memory"
 	// StorageEngineMemory2 defines the storage engine memory-2.
 	StorageEngineMemory2 StorageEngine = "memory-2"
+	// StorageEngineRocksDbExperimental defines the storage engine ssd-rocksdb-experimental.
+	StorageEngineRocksDbExperimental StorageEngine = "ssd-rocksdb-experimental"
+	// StorageEngineRocksDbV1 defines the storage engine ssd-rocksdb-v1.
+	StorageEngineRocksDbV1 StorageEngine = "ssd-rocksdb-v1"
 )
 
 // RoleCounts represents the roles whose counts can be customized.
diff --git a/api/v1beta2/foundationdb_version.go b/api/v1beta2/foundationdb_version.go
index 2eb45052..2df26cb2 100644
--- a/api/v1beta2/foundationdb_version.go
+++ b/api/v1beta2/foundationdb_version.go
@@ -111,6 +111,12 @@ func (version Version) IsAtLeast(other Version) bool {
 	if version.Patch > other.Patch {
 		return true
 	}
+	if version.ReleaseCandidate == 0 {
+		return true
+	}
+	if other.ReleaseCandidate == 0 {
+		return false
+	}
 	if version.ReleaseCandidate < other.ReleaseCandidate {
 		return false
 	}
@@ -174,6 +180,16 @@ func (version Version) IsSupported() bool {
 	return version.IsAtLeast(Versions.MinimumVersion)
 }
 
+// IsStorageEngineSupported return true if storage engine is supported by FDB version.
+func (version Version) IsStorageEngineSupported(storageEngine StorageEngine) bool {
+	if storageEngine == StorageEngineRocksDbV1 {
+		return version.IsAtLeast(Versions.SupportsRocksDBV1)
+	} else if storageEngine == StorageEngineRocksDbExperimental {
+		return !version.IsAtLeast(Versions.SupportsRocksDBV1)
+	}
+	return true
+}
+
 // IsReleaseCandidate returns true if the version is a release candidate or not
 func (version Version) IsReleaseCandidate() bool {
 	return version.ReleaseCandidate > 0
@@ -185,10 +201,12 @@ var Versions = struct {
 	NextMajorVersion,
 	NextPatchVersion,
 	MinimumVersion,
+	SupportsRocksDBV1,
 	Default Version
 }{
-	Default:          Version{Major: 6, Minor: 2, Patch: 20},
-	NextPatchVersion: Version{Major: 6, Minor: 2, Patch: 21},
-	NextMajorVersion: Version{Major: 7, Minor: 0, Patch: 0},
-	MinimumVersion:   Version{Major: 6, Minor: 2, Patch: 20},
+	Default:           Version{Major: 6, Minor: 2, Patch: 20},
+	NextPatchVersion:  Version{Major: 6, Minor: 2, Patch: 21},
+	NextMajorVersion:  Version{Major: 7, Minor: 0, Patch: 0},
+	MinimumVersion:    Version{Major: 6, Minor: 2, Patch: 20},
+	SupportsRocksDBV1: Version{Major: 7, Minor: 1, Patch: 0, ReleaseCandidate: 4},
 }
diff --git a/config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml b/config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml
index e3aee542..5f833a19 100644
--- a/config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml
+++ b/config/crd/bases/apps.foundationdb.org_foundationdbclusters.yaml
@@ -9484,6 +9484,7 @@ spec:
                     - memory-2
                     - ssd-redwood-1-experimental
                     - ssd-rocksdb-experimental
+                    - ssd-rocksdb-v1
                     - memory-radixtree-beta
                     - custom
                     maxLength: 100
@@ -12877,6 +12878,7 @@ spec:
                     - memory-2
                     - ssd-redwood-1-experimental
                     - ssd-rocksdb-experimental
+                    - ssd-rocksdb-v1
                     - memory-radixtree-beta
                     - custom
                     maxLength: 100
diff --git a/controllers/cluster_controller.go b/controllers/cluster_controller.go
index e0867a31..7c5e5b65 100644
--- a/controllers/cluster_controller.go
+++ b/controllers/cluster_controller.go
@@ -115,6 +115,15 @@ func (r *FoundationDBClusterReconciler) Reconcile(ctx context.Context, request c
 		return ctrl.Result{}, fmt.Errorf("version %s is not supported", cluster.Spec.Version)
 	}
 
+	storageEngineSupported, err := isStorageEngineSupported(cluster.Spec.Version, cluster.Spec.DatabaseConfiguration.StorageEngine)
+	if err != nil {
+		return ctrl.Result{}, err
+	}
+	if !storageEngineSupported {
+		r.Recorder.Event(cluster, corev1.EventTypeWarning, "Storage engine not supported", fmt.Sprintf("storage engine %s is not supported on version %s", cluster.Spec.DatabaseConfiguration.StorageEngine, cluster.Spec.Version))
+		return ctrl.Result{}, fmt.Errorf("storage engine %s is not supported on version %s", cluster.Spec.DatabaseConfiguration.StorageEngine, cluster.Spec.Version)
+	}
+
 	subReconcilers := []clusterSubReconciler{
 		updateStatus{},
 		updateLockConfiguration{},
@@ -735,3 +744,11 @@ func (r *FoundationDBClusterReconciler) getCoordinatorSet(cluster *fdbv1beta2.Fo
 
 	return adminClient.GetCoordinatorSet()
 }
+
+func isStorageEngineSupported(versionString string, storageEngine fdbv1beta2.StorageEngine) (bool, error) {
+	version, err := fdbv1beta2.ParseFdbVersion(versionString)
+	if err != nil {
+		return false, err
+	}
+	return version.IsStorageEngineSupported(storageEngine), nil
+}


Test patch:
diff --git a/api/v1beta2/foundationdb_version_test.go b/api/v1beta2/foundationdb_version_test.go
index 46c95adc..17d81e54 100644
--- a/api/v1beta2/foundationdb_version_test.go
+++ b/api/v1beta2/foundationdb_version_test.go
@@ -107,6 +107,16 @@ var _ = Describe("[api] FDBVersion", func() {
 			Expect(version.Equal(Version{Major: 6, Minor: 3, Patch: 20})).To(BeFalse())
 			Expect(version.Equal(Version{Major: 6, Minor: 2, Patch: 21})).To(BeFalse())
 		})
+
+		It("should return correct result for IsAtleast", func() {
+			version := Version{Major: 7, Minor: 1, Patch: 0, ReleaseCandidate: 2}
+			Expect(version.IsAtLeast(Version{Major: 7, Minor: 1, Patch: 0})).To(BeFalse())
+			Expect(version.IsAtLeast(Version{Major: 7, Minor: 1, Patch: 0, ReleaseCandidate: 1})).To(BeTrue())
+			Expect(version.IsAtLeast(Version{Major: 7, Minor: 1, Patch: 0, ReleaseCandidate: 3})).To(BeFalse())
+
+			version = Version{Major: 7, Minor: 1, Patch: 0}
+			Expect(version.IsAtLeast(Version{Major: 7, Minor: 1, Patch: 0, ReleaseCandidate: 1})).To(BeTrue())
+		})
 	})
 
 	When("checking if the version has support for non-blocking exclude commands", func() {
diff --git a/controllers/cluster_controller_test.go b/controllers/cluster_controller_test.go
index 9af0eda7..60226965 100644
--- a/controllers/cluster_controller_test.go
+++ b/controllers/cluster_controller_test.go
@@ -2535,6 +2535,122 @@ var _ = Describe("cluster_controller", func() {
 			})
 		})
 
+		When("creating a cluster with RocksDB as storage engine", func() {
+			When("using rocksdb-v1 engine", func() {
+				When("using 6.3.26", func() {
+					BeforeEach(func() {
+						cluster.Spec.DatabaseConfiguration.StorageEngine = "ssd-rocksdb-v1"
+						cluster.Spec.Version = "6.3.26"
+						err := k8sClient.Update(context.TODO(), cluster)
+						Expect(err).NotTo(HaveOccurred())
+						shouldCompleteReconciliation = false
+					})
+					It("generations are not matching", func() {
+						generations, err := reloadClusterGenerations(cluster)
+						Expect(err).NotTo(HaveOccurred())
+						Expect(generations.Reconciled).ToNot(Equal(cluster.ObjectMeta.Generation))
+					})
+				})
+				When("using 7.1.0-rc3", func() {
+					BeforeEach(func() {
+						cluster.Spec.DatabaseConfiguration.StorageEngine = "ssd-rocksdb-v1"
+						cluster.Spec.Version = "7.1.0-rc3"
+						err := k8sClient.Update(context.TODO(), cluster)
+						Expect(err).NotTo(HaveOccurred())
+						shouldCompleteReconciliation = false
+					})
+					It("generations are not matching", func() {
+						generations, err := reloadClusterGenerations(cluster)
+						Expect(err).NotTo(HaveOccurred())
+						Expect(generations.Reconciled).ToNot(Equal(cluster.ObjectMeta.Generation))
+					})
+				})
+				When("using 7.1.0", func() {
+					BeforeEach(func() {
+						cluster.Spec.DatabaseConfiguration.StorageEngine = "ssd-rocksdb-v1"
+						cluster.Spec.Version = "7.1.0"
+						err := k8sClient.Update(context.TODO(), cluster)
+						Expect(err).NotTo(HaveOccurred())
+					})
+					It("generations are matching", func() {
+						generations, err := reloadClusterGenerations(cluster)
+						Expect(err).NotTo(HaveOccurred())
+						Expect(generations.Reconciled).To(Equal(cluster.ObjectMeta.Generation))
+					})
+				})
+				When("using 7.1.0-rc4", func() {
+					BeforeEach(func() {
+						cluster.Spec.DatabaseConfiguration.StorageEngine = "ssd-rocksdb-v1"
+						cluster.Spec.Version = "7.1.0-rc4"
+						err := k8sClient.Update(context.TODO(), cluster)
+						Expect(err).NotTo(HaveOccurred())
+					})
+					It("generations are matching", func() {
+						generations, err := reloadClusterGenerations(cluster)
+						Expect(err).NotTo(HaveOccurred())
+						Expect(generations.Reconciled).To(Equal(cluster.ObjectMeta.Generation))
+					})
+				})
+			})
+
+			When("using ssd-rocksdb-experimental", func() {
+				When("using 7.1.0-rc4", func() {
+					BeforeEach(func() {
+						cluster.Spec.DatabaseConfiguration.StorageEngine = "ssd-rocksdb-experimental"
+						cluster.Spec.Version = "7.1.0-rc4"
+						err := k8sClient.Update(context.TODO(), cluster)
+						Expect(err).NotTo(HaveOccurred())
+						shouldCompleteReconciliation = false
+					})
+					It("generations are not matching", func() {
+						generations, err := reloadClusterGenerations(cluster)
+						Expect(err).NotTo(HaveOccurred())
+						Expect(generations.Reconciled).ToNot(Equal(cluster.ObjectMeta.Generation))
+					})
+				})
+				When("using 7.1.0", func() {
+					BeforeEach(func() {
+						cluster.Spec.DatabaseConfiguration.StorageEngine = "ssd-rocksdb-experimental"
+						cluster.Spec.Version = "7.1.0"
+						err := k8sClient.Update(context.TODO(), cluster)
+						Expect(err).NotTo(HaveOccurred())
+						shouldCompleteReconciliation = false
+					})
+					It("generations are not matching", func() {
+						generations, err := reloadClusterGenerations(cluster)
+						Expect(err).NotTo(HaveOccurred())
+						Expect(generations.Reconciled).ToNot(Equal(cluster.ObjectMeta.Generation))
+					})
+				})
+				When("using 7.1.0-rc3", func() {
+					BeforeEach(func() {
+						cluster.Spec.DatabaseConfiguration.StorageEngine = "ssd-rocksdb-experimental"
+						cluster.Spec.Version = "7.1.0-rc3"
+						err := k8sClient.Update(context.TODO(), cluster)
+						Expect(err).NotTo(HaveOccurred())
+					})
+					It("generations are matching", func() {
+						generations, err := reloadClusterGenerations(cluster)
+						Expect(err).NotTo(HaveOccurred())
+						Expect(generations.Reconciled).To(Equal(cluster.ObjectMeta.Generation))
+					})
+				})
+				When("using 6.3.24", func() {
+					BeforeEach(func() {
+						cluster.Spec.DatabaseConfiguration.StorageEngine = "ssd-rocksdb-experimental"
+						cluster.Spec.Version = "6.3.24"
+						err := k8sClient.Update(context.TODO(), cluster)
+						Expect(err).NotTo(HaveOccurred())
+					})
+					It("generations are matching", func() {
+						generations, err := reloadClusterGenerations(cluster)
+						Expect(err).NotTo(HaveOccurred())
+						Expect(generations.Reconciled).To(Equal(cluster.ObjectMeta.Generation))
+					})
+				})
+			})
+		})
+
 		When("When a process have an incorrect commandline", func() {
 			var adminClient *mockAdminClient