diff --git a/apis/v1alpha1/common.go b/apis/v1alpha1/common.go index 79489da4..6edccc05 100644 --- a/apis/v1alpha1/common.go +++ b/apis/v1alpha1/common.go @@ -573,7 +573,7 @@ func (in *TLSSpec) GetSecretName() string { // ObjectStorageProviderSpec defines the object storage provider for the cluster. The data will be stored in the storage. type ObjectStorageProviderSpec struct { - // S3 is the S3 storage configuration. + // S3 is the AWS S3 storage configuration. // +optional S3 *S3Storage `json:"s3,omitempty"` @@ -581,10 +581,14 @@ type ObjectStorageProviderSpec struct { // +optional OSS *OSSStorage `json:"oss,omitempty"` - // GCS is the Google GCS storage configuration. + // GCS is the Google cloud storage configuration. // +optional GCS *GCSStorage `json:"gcs,omitempty"` + // AZBlob is the Azure Blob storage configuration. + // +optional + AZBlob *AZBlobStorage `json:"azblob,omitempty"` + // Cache is the cache storage configuration for object storage. // +optional Cache *CacheStorage `json:"cache,omitempty"` @@ -596,6 +600,7 @@ type ObjectStorageProviderAccessor interface { GetS3Storage() *S3Storage GetGCSStorage() *GCSStorage GetOSSStorage() *OSSStorage + GetAZBlobStorage() *AZBlobStorage GetCacheFileStorage() *FileStorage } @@ -629,6 +634,13 @@ func (in *ObjectStorageProviderSpec) GetOSSStorage() *OSSStorage { return nil } +func (in *ObjectStorageProviderSpec) GetAZBlobStorage() *AZBlobStorage { + if in != nil { + return in.AZBlob + } + return nil +} + func (in *ObjectStorageProviderSpec) getSetObjectStorageCount() int { count := 0 if in.S3 != nil { @@ -640,6 +652,9 @@ func (in *ObjectStorageProviderSpec) getSetObjectStorageCount() int { if in.GCS != nil { count++ } + if in.AZBlob != nil { + count++ + } return count } @@ -782,6 +797,41 @@ func (in *GCSStorage) GetRoot() string { return "" } +// AZBlobStorage defines the Azure Blob storage specification. +type AZBlobStorage struct { + // The data will be stored in the container. + // +required + Container string `json:"container"` + + // The secret of storing the credentials of account name and account key. + // The secret should contain keys named `account-name` and `account-key`. + // The secret must be the same namespace with the GreptimeDBCluster resource. + // +optional + SecretName string `json:"secretName,omitempty"` + + // The Blob directory path. + // +required + Root string `json:"root"` + + // The Blob Storage endpoint. + // +optional + Endpoint string `json:"endpoint,omitempty"` +} + +func (in *AZBlobStorage) GetSecretName() string { + if in != nil { + return in.SecretName + } + return "" +} + +func (in *AZBlobStorage) GetRoot() string { + if in != nil { + return in.Root + } + return "" +} + // PrometheusMonitorSpec defines the PodMonitor configuration. type PrometheusMonitorSpec struct { // Enabled indicates whether the PodMonitor is enabled. diff --git a/apis/v1alpha1/constants.go b/apis/v1alpha1/constants.go index 7998c973..74945ecc 100644 --- a/apis/v1alpha1/constants.go +++ b/apis/v1alpha1/constants.go @@ -93,4 +93,10 @@ const ( // ServiceAccountKey is the key for the service account in the secret. ServiceAccountKey = "service-account-key" + + // AccountName is the name for the account in the secret. + AccountName = "account-name" + + // AccountKey is the key for the account in the secret. + AccountKey = "account-key" ) diff --git a/apis/v1alpha1/validate.go b/apis/v1alpha1/validate.go index c3231a55..5aaf9e76 100644 --- a/apis/v1alpha1/validate.go +++ b/apis/v1alpha1/validate.go @@ -98,6 +98,12 @@ func (in *GreptimeDBCluster) Check(ctx context.Context, client client.Client) er } } + if secretName := in.GetObjectStorageProvider().GetAZBlobStorage().GetSecretName(); secretName != "" { + if err := checkAZBlobCredentialsSecret(ctx, client, in.GetNamespace(), secretName); err != nil { + return err + } + } + return nil } @@ -195,6 +201,12 @@ func (in *GreptimeDBStandalone) Check(ctx context.Context, client client.Client) } } + if secretName := in.GetObjectStorageProvider().GetAZBlobStorage().GetSecretName(); secretName != "" { + if err := checkAZBlobCredentialsSecret(ctx, client, in.GetNamespace(), secretName); err != nil { + return err + } + } + return nil } @@ -282,6 +294,10 @@ func checkS3CredentialsSecret(ctx context.Context, client client.Client, namespa return checkSecretData(ctx, client, namespace, name, []string{AccessKeyIDSecretKey, SecretAccessKeySecretKey}) } +func checkAZBlobCredentialsSecret(ctx context.Context, client client.Client, namespace, name string) error { + return checkSecretData(ctx, client, namespace, name, []string{AccountName, AccountKey}) +} + // checkPodMonitorExists checks if the PodMonitor CRD exists. func checkPodMonitorExists(ctx context.Context, client client.Client) error { const ( diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index a374937c..f61e5608 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -24,6 +24,21 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AZBlobStorage) DeepCopyInto(out *AZBlobStorage) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AZBlobStorage. +func (in *AZBlobStorage) DeepCopy() *AZBlobStorage { + if in == nil { + return nil + } + out := new(AZBlobStorage) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CacheStorage) DeepCopyInto(out *CacheStorage) { *out = *in @@ -880,6 +895,11 @@ func (in *ObjectStorageProviderSpec) DeepCopyInto(out *ObjectStorageProviderSpec *out = new(GCSStorage) **out = **in } + if in.AZBlob != nil { + in, out := &in.AZBlob, &out.AZBlob + *out = new(AZBlobStorage) + **out = **in + } if in.Cache != nil { in, out := &in.Cache, &out.Cache *out = new(CacheStorage) diff --git a/config/crd/resources/greptime.io_greptimedbclusters.yaml b/config/crd/resources/greptime.io_greptimedbclusters.yaml index 4c0bc286..06a804f4 100644 --- a/config/crd/resources/greptime.io_greptimedbclusters.yaml +++ b/config/crd/resources/greptime.io_greptimedbclusters.yaml @@ -18318,6 +18318,20 @@ spec: type: integer objectStorage: properties: + azblob: + properties: + container: + type: string + endpoint: + type: string + root: + type: string + secretName: + type: string + required: + - container + - root + type: object cache: properties: cacheCapacity: @@ -18528,6 +18542,20 @@ spec: type: integer objectStorage: properties: + azblob: + properties: + container: + type: string + endpoint: + type: string + root: + type: string + secretName: + type: string + required: + - container + - root + type: object cache: properties: cacheCapacity: diff --git a/config/crd/resources/greptime.io_greptimedbstandalones.yaml b/config/crd/resources/greptime.io_greptimedbstandalones.yaml index 39fdc736..b88cd30a 100644 --- a/config/crd/resources/greptime.io_greptimedbstandalones.yaml +++ b/config/crd/resources/greptime.io_greptimedbstandalones.yaml @@ -3082,6 +3082,20 @@ spec: type: integer objectStorage: properties: + azblob: + properties: + container: + type: string + endpoint: + type: string + root: + type: string + secretName: + type: string + required: + - container + - root + type: object cache: properties: cacheCapacity: diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index 8fdb0d8e..821e1887 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -15,6 +15,25 @@ +#### AZBlobStorage + + + +AZBlobStorage defines the Azure Blob storage specification. + + + +_Appears in:_ +- [ObjectStorageProviderSpec](#objectstorageproviderspec) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `container` _string_ | The data will be stored in the container. | | | +| `secretName` _string_ | The secret of storing the credentials of account name and account key.
The secret should contain keys named `account-name` and `account-key`.
The secret must be the same namespace with the GreptimeDBCluster resource. | | | +| `root` _string_ | The Blob directory path. | | | +| `endpoint` _string_ | The Blob Storage endpoint. | | | + + #### CacheStorage @@ -691,9 +710,10 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `s3` _[S3Storage](#s3storage)_ | S3 is the S3 storage configuration. | | | +| `s3` _[S3Storage](#s3storage)_ | S3 is the AWS S3 storage configuration. | | | | `oss` _[OSSStorage](#ossstorage)_ | OSS is the Aliyun OSS storage configuration. | | | -| `gcs` _[GCSStorage](#gcsstorage)_ | GCS is the Google GCS storage configuration. | | | +| `gcs` _[GCSStorage](#gcsstorage)_ | GCS is the Google cloud storage configuration. | | | +| `azblob` _[AZBlobStorage](#azblobstorage)_ | AZBlob is the Azure Blob storage configuration. | | | | `cache` _[CacheStorage](#cachestorage)_ | Cache is the cache storage configuration for object storage. | | | diff --git a/examples/README.md b/examples/README.md index ecf21848..90c95a23 100644 --- a/examples/README.md +++ b/examples/README.md @@ -8,6 +8,7 @@ The following examples suppose that you have installed the etcd cluster in the ` - [S3](./cluster/s3/cluster.yaml): Create a GreptimeDB cluster with S3 storage. - [GCS](./cluster/gcs/cluster.yaml): Create a GreptimeDB cluster with Google GCS storage. - [OSS](./cluster/oss/cluster.yaml): Create a GreptimeDB cluster with Aliyun OSS storage. +- [AZBlob](./cluster/azblob/cluster.yaml): Create a GreptimeDB cluster with Azure Blob storage. - [Flownode](./cluster/flownode/cluster.yaml): Create a GreptimeDB cluster with `flownode` enabled. By adding the `flownode` configuration, you can use [continuous aggregation](https://docs.greptime.com/user-guide/continuous-aggregation/overview) in the GreptimeDB cluster. - [TLS Service](./cluster/tls-service/cluster.yaml): Create a GreptimeDB cluster with TLS service. - [Prometheus Monitoring](./cluster/prometheus-monitor/cluster.yaml): Create a GreptimeDB cluster with Prometheus monitoring. Please ensure you have already installed prometheus-operator and created a Prometheus instance with the label `release=prometheus`. @@ -24,3 +25,4 @@ The following examples suppose that you have installed the etcd cluster in the ` - [S3](./standalone/s3/standalone.yaml): Create a GreptimeDB standalone with S3 storage. - [GCS](./standalone/gcs/standalone.yaml): Create a GreptimeDB standalone with Google GCS storage. - [OSS](./standalone/oss/standalone.yaml): Create a GreptimeDB standalone with Aliyun OSS storage. +- [AZBlob](./standalone/azblob/standalone.yaml): Create a GreptimeDB standalone with Azure Blob storage. diff --git a/examples/cluster/azblob/azblob-credentials.yaml b/examples/cluster/azblob/azblob-credentials.yaml new file mode 100644 index 00000000..00b7d8ea --- /dev/null +++ b/examples/cluster/azblob/azblob-credentials.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: azblob-credentials +stringData: + account-name: "your-blob-account-name" + account-key: "your-blob-account-key" diff --git a/examples/cluster/azblob/cluster.yaml b/examples/cluster/azblob/cluster.yaml new file mode 100644 index 00000000..8bf92f25 --- /dev/null +++ b/examples/cluster/azblob/cluster.yaml @@ -0,0 +1,22 @@ +apiVersion: greptime.io/v1alpha1 +kind: GreptimeDBCluster +metadata: + name: cluster-with-blob +spec: + base: + main: + image: greptime/greptimedb:latest + frontend: + replicas: 1 + meta: + replicas: 1 + etcdEndpoints: + - "etcd.etcd-cluster.svc.cluster.local:2379" + datanode: + replicas: 1 + objectStorage: + azblob: + container: "greptimedb" + secretName: "azblob-credentials" + endpoint: "https://.blob.core.windows.net" + root: "cluster-with-blob-data" diff --git a/examples/cluster/oss/oss-credentials.yaml b/examples/cluster/oss/oss-credentials.yaml index fa3b893a..e529480c 100644 --- a/examples/cluster/oss/oss-credentials.yaml +++ b/examples/cluster/oss/oss-credentials.yaml @@ -3,6 +3,6 @@ kind: Secret type: Opaque metadata: name: oss-credentials -data: +stringData: access-key-id: "your-access-key-id" secret-access-key: "your-secret-access-key" diff --git a/examples/cluster/s3/s3-credentials.yaml b/examples/cluster/s3/s3-credentials.yaml index 06af313b..eafb9195 100644 --- a/examples/cluster/s3/s3-credentials.yaml +++ b/examples/cluster/s3/s3-credentials.yaml @@ -3,6 +3,6 @@ kind: Secret type: Opaque metadata: name: s3-credentials -data: - access-key-id: "your-access-key-id-in-base64" - secret-access-key: "your-secret-access-key-in-base64" +stringData: + access-key-id: "your-access-key-id" + secret-access-key: "your-secret-access-key" diff --git a/examples/standalone/azblob/azblob-credentials.yaml b/examples/standalone/azblob/azblob-credentials.yaml new file mode 100644 index 00000000..00b7d8ea --- /dev/null +++ b/examples/standalone/azblob/azblob-credentials.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: azblob-credentials +stringData: + account-name: "your-blob-account-name" + account-key: "your-blob-account-key" diff --git a/examples/standalone/azblob/standalone.yaml b/examples/standalone/azblob/standalone.yaml new file mode 100644 index 00000000..554c84ed --- /dev/null +++ b/examples/standalone/azblob/standalone.yaml @@ -0,0 +1,14 @@ +apiVersion: greptime.io/v1alpha1 +kind: GreptimeDBStandalone +metadata: + name: standalone-with-blob +spec: + base: + main: + image: greptime/greptimedb:latest + objectStorage: + azblob: + container: "greptimedb" + secretName: "azblob-credentials" + endpoint: "https://.blob.core.windows.net" + root: "standalone-with-blob" diff --git a/examples/standalone/oss/oss-credentials.yaml b/examples/standalone/oss/oss-credentials.yaml index 9f8fc49d..c0fdb180 100644 --- a/examples/standalone/oss/oss-credentials.yaml +++ b/examples/standalone/oss/oss-credentials.yaml @@ -3,6 +3,6 @@ kind: Secret type: Opaque metadata: name: oss-credentials -data: +stringData: access-key-id: "your-access-key-id-in-base64" secret-access-key: "your-secret-access-key-in-base64" diff --git a/examples/standalone/s3/s3-credentials.yaml b/examples/standalone/s3/s3-credentials.yaml index 06af313b..eafb9195 100644 --- a/examples/standalone/s3/s3-credentials.yaml +++ b/examples/standalone/s3/s3-credentials.yaml @@ -3,6 +3,6 @@ kind: Secret type: Opaque metadata: name: s3-credentials -data: - access-key-id: "your-access-key-id-in-base64" - secret-access-key: "your-secret-access-key-in-base64" +stringData: + access-key-id: "your-access-key-id" + secret-access-key: "your-secret-access-key" diff --git a/manifests/bundle.yaml b/manifests/bundle.yaml index a1a8742a..189b64e6 100644 --- a/manifests/bundle.yaml +++ b/manifests/bundle.yaml @@ -18324,6 +18324,20 @@ spec: type: integer objectStorage: properties: + azblob: + properties: + container: + type: string + endpoint: + type: string + root: + type: string + secretName: + type: string + required: + - container + - root + type: object cache: properties: cacheCapacity: @@ -18534,6 +18548,20 @@ spec: type: integer objectStorage: properties: + azblob: + properties: + container: + type: string + endpoint: + type: string + root: + type: string + secretName: + type: string + required: + - container + - root + type: object cache: properties: cacheCapacity: @@ -21843,6 +21871,20 @@ spec: type: integer objectStorage: properties: + azblob: + properties: + container: + type: string + endpoint: + type: string + root: + type: string + secretName: + type: string + required: + - container + - root + type: object cache: properties: cacheCapacity: diff --git a/manifests/crds.yaml b/manifests/crds.yaml index 76a6573a..ca24ee65 100644 --- a/manifests/crds.yaml +++ b/manifests/crds.yaml @@ -18317,6 +18317,20 @@ spec: type: integer objectStorage: properties: + azblob: + properties: + container: + type: string + endpoint: + type: string + root: + type: string + secretName: + type: string + required: + - container + - root + type: object cache: properties: cacheCapacity: @@ -18527,6 +18541,20 @@ spec: type: integer objectStorage: properties: + azblob: + properties: + container: + type: string + endpoint: + type: string + root: + type: string + secretName: + type: string + required: + - container + - root + type: object cache: properties: cacheCapacity: @@ -21836,6 +21864,20 @@ spec: type: integer objectStorage: properties: + azblob: + properties: + container: + type: string + endpoint: + type: string + root: + type: string + secretName: + type: string + required: + - container + - root + type: object cache: properties: cacheCapacity: diff --git a/pkg/dbconfig/common.go b/pkg/dbconfig/common.go index 60952f63..d373d59e 100644 --- a/pkg/dbconfig/common.go +++ b/pkg/dbconfig/common.go @@ -40,6 +40,9 @@ type StorageConfig struct { StorageEndpoint *string `tomlmapping:"storage.endpoint"` StorageScope *string `tomlmapping:"storage.scope"` StorageCredential *string `tomlmapping:"storage.credential"` + Container *string `tomlmapping:"storage.container"` + AccountName *string `tomlmapping:"storage.account_name"` + AccountKey *string `tomlmapping:"storage.account_key"` } // ConfigureObjectStorage configures the storage config by the given object storage provider accessor. @@ -56,6 +59,10 @@ func (c *StorageConfig) ConfigureObjectStorage(namespace string, accessor v1alph if err := c.configureGCS(namespace, gcs); err != nil { return err } + } else if blob := accessor.GetAZBlobStorage(); blob != nil { + if err := c.configureAZBlob(namespace, blob); err != nil { + return err + } } return nil @@ -133,6 +140,28 @@ func (c *StorageConfig) configureGCS(namespace string, gcs *v1alpha1.GCSStorage) return nil } +func (c *StorageConfig) configureAZBlob(namespace string, azblob *v1alpha1.AZBlobStorage) error { + if azblob == nil { + return nil + } + + c.StorageType = pointer.String("Azblob") + c.Container = pointer.String(azblob.Container) + c.StorageRoot = pointer.String(azblob.Root) + c.StorageEndpoint = pointer.String(azblob.Endpoint) + + if azblob.SecretName != "" { + data, err := k8sutil.GetSecretsData(namespace, azblob.SecretName, []string{v1alpha1.AccountName, v1alpha1.AccountKey}) + if err != nil { + return err + } + c.AccountName = pointer.String(string(data[0])) + c.AccountKey = pointer.String(string(data[1])) + } + + return nil +} + // WALConfig is the configuration for the WAL. type WALConfig struct { // The wal file directory.