From 04ac038752a4960d81d5fd018a7154089e24664c Mon Sep 17 00:00:00 2001
From: ajanikow <12255597+ajanikow@users.noreply.github.com>
Date: Wed, 17 Dec 2025 09:24:12 +0000
Subject: [PATCH 1/8] [Feature] [Platform] Azure Storage Integration
---
.golangci.yaml | 2 +
CHANGELOG.md | 3 +-
docs/api/ArangoPlatformStorage.V1Beta1.md | 59 +++++
docs/cli/arangodb_operator_integration.md | 214 +++++++++---------
go.mod | 11 +-
go.sum | 17 ++
integrations/storage/v2/configuration.go | 13 +-
integrations/storage/v2/object.go | 38 ++++
.../storage/v2/shared/abs/configuration.go | 59 +++++
integrations/storage/v2/shared/abs/delete.go | 53 +++++
integrations/storage/v2/shared/abs/head.go | 50 ++++
integrations/storage/v2/shared/abs/init.go | 40 ++++
integrations/storage/v2/shared/abs/io.go | 54 +++++
integrations/storage/v2/shared/abs/io_test.go | 115 ++++++++++
integrations/storage/v2/shared/abs/list.go | 90 ++++++++
integrations/storage/v2/shared/abs/read.go | 112 +++++++++
integrations/storage/v2/shared/abs/write.go | 118 ++++++++++
integrations/storage/v2/suite_azure_test.go | 113 +++++++++
.../platform/v1beta1/storage_spec_backend.go | 24 +-
.../v1beta1/storage_spec_backend_abs.go | 129 +++++++++++
.../platform/v1beta1/zz_generated.deepcopy.go | 51 +++++
.../platform-storage.schema.generated.yaml | 36 +++
.../sidecar/integration.storage.v2.go | 37 +++
pkg/integrations/storage_v2.go | 10 +
pkg/util/azure/config.go | 53 +++++
pkg/util/azure/provider.go | 109 +++++++++
pkg/util/constants/constants.go | 3 +
pkg/util/env_t.go | 35 +++
pkg/util/refs.go | 12 +
pkg/util/tests/azure.go | 76 +++++++
30 files changed, 1624 insertions(+), 112 deletions(-)
create mode 100644 integrations/storage/v2/shared/abs/configuration.go
create mode 100644 integrations/storage/v2/shared/abs/delete.go
create mode 100644 integrations/storage/v2/shared/abs/head.go
create mode 100644 integrations/storage/v2/shared/abs/init.go
create mode 100644 integrations/storage/v2/shared/abs/io.go
create mode 100644 integrations/storage/v2/shared/abs/io_test.go
create mode 100644 integrations/storage/v2/shared/abs/list.go
create mode 100644 integrations/storage/v2/shared/abs/read.go
create mode 100644 integrations/storage/v2/shared/abs/write.go
create mode 100644 integrations/storage/v2/suite_azure_test.go
create mode 100644 pkg/apis/platform/v1beta1/storage_spec_backend_abs.go
create mode 100644 pkg/util/azure/config.go
create mode 100644 pkg/util/azure/provider.go
create mode 100644 pkg/util/env_t.go
create mode 100644 pkg/util/tests/azure.go
diff --git a/.golangci.yaml b/.golangci.yaml
index ca3f9dd44..ddec61b7e 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -95,6 +95,8 @@ linters-settings:
pkg: github.com/arangodb/kube-arangodb/integrations/storage/v2/shared
- alias: pbImplStorageV2SharedGCS
pkg: github.com/arangodb/kube-arangodb/integrations/storage/v2/shared/gcs
+ - alias: pbImplStorageV2SharedAzureBlobStorage
+ pkg: github.com/arangodb/kube-arangodb/integrations/storage/v2/shared/abs
- alias: pbImplStorageV2SharedS3
pkg: github.com/arangodb/kube-arangodb/integrations/storage/v2/shared/s3
- alias: pbStorageV2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fdc197988..c50fcab6c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
# Change Log
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
+- (Feature) Add scrape annotations for ArangoD pods
+- (Feature) (Platform) Azure Storage Integration
## [1.3.3](https://github.com/arangodb/kube-arangodb/tree/1.3.3) (2025-12-02)
- (Bugfix) (Platform) Fix Container Resource Adjustments
@@ -9,7 +11,6 @@
- (Feature) (Platform) Dump CLI switch to Services
- (Feature) (Platform) Fix ImagePullSecrets Merge
- (Feature) (Platform) Update Failed Releases
-- (Feature) Add scrape annotations for ArangoD pods
## [1.3.2](https://github.com/arangodb/kube-arangodb/tree/1.3.2) (2025-11-20)
- (Bugfix) (Platform) Increase memory limit for Inventory
diff --git a/docs/api/ArangoPlatformStorage.V1Beta1.md b/docs/api/ArangoPlatformStorage.V1Beta1.md
index 40241e3fa..095bb57ac 100644
--- a/docs/api/ArangoPlatformStorage.V1Beta1.md
+++ b/docs/api/ArangoPlatformStorage.V1Beta1.md
@@ -8,6 +8,65 @@ title: ArangoPlatformStorage V1Beta1
## Spec
+### .spec.backend.azureBlobStorage.accountName
+
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go#L39)
+
+This field is **required**
+
+AccountName specifies the Azure Storage AccountName
+used in format https://.blob.core.windows.net/
+
+***
+
+### .spec.backend.azureBlobStorage.bucketName
+
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go#L47)
+
+This field is **required**
+
+BucketName specifies the name of the bucket
+
+***
+
+### .spec.backend.azureBlobStorage.bucketPath
+
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go#L51)
+
+BucketPath specifies the Prefix within the bucket
+
+***
+
+### .spec.backend.azureBlobStorage.credentialsSecret.name
+
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/shared/v1/object.go#L53)
+
+This field is **required**
+
+Name of the object
+
+***
+
+### .spec.backend.azureBlobStorage.endpoint
+
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go#L43)
+
+This field is **required**
+
+Endpoint specifies the Azure Storage custom endpoint
+
+***
+
+### .spec.backend.azureBlobStorage.tenantID
+
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go#L34)
+
+This field is **required**
+
+TenantID specifies the Azure TenantID
+
+***
+
### .spec.backend.gcs.bucketName
Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/platform/v1beta1/storage_spec_backend_gcs.go#L35)
diff --git a/docs/cli/arangodb_operator_integration.md b/docs/cli/arangodb_operator_integration.md
index db5492d9d..cd5b22023 100644
--- a/docs/cli/arangodb_operator_integration.md
+++ b/docs/cli/arangodb_operator_integration.md
@@ -18,109 +18,117 @@ Available Commands:
help Help about any command
Flags:
- --database.endpoint string Endpoint of ArangoDB (Env: DATABASE_ENDPOINT) (default "localhost")
- --database.port int Port of ArangoDB (Env: DATABASE_PORT) (default 8529)
- --database.proto string Proto of the ArangoDB endpoint (Env: DATABASE_PROTO) (default "http")
- --database.rf int ArangoDB ReplicationFactor (Env: DATABASE_RF) (default 1)
- --database.wc int ArangoDB WriteConcern (Env: DATABASE_WC) (default 1)
- --health.address string Address to expose health service (Env: HEALTH_ADDRESS) (default "0.0.0.0:9091")
- --health.auth.token string Token for health service (when auth service is token) (Env: HEALTH_AUTH_TOKEN)
- --health.auth.type string Auth type for health service (Env: HEALTH_AUTH_TYPE) (default "None")
- --health.shutdown.enabled Determines if shutdown service should be enabled and exposed (Env: HEALTH_SHUTDOWN_ENABLED) (default true)
- --health.tls.keyfile string Path to the keyfile (Env: HEALTH_TLS_KEYFILE)
- -h, --help help for arangodb_operator_integration
- --integration.authentication.v1 Enable AuthenticationV1 Integration Service (Env: INTEGRATION_AUTHENTICATION_V1)
- --integration.authentication.v1.enabled Defines if Authentication is enabled (Env: INTEGRATION_AUTHENTICATION_V1_ENABLED) (default true)
- --integration.authentication.v1.external Defines if External access to service authentication.v1 is enabled (Env: INTEGRATION_AUTHENTICATION_V1_EXTERNAL)
- --integration.authentication.v1.internal Defines if Internal access to service authentication.v1 is enabled (Env: INTEGRATION_AUTHENTICATION_V1_INTERNAL) (default true)
- --integration.authentication.v1.path string Path to the JWT Folder (Env: INTEGRATION_AUTHENTICATION_V1_PATH)
- --integration.authentication.v1.token.allowed strings Allowed users for the Token (Env: INTEGRATION_AUTHENTICATION_V1_TOKEN_ALLOWED)
- --integration.authentication.v1.token.max-size uint16 Max Token max size in bytes (Env: INTEGRATION_AUTHENTICATION_V1_TOKEN_MAX_SIZE) (default 64)
- --integration.authentication.v1.token.ttl.default duration Default Token TTL (Env: INTEGRATION_AUTHENTICATION_V1_TOKEN_TTL_DEFAULT) (default 1h0m0s)
- --integration.authentication.v1.token.ttl.max duration Max Token TTL (Env: INTEGRATION_AUTHENTICATION_V1_TOKEN_TTL_MAX) (default 1h0m0s)
- --integration.authentication.v1.token.ttl.min duration Min Token TTL (Env: INTEGRATION_AUTHENTICATION_V1_TOKEN_TTL_MIN) (default 1m0s)
- --integration.authentication.v1.token.user string Default user of the Token (Env: INTEGRATION_AUTHENTICATION_V1_TOKEN_USER) (default "root")
- --integration.authentication.v1.ttl duration TTL of the JWT cache (Env: INTEGRATION_AUTHENTICATION_V1_TTL) (default 15s)
- --integration.authorization.v0 Enable AuthorizationV0 Integration Service (Env: INTEGRATION_AUTHORIZATION_V0)
- --integration.authorization.v0.external Defines if External access to service authorization.v0 is enabled (Env: INTEGRATION_AUTHORIZATION_V0_EXTERNAL)
- --integration.authorization.v0.internal Defines if Internal access to service authorization.v0 is enabled (Env: INTEGRATION_AUTHORIZATION_V0_INTERNAL) (default true)
- --integration.config.v1 Enable ConfigV1 Integration Service (Env: INTEGRATION_CONFIG_V1)
- --integration.config.v1.external Defines if External access to service config.v1 is enabled (Env: INTEGRATION_CONFIG_V1_EXTERNAL)
- --integration.config.v1.internal Defines if Internal access to service config.v1 is enabled (Env: INTEGRATION_CONFIG_V1_INTERNAL) (default true)
- --integration.config.v1.module strings Module in the reference = (Env: INTEGRATION_CONFIG_V1_MODULE)
- --integration.envoy.auth.v3 Enable EnvoyAuthV3 Integration Service (Env: INTEGRATION_ENVOY_AUTH_V3)
- --integration.envoy.auth.v3.auth.enabled Defines if SSO Auth extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_AUTH_ENABLED)
- --integration.envoy.auth.v3.auth.path string Path of the config file (Env: INTEGRATION_ENVOY_AUTH_V3_AUTH_PATH)
- --integration.envoy.auth.v3.auth.type string Defines type of the authentication (Env: INTEGRATION_ENVOY_AUTH_V3_AUTH_TYPE) (default "OpenID")
- --integration.envoy.auth.v3.enabled Defines if Auth extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_ENABLED) (default true)
- --integration.envoy.auth.v3.extensions.cookie.jwt Defines if Cookie JWT extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_EXTENSIONS_COOKIE_JWT) (default true)
- --integration.envoy.auth.v3.extensions.jwt Defines if JWT extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_EXTENSIONS_JWT) (default true)
- --integration.envoy.auth.v3.extensions.users.create Defines if UserCreation extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_EXTENSIONS_USERS_CREATE)
- --integration.envoy.auth.v3.external Defines if External access to service envoy.auth.v3 is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_EXTERNAL)
- --integration.envoy.auth.v3.internal Defines if Internal access to service envoy.auth.v3 is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_INTERNAL) (default true)
- --integration.events.v1 Enable EventsV1 Integration Service (Env: INTEGRATION_EVENTS_V1)
- --integration.events.v1.async Enables async injection of the events (Env: INTEGRATION_EVENTS_V1_ASYNC) (default true)
- --integration.events.v1.async.retry.delay duration Delay of the retries (Env: INTEGRATION_EVENTS_V1_ASYNC_RETRY_DELAY) (default 1s)
- --integration.events.v1.async.retry.timeout duration Timeout for the event injection (Env: INTEGRATION_EVENTS_V1_ASYNC_RETRY_TIMEOUT) (default 1m0s)
- --integration.events.v1.async.size int Size of the async queue (Env: INTEGRATION_EVENTS_V1_ASYNC_SIZE) (default 16)
- --integration.events.v1.external Defines if External access to service events.v1 is enabled (Env: INTEGRATION_EVENTS_V1_EXTERNAL)
- --integration.events.v1.internal Defines if Internal access to service events.v1 is enabled (Env: INTEGRATION_EVENTS_V1_INTERNAL) (default true)
- --integration.meta.v1 Enable MetaV1 Integration Service (Env: INTEGRATION_META_V1)
- --integration.meta.v1.external Defines if External access to service meta.v1 is enabled (Env: INTEGRATION_META_V1_EXTERNAL)
- --integration.meta.v1.internal Defines if Internal access to service meta.v1 is enabled (Env: INTEGRATION_META_V1_INTERNAL) (default true)
- --integration.meta.v1.prefix string Meta Key Prefix (Env: INTEGRATION_META_V1_PREFIX)
- --integration.meta.v1.ttl duration Cache Object TTL (Env: INTEGRATION_META_V1_TTL)
- --integration.scheduler.v1 SchedulerV1 Integration (Env: INTEGRATION_SCHEDULER_V1)
- --integration.scheduler.v1.external Defines if External access to service scheduler.v1 is enabled (Env: INTEGRATION_SCHEDULER_V1_EXTERNAL)
- --integration.scheduler.v1.internal Defines if Internal access to service scheduler.v1 is enabled (Env: INTEGRATION_SCHEDULER_V1_INTERNAL) (default true)
- --integration.scheduler.v1.namespace string Kubernetes Namespace (Env: INTEGRATION_SCHEDULER_V1_NAMESPACE) (default "default")
- --integration.scheduler.v1.verify-access Verify the CRD Access (Env: INTEGRATION_SCHEDULER_V1_VERIFY_ACCESS) (default true)
- --integration.scheduler.v2 SchedulerV2 Integration (Env: INTEGRATION_SCHEDULER_V2)
- --integration.scheduler.v2.deployment string ArangoDeployment Name (Env: INTEGRATION_SCHEDULER_V2_DEPLOYMENT)
- --integration.scheduler.v2.driver string Helm Driver (Env: INTEGRATION_SCHEDULER_V2_DRIVER) (default "secret")
- --integration.scheduler.v2.external Defines if External access to service scheduler.v2 is enabled (Env: INTEGRATION_SCHEDULER_V2_EXTERNAL)
- --integration.scheduler.v2.internal Defines if Internal access to service scheduler.v2 is enabled (Env: INTEGRATION_SCHEDULER_V2_INTERNAL) (default true)
- --integration.scheduler.v2.namespace string Kubernetes Namespace (Env: INTEGRATION_SCHEDULER_V2_NAMESPACE) (default "default")
- --integration.shutdown.v1 ShutdownV1 Handler (Env: INTEGRATION_SHUTDOWN_V1)
- --integration.shutdown.v1.debug.enabled Defines if debug extension is enabled (Env: INTEGRATION_SHUTDOWN_V1_DEBUG_ENABLED)
- --integration.shutdown.v1.debug.path string Path of the Debug Directory (Env: INTEGRATION_SHUTDOWN_V1_DEBUG_PATH) (default "/debug")
- --integration.shutdown.v1.debug.timeout duration Timeout of the Debug action (Env: INTEGRATION_SHUTDOWN_V1_DEBUG_TIMEOUT) (default 1m0s)
- --integration.shutdown.v1.external Defines if External access to service shutdown.v1 is enabled (Env: INTEGRATION_SHUTDOWN_V1_EXTERNAL)
- --integration.shutdown.v1.internal Defines if Internal access to service shutdown.v1 is enabled (Env: INTEGRATION_SHUTDOWN_V1_INTERNAL) (default true)
- --integration.storage.v2 StorageBucket V2 Integration (Env: INTEGRATION_STORAGE_V2)
- --integration.storage.v2.external Defines if External access to service storage.v2 is enabled (Env: INTEGRATION_STORAGE_V2_EXTERNAL)
- --integration.storage.v2.gcs.bucket.name string Bucket name (Env: INTEGRATION_STORAGE_V2_GCS_BUCKET_NAME)
- --integration.storage.v2.gcs.bucket.prefix string Bucket Prefix (Env: INTEGRATION_STORAGE_V2_GCS_BUCKET_PREFIX)
- --integration.storage.v2.gcs.project-id string GCP Project ID (Env: INTEGRATION_STORAGE_V2_GCS_PROJECT_ID)
- --integration.storage.v2.gcs.provider.sa.file string Path to the file with ServiceAccount JSON (Env: INTEGRATION_STORAGE_V2_GCS_PROVIDER_SA_FILE)
- --integration.storage.v2.gcs.provider.sa.json string ServiceAccount JSON (Env: INTEGRATION_STORAGE_V2_GCS_PROVIDER_SA_JSON)
- --integration.storage.v2.gcs.provider.type string Type of the provided credentials (Env: INTEGRATION_STORAGE_V2_GCS_PROVIDER_TYPE) (default "serviceAccount")
- --integration.storage.v2.internal Defines if Internal access to service storage.v2 is enabled (Env: INTEGRATION_STORAGE_V2_INTERNAL) (default true)
- --integration.storage.v2.s3.allow-insecure If set to true, the Endpoint certificates won't be checked (Env: INTEGRATION_STORAGE_V2_S3_ALLOW_INSECURE)
- --integration.storage.v2.s3.bucket.name string Bucket name (Env: INTEGRATION_STORAGE_V2_S3_BUCKET_NAME)
- --integration.storage.v2.s3.bucket.prefix string Bucket Prefix (Env: INTEGRATION_STORAGE_V2_S3_BUCKET_PREFIX)
- --integration.storage.v2.s3.ca strings Path to file containing CA certificate to validate endpoint connection (Env: INTEGRATION_STORAGE_V2_S3_CA)
- --integration.storage.v2.s3.disable-ssl If set to true, the SSL won't be used when connecting to Endpoint (Env: INTEGRATION_STORAGE_V2_S3_DISABLE_SSL)
- --integration.storage.v2.s3.endpoint string Endpoint of S3 API implementation (Env: INTEGRATION_STORAGE_V2_S3_ENDPOINT)
- --integration.storage.v2.s3.provider.file.access-key string Path to file containing S3 AccessKey (Env: INTEGRATION_STORAGE_V2_S3_PROVIDER_FILE_ACCESS_KEY)
- --integration.storage.v2.s3.provider.file.secret-key string Path to file containing S3 SecretKey (Env: INTEGRATION_STORAGE_V2_S3_PROVIDER_FILE_SECRET_KEY)
- --integration.storage.v2.s3.provider.type string S3 Credentials Provider type (Env: INTEGRATION_STORAGE_V2_S3_PROVIDER_TYPE) (default "file")
- --integration.storage.v2.s3.region string Region (Env: INTEGRATION_STORAGE_V2_S3_REGION)
- --integration.storage.v2.type string Type of the Storage Integration (Env: INTEGRATION_STORAGE_V2_TYPE) (default "s3")
- --services.address string Address to expose internal services (Env: SERVICES_ADDRESS) (default "127.0.0.1:9092")
- --services.auth.token string Token for internal service (when auth service is token) (Env: SERVICES_AUTH_TOKEN)
- --services.auth.type string Auth type for internal service (Env: SERVICES_AUTH_TYPE) (default "None")
- --services.enabled Defines if internal access is enabled (Env: SERVICES_ENABLED) (default true)
- --services.external.address string Address to expose external services (Env: SERVICES_EXTERNAL_ADDRESS) (default "0.0.0.0:9093")
- --services.external.auth.token string Token for external service (when auth service is token) (Env: SERVICES_EXTERNAL_AUTH_TOKEN)
- --services.external.auth.type string Auth type for external service (Env: SERVICES_EXTERNAL_AUTH_TYPE) (default "None")
- --services.external.enabled Defines if external access is enabled (Env: SERVICES_EXTERNAL_ENABLED)
- --services.external.gateway.address string Address to expose external gateway services (Env: SERVICES_EXTERNAL_GATEWAY_ADDRESS) (default "0.0.0.0:9193")
- --services.external.gateway.enabled Defines if external gateway is enabled (Env: SERVICES_EXTERNAL_GATEWAY_ENABLED)
- --services.external.tls.keyfile string Path to the keyfile (Env: SERVICES_EXTERNAL_TLS_KEYFILE)
- --services.gateway.address string Address to expose internal gateway services (Env: SERVICES_GATEWAY_ADDRESS) (default "127.0.0.1:9192")
- --services.gateway.enabled Defines if internal gateway is enabled (Env: SERVICES_GATEWAY_ENABLED) (default true)
- --services.tls.keyfile string Path to the keyfile (Env: SERVICES_TLS_KEYFILE)
+ --database.endpoint string Endpoint of ArangoDB (Env: DATABASE_ENDPOINT) (default "localhost")
+ --database.port int Port of ArangoDB (Env: DATABASE_PORT) (default 8529)
+ --database.proto string Proto of the ArangoDB endpoint (Env: DATABASE_PROTO) (default "http")
+ --database.rf int ArangoDB ReplicationFactor (Env: DATABASE_RF) (default 1)
+ --database.wc int ArangoDB WriteConcern (Env: DATABASE_WC) (default 1)
+ --health.address string Address to expose health service (Env: HEALTH_ADDRESS) (default "0.0.0.0:9091")
+ --health.auth.token string Token for health service (when auth service is token) (Env: HEALTH_AUTH_TOKEN)
+ --health.auth.type string Auth type for health service (Env: HEALTH_AUTH_TYPE) (default "None")
+ --health.shutdown.enabled Determines if shutdown service should be enabled and exposed (Env: HEALTH_SHUTDOWN_ENABLED) (default true)
+ --health.tls.keyfile string Path to the keyfile (Env: HEALTH_TLS_KEYFILE)
+ -h, --help help for arangodb_operator_integration
+ --integration.authentication.v1 Enable AuthenticationV1 Integration Service (Env: INTEGRATION_AUTHENTICATION_V1)
+ --integration.authentication.v1.enabled Defines if Authentication is enabled (Env: INTEGRATION_AUTHENTICATION_V1_ENABLED) (default true)
+ --integration.authentication.v1.external Defines if External access to service authentication.v1 is enabled (Env: INTEGRATION_AUTHENTICATION_V1_EXTERNAL)
+ --integration.authentication.v1.internal Defines if Internal access to service authentication.v1 is enabled (Env: INTEGRATION_AUTHENTICATION_V1_INTERNAL) (default true)
+ --integration.authentication.v1.path string Path to the JWT Folder (Env: INTEGRATION_AUTHENTICATION_V1_PATH)
+ --integration.authentication.v1.token.allowed strings Allowed users for the Token (Env: INTEGRATION_AUTHENTICATION_V1_TOKEN_ALLOWED)
+ --integration.authentication.v1.token.max-size uint16 Max Token max size in bytes (Env: INTEGRATION_AUTHENTICATION_V1_TOKEN_MAX_SIZE) (default 64)
+ --integration.authentication.v1.token.ttl.default duration Default Token TTL (Env: INTEGRATION_AUTHENTICATION_V1_TOKEN_TTL_DEFAULT) (default 1h0m0s)
+ --integration.authentication.v1.token.ttl.max duration Max Token TTL (Env: INTEGRATION_AUTHENTICATION_V1_TOKEN_TTL_MAX) (default 1h0m0s)
+ --integration.authentication.v1.token.ttl.min duration Min Token TTL (Env: INTEGRATION_AUTHENTICATION_V1_TOKEN_TTL_MIN) (default 1m0s)
+ --integration.authentication.v1.token.user string Default user of the Token (Env: INTEGRATION_AUTHENTICATION_V1_TOKEN_USER) (default "root")
+ --integration.authentication.v1.ttl duration TTL of the JWT cache (Env: INTEGRATION_AUTHENTICATION_V1_TTL) (default 15s)
+ --integration.authorization.v0 Enable AuthorizationV0 Integration Service (Env: INTEGRATION_AUTHORIZATION_V0)
+ --integration.authorization.v0.external Defines if External access to service authorization.v0 is enabled (Env: INTEGRATION_AUTHORIZATION_V0_EXTERNAL)
+ --integration.authorization.v0.internal Defines if Internal access to service authorization.v0 is enabled (Env: INTEGRATION_AUTHORIZATION_V0_INTERNAL) (default true)
+ --integration.config.v1 Enable ConfigV1 Integration Service (Env: INTEGRATION_CONFIG_V1)
+ --integration.config.v1.external Defines if External access to service config.v1 is enabled (Env: INTEGRATION_CONFIG_V1_EXTERNAL)
+ --integration.config.v1.internal Defines if Internal access to service config.v1 is enabled (Env: INTEGRATION_CONFIG_V1_INTERNAL) (default true)
+ --integration.config.v1.module strings Module in the reference = (Env: INTEGRATION_CONFIG_V1_MODULE)
+ --integration.envoy.auth.v3 Enable EnvoyAuthV3 Integration Service (Env: INTEGRATION_ENVOY_AUTH_V3)
+ --integration.envoy.auth.v3.auth.enabled Defines if SSO Auth extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_AUTH_ENABLED)
+ --integration.envoy.auth.v3.auth.path string Path of the config file (Env: INTEGRATION_ENVOY_AUTH_V3_AUTH_PATH)
+ --integration.envoy.auth.v3.auth.type string Defines type of the authentication (Env: INTEGRATION_ENVOY_AUTH_V3_AUTH_TYPE) (default "OpenID")
+ --integration.envoy.auth.v3.enabled Defines if Auth extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_ENABLED) (default true)
+ --integration.envoy.auth.v3.extensions.cookie.jwt Defines if Cookie JWT extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_EXTENSIONS_COOKIE_JWT) (default true)
+ --integration.envoy.auth.v3.extensions.jwt Defines if JWT extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_EXTENSIONS_JWT) (default true)
+ --integration.envoy.auth.v3.extensions.users.create Defines if UserCreation extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_EXTENSIONS_USERS_CREATE)
+ --integration.envoy.auth.v3.external Defines if External access to service envoy.auth.v3 is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_EXTERNAL)
+ --integration.envoy.auth.v3.internal Defines if Internal access to service envoy.auth.v3 is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_INTERNAL) (default true)
+ --integration.events.v1 Enable EventsV1 Integration Service (Env: INTEGRATION_EVENTS_V1)
+ --integration.events.v1.async Enables async injection of the events (Env: INTEGRATION_EVENTS_V1_ASYNC) (default true)
+ --integration.events.v1.async.retry.delay duration Delay of the retries (Env: INTEGRATION_EVENTS_V1_ASYNC_RETRY_DELAY) (default 1s)
+ --integration.events.v1.async.retry.timeout duration Timeout for the event injection (Env: INTEGRATION_EVENTS_V1_ASYNC_RETRY_TIMEOUT) (default 1m0s)
+ --integration.events.v1.async.size int Size of the async queue (Env: INTEGRATION_EVENTS_V1_ASYNC_SIZE) (default 16)
+ --integration.events.v1.external Defines if External access to service events.v1 is enabled (Env: INTEGRATION_EVENTS_V1_EXTERNAL)
+ --integration.events.v1.internal Defines if Internal access to service events.v1 is enabled (Env: INTEGRATION_EVENTS_V1_INTERNAL) (default true)
+ --integration.meta.v1 Enable MetaV1 Integration Service (Env: INTEGRATION_META_V1)
+ --integration.meta.v1.external Defines if External access to service meta.v1 is enabled (Env: INTEGRATION_META_V1_EXTERNAL)
+ --integration.meta.v1.internal Defines if Internal access to service meta.v1 is enabled (Env: INTEGRATION_META_V1_INTERNAL) (default true)
+ --integration.meta.v1.prefix string Meta Key Prefix (Env: INTEGRATION_META_V1_PREFIX)
+ --integration.meta.v1.ttl duration Cache Object TTL (Env: INTEGRATION_META_V1_TTL)
+ --integration.scheduler.v1 SchedulerV1 Integration (Env: INTEGRATION_SCHEDULER_V1)
+ --integration.scheduler.v1.external Defines if External access to service scheduler.v1 is enabled (Env: INTEGRATION_SCHEDULER_V1_EXTERNAL)
+ --integration.scheduler.v1.internal Defines if Internal access to service scheduler.v1 is enabled (Env: INTEGRATION_SCHEDULER_V1_INTERNAL) (default true)
+ --integration.scheduler.v1.namespace string Kubernetes Namespace (Env: INTEGRATION_SCHEDULER_V1_NAMESPACE) (default "default")
+ --integration.scheduler.v1.verify-access Verify the CRD Access (Env: INTEGRATION_SCHEDULER_V1_VERIFY_ACCESS) (default true)
+ --integration.scheduler.v2 SchedulerV2 Integration (Env: INTEGRATION_SCHEDULER_V2)
+ --integration.scheduler.v2.deployment string ArangoDeployment Name (Env: INTEGRATION_SCHEDULER_V2_DEPLOYMENT)
+ --integration.scheduler.v2.driver string Helm Driver (Env: INTEGRATION_SCHEDULER_V2_DRIVER) (default "secret")
+ --integration.scheduler.v2.external Defines if External access to service scheduler.v2 is enabled (Env: INTEGRATION_SCHEDULER_V2_EXTERNAL)
+ --integration.scheduler.v2.internal Defines if Internal access to service scheduler.v2 is enabled (Env: INTEGRATION_SCHEDULER_V2_INTERNAL) (default true)
+ --integration.scheduler.v2.namespace string Kubernetes Namespace (Env: INTEGRATION_SCHEDULER_V2_NAMESPACE) (default "default")
+ --integration.shutdown.v1 ShutdownV1 Handler (Env: INTEGRATION_SHUTDOWN_V1)
+ --integration.shutdown.v1.debug.enabled Defines if debug extension is enabled (Env: INTEGRATION_SHUTDOWN_V1_DEBUG_ENABLED)
+ --integration.shutdown.v1.debug.path string Path of the Debug Directory (Env: INTEGRATION_SHUTDOWN_V1_DEBUG_PATH) (default "/debug")
+ --integration.shutdown.v1.debug.timeout duration Timeout of the Debug action (Env: INTEGRATION_SHUTDOWN_V1_DEBUG_TIMEOUT) (default 1m0s)
+ --integration.shutdown.v1.external Defines if External access to service shutdown.v1 is enabled (Env: INTEGRATION_SHUTDOWN_V1_EXTERNAL)
+ --integration.shutdown.v1.internal Defines if Internal access to service shutdown.v1 is enabled (Env: INTEGRATION_SHUTDOWN_V1_INTERNAL) (default true)
+ --integration.storage.v2 StorageBucket V2 Integration (Env: INTEGRATION_STORAGE_V2)
+ --integration.storage.v2.azure-blob-storage.account-name string AzureBlobStorage Account ID (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_ACCOUNT_NAME)
+ --integration.storage.v2.azure-blob-storage.client.secret.client-id string Azure ClientID (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_CLIENT_SECRET_CLIENT_ID)
+ --integration.storage.v2.azure-blob-storage.client.secret.client-id-file string Azure ClientID File (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_CLIENT_SECRET_CLIENT_ID_FILE)
+ --integration.storage.v2.azure-blob-storage.client.secret.client-secret string Azure ClientSecret (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_CLIENT_SECRET_CLIENT_SECRET)
+ --integration.storage.v2.azure-blob-storage.client.secret.client-secret-file string Azure ClientSecret File (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_CLIENT_SECRET_CLIENT_SECRET_FILE)
+ --integration.storage.v2.azure-blob-storage.client.tenant-id string Azure Client Tenant ID (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_CLIENT_TENANT_ID)
+ --integration.storage.v2.azure-blob-storage.client.type string Azure Client Provider (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_CLIENT_TYPE) (default "secret")
+ --integration.storage.v2.azure-blob-storage.endpoint string AzureBlobStorage Endpoint (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_ENDPOINT)
+ --integration.storage.v2.external Defines if External access to service storage.v2 is enabled (Env: INTEGRATION_STORAGE_V2_EXTERNAL)
+ --integration.storage.v2.gcs.bucket.name string Bucket name (Env: INTEGRATION_STORAGE_V2_GCS_BUCKET_NAME)
+ --integration.storage.v2.gcs.bucket.prefix string Bucket Prefix (Env: INTEGRATION_STORAGE_V2_GCS_BUCKET_PREFIX)
+ --integration.storage.v2.gcs.project-id string GCP Project ID (Env: INTEGRATION_STORAGE_V2_GCS_PROJECT_ID)
+ --integration.storage.v2.gcs.provider.sa.file string Path to the file with ServiceAccount JSON (Env: INTEGRATION_STORAGE_V2_GCS_PROVIDER_SA_FILE)
+ --integration.storage.v2.gcs.provider.sa.json string ServiceAccount JSON (Env: INTEGRATION_STORAGE_V2_GCS_PROVIDER_SA_JSON)
+ --integration.storage.v2.gcs.provider.type string Type of the provided credentials (Env: INTEGRATION_STORAGE_V2_GCS_PROVIDER_TYPE) (default "serviceAccount")
+ --integration.storage.v2.internal Defines if Internal access to service storage.v2 is enabled (Env: INTEGRATION_STORAGE_V2_INTERNAL) (default true)
+ --integration.storage.v2.s3.allow-insecure If set to true, the Endpoint certificates won't be checked (Env: INTEGRATION_STORAGE_V2_S3_ALLOW_INSECURE)
+ --integration.storage.v2.s3.bucket.name string Bucket name (Env: INTEGRATION_STORAGE_V2_S3_BUCKET_NAME)
+ --integration.storage.v2.s3.bucket.prefix string Bucket Prefix (Env: INTEGRATION_STORAGE_V2_S3_BUCKET_PREFIX)
+ --integration.storage.v2.s3.ca strings Path to file containing CA certificate to validate endpoint connection (Env: INTEGRATION_STORAGE_V2_S3_CA)
+ --integration.storage.v2.s3.disable-ssl If set to true, the SSL won't be used when connecting to Endpoint (Env: INTEGRATION_STORAGE_V2_S3_DISABLE_SSL)
+ --integration.storage.v2.s3.endpoint string Endpoint of S3 API implementation (Env: INTEGRATION_STORAGE_V2_S3_ENDPOINT)
+ --integration.storage.v2.s3.provider.file.access-key string Path to file containing S3 AccessKey (Env: INTEGRATION_STORAGE_V2_S3_PROVIDER_FILE_ACCESS_KEY)
+ --integration.storage.v2.s3.provider.file.secret-key string Path to file containing S3 SecretKey (Env: INTEGRATION_STORAGE_V2_S3_PROVIDER_FILE_SECRET_KEY)
+ --integration.storage.v2.s3.provider.type string S3 Credentials Provider type (Env: INTEGRATION_STORAGE_V2_S3_PROVIDER_TYPE) (default "file")
+ --integration.storage.v2.s3.region string Region (Env: INTEGRATION_STORAGE_V2_S3_REGION)
+ --integration.storage.v2.type string Type of the Storage Integration (Env: INTEGRATION_STORAGE_V2_TYPE) (default "s3")
+ --services.address string Address to expose internal services (Env: SERVICES_ADDRESS) (default "127.0.0.1:9092")
+ --services.auth.token string Token for internal service (when auth service is token) (Env: SERVICES_AUTH_TOKEN)
+ --services.auth.type string Auth type for internal service (Env: SERVICES_AUTH_TYPE) (default "None")
+ --services.enabled Defines if internal access is enabled (Env: SERVICES_ENABLED) (default true)
+ --services.external.address string Address to expose external services (Env: SERVICES_EXTERNAL_ADDRESS) (default "0.0.0.0:9093")
+ --services.external.auth.token string Token for external service (when auth service is token) (Env: SERVICES_EXTERNAL_AUTH_TOKEN)
+ --services.external.auth.type string Auth type for external service (Env: SERVICES_EXTERNAL_AUTH_TYPE) (default "None")
+ --services.external.enabled Defines if external access is enabled (Env: SERVICES_EXTERNAL_ENABLED)
+ --services.external.gateway.address string Address to expose external gateway services (Env: SERVICES_EXTERNAL_GATEWAY_ADDRESS) (default "0.0.0.0:9193")
+ --services.external.gateway.enabled Defines if external gateway is enabled (Env: SERVICES_EXTERNAL_GATEWAY_ENABLED)
+ --services.external.tls.keyfile string Path to the keyfile (Env: SERVICES_EXTERNAL_TLS_KEYFILE)
+ --services.gateway.address string Address to expose internal gateway services (Env: SERVICES_GATEWAY_ADDRESS) (default "127.0.0.1:9192")
+ --services.gateway.enabled Defines if internal gateway is enabled (Env: SERVICES_GATEWAY_ENABLED) (default true)
+ --services.tls.keyfile string Path to the keyfile (Env: SERVICES_TLS_KEYFILE)
Use "arangodb_operator_integration [command] --help" for more information about a command.
```
diff --git a/go.mod b/go.mod
index d92617ce7..4f42e4b62 100644
--- a/go.mod
+++ b/go.mod
@@ -55,7 +55,7 @@ require (
github.com/rs/zerolog v1.33.0
github.com/spf13/cobra v1.10.1
github.com/spf13/pflag v1.0.10
- github.com/stretchr/testify v1.10.0
+ github.com/stretchr/testify v1.11.1
golang.org/x/sync v0.17.0
golang.org/x/sys v0.38.0
golang.org/x/text v0.30.0
@@ -74,6 +74,9 @@ require (
require (
cloud.google.com/go/storage v1.55.0
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0
+ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
+ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.3
github.com/Masterminds/semver/v3 v3.3.0
github.com/arangodb-managed/apis v0.89.1
github.com/arangodb-managed/integration-apis v0.2.1
@@ -81,7 +84,7 @@ require (
github.com/coreos/go-oidc/v3 v3.14.1
github.com/envoyproxy/go-control-plane/envoy v1.35.0
github.com/go-logr/zerologr v1.2.3
- github.com/golang-jwt/jwt/v5 v5.2.2
+ github.com/golang-jwt/jwt/v5 v5.3.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1
github.com/jedib0t/go-pretty/v6 v6.6.5
github.com/regclient/regclient v0.10.0
@@ -103,7 +106,9 @@ require (
cloud.google.com/go/monitoring v1.24.2 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
+ github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
@@ -183,6 +188,7 @@ require (
github.com/kkdai/maglev v0.2.0 // indirect
github.com/klauspost/compress v1.18.1 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
+ github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
@@ -209,6 +215,7 @@ require (
github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
+ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/common v0.55.0 // indirect
diff --git a/go.sum b/go.sum
index 97cefcf26..df5c36ee2 100644
--- a/go.sum
+++ b/go.sum
@@ -26,8 +26,18 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.3 h1:ZJJNFaQ86GVKQ9ehwqyAFE6pIfyicpuJ8IkVaPBc6/4=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.3/go.mod h1:URuDvhmATVKqHBH9/0nOiNKk0+YcwfQ3WkK5PqHKxc8=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
@@ -244,6 +254,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
+github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -342,6 +354,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
@@ -422,6 +436,8 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -595,6 +611,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
diff --git a/integrations/storage/v2/configuration.go b/integrations/storage/v2/configuration.go
index 4378122c5..e67ef0010 100644
--- a/integrations/storage/v2/configuration.go
+++ b/integrations/storage/v2/configuration.go
@@ -24,6 +24,7 @@ import (
"context"
pbImplStorageV2Shared "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared"
+ pbImplStorageV2SharedAzureBlobStorage "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared/abs"
pbImplStorageV2SharedGCS "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared/gcs"
pbImplStorageV2SharedS3 "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared/s3"
"github.com/arangodb/kube-arangodb/pkg/util"
@@ -33,8 +34,9 @@ import (
type ConfigurationType string
const (
- ConfigurationTypeS3 ConfigurationType = "s3"
- ConfigurationTypeGCS ConfigurationType = "gcs"
+ ConfigurationTypeS3 ConfigurationType = "s3"
+ ConfigurationTypeGCS ConfigurationType = "gcs"
+ ConfigurationTypeAzure ConfigurationType = "azureBlobStorage"
)
func NewConfiguration(mods ...util.ModR[Configuration]) Configuration {
@@ -46,8 +48,9 @@ func NewConfiguration(mods ...util.ModR[Configuration]) Configuration {
type Configuration struct {
Type ConfigurationType
- S3 pbImplStorageV2SharedS3.Configuration
- GCS pbImplStorageV2SharedGCS.Configuration
+ S3 pbImplStorageV2SharedS3.Configuration
+ GCS pbImplStorageV2SharedGCS.Configuration
+ AzureBlobStorage pbImplStorageV2SharedAzureBlobStorage.Configuration
}
func (c Configuration) IO(ctx context.Context) (pbImplStorageV2Shared.IO, error) {
@@ -56,6 +59,8 @@ func (c Configuration) IO(ctx context.Context) (pbImplStorageV2Shared.IO, error)
return c.S3.New()
case ConfigurationTypeGCS:
return c.GCS.New(ctx)
+ case ConfigurationTypeAzure:
+ return c.AzureBlobStorage.New()
default:
return nil, errors.Errorf("Unknown Type: %s", c.Type)
}
diff --git a/integrations/storage/v2/object.go b/integrations/storage/v2/object.go
index 0860b7775..de54f8496 100644
--- a/integrations/storage/v2/object.go
+++ b/integrations/storage/v2/object.go
@@ -27,10 +27,12 @@ import (
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
pbImplStorageV2Shared "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared"
+ pbImplStorageV2SharedAzureBlobStorage "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared/abs"
pbImplStorageV2SharedGCS "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared/gcs"
pbImplStorageV2SharedS3 "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared/s3"
platformApi "github.com/arangodb/kube-arangodb/pkg/apis/platform/v1beta1"
awsHelper "github.com/arangodb/kube-arangodb/pkg/util/aws"
+ "github.com/arangodb/kube-arangodb/pkg/util/azure"
utilConstants "github.com/arangodb/kube-arangodb/pkg/util/constants"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
gcsHelper "github.com/arangodb/kube-arangodb/pkg/util/gcs"
@@ -138,6 +140,42 @@ func NewIOFromObject(ctx context.Context, client kclient.Client, in *platformApi
return cfg.New(ctx)
}
+
+ if azureBlobStorage := backend.AzureBlobStorage; azureBlobStorage != nil {
+ var config azure.Config
+
+ if v := azureBlobStorage.CredentialsSecret; v != nil {
+ secret, err := client.Kubernetes().CoreV1().Secrets(v.GetNamespace(in)).Get(ctx, v.GetName(), meta.GetOptions{})
+ if err != nil {
+ return nil, errors.WithMessage(err, "Failed to get AzureBlueStorage secret")
+ }
+
+ cid, ok := secret.Data[utilConstants.SecretCredentialsAzureBlobStorageClientID]
+ if !ok {
+ return nil, errors.Errorf("Failed to get AzureBlueStorage secret %s data: Key %s not found", secret.GetName(), utilConstants.SecretCredentialsAzureBlobStorageClientID)
+ }
+
+ cs, ok := secret.Data[utilConstants.SecretCredentialsAzureBlobStorageClientSecret]
+ if !ok {
+ return nil, errors.Errorf("Failed to get AzureBlueStorage secret %s data: Key %s not found", secret.GetName(), utilConstants.SecretCredentialsAzureBlobStorageClientSecret)
+ }
+
+ config.Provider.Secret.ClientID = string(cid)
+ config.Provider.Secret.ClientSecret = string(cs)
+ config.Provider.Type = azure.ProviderTypeSecret
+ }
+
+ config.AccountName = azureBlobStorage.GetAccountName()
+ config.Provider.TenantID = azureBlobStorage.GetTenantID()
+
+ var cfg pbImplStorageV2SharedAzureBlobStorage.Configuration
+
+ cfg.BucketName = azureBlobStorage.GetBucketName()
+ cfg.BucketPrefix = azureBlobStorage.GetBucketPrefix()
+ cfg.Client = config
+
+ return cfg.New()
+ }
}
return nil, errors.Errorf("Unable to init the storage")
diff --git a/integrations/storage/v2/shared/abs/configuration.go b/integrations/storage/v2/shared/abs/configuration.go
new file mode 100644
index 000000000..2d080d740
--- /dev/null
+++ b/integrations/storage/v2/shared/abs/configuration.go
@@ -0,0 +1,59 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package abs
+
+import (
+ "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
+
+ pbImplStorageV2Shared "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared"
+ "github.com/arangodb/kube-arangodb/pkg/util/azure"
+)
+
+type Configuration struct {
+ BucketName string
+ BucketPrefix string
+
+ MaxListKeys *int32
+
+ Client azure.Config
+}
+
+func (c Configuration) New() (pbImplStorageV2Shared.IO, error) {
+ prov, err := c.Client.GetCredentials()
+ if err != nil {
+ return nil, err
+ }
+
+ endpoint, err := c.Client.GetEndpoint()
+ if err != nil {
+ return nil, err
+ }
+
+ client, err := azblob.NewClient(endpoint, prov, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return &ios{
+ config: c,
+ client: client.ServiceClient(),
+ }, nil
+}
diff --git a/integrations/storage/v2/shared/abs/delete.go b/integrations/storage/v2/shared/abs/delete.go
new file mode 100644
index 000000000..e40683b4a
--- /dev/null
+++ b/integrations/storage/v2/shared/abs/delete.go
@@ -0,0 +1,53 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package abs
+
+import (
+ "context"
+
+ "github.com/Azure/azure-sdk-for-go/sdk/azcore"
+
+ "github.com/arangodb/kube-arangodb/pkg/util/errors"
+)
+
+func (i *ios) Delete(ctx context.Context, key string) (bool, error) {
+ q := i.container().NewBlockBlobClient(i.key(key))
+
+ _, err := q.GetProperties(ctx, nil)
+ if err != nil {
+ var respErr *azcore.ResponseError
+ if errors.As(err, &respErr) && respErr.StatusCode == 404 {
+ return true, nil
+ }
+ return false, err
+ }
+
+ _, err = q.Delete(ctx, nil)
+ if err != nil {
+ var respErr *azcore.ResponseError
+ if errors.As(err, &respErr) && respErr.StatusCode == 404 {
+ return true, nil
+ }
+ return false, err
+ }
+
+ return true, nil
+}
diff --git a/integrations/storage/v2/shared/abs/head.go b/integrations/storage/v2/shared/abs/head.go
new file mode 100644
index 000000000..87b662f62
--- /dev/null
+++ b/integrations/storage/v2/shared/abs/head.go
@@ -0,0 +1,50 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package abs
+
+import (
+ "context"
+ "time"
+
+ "github.com/Azure/azure-sdk-for-go/sdk/azcore"
+
+ pbImplStorageV2Shared "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared"
+ "github.com/arangodb/kube-arangodb/pkg/util"
+ "github.com/arangodb/kube-arangodb/pkg/util/errors"
+)
+
+func (i *ios) Head(ctx context.Context, key string) (*pbImplStorageV2Shared.Info, error) {
+ q := i.container().NewBlockBlobClient(i.key(key))
+
+ prop, err := q.GetProperties(ctx, nil)
+ if err != nil {
+ var respErr *azcore.ResponseError
+ if errors.As(err, &respErr) && respErr.StatusCode == 404 {
+ return nil, nil
+ }
+ return nil, err
+ }
+
+ return &pbImplStorageV2Shared.Info{
+ Size: uint64(util.OptionalType(prop.ContentLength, 0)),
+ LastUpdatedAt: util.OptionalType(prop.LastModified, time.Time{}),
+ }, nil
+}
diff --git a/integrations/storage/v2/shared/abs/init.go b/integrations/storage/v2/shared/abs/init.go
new file mode 100644
index 000000000..468942eba
--- /dev/null
+++ b/integrations/storage/v2/shared/abs/init.go
@@ -0,0 +1,40 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package abs
+
+import (
+ "context"
+
+ "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
+
+ pbImplStorageV2Shared "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared"
+)
+
+func (i *ios) Init(ctx context.Context, opts *pbImplStorageV2Shared.InitOptions) error {
+ c := i.container()
+
+ _, err := c.GetProperties(ctx, &container.GetPropertiesOptions{})
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/integrations/storage/v2/shared/abs/io.go b/integrations/storage/v2/shared/abs/io.go
new file mode 100644
index 000000000..380044976
--- /dev/null
+++ b/integrations/storage/v2/shared/abs/io.go
@@ -0,0 +1,54 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package abs
+
+import (
+ "path"
+ goStrings "strings"
+
+ "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
+ "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service"
+)
+
+type ios struct {
+ config Configuration
+ client *service.Client
+}
+
+func (i *ios) container() *container.Client {
+ return i.client.NewContainerClient(i.config.BucketName)
+}
+
+func (i *ios) clean(key string) string {
+ return goStrings.TrimPrefix(goStrings.TrimPrefix(key, i.key()), "/")
+}
+
+func (i *ios) key(keys ...string) string {
+ out := path.Join(goStrings.TrimPrefix(i.config.BucketPrefix, "/"), path.Join(keys...))
+
+ if len(keys) > 0 {
+ if goStrings.HasSuffix(keys[len(keys)-1], "/") {
+ out = out + "/"
+ }
+ }
+
+ return out
+}
diff --git a/integrations/storage/v2/shared/abs/io_test.go b/integrations/storage/v2/shared/abs/io_test.go
new file mode 100644
index 000000000..c67518389
--- /dev/null
+++ b/integrations/storage/v2/shared/abs/io_test.go
@@ -0,0 +1,115 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package abs
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "k8s.io/apimachinery/pkg/util/uuid"
+
+ pbImplStorageV2Shared "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared"
+ "github.com/arangodb/kube-arangodb/pkg/util/shutdown"
+ "github.com/arangodb/kube-arangodb/pkg/util/tests"
+)
+
+func Test(t *testing.T) {
+ var config = Configuration{
+ BucketName: tests.GetAzureBlobStorageContainer(t),
+ BucketPrefix: fmt.Sprintf("tmp/unit-test/%s/", uuid.NewUUID()),
+ MaxListKeys: nil,
+ Client: tests.GetAzureConfig(t),
+ }
+
+ client, err := config.New()
+ require.NoError(t, err)
+
+ require.NoError(t, client.Init(shutdown.Context(), &pbImplStorageV2Shared.InitOptions{}))
+
+ // List Done
+ {
+ objs, err := client.List(shutdown.Context(), "")
+ require.NoError(t, err)
+ data, err := objs.Next(shutdown.Context())
+ require.NoError(t, err)
+ require.Len(t, data, 0)
+ _, err = objs.Next(shutdown.Context())
+ require.ErrorIs(t, err, io.EOF)
+ }
+
+ {
+ // Write
+ data, err := client.Write(shutdown.Context(), "my-file.txt")
+ require.NoError(t, err)
+
+ require.False(t, data.Closed())
+
+ _, err = data.Write([]byte("hello world"))
+ require.NoError(t, err)
+
+ checksum, bytes, err := data.Close(shutdown.Context())
+ require.NoError(t, err)
+ require.Equal(t, checksum, "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9")
+ require.EqualValues(t, bytes, 11)
+
+ require.True(t, data.Closed())
+ }
+
+ // List Done
+ {
+ objs, err := client.List(shutdown.Context(), "")
+ require.NoError(t, err)
+ data, err := objs.Next(shutdown.Context())
+ require.NoError(t, err)
+ require.Len(t, data, 1)
+ _, err = objs.Next(shutdown.Context())
+ require.ErrorIs(t, err, io.EOF)
+ }
+
+ {
+ // Read
+ _, err := client.Read(shutdown.Context(), "my-file2.txt")
+ require.ErrorIs(t, err, os.ErrNotExist)
+
+ }
+
+ {
+ // Read
+ data, err := client.Read(shutdown.Context(), "my-file.txt")
+ require.NoError(t, err)
+
+ require.False(t, data.Closed())
+
+ z, err := io.ReadAll(data)
+ require.NoError(t, err)
+
+ checksum, bytes, err := data.Close(shutdown.Context())
+ require.NoError(t, err)
+ require.Equal(t, checksum, "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9")
+ require.EqualValues(t, bytes, 11)
+ require.Len(t, z, 11)
+
+ require.True(t, data.Closed())
+ }
+}
diff --git a/integrations/storage/v2/shared/abs/list.go b/integrations/storage/v2/shared/abs/list.go
new file mode 100644
index 000000000..022f2f8d8
--- /dev/null
+++ b/integrations/storage/v2/shared/abs/list.go
@@ -0,0 +1,90 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package abs
+
+import (
+ "context"
+ "io"
+ "sync"
+ "time"
+
+ "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
+ "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
+
+ pbImplStorageV2Shared "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared"
+ "github.com/arangodb/kube-arangodb/pkg/util"
+ "github.com/arangodb/kube-arangodb/pkg/util/errors"
+)
+
+func (i *ios) List(ctx context.Context, key string) (util.NextIterator[[]pbImplStorageV2Shared.File], error) {
+ return &listIterator{
+ pager: i.container().NewListBlobsFlatPager(&container.ListBlobsFlatOptions{
+ Include: container.ListBlobsInclude{},
+ Marker: nil,
+ MaxResults: i.config.MaxListKeys,
+ Prefix: util.NewType(i.key(key)),
+ }),
+ parent: i,
+ }, nil
+}
+
+type listIterator struct {
+ lock sync.Mutex
+ parent *ios
+
+ pager *runtime.Pager[container.ListBlobsFlatResponse]
+}
+
+func (l *listIterator) Next(ctx context.Context) ([]pbImplStorageV2Shared.File, error) {
+ l.lock.Lock()
+ defer l.lock.Unlock()
+
+ if !l.pager.More() {
+ return nil, io.EOF
+ }
+
+ resp, err := l.pager.NextPage(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ if resp.Segment == nil {
+ return nil, errors.Errorf("Invalid segment response")
+ }
+
+ data := make([]pbImplStorageV2Shared.File, len(resp.Segment.BlobItems))
+
+ for id, file := range resp.Segment.BlobItems {
+ if file == nil || file.Properties == nil {
+ return nil, errors.Errorf("Invalid file response")
+ }
+
+ data[id] = pbImplStorageV2Shared.File{
+ Key: l.parent.clean(*file.Name),
+ Info: pbImplStorageV2Shared.Info{
+ Size: uint64(util.OptionalType(file.Properties.ContentLength, 0)),
+ LastUpdatedAt: util.OptionalType(file.Properties.LastModified, time.Time{}),
+ },
+ }
+ }
+
+ return data, nil
+}
diff --git a/integrations/storage/v2/shared/abs/read.go b/integrations/storage/v2/shared/abs/read.go
new file mode 100644
index 000000000..b84fff03a
--- /dev/null
+++ b/integrations/storage/v2/shared/abs/read.go
@@ -0,0 +1,112 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package abs
+
+import (
+ "context"
+ "crypto/sha256"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "os"
+ "sync"
+
+ "github.com/Azure/azure-sdk-for-go/sdk/azcore"
+
+ pbImplStorageV2Shared "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared"
+)
+
+type reader struct {
+ lock sync.Mutex
+
+ in io.ReadCloser
+
+ bytes int64
+ checksum hash.Hash
+
+ closed bool
+}
+
+func (r *reader) Read(p []byte) (n int, err error) {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+
+ n, err = r.in.Read(p)
+ if n > 0 {
+ r.bytes += int64(n)
+ r.checksum.Write(p[:n])
+ }
+
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ r.closed = true
+ if n > 0 {
+ return n, nil
+ }
+ return
+ }
+
+ var respErr *azcore.ResponseError
+ if errors.As(err, &respErr) && respErr.StatusCode == 404 {
+ return 0, os.ErrNotExist
+ }
+ return 0, err
+ }
+
+ return
+}
+
+func (r *reader) Close(ctx context.Context) (string, int64, error) {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+
+ if err := r.in.Close(); err != nil {
+ return "", 0, err
+ }
+
+ return fmt.Sprintf("%02x", r.checksum.Sum(nil)), r.bytes, nil
+}
+
+func (r *reader) Closed() bool {
+ return r.closed
+}
+
+func (i *ios) Read(ctx context.Context, key string) (pbImplStorageV2Shared.Reader, error) {
+ q := i.container().NewBlockBlobClient(i.key(key))
+
+ resp, err := q.DownloadStream(ctx, nil)
+ if err != nil {
+ var respErr *azcore.ResponseError
+ if errors.As(err, &respErr) && respErr.StatusCode == 404 {
+ return nil, os.ErrNotExist
+ }
+
+ return nil, err
+ }
+
+ var reader reader
+
+ reader.in = resp.Body
+ reader.checksum = sha256.New()
+
+ return &reader, nil
+}
diff --git a/integrations/storage/v2/shared/abs/write.go b/integrations/storage/v2/shared/abs/write.go
new file mode 100644
index 000000000..810425825
--- /dev/null
+++ b/integrations/storage/v2/shared/abs/write.go
@@ -0,0 +1,118 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package abs
+
+import (
+ "context"
+ "crypto/sha256"
+ "fmt"
+ "hash"
+ "io"
+ "sync"
+
+ "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blockblob"
+
+ pbImplStorageV2Shared "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared"
+)
+
+type writer struct {
+ lock sync.Mutex
+
+ in io.WriteCloser
+
+ done chan struct{}
+
+ bytes int64
+ checksum hash.Hash
+
+ err error
+}
+
+func (w *writer) Write(p []byte) (n int, err error) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ n, err = w.in.Write(p)
+ if err != nil {
+ return 0, err
+ }
+
+ if n > 0 {
+ w.bytes += int64(n)
+ w.checksum.Write(p[:n])
+ }
+
+ return
+}
+
+func (w *writer) Close(ctx context.Context) (string, int64, error) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ if err := w.in.Close(); err != nil {
+ return "", 0, err
+ }
+
+ <-w.done
+
+ if w.err != nil {
+ return "", 0, w.err
+ }
+
+ return fmt.Sprintf("%02x", w.checksum.Sum(nil)), w.bytes, nil
+}
+
+func (w *writer) Closed() bool {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ select {
+ case <-w.done:
+ return true
+ default:
+ return false
+ }
+}
+
+func (w *writer) run(ctx context.Context, client *blockblob.Client, data io.Reader) {
+ defer close(w.done)
+
+ _, err := client.UploadStream(ctx, data, &blockblob.UploadStreamOptions{
+ BlockSize: 4 * 1024 * 1024,
+ })
+ w.err = err
+}
+
+func (i *ios) Write(ctx context.Context, key string) (pbImplStorageV2Shared.Writer, error) {
+ q := i.container().NewBlockBlobClient(i.key(key))
+
+ in, out := io.Pipe()
+
+ var writer writer
+
+ writer.done = make(chan struct{})
+ writer.in = out
+ writer.checksum = sha256.New()
+
+ go writer.run(ctx, q, in)
+
+ return &writer, nil
+}
diff --git a/integrations/storage/v2/suite_azure_test.go b/integrations/storage/v2/suite_azure_test.go
new file mode 100644
index 000000000..fb40e113e
--- /dev/null
+++ b/integrations/storage/v2/suite_azure_test.go
@@ -0,0 +1,113 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package v2
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ core "k8s.io/api/core/v1"
+ meta "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/util/uuid"
+
+ pbImplStorageV2SharedAzureBlobStorage "github.com/arangodb/kube-arangodb/integrations/storage/v2/shared/abs"
+ platformApi "github.com/arangodb/kube-arangodb/pkg/apis/platform/v1beta1"
+ sharedApi "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1"
+ "github.com/arangodb/kube-arangodb/pkg/util"
+ utilConstants "github.com/arangodb/kube-arangodb/pkg/util/constants"
+ "github.com/arangodb/kube-arangodb/pkg/util/kclient"
+ "github.com/arangodb/kube-arangodb/pkg/util/shutdown"
+ "github.com/arangodb/kube-arangodb/pkg/util/tests"
+)
+
+func azureConfiguration(t *testing.T, mods ...util.ModR[Configuration]) Configuration {
+ var scfg pbImplStorageV2SharedAzureBlobStorage.Configuration
+ scfg.BucketName = tests.GetAzureBlobStorageContainer(t)
+ scfg.BucketPrefix = fmt.Sprintf("tmp/unit-test/%s/", uuid.NewUUID())
+ scfg.MaxListKeys = nil
+ scfg.Client = tests.GetAzureConfig(t)
+
+ var cfg Configuration
+
+ cfg.Type = ConfigurationTypeAzure
+ cfg.AzureBlobStorage = scfg
+
+ return cfg.With(mods...)
+}
+
+func azureKubernetesObject(t *testing.T, mods ...util.Mod[platformApi.ArangoPlatformStorage]) (string, string, kclient.Client) {
+ client := kclient.NewFakeClient()
+
+ config := tests.GetAzureConfig(t)
+ bucketName := tests.GetAzureBlobStorageContainer(t)
+ bucketPrefix := fmt.Sprintf("tmp/unit-test-object/%s/", uuid.NewUUID())
+
+ creds, err := client.Kubernetes().CoreV1().Secrets(tests.FakeNamespace).Create(shutdown.Context(), &core.Secret{
+ ObjectMeta: meta.ObjectMeta{
+ Name: "credentials",
+ Namespace: tests.FakeNamespace,
+ },
+ Data: map[string][]byte{
+ utilConstants.SecretCredentialsAzureBlobStorageClientID: []byte(config.Provider.Secret.ClientID),
+ utilConstants.SecretCredentialsAzureBlobStorageClientSecret: []byte(config.Provider.Secret.ClientSecret),
+ },
+ }, meta.CreateOptions{})
+ require.NoError(t, err)
+
+ obj := &platformApi.ArangoPlatformStorage{
+ ObjectMeta: meta.ObjectMeta{
+ Name: "storage",
+ Namespace: tests.FakeNamespace,
+ },
+ Spec: platformApi.ArangoPlatformStorageSpec{
+ Backend: &platformApi.ArangoPlatformStorageSpecBackend{
+ AzureBlobStorage: &platformApi.ArangoPlatformStorageSpecBackendAzureBlobStorage{
+ TenantID: util.NewType(config.Provider.TenantID),
+ AccountName: util.NewType(config.AccountName),
+ BucketName: util.NewType(bucketName),
+ BucketPrefix: util.NewType(bucketPrefix),
+ CredentialsSecret: &sharedApi.Object{
+ Name: creds.GetName(),
+ },
+ },
+ },
+ },
+ }
+
+ util.ApplyMods(obj, mods...)
+
+ obj, err = client.Arango().PlatformV1beta1().ArangoPlatformStorages(tests.FakeNamespace).Create(shutdown.Context(), obj, meta.CreateOptions{})
+ require.NoError(t, err)
+
+ return obj.GetName(), obj.GetNamespace(), client
+}
+
+func Test_Azure_Handler(t *testing.T) {
+ testConfiguration(t, azureConfiguration, func(in Configuration) Configuration {
+ in.AzureBlobStorage.MaxListKeys = util.NewType[int32](32)
+ return in
+ })
+}
+
+func Test_Azure_Object(t *testing.T) {
+ testObject(t, azureKubernetesObject)
+}
diff --git a/pkg/apis/platform/v1beta1/storage_spec_backend.go b/pkg/apis/platform/v1beta1/storage_spec_backend.go
index bdea2c525..145431fb7 100644
--- a/pkg/apis/platform/v1beta1/storage_spec_backend.go
+++ b/pkg/apis/platform/v1beta1/storage_spec_backend.go
@@ -22,6 +22,7 @@ package v1beta1
import (
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
+ "github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
)
@@ -31,6 +32,9 @@ type ArangoPlatformStorageSpecBackend struct {
// GCS backend implements storage as a proxy to the provided GCS API endpoint
GCS *ArangoPlatformStorageSpecBackendGCS `json:"gcs,omitempty"`
+
+ // AzureBlobStorage backend implements storage as a proxy to the provided AzureBlobStorage
+ AzureBlobStorage *ArangoPlatformStorageSpecBackendAzureBlobStorage `json:"azureBlobStorage,omitempty"`
}
func (s *ArangoPlatformStorageSpecBackend) GetS3() *ArangoPlatformStorageSpecBackendS3 {
@@ -40,6 +44,13 @@ func (s *ArangoPlatformStorageSpecBackend) GetS3() *ArangoPlatformStorageSpecBac
return s.S3
}
+func (s *ArangoPlatformStorageSpecBackend) GetAzureBlobStorage() *ArangoPlatformStorageSpecBackendAzureBlobStorage {
+ if s == nil || s.AzureBlobStorage == nil {
+ return nil
+ }
+ return s.AzureBlobStorage
+}
+
func (s *ArangoPlatformStorageSpecBackend) GetGCS() *ArangoPlatformStorageSpecBackendGCS {
if s == nil || s.GCS == nil {
return nil
@@ -52,11 +63,16 @@ func (s *ArangoPlatformStorageSpecBackend) Validate() error {
return errors.Errorf("Backend is not specified")
}
- if s.S3 == nil && s.GCS == nil {
+ if s.S3 == nil && s.GCS == nil && s.AzureBlobStorage == nil {
return errors.Errorf("At least one backend needs to be defined")
}
- if s.S3 != nil && s.GCS != nil {
+ switch util.Count(true, s.S3 != nil, s.GCS != nil, s.AzureBlobStorage != nil) {
+ case 0:
+ return errors.Errorf("At least one backend needs to be defined")
+ case 1:
+ break
+ default:
return errors.Errorf("Only one backend can be defined")
}
@@ -68,5 +84,9 @@ func (s *ArangoPlatformStorageSpecBackend) Validate() error {
return shared.WithErrors(shared.PrefixResourceError("gcs", s.GCS.Validate()))
}
+ if s.AzureBlobStorage != nil {
+ return shared.WithErrors(shared.PrefixResourceError("azureBlobStorage", s.AzureBlobStorage.Validate()))
+ }
+
return nil
}
diff --git a/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go b/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go
new file mode 100644
index 000000000..0afc9f6d5
--- /dev/null
+++ b/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go
@@ -0,0 +1,129 @@
+//
+// DISCLAIMER
+//
+// Copyright 2024-2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package v1beta1
+
+import (
+ "net/url"
+
+ shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
+ sharedApi "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1"
+ "github.com/arangodb/kube-arangodb/pkg/util/errors"
+)
+
+type ArangoPlatformStorageSpecBackendAzureBlobStorage struct {
+ // TenantID specifies the Azure TenantID
+ // +doc/required
+ TenantID *string `json:"tenantID,omitempty"`
+
+ // AccountName specifies the Azure Storage AccountName
+ // used in format https://.blob.core.windows.net/
+ // +doc/required
+ AccountName *string `json:"accountName,omitempty"`
+
+ // Endpoint specifies the Azure Storage custom endpoint
+ // +doc/required
+ Endpoint *string `json:"endpoint,omitempty"`
+
+ // BucketName specifies the name of the bucket
+ // +doc/required
+ BucketName *string `json:"bucketName,omitempty"`
+
+ // BucketPath specifies the Prefix within the bucket
+ // +doc/default:
+ BucketPrefix *string `json:"bucketPath,omitempty"`
+
+ // CredentialsSecret specifies the Kubernetes Secret containing ClientID and ClientSecret for Azure API authorization
+ // +doc/required
+ // +doc/skip: namespace
+ // +doc/skip: uid
+ // +doc/skip: checksum
+ CredentialsSecret *sharedApi.Object `json:"credentialsSecret"`
+}
+
+func (s *ArangoPlatformStorageSpecBackendAzureBlobStorage) Validate() error {
+ if s == nil {
+ s = &ArangoPlatformStorageSpecBackendAzureBlobStorage{}
+ }
+
+ var errs []error
+
+ if end := s.GetEndpoint(); end != "" {
+ if _, err := url.Parse(s.GetEndpoint()); err != nil {
+ errs = append(errs, shared.PrefixResourceErrors("endpoint", errors.Errorf("invalid URL: %s", err.Error())))
+ }
+ }
+ if acc := s.GetTenantID(); acc == "" {
+ errs = append(errs, shared.PrefixResourceErrors("tenantID", errors.Errorf("TenantID needs to be defined")))
+ }
+
+ if acc := s.GetAccountName(); acc == "" && s.GetEndpoint() == "" {
+ errs = append(errs, shared.PrefixResourceErrors("accountName", errors.Errorf("AccountName needs to be defined")))
+ }
+
+ errs = append(errs,
+ shared.PrefixResourceErrors("credentialsSecret", s.GetCredentialsSecret().Validate()),
+ shared.PrefixResourceError("bucketName", shared.ValidateRequired(s.BucketName, shared.ValidateResourceName)),
+ )
+
+ return shared.WithErrors(errs...)
+}
+
+func (s *ArangoPlatformStorageSpecBackendAzureBlobStorage) GetAccountName() string {
+ if s == nil || s.AccountName == nil {
+ return ""
+ }
+ return *s.AccountName
+}
+
+func (s *ArangoPlatformStorageSpecBackendAzureBlobStorage) GetEndpoint() string {
+ if s == nil || s.Endpoint == nil {
+ return ""
+ }
+ return *s.Endpoint
+}
+
+func (s *ArangoPlatformStorageSpecBackendAzureBlobStorage) GetTenantID() string {
+ if s == nil || s.TenantID == nil {
+ return ""
+ }
+ return *s.TenantID
+}
+
+func (s *ArangoPlatformStorageSpecBackendAzureBlobStorage) GetBucketName() string {
+ if s == nil || s.BucketName == nil {
+ return ""
+ }
+ return *s.BucketName
+}
+
+func (s *ArangoPlatformStorageSpecBackendAzureBlobStorage) GetBucketPrefix() string {
+ if s == nil || s.BucketPrefix == nil {
+ return ""
+ }
+ return *s.BucketPrefix
+}
+
+func (s *ArangoPlatformStorageSpecBackendAzureBlobStorage) GetCredentialsSecret() *sharedApi.Object {
+ if s == nil || s.CredentialsSecret == nil {
+ return &sharedApi.Object{}
+ }
+ return s.CredentialsSecret
+}
diff --git a/pkg/apis/platform/v1beta1/zz_generated.deepcopy.go b/pkg/apis/platform/v1beta1/zz_generated.deepcopy.go
index 1ee131cb8..5edd1fb84 100644
--- a/pkg/apis/platform/v1beta1/zz_generated.deepcopy.go
+++ b/pkg/apis/platform/v1beta1/zz_generated.deepcopy.go
@@ -422,6 +422,11 @@ func (in *ArangoPlatformStorageSpecBackend) DeepCopyInto(out *ArangoPlatformStor
*out = new(ArangoPlatformStorageSpecBackendGCS)
(*in).DeepCopyInto(*out)
}
+ if in.AzureBlobStorage != nil {
+ in, out := &in.AzureBlobStorage, &out.AzureBlobStorage
+ *out = new(ArangoPlatformStorageSpecBackendAzureBlobStorage)
+ (*in).DeepCopyInto(*out)
+ }
return
}
@@ -435,6 +440,52 @@ func (in *ArangoPlatformStorageSpecBackend) DeepCopy() *ArangoPlatformStorageSpe
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ArangoPlatformStorageSpecBackendAzureBlobStorage) DeepCopyInto(out *ArangoPlatformStorageSpecBackendAzureBlobStorage) {
+ *out = *in
+ if in.TenantID != nil {
+ in, out := &in.TenantID, &out.TenantID
+ *out = new(string)
+ **out = **in
+ }
+ if in.AccountName != nil {
+ in, out := &in.AccountName, &out.AccountName
+ *out = new(string)
+ **out = **in
+ }
+ if in.Endpoint != nil {
+ in, out := &in.Endpoint, &out.Endpoint
+ *out = new(string)
+ **out = **in
+ }
+ if in.BucketName != nil {
+ in, out := &in.BucketName, &out.BucketName
+ *out = new(string)
+ **out = **in
+ }
+ if in.BucketPrefix != nil {
+ in, out := &in.BucketPrefix, &out.BucketPrefix
+ *out = new(string)
+ **out = **in
+ }
+ if in.CredentialsSecret != nil {
+ in, out := &in.CredentialsSecret, &out.CredentialsSecret
+ *out = new(v1.Object)
+ (*in).DeepCopyInto(*out)
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoPlatformStorageSpecBackendAzureBlobStorage.
+func (in *ArangoPlatformStorageSpecBackendAzureBlobStorage) DeepCopy() *ArangoPlatformStorageSpecBackendAzureBlobStorage {
+ if in == nil {
+ return nil
+ }
+ out := new(ArangoPlatformStorageSpecBackendAzureBlobStorage)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ArangoPlatformStorageSpecBackendGCS) DeepCopyInto(out *ArangoPlatformStorageSpecBackendGCS) {
*out = *in
diff --git a/pkg/crd/crds/platform-storage.schema.generated.yaml b/pkg/crd/crds/platform-storage.schema.generated.yaml
index 23a1f2669..ff8f9c238 100644
--- a/pkg/crd/crds/platform-storage.schema.generated.yaml
+++ b/pkg/crd/crds/platform-storage.schema.generated.yaml
@@ -92,6 +92,42 @@ v1beta1:
backend:
description: Backend defines how storage is implemented
properties:
+ azureBlobStorage:
+ description: AzureBlobStorage backend implements storage as a proxy to the provided AzureBlobStorage
+ properties:
+ accountName:
+ description: |-
+ AccountName specifies the Azure Storage AccountName
+ used in format https://.blob.core.windows.net/
+ type: string
+ bucketName:
+ description: BucketName specifies the name of the bucket
+ type: string
+ bucketPath:
+ description: BucketPath specifies the Prefix within the bucket
+ type: string
+ credentialsSecret:
+ description: CredentialsSecret specifies the Kubernetes Secret containing ClientID and ClientSecret for Azure API authorization
+ properties:
+ name:
+ description: Name of the object
+ type: string
+ required:
+ - name
+ type: object
+ endpoint:
+ description: Endpoint specifies the Azure Storage custom endpoint
+ type: string
+ tenantID:
+ description: TenantID specifies the Azure TenantID
+ type: string
+ required:
+ - accountName
+ - bucketName
+ - credentialsSecret
+ - endpoint
+ - tenantID
+ type: object
gcs:
description: GCS backend implements storage as a proxy to the provided GCS API endpoint
properties:
diff --git a/pkg/integrations/sidecar/integration.storage.v2.go b/pkg/integrations/sidecar/integration.storage.v2.go
index 371b6a40a..532c9b103 100644
--- a/pkg/integrations/sidecar/integration.storage.v2.go
+++ b/pkg/integrations/sidecar/integration.storage.v2.go
@@ -155,6 +155,33 @@ func (i IntegrationStorageV2) Envs() ([]core.EnvVar, error) {
Value: filepath.Join(mountPathStorageCredentials, utilConstants.SecretCredentialsServiceAccount),
},
)
+ } else if azureBlobStorage := i.Storage.Spec.GetBackend().GetAzureBlobStorage(); azureBlobStorage != nil {
+ envs = append(envs,
+ core.EnvVar{
+ Name: "INTEGRATION_STORAGE_V2_TYPE",
+ Value: string(pbImplStorageV2.ConfigurationTypeAzure),
+ },
+ core.EnvVar{
+ Name: "INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_CLIENT_SECRET_CLIENT_ID_FILE",
+ Value: filepath.Join(mountPathStorageCredentials, utilConstants.SecretCredentialsAzureBlobStorageClientID),
+ },
+ core.EnvVar{
+ Name: "INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_CLIENT_SECRET_CLIENT_SECRET_FILE",
+ Value: filepath.Join(mountPathStorageCredentials, utilConstants.SecretCredentialsAzureBlobStorageClientSecret),
+ },
+ core.EnvVar{
+ Name: "INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_CLIENT_TENANT_ID",
+ Value: azureBlobStorage.GetTenantID(),
+ },
+ core.EnvVar{
+ Name: "INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_ACCOUNT_NAME",
+ Value: azureBlobStorage.GetAccountName(),
+ },
+ core.EnvVar{
+ Name: "INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_ENDPOINT",
+ Value: azureBlobStorage.GetEndpoint(),
+ },
+ )
}
return i.Core.Envs(i, envs...), nil
@@ -199,6 +226,16 @@ func (i IntegrationStorageV2) Volumes() ([]core.Volume, []core.VolumeMount, erro
Name: mountNameStorageCredentials,
MountPath: mountPathStorageCredentials,
})
+ } else if azureBlobStorage := i.Storage.Spec.GetBackend().GetAzureBlobStorage(); azureBlobStorage != nil {
+ secretObj := azureBlobStorage.GetCredentialsSecret()
+ if secretObj.GetNamespace(i.Storage) != i.Storage.GetNamespace() {
+ return nil, nil, errors.New("secrets from different namespace are not supported yet")
+ }
+ volumes = append(volumes, k8sutil.CreateVolumeWithSecret(mountNameStorageCredentials, secretObj.GetName()))
+ volumeMounts = append(volumeMounts, core.VolumeMount{
+ Name: mountNameStorageCredentials,
+ MountPath: mountPathStorageCredentials,
+ })
}
return volumes, volumeMounts, nil
diff --git a/pkg/integrations/storage_v2.go b/pkg/integrations/storage_v2.go
index ae5c1131f..c9a433f32 100644
--- a/pkg/integrations/storage_v2.go
+++ b/pkg/integrations/storage_v2.go
@@ -28,6 +28,7 @@ import (
pbImplStorageV2 "github.com/arangodb/kube-arangodb/integrations/storage/v2"
pbStorageV2 "github.com/arangodb/kube-arangodb/integrations/storage/v2/definition"
awsHelper "github.com/arangodb/kube-arangodb/pkg/util/aws"
+ "github.com/arangodb/kube-arangodb/pkg/util/azure"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
"github.com/arangodb/kube-arangodb/pkg/util/gcs"
"github.com/arangodb/kube-arangodb/pkg/util/svc"
@@ -72,6 +73,15 @@ func (b *storageV2) Register(cmd *cobra.Command, fs FlagEnvHandler) error {
fs.StringVar((*string)(&b.Configuration.GCS.Client.Provider.Type), "gcs.provider.type", string(gcs.ProviderTypeServiceAccount), "Type of the provided credentials"),
fs.StringVar(&b.Configuration.GCS.Client.Provider.ServiceAccount.File, "gcs.provider.sa.file", "", "Path to the file with ServiceAccount JSON"),
fs.StringVar(&b.Configuration.GCS.Client.Provider.ServiceAccount.JSON, "gcs.provider.sa.json", "", "ServiceAccount JSON"),
+
+ fs.StringVar(&b.Configuration.AzureBlobStorage.Client.Provider.TenantID, "azure-blob-storage.client.tenant-id", "", "Azure Client Tenant ID"),
+ fs.StringVar(&b.Configuration.AzureBlobStorage.Client.AccountName, "azure-blob-storage.account-name", "", "AzureBlobStorage Account ID"),
+ fs.StringVar(&b.Configuration.AzureBlobStorage.Client.Endpoint, "azure-blob-storage.endpoint", "", "AzureBlobStorage Endpoint"),
+ fs.StringVar((*string)(&b.Configuration.AzureBlobStorage.Client.Provider.Type), "azure-blob-storage.client.type", string(azure.ProviderTypeSecret), "Azure Client Provider"),
+ fs.StringVar(&b.Configuration.AzureBlobStorage.Client.Provider.Secret.ClientID, "azure-blob-storage.client.secret.client-id", "", "Azure ClientID"),
+ fs.StringVar(&b.Configuration.AzureBlobStorage.Client.Provider.Secret.ClientIDFile, "azure-blob-storage.client.secret.client-id-file", "", "Azure ClientID File"),
+ fs.StringVar(&b.Configuration.AzureBlobStorage.Client.Provider.Secret.ClientSecret, "azure-blob-storage.client.secret.client-secret", "", "Azure ClientSecret"),
+ fs.StringVar(&b.Configuration.AzureBlobStorage.Client.Provider.Secret.ClientSecretFile, "azure-blob-storage.client.secret.client-secret-file", "", "Azure ClientSecret File"),
)
}
diff --git a/pkg/util/azure/config.go b/pkg/util/azure/config.go
new file mode 100644
index 000000000..15a84577c
--- /dev/null
+++ b/pkg/util/azure/config.go
@@ -0,0 +1,53 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package azure
+
+import (
+ "fmt"
+
+ "github.com/Azure/azure-sdk-for-go/sdk/azcore"
+
+ "github.com/arangodb/kube-arangodb/pkg/util/errors"
+)
+
+type Config struct {
+ AccountName string
+
+ Endpoint string
+
+ Provider Provider
+}
+
+func (c Config) GetCredentials() (azcore.TokenCredential, error) {
+ return c.Provider.GetCredentials()
+}
+
+func (c Config) GetEndpoint() (string, error) {
+ if f := c.Endpoint; f != "" {
+ return f, nil
+ }
+
+ if f := c.AccountName; f != "" {
+ return fmt.Sprintf("https://%s.blob.core.windows.net/", f), nil
+ }
+
+ return "", errors.Errorf("account name or url not provided")
+}
diff --git a/pkg/util/azure/provider.go b/pkg/util/azure/provider.go
new file mode 100644
index 000000000..7f9e1ece7
--- /dev/null
+++ b/pkg/util/azure/provider.go
@@ -0,0 +1,109 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package azure
+
+import (
+ "os"
+
+ "github.com/Azure/azure-sdk-for-go/sdk/azcore"
+ "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
+
+ "github.com/arangodb/kube-arangodb/pkg/util/errors"
+)
+
+type ProviderType string
+
+const (
+ ProviderTypeSecret ProviderType = "secret"
+)
+
+type Provider struct {
+ Type ProviderType
+
+ TenantID string
+
+ Secret ProviderSecret
+}
+
+func (c Provider) GetCredentials() (azcore.TokenCredential, error) {
+ switch c.Type {
+ case ProviderTypeSecret:
+ return c.Secret.getCredentials(c.TenantID)
+ }
+
+ return nil, errors.Errorf("unable to get credentials for type '%s'", c.Type)
+}
+
+type ProviderSecret struct {
+ ClientID string
+ ClientIDFile string
+
+ ClientSecret string
+ ClientSecretFile string
+}
+
+func (p ProviderSecret) getCredentials(tenantID string) (*azidentity.ClientSecretCredential, error) {
+ id, err := p.GetClientID()
+ if err != nil {
+ return nil, err
+ }
+
+ secret, err := p.GetClientSecret()
+ if err != nil {
+ return nil, err
+ }
+
+ return azidentity.NewClientSecretCredential(tenantID, id, secret, nil)
+}
+
+func (p ProviderSecret) GetClientID() (string, error) {
+ if f := p.ClientIDFile; f != "" {
+ data, err := os.ReadFile(f)
+ if err != nil {
+ return "", err
+ }
+
+ return string(data), nil
+ }
+
+ if f := p.ClientID; f != "" {
+ return f, nil
+ }
+
+ return "", errors.New("no client id found")
+}
+
+func (p ProviderSecret) GetClientSecret() (string, error) {
+ if f := p.ClientSecretFile; f != "" {
+ data, err := os.ReadFile(f)
+ if err != nil {
+ return "", err
+ }
+
+ return string(data), nil
+ }
+
+ if f := p.ClientSecret; f != "" {
+ return f, nil
+ }
+
+ return "", errors.New("no client secret found")
+}
diff --git a/pkg/util/constants/constants.go b/pkg/util/constants/constants.go
index 3dc9f4870..ea7aaed8a 100644
--- a/pkg/util/constants/constants.go
+++ b/pkg/util/constants/constants.go
@@ -65,6 +65,9 @@ const (
SecretCredentialsServiceAccount = "serviceAccount" // Key in Secret used to store an GCS ServiceAccount File
+ SecretCredentialsAzureBlobStorageClientID = "clientId" // Key in Secret used to store an AzureBlobStorage ClientID
+ SecretCredentialsAzureBlobStorageClientSecret = "clientSecret" // Key in Secret used to store an AzureBlobStorage ClientSecret
+
SecretAccessPackageYaml = "accessPackage.yaml" // Key in Secret.data used to store a YAML encoded access package
FinalizerDeplRemoveChildFinalizers = "database.arangodb.com/remove-child-finalizers" // Finalizer added to ArangoDeployment, indicating the need to remove finalizers from all children
diff --git a/pkg/util/env_t.go b/pkg/util/env_t.go
new file mode 100644
index 000000000..b7b0f9207
--- /dev/null
+++ b/pkg/util/env_t.go
@@ -0,0 +1,35 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+//go:build testing
+
+package util
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func (e EnvironmentVariable) Require(t *testing.T) string {
+ v, ok := e.Lookup()
+ require.True(t, ok)
+ return v
+}
diff --git a/pkg/util/refs.go b/pkg/util/refs.go
index 6647ab74a..5e83cc7a7 100644
--- a/pkg/util/refs.go
+++ b/pkg/util/refs.go
@@ -238,3 +238,15 @@ func InitOptional[T any](in *T, ok bool) *T {
var z T
return &z
}
+
+func Count[T comparable](value T, values ...T) int {
+ r := 0
+
+ for _, v := range values {
+ if value == v {
+ r++
+ }
+ }
+
+ return r
+}
diff --git a/pkg/util/tests/azure.go b/pkg/util/tests/azure.go
new file mode 100644
index 000000000..3f3115bca
--- /dev/null
+++ b/pkg/util/tests/azure.go
@@ -0,0 +1,76 @@
+//
+// DISCLAIMER
+//
+// Copyright 2025 ArangoDB GmbH, Cologne, Germany
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Copyright holder is ArangoDB GmbH, Cologne, Germany
+//
+
+package tests
+
+import (
+ "testing"
+
+ "github.com/arangodb/kube-arangodb/pkg/util"
+ "github.com/arangodb/kube-arangodb/pkg/util/azure"
+)
+
+const (
+ TestAzureClientTenant util.EnvironmentVariable = "TEST_AZURE_CLIENT_TENANT"
+ TestAzureClientID util.EnvironmentVariable = "TEST_AZURE_CLIENT_ID"
+ TestAzureClientSecret util.EnvironmentVariable = "TEST_AZURE_CLIENT_SECRET"
+ TestAzureAccountName util.EnvironmentVariable = "TEST_AZURE_ACCOUNT_NAME"
+ TestAzureEndpoint util.EnvironmentVariable = "TEST_AZURE_ENDPOINT"
+ TestAzureContainer util.EnvironmentVariable = "TEST_AZURE_CONTAINER"
+)
+
+func GetAzureBlobStorageContainer(t *testing.T) string {
+ b, ok := TestAzureContainer.Lookup()
+ if !ok {
+ t.Skipf("Bucket does not exist")
+ }
+
+ return b
+}
+
+func GetAzureConfig(t *testing.T) azure.Config {
+ p := GetAzureProvider(t)
+
+ var z azure.Config
+
+ z.AccountName = TestAzureAccountName.GetOrDefault("")
+ z.Endpoint = TestAzureEndpoint.GetOrDefault("")
+ z.Provider = p
+
+ return z
+}
+
+func GetAzureProvider(t *testing.T) azure.Provider {
+ v, ok := TestAzureClientTenant.Lookup()
+ if !ok {
+ t.Skipf("Tenant Not Provided")
+ }
+
+ var p azure.Provider
+
+ p.TenantID = v
+
+ p.Type = azure.ProviderTypeSecret
+
+ p.Secret.ClientID = TestAzureClientID.Require(t)
+ p.Secret.ClientSecret = TestAzureClientSecret.Require(t)
+
+ return p
+}
From 0dc38b603c048f0f5e78540444ce7c367ffd49c4 Mon Sep 17 00:00:00 2001
From: ajanikow <12255597+ajanikow@users.noreply.github.com>
Date: Wed, 17 Dec 2025 12:16:44 +0000
Subject: [PATCH 2/8] Add docs
---
docs/platform/storage/azure_blob_storage.md | 43 +++++++++++++++++++++
docs/platform/storage/gcs.md | 2 +-
docs/platform/storage/minio.md | 2 +-
3 files changed, 45 insertions(+), 2 deletions(-)
create mode 100644 docs/platform/storage/azure_blob_storage.md
diff --git a/docs/platform/storage/azure_blob_storage.md b/docs/platform/storage/azure_blob_storage.md
new file mode 100644
index 000000000..8993494da
--- /dev/null
+++ b/docs/platform/storage/azure_blob_storage.md
@@ -0,0 +1,43 @@
+---
+layout: page
+title: Azure Blob Storage
+parent: Storage
+grand_parent: ArangoDBPlatform
+nav_order: 3
+---
+
+# Integration
+
+In order to connect to the Azure Blob storage:
+
+## Azure Credentials
+
+Client ID & Secret with access to the storage container and accounts needs to be saved in the secret.
+
+```shell
+kubectl create secret generic credentials --from-literal 'clientId=' --from-literal 'clientSecret='
+```
+
+## Object
+
+Once the Secret is created, we are able to create ArangoPlatformStorage.
+
+```
+echo "---
+apiVersion: platform.arangodb.com/v1beta1
+kind: ArangoPlatformStorage
+metadata:
+ name: deployment
+ namespace: namespace
+spec:
+ backend:
+ azureBlobStorage:
+ bucketName:
+ bucketPath:
+ credentialsSecret:
+ name: credentials
+ tenantID: gcr-for-testing
+ accountName:
+ endpoint:
+" | kubectl apply -f -
+```
\ No newline at end of file
diff --git a/docs/platform/storage/gcs.md b/docs/platform/storage/gcs.md
index aefb1f9d0..d1371426b 100644
--- a/docs/platform/storage/gcs.md
+++ b/docs/platform/storage/gcs.md
@@ -15,7 +15,7 @@ In order to connect to the GCS (Google Cloud Storage):
ServiceAccount with access to the storage needs to be saved in the secret.
```shell
-kubectl create secret generic ca --from-file 'serviceAccount='
+kubectl create secret generic credentials --from-file 'serviceAccount='
```
## Object
diff --git a/docs/platform/storage/minio.md b/docs/platform/storage/minio.md
index b083cde1f..c9d401eab 100644
--- a/docs/platform/storage/minio.md
+++ b/docs/platform/storage/minio.md
@@ -3,7 +3,7 @@ layout: page
title: MinIO
parent: Storage
grand_parent: ArangoDBPlatform
-nav_order: 3
+nav_order: 4
---
# Integration
From 4837c844784f4e998e0ce63ce6db961b9d979c48 Mon Sep 17 00:00:00 2001
From: ajanikow <12255597+ajanikow@users.noreply.github.com>
Date: Wed, 17 Dec 2025 12:59:35 +0000
Subject: [PATCH 3/8] Add docs
---
docs/api/ArangoPlatformStorage.V1Beta1.md | 8 +++-----
pkg/apis/platform/v1beta1/storage_spec_backend_abs.go | 1 -
pkg/crd/crds/platform-storage.schema.generated.yaml | 1 -
3 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/docs/api/ArangoPlatformStorage.V1Beta1.md b/docs/api/ArangoPlatformStorage.V1Beta1.md
index 095bb57ac..90cade7b6 100644
--- a/docs/api/ArangoPlatformStorage.V1Beta1.md
+++ b/docs/api/ArangoPlatformStorage.V1Beta1.md
@@ -21,7 +21,7 @@ used in format https://.blob.core.windows.net/
### .spec.backend.azureBlobStorage.bucketName
-Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go#L47)
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go#L46)
This field is **required**
@@ -31,7 +31,7 @@ BucketName specifies the name of the bucket
### .spec.backend.azureBlobStorage.bucketPath
-Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go#L51)
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go#L50)
BucketPath specifies the Prefix within the bucket
@@ -49,9 +49,7 @@ Name of the object
### .spec.backend.azureBlobStorage.endpoint
-Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go#L43)
-
-This field is **required**
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.3.3/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go#L42)
Endpoint specifies the Azure Storage custom endpoint
diff --git a/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go b/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go
index 0afc9f6d5..e13de8705 100644
--- a/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go
+++ b/pkg/apis/platform/v1beta1/storage_spec_backend_abs.go
@@ -39,7 +39,6 @@ type ArangoPlatformStorageSpecBackendAzureBlobStorage struct {
AccountName *string `json:"accountName,omitempty"`
// Endpoint specifies the Azure Storage custom endpoint
- // +doc/required
Endpoint *string `json:"endpoint,omitempty"`
// BucketName specifies the name of the bucket
diff --git a/pkg/crd/crds/platform-storage.schema.generated.yaml b/pkg/crd/crds/platform-storage.schema.generated.yaml
index ff8f9c238..de69bd7ac 100644
--- a/pkg/crd/crds/platform-storage.schema.generated.yaml
+++ b/pkg/crd/crds/platform-storage.schema.generated.yaml
@@ -125,7 +125,6 @@ v1beta1:
- accountName
- bucketName
- credentialsSecret
- - endpoint
- tenantID
type: object
gcs:
From e7c8e0e21bc7718256c20c2f28175a17c8878f4f Mon Sep 17 00:00:00 2001
From: ajanikow <12255597+ajanikow@users.noreply.github.com>
Date: Wed, 17 Dec 2025 13:27:23 +0000
Subject: [PATCH 4/8] Add docs
---
docs/cli/arangodb_operator_integration.md | 2 ++
pkg/integrations/sidecar/integration.storage.v2.go | 12 ++++++++++++
pkg/integrations/storage_v2.go | 2 ++
3 files changed, 16 insertions(+)
diff --git a/docs/cli/arangodb_operator_integration.md b/docs/cli/arangodb_operator_integration.md
index cd5b22023..dcd272bbc 100644
--- a/docs/cli/arangodb_operator_integration.md
+++ b/docs/cli/arangodb_operator_integration.md
@@ -89,6 +89,8 @@ Flags:
--integration.shutdown.v1.internal Defines if Internal access to service shutdown.v1 is enabled (Env: INTEGRATION_SHUTDOWN_V1_INTERNAL) (default true)
--integration.storage.v2 StorageBucket V2 Integration (Env: INTEGRATION_STORAGE_V2)
--integration.storage.v2.azure-blob-storage.account-name string AzureBlobStorage Account ID (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_ACCOUNT_NAME)
+ --integration.storage.v2.azure-blob-storage.bucket.name string Bucket name (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_BUCKET_NAME)
+ --integration.storage.v2.azure-blob-storage.bucket.prefix string Bucket Prefix (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_BUCKET_PREFIX)
--integration.storage.v2.azure-blob-storage.client.secret.client-id string Azure ClientID (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_CLIENT_SECRET_CLIENT_ID)
--integration.storage.v2.azure-blob-storage.client.secret.client-id-file string Azure ClientID File (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_CLIENT_SECRET_CLIENT_ID_FILE)
--integration.storage.v2.azure-blob-storage.client.secret.client-secret string Azure ClientSecret (Env: INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_CLIENT_SECRET_CLIENT_SECRET)
diff --git a/pkg/integrations/sidecar/integration.storage.v2.go b/pkg/integrations/sidecar/integration.storage.v2.go
index 532c9b103..f136eb925 100644
--- a/pkg/integrations/sidecar/integration.storage.v2.go
+++ b/pkg/integrations/sidecar/integration.storage.v2.go
@@ -181,6 +181,18 @@ func (i IntegrationStorageV2) Envs() ([]core.EnvVar, error) {
Name: "INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_ENDPOINT",
Value: azureBlobStorage.GetEndpoint(),
},
+ core.EnvVar{
+ Name: "INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_ACCOUNT_NAME",
+ Value: azureBlobStorage.GetAccountName(),
+ },
+ core.EnvVar{
+ Name: "INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_BUCKET_NAME",
+ Value: azureBlobStorage.GetBucketName(),
+ },
+ core.EnvVar{
+ Name: "INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_BUCKET_PREFIX",
+ Value: azureBlobStorage.GetBucketPrefix(),
+ },
)
}
diff --git a/pkg/integrations/storage_v2.go b/pkg/integrations/storage_v2.go
index c9a433f32..c3b4bf05e 100644
--- a/pkg/integrations/storage_v2.go
+++ b/pkg/integrations/storage_v2.go
@@ -77,6 +77,8 @@ func (b *storageV2) Register(cmd *cobra.Command, fs FlagEnvHandler) error {
fs.StringVar(&b.Configuration.AzureBlobStorage.Client.Provider.TenantID, "azure-blob-storage.client.tenant-id", "", "Azure Client Tenant ID"),
fs.StringVar(&b.Configuration.AzureBlobStorage.Client.AccountName, "azure-blob-storage.account-name", "", "AzureBlobStorage Account ID"),
fs.StringVar(&b.Configuration.AzureBlobStorage.Client.Endpoint, "azure-blob-storage.endpoint", "", "AzureBlobStorage Endpoint"),
+ fs.StringVar(&b.Configuration.AzureBlobStorage.BucketName, "azure-blob-storage.bucket.name", "", "Bucket name"),
+ fs.StringVar(&b.Configuration.AzureBlobStorage.BucketPrefix, "azure-blob-storage.bucket.prefix", "", "Bucket Prefix"),
fs.StringVar((*string)(&b.Configuration.AzureBlobStorage.Client.Provider.Type), "azure-blob-storage.client.type", string(azure.ProviderTypeSecret), "Azure Client Provider"),
fs.StringVar(&b.Configuration.AzureBlobStorage.Client.Provider.Secret.ClientID, "azure-blob-storage.client.secret.client-id", "", "Azure ClientID"),
fs.StringVar(&b.Configuration.AzureBlobStorage.Client.Provider.Secret.ClientIDFile, "azure-blob-storage.client.secret.client-id-file", "", "Azure ClientID File"),
From a582f05cceb5ffcc7adcf413bc70535169056c7e Mon Sep 17 00:00:00 2001
From: ajanikow <12255597+ajanikow@users.noreply.github.com>
Date: Wed, 17 Dec 2025 14:12:13 +0000
Subject: [PATCH 5/8] Add docs
---
pkg/apis/platform/v1beta1/storage_spec_backend.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/pkg/apis/platform/v1beta1/storage_spec_backend.go b/pkg/apis/platform/v1beta1/storage_spec_backend.go
index 145431fb7..4ebe12e85 100644
--- a/pkg/apis/platform/v1beta1/storage_spec_backend.go
+++ b/pkg/apis/platform/v1beta1/storage_spec_backend.go
@@ -63,10 +63,6 @@ func (s *ArangoPlatformStorageSpecBackend) Validate() error {
return errors.Errorf("Backend is not specified")
}
- if s.S3 == nil && s.GCS == nil && s.AzureBlobStorage == nil {
- return errors.Errorf("At least one backend needs to be defined")
- }
-
switch util.Count(true, s.S3 != nil, s.GCS != nil, s.AzureBlobStorage != nil) {
case 0:
return errors.Errorf("At least one backend needs to be defined")
From 1eb16c66339e2169355f2d7d13fec7a34c81bb43 Mon Sep 17 00:00:00 2001
From: ajanikow <12255597+ajanikow@users.noreply.github.com>
Date: Wed, 17 Dec 2025 14:25:28 +0000
Subject: [PATCH 6/8] Add docs
---
integrations/storage/v2/object.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/integrations/storage/v2/object.go b/integrations/storage/v2/object.go
index de54f8496..bbeee5cdc 100644
--- a/integrations/storage/v2/object.go
+++ b/integrations/storage/v2/object.go
@@ -147,17 +147,17 @@ func NewIOFromObject(ctx context.Context, client kclient.Client, in *platformApi
if v := azureBlobStorage.CredentialsSecret; v != nil {
secret, err := client.Kubernetes().CoreV1().Secrets(v.GetNamespace(in)).Get(ctx, v.GetName(), meta.GetOptions{})
if err != nil {
- return nil, errors.WithMessage(err, "Failed to get AzureBlueStorage secret")
+ return nil, errors.WithMessage(err, "Failed to get AzureBlobStorage secret")
}
cid, ok := secret.Data[utilConstants.SecretCredentialsAzureBlobStorageClientID]
if !ok {
- return nil, errors.Errorf("Failed to get AzureBlueStorage secret %s data: Key %s not found", secret.GetName(), utilConstants.SecretCredentialsAzureBlobStorageClientID)
+ return nil, errors.Errorf("Failed to get AzureBlobStorage secret %s data: Key %s not found", secret.GetName(), utilConstants.SecretCredentialsAzureBlobStorageClientID)
}
cs, ok := secret.Data[utilConstants.SecretCredentialsAzureBlobStorageClientSecret]
if !ok {
- return nil, errors.Errorf("Failed to get AzureBlueStorage secret %s data: Key %s not found", secret.GetName(), utilConstants.SecretCredentialsAzureBlobStorageClientSecret)
+ return nil, errors.Errorf("Failed to get AzureBlobStorage secret %s data: Key %s not found", secret.GetName(), utilConstants.SecretCredentialsAzureBlobStorageClientSecret)
}
config.Provider.Secret.ClientID = string(cid)
From 076e8c23cf5176376d735414643b889ae39ed9d7 Mon Sep 17 00:00:00 2001
From: ajanikow <12255597+ajanikow@users.noreply.github.com>
Date: Wed, 17 Dec 2025 18:46:48 +0000
Subject: [PATCH 7/8] Add docs
---
docs/platform/storage/azure_blob_storage.md | 2 +-
docs/platform/storage/gcs.md | 2 +-
pkg/integrations/sidecar/integration.storage.v2.go | 4 ----
3 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/docs/platform/storage/azure_blob_storage.md b/docs/platform/storage/azure_blob_storage.md
index 8993494da..47fe214ed 100644
--- a/docs/platform/storage/azure_blob_storage.md
+++ b/docs/platform/storage/azure_blob_storage.md
@@ -36,7 +36,7 @@ spec:
bucketPath:
credentialsSecret:
name: credentials
- tenantID: gcr-for-testing
+ tenantID:
accountName:
endpoint:
" | kubectl apply -f -
diff --git a/docs/platform/storage/gcs.md b/docs/platform/storage/gcs.md
index d1371426b..3d4258e47 100644
--- a/docs/platform/storage/gcs.md
+++ b/docs/platform/storage/gcs.md
@@ -36,6 +36,6 @@ spec:
bucketPath:
credentialsSecret:
name: credentials
- projectID: gcr-for-testing
+ projectID:
" | kubectl apply -f -
```
\ No newline at end of file
diff --git a/pkg/integrations/sidecar/integration.storage.v2.go b/pkg/integrations/sidecar/integration.storage.v2.go
index f136eb925..6e9a82fe4 100644
--- a/pkg/integrations/sidecar/integration.storage.v2.go
+++ b/pkg/integrations/sidecar/integration.storage.v2.go
@@ -181,10 +181,6 @@ func (i IntegrationStorageV2) Envs() ([]core.EnvVar, error) {
Name: "INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_ENDPOINT",
Value: azureBlobStorage.GetEndpoint(),
},
- core.EnvVar{
- Name: "INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_ACCOUNT_NAME",
- Value: azureBlobStorage.GetAccountName(),
- },
core.EnvVar{
Name: "INTEGRATION_STORAGE_V2_AZURE_BLOB_STORAGE_BUCKET_NAME",
Value: azureBlobStorage.GetBucketName(),
From eed3f32bc890950b3c59fc9b5baa5394500eba86 Mon Sep 17 00:00:00 2001
From: ajanikow <12255597+ajanikow@users.noreply.github.com>
Date: Wed, 17 Dec 2025 19:48:21 +0000
Subject: [PATCH 8/8] Add docs
---
docs/platform/storage/azure_blob_storage.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/platform/storage/azure_blob_storage.md b/docs/platform/storage/azure_blob_storage.md
index 47fe214ed..ae3185a4e 100644
--- a/docs/platform/storage/azure_blob_storage.md
+++ b/docs/platform/storage/azure_blob_storage.md
@@ -12,7 +12,7 @@ In order to connect to the Azure Blob storage:
## Azure Credentials
-Client ID & Secret with access to the storage container and accounts needs to be saved in the secret.
+Client ID & Secret with access to the storage container and accounts need to be saved in the secret.
```shell
kubectl create secret generic credentials --from-literal 'clientId=' --from-literal 'clientSecret='