Skip to content

Commit cf8999d

Browse files
authored
feat: Add feature flag to gate the Azure Blob storage connector (#2029) (#2036)
feat: Add feature flag to gate the Azure Blob storage connector Issue - #2027 Summary - Added `OPENRAG_AZURE_BLOB_ENABLED`, a standalone kill switch (default `true`) that force-hides the Azure Blob connector independently of `IBM_AUTH_ENABLED`, and wired it through every deployment surface. Backend - Added `is_azure_blob_enabled()` in `src/config/settings.py`, reading `OPENRAG_AZURE_BLOB_ENABLED` (default `true`). - Updated `AzureBlobConnector.is_available()` to AND the new flag with the existing `IBM_AUTH_ENABLED` / `OPENRAG_DEV_AZURE_BLOB` gate. Deployment - Documented and defaulted the new env var in `.env.example` and `docker-compose.yml`. - Threaded `OPENRAG_AZURE_BLOB_ENABLED` through the Helm chart (`values.yaml` `global.azureBlob.enabled`, `backend-dotenv.yaml`) and the Go operator's `env.go` default env vars. Connector Access - `governable_connector_types()` now drops `azure_blob` when `OPENRAG_AZURE_BLOB_ENABLED` is off, even if IBM auth is on. Tests - Added a unit test verifying `azure_blob` is excluded under the kill switch while `aws_s3` and `ibm_cos` remain governable.
1 parent eeb660d commit cf8999d

9 files changed

Lines changed: 64 additions & 14 deletions

File tree

.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,13 @@ AWS_SECRET_ACCESS_KEY=
170170
# dialog. Two auth modes are supported: a connection string, OR account name +
171171
# key (+ optional custom blob endpoint).
172172
#
173+
# Feature kill switch for the Azure Blob connector (default: true = enabled).
174+
# Independent of IBM_AUTH_ENABLED: set to false to force-hide the connector in
175+
# the UI even when IBM auth is on. When true, the connector still requires the
176+
# Enterprise/SaaS gate (IBM_AUTH_ENABLED) or OPENRAG_DEV_AZURE_BLOB below --
177+
# this flag is AND-ed with that gate, not an override.
178+
# OPENRAG_AZURE_BLOB_ENABLED=true
179+
#
173180
# Local dev / Azurite testing: set OPENRAG_DEV_AZURE_BLOB=true to enable the
174181
# connector without IBM_AUTH_ENABLED. NEVER use in production.
175182
# OPENRAG_DEV_AZURE_BLOB=false

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ services:
121121
- UPLOAD_BATCH_SIZE=${UPLOAD_BATCH_SIZE:-25}
122122
- MAX_WORKERS=${MAX_WORKERS:-4}
123123
- IBM_AUTH_ENABLED=${IBM_AUTH_ENABLED:-false}
124+
- OPENRAG_AZURE_BLOB_ENABLED=${OPENRAG_AZURE_BLOB_ENABLED:-true}
124125
- PLATFORM_AUTH_DEV_MODE=${PLATFORM_AUTH_DEV_MODE:-false}
125126
- PLATFORM_USERNAME=${PLATFORM_USERNAME}
126127
- PLATFORM_PASSWORD=${PLATFORM_PASSWORD}

enhancements/connectors/azure_blob/connector.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
from posixpath import basename
2020
from typing import Any
2121

22-
from config.settings import IBM_AUTH_ENABLED, is_dev_azure_blob_enabled
22+
from config.settings import (
23+
IBM_AUTH_ENABLED,
24+
is_azure_blob_enabled,
25+
is_dev_azure_blob_enabled,
26+
)
2327
from connectors.base import BaseConnector, ConnectorDocument, DocumentACL
2428
from utils.logging_config import get_logger
2529

@@ -71,17 +75,19 @@ class AzureBlobConnector(BaseConnector):
7175
SECRET_CONFIG_KEYS = ("connection_string", "account_key")
7276

7377
# BaseConnector uses these for the default env-availability probe; the
74-
# bucket-kind override below makes availability hinge on IBM_AUTH_ENABLED
75-
# (or OPENRAG_DEV_AZURE_BLOB for local dev).
78+
# bucket-kind override below makes availability hinge on the kill switch and
79+
# IBM_AUTH_ENABLED (or OPENRAG_DEV_AZURE_BLOB for local dev).
7680
CLIENT_ID_ENV_VAR = "AZURE_STORAGE_ACCOUNT_NAME"
7781
CLIENT_SECRET_ENV_VAR = "AZURE_STORAGE_ACCOUNT_KEY"
7882

7983
@classmethod
8084
def is_available(cls, manager, user_id=None) -> bool:
81-
# Gated by feature flag like the other bucket connectors (aws_s3, ibm_cos).
82-
# OPENRAG_DEV_AZURE_BLOB=true bypasses the IBM_AUTH_ENABLED requirement for
83-
# local dev testing (e.g. against Azurite). Never use in production.
84-
return IBM_AUTH_ENABLED or is_dev_azure_blob_enabled()
85+
# OPENRAG_AZURE_BLOB_ENABLED is a kill switch (default true): set it false
86+
# to force-hide the connector even when IBM auth is on. When true, the
87+
# Enterprise/SaaS gate still applies -- IBM_AUTH_ENABLED like the other
88+
# bucket connectors (aws_s3, ibm_cos), or OPENRAG_DEV_AZURE_BLOB=true to
89+
# bypass IBM auth for local dev (e.g. against Azurite; never in production).
90+
return is_azure_blob_enabled() and (IBM_AUTH_ENABLED or is_dev_azure_blob_enabled())
8591

8692
@classmethod
8793
def register_routes(cls, app) -> None:

kubernetes/helm/openrag/templates/backend/backend-dotenv.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ stringData:
103103
{{- end }}
104104
105105
# Azure Blob Storage connector (optional env defaults; gated by IBM_AUTH_ENABLED or OPENRAG_DEV_AZURE_BLOB)
106+
OPENRAG_AZURE_BLOB_ENABLED={{ .Values.global.azureBlob.enabled }}
106107
{{- if .Values.global.azureBlob.connectionString }}
107108
AZURE_STORAGE_CONNECTION_STRING={{ .Values.global.azureBlob.connectionString | quote }}
108109
{{- end }}
@@ -210,7 +211,7 @@ stringData:
210211
{{- if .Values.backend.encryption.key }}
211212
OPENRAG_ENCRYPTION_KEY={{ .Values.backend.encryption.key | quote }}
212213
{{- end }}
213-
214+
214215
# Langflow auth (OpenRag uses this to determine how to authenticate with Langflow)
215216
LANGFLOW_AUTO_LOGIN={{ ternary "true" "false" .Values.langflow.auth.autoLogin }}
216217

kubernetes/helm/openrag/values.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ global:
6060
# normally entered per-connection in the UI. Provide EITHER a connection string
6161
# OR account name + key (+ optional custom blob endpoint).
6262
azureBlob:
63-
enabled: false
63+
enabled: true # OPENRAG_AZURE_BLOB_ENABLED
6464
connectionString: "" # AZURE_STORAGE_CONNECTION_STRING
6565
accountName: "" # AZURE_STORAGE_ACCOUNT_NAME
6666
accountKey: "" # AZURE_STORAGE_ACCOUNT_KEY
6767
endpoint: "" # AZURE_STORAGE_ENDPOINT (optional)
68-
68+
6969
# ============================================================================
7070
# Langflow Configuration
7171
# ============================================================================
@@ -182,7 +182,7 @@ langflow:
182182
value: "langflow-db"
183183
sqlite:
184184
enabled: false
185-
185+
186186
# Langfuse Tracking
187187
langfuse:
188188
secretKey: ""

kubernetes/operator/internal/controller/env.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ func NewEnvVarManager() *EnvVarManager {
151151
// Azure Blob Storage connector (Enterprise/SaaS; gated by IBM_AUTH_ENABLED or OPENRAG_DEV_AZURE_BLOB for local dev).
152152
// Optional env defaults — credentials are normally entered per-connection
153153
// in the UI. Override via the CR spec.env when env-based defaults are wanted.
154+
// OPENRAG_AZURE_BLOB_ENABLED is intentionally omitted: the backend defaults it to
155+
// true (kill switch); set it via spec.env to force-hide the connector.
154156
"AZURE_STORAGE_CONNECTION_STRING": "",
155157
"AZURE_STORAGE_ACCOUNT_NAME": "",
156158
"AZURE_STORAGE_ACCOUNT_KEY": "",

src/config/settings.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,19 @@ def is_dev_azure_blob_enabled() -> bool:
258258
return raw in ("true", "1", "yes", "on")
259259

260260

261+
def is_azure_blob_enabled() -> bool:
262+
"""Feature kill switch for the Azure Blob connector (default: enabled).
263+
264+
Independent of ``IBM_AUTH_ENABLED``. Set ``OPENRAG_AZURE_BLOB_ENABLED=false``
265+
to force-hide the connector in the UI even when IBM auth is on. When true
266+
(the default), availability still requires the Enterprise/SaaS gate
267+
(``IBM_AUTH_ENABLED``) or the ``OPENRAG_DEV_AZURE_BLOB`` dev bypass -- this
268+
flag is subtractive (AND-ed with that gate), not an override.
269+
"""
270+
raw = os.getenv("OPENRAG_AZURE_BLOB_ENABLED", "true").strip().lower()
271+
return raw in ("true", "1", "yes", "on")
272+
273+
261274
def is_cloud_context() -> bool:
262275
"""True when connector policy and SaaS settings guards should apply."""
263276
from utils.run_mode_utils import is_run_mode_saas

src/services/connector_access_service.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,16 @@ def is_bucket_connector_type(connector_type: str) -> bool:
2323

2424
def governable_connector_types() -> tuple[str, ...]:
2525
"""Types shown in admin Connectors Permission — independent of the live connectors list."""
26-
from config.settings import IBM_AUTH_ENABLED, is_cloud_context
26+
from config.settings import IBM_AUTH_ENABLED, is_azure_blob_enabled, is_cloud_context
2727

28+
types = CONNECTOR_TYPES
29+
# Honor the Azure Blob kill switch so a force-hidden connector doesn't linger
30+
# as a governable row here (mirrors AzureBlobConnector.is_available()).
31+
if not is_azure_blob_enabled():
32+
types = tuple(t for t in types if t != "azure_blob")
2833
if is_cloud_context() and not IBM_AUTH_ENABLED:
29-
return tuple(t for t in CONNECTOR_TYPES if t not in _BUCKET_CONNECTOR_TYPES)
30-
return CONNECTOR_TYPES
34+
types = tuple(t for t in types if t not in _BUCKET_CONNECTOR_TYPES)
35+
return types
3136

3237

3338
async def get_access_map(session: AsyncSession) -> dict[str, bool]:

tests/unit/services/test_connector_access_service.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,21 @@ def test_governable_connector_types_includes_buckets_with_ibm_auth(monkeypatch):
162162
assert "ibm_cos" in governable
163163

164164

165+
def test_governable_connector_types_excludes_azure_blob_when_kill_switch_off(monkeypatch):
166+
# Kill switch off must hide azure_blob from the admin permission tab even
167+
# when IBM auth is on (matches AzureBlobConnector.is_available()).
168+
monkeypatch.setenv("OPENRAG_RUN_MODE", "saas")
169+
monkeypatch.setattr("config.settings.IBM_AUTH_ENABLED", True)
170+
monkeypatch.setenv("OPENRAG_AZURE_BLOB_ENABLED", "false")
171+
172+
governable = governable_connector_types()
173+
174+
assert "azure_blob" not in governable
175+
# Other bucket connectors are unaffected by the Azure-specific kill switch.
176+
assert "aws_s3" in governable
177+
assert "ibm_cos" in governable
178+
179+
165180
@pytest.mark.asyncio
166181
async def test_list_access_for_admin_includes_disabled_types(session, monkeypatch):
167182
"""Admin permission list is independent of the filtered connectors tab."""

0 commit comments

Comments
 (0)