diff --git a/.gitignore b/.gitignore
index e97bb81..5c30c49 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@
out
gen
vendor/
-bin/
\ No newline at end of file
+bin/
+cover.out
diff --git a/Makefile b/Makefile
index f704ff2..645ec7f 100644
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,7 @@ CRD_REF_DOCS ?= $(LOCALBIN)/crd-ref-docs
GCI ?= $(LOCALBIN)/gci
CONTROLLER_TOOLS_VERSION ?= v0.18.0
-CRD_REF_DOCS_VERSION ?= v0.1.0
+CRD_REF_DOCS_VERSION ?= v0.2.0
CHART_DIR ?= charts/qdrant-kubernetes-api
CRDS_DIR ?= crds
@@ -102,4 +102,4 @@ GOBIN=$(LOCALBIN) go install $${package} ;\
mv $(1) $(1)-$(3) ;\
} ;\
ln -sf $(1)-$(3) $(1)
-endef
\ No newline at end of file
+endef
diff --git a/api/v1/qdrantcluster_types.go b/api/v1/qdrantcluster_types.go
index 0640e8e..5efacee 100644
--- a/api/v1/qdrantcluster_types.go
+++ b/api/v1/qdrantcluster_types.go
@@ -55,6 +55,17 @@ const (
ByCountAndSize RebalanceStrategy = "by_count_and_size"
)
+// StorageTier specifies the performance profile for the disk to use.
+// +kubebuilder:validation:Enum=budget;balanced;performance
+type StorageTier string
+
+//goland:noinspection GoUnusedConst
+const (
+ StorageTierBudget StorageTier = "budget"
+ StorageTierBalanced StorageTier = "balanced"
+ StorageTierPerformance StorageTier = "performance"
+)
+
// QdrantClusterSpec defines the desired state of QdrantCluster
// +kubebuilder:pruning:PreserveUnknownFields
type QdrantClusterSpec struct {
@@ -117,6 +128,10 @@ type QdrantClusterSpec struct {
// StorageClassNames specifies the storage class names for db and snapshots.
// +optional
StorageClassNames *StorageClassNames `json:"storageClassNames,omitempty"`
+ // StorageTier specifies the performance tier to use for the disk
+ // +kubebuilder:validation:Enum=budget;balanced;performance
+ // +optional
+ StorageTier *StorageTier `json:"storageTier,omitempty"`
// TopologySpreadConstraints specifies the topology spread constraints for the cluster.
// +optional
TopologySpreadConstraints *[]corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"`
diff --git a/api/v1/qdrantcluster_types_test.go b/api/v1/qdrantcluster_types_test.go
index 8abe5ba..961a46b 100644
--- a/api/v1/qdrantcluster_types_test.go
+++ b/api/v1/qdrantcluster_types_test.go
@@ -1,19 +1,92 @@
package v1
import (
+ "fmt"
"testing"
- "github.com/stretchr/testify/require"
+ "github.com/stretchr/testify/assert"
)
func TestValidate(t *testing.T) {
- r := Resources{}
- err := r.Validate("test")
- require.Error(t, err)
- require.ErrorContains(t, err, "test.CPU error: quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'")
+ testCases := []struct {
+ name string
+ spec QdrantClusterSpec
+ expectedError error
+ }{
+ {
+ name: "Storage size is not specified",
+ spec: QdrantClusterSpec{
+ Resources: Resources{
+ CPU: "100m",
+ Memory: "128Mi",
+ },
+ },
+ expectedError: fmt.Errorf("Spec.Resources.Storage error: quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'"),
+ },
+ {
+ name: "Invalid storage size",
+ spec: QdrantClusterSpec{
+ Resources: Resources{
+ CPU: "100m",
+ Memory: "128Mi",
+ Storage: "foo",
+ },
+ },
+ expectedError: fmt.Errorf("Spec.Resources.Storage error: quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'"),
+ },
+ {
+ name: "CPU amount is not specified",
+ spec: QdrantClusterSpec{
+ Resources: Resources{
+ Memory: "128Mi",
+ Storage: "2Gi",
+ },
+ },
+ expectedError: fmt.Errorf("Spec.Resources.CPU error: quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'"),
+ },
- spec := QdrantClusterSpec{}
- err = spec.Validate()
- require.Error(t, err)
- require.ErrorContains(t, err, "Spec.Resources.CPU error: quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'")
+ {
+ name: "Invalid CPU amount",
+ spec: QdrantClusterSpec{
+ Resources: Resources{
+ CPU: "foo",
+ Memory: "128Mi",
+ Storage: "2Gi",
+ },
+ },
+ expectedError: fmt.Errorf("Spec.Resources.CPU error: quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'"),
+ },
+ {
+ name: "Memory amount is not specified",
+ spec: QdrantClusterSpec{
+ Resources: Resources{
+ CPU: "100m",
+ Storage: "2Gi",
+ },
+ },
+ expectedError: fmt.Errorf("Spec.Resources.Memory error: quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'"),
+ },
+ {
+ name: "Invalid Memory amount",
+ spec: QdrantClusterSpec{
+ Resources: Resources{
+ CPU: "100m",
+ Memory: "foo",
+ Storage: "2Gi",
+ },
+ },
+ expectedError: fmt.Errorf("Spec.Resources.Memory error: quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'"),
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ err := tt.spec.Validate()
+ if tt.expectedError == nil {
+ assert.NoError(t, err)
+ } else {
+ assert.EqualError(t, err, tt.expectedError.Error())
+ }
+ })
+ }
}
diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go
index ad19dfc..978de84 100644
--- a/api/v1/zz_generated.deepcopy.go
+++ b/api/v1/zz_generated.deepcopy.go
@@ -951,6 +951,11 @@ func (in *QdrantClusterSpec) DeepCopyInto(out *QdrantClusterSpec) {
*out = new(StorageClassNames)
(*in).DeepCopyInto(*out)
}
+ if in.StorageTier != nil {
+ in, out := &in.StorageTier, &out.StorageTier
+ *out = new(StorageTier)
+ **out = **in
+ }
if in.TopologySpreadConstraints != nil {
in, out := &in.TopologySpreadConstraints, &out.TopologySpreadConstraints
*out = new([]corev1.TopologySpreadConstraint)
diff --git a/charts/qdrant-kubernetes-api/templates/region-crds/qdrant.io_qdrantclusters.yaml b/charts/qdrant-kubernetes-api/templates/region-crds/qdrant.io_qdrantclusters.yaml
index 6a0453f..f2060f6 100644
--- a/charts/qdrant-kubernetes-api/templates/region-crds/qdrant.io_qdrantclusters.yaml
+++ b/charts/qdrant-kubernetes-api/templates/region-crds/qdrant.io_qdrantclusters.yaml
@@ -870,6 +870,19 @@ spec:
volume.
type: string
type: object
+ storageTier:
+ allOf:
+ - enum:
+ - budget
+ - balanced
+ - performance
+ - enum:
+ - budget
+ - balanced
+ - performance
+ description: StorageTier specifies the performance tier to use for
+ the disk
+ type: string
suspend:
default: false
description: |-
diff --git a/crds/qdrant.io_qdrantclusters.yaml b/crds/qdrant.io_qdrantclusters.yaml
index f6a90f8..908a03c 100644
--- a/crds/qdrant.io_qdrantclusters.yaml
+++ b/crds/qdrant.io_qdrantclusters.yaml
@@ -869,6 +869,19 @@ spec:
volume.
type: string
type: object
+ storageTier:
+ allOf:
+ - enum:
+ - budget
+ - balanced
+ - performance
+ - enum:
+ - budget
+ - balanced
+ - performance
+ description: StorageTier specifies the performance tier to use for
+ the disk
+ type: string
suspend:
default: false
description: |-
diff --git a/docs/api.md b/docs/api.md
index b0c84b2..41cb91d 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -796,6 +796,7 @@ _Appears in:_
| `gpu` _[GPU](#gpu)_ | GPU specifies GPU configuration for the cluster. If this field is not set, no GPU will be used. | | |
| `statefulSet` _[KubernetesStatefulSet](#kubernetesstatefulset)_ | StatefulSet specifies the configuration of the Qdrant Kubernetes StatefulSet. | | |
| `storageClassNames` _[StorageClassNames](#storageclassnames)_ | StorageClassNames specifies the storage class names for db and snapshots. | | |
+| `storageTier` _[StorageTier](#storagetier)_ | StorageTier specifies the performance tier to use for the disk | | Enum: [budget balanced performance]
|
| `topologySpreadConstraints` _[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#topologyspreadconstraint-v1-core)_ | TopologySpreadConstraints specifies the topology spread constraints for the cluster. | | |
| `podDisruptionBudget` _[PodDisruptionBudgetSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#poddisruptionbudgetspec-v1-policy)_ | PodDisruptionBudget specifies the pod disruption budget for the cluster. | | |
| `restartAllPodsConcurrently` _boolean_ | RestartAllPodsConcurrently specifies whether to restart all pods concurrently (also called one-shot-restart).
If enabled, all the pods in the cluster will be restarted concurrently in situations where multiple pods
need to be restarted, like when RestartedAtAnnotationKey is added/updated or the Qdrant version needs to be upgraded.
This helps sharded but not replicated clusters to reduce downtime to a possible minimum during restart.
If unset, the operator is going to restart nodes concurrently if none of the collections if replicated. | | |
@@ -1361,6 +1362,25 @@ _Appears in:_
| `async_scorer` _boolean_ | AsyncScorer enables io_uring when rescoring | | |
+#### StorageTier
+
+_Underlying type:_ _string_
+
+StorageTier specifies the performance profile for the disk to use.
+
+_Validation:_
+- Enum: [budget balanced performance]
+
+_Appears in:_
+- [QdrantClusterSpec](#qdrantclusterspec)
+
+| Field | Description |
+| --- | --- |
+| `budget` | |
+| `balanced` | |
+| `performance` | |
+
+
#### TraefikConfig
diff --git a/go.mod b/go.mod
index a4b328e..7d80c9b 100644
--- a/go.mod
+++ b/go.mod
@@ -14,6 +14,7 @@ require (
k8s.io/apiextensions-apiserver v0.33.3
k8s.io/apimachinery v0.33.3
k8s.io/client-go v0.33.3
+ k8s.io/utils v0.0.0-20241210054802-24370beab758
sigs.k8s.io/controller-runtime v0.21.0
)
@@ -67,7 +68,6 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
- k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect