Skip to content

Commit 68d3246

Browse files
shirshankaclaude
authored andcommitted
feat(cli): add --support flag for DataHub Cloud support team login
Adds `--support` flag (used with `--sso`) that navigates to `/support/authenticate` instead of `/authenticate`, enabling the support team to log into customer instances for debugging. Usage: datahub init --sso --support --host https://customer.acryl.io/gms Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 31df6b6 commit 68d3246

File tree

4 files changed

+87
-7
lines changed

4 files changed

+87
-7
lines changed

metadata-ingestion/src/datahub/cli/resources/INIT_AGENT_CONTEXT.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ playwright install chromium
7070
`--sso` is mutually exclusive with `--token`, `--username`, and `--password`.
7171
If Playwright is not installed, the command prints step-by-step install instructions and exits.
7272

73+
### Support login (DataHub Cloud)
74+
75+
For the support team debugging customer instances, add `--support` to use the
76+
`/support/authenticate` login path:
77+
78+
```bash
79+
datahub init --sso --support --host https://customer.acryl.io/gms
80+
```
81+
82+
`--support` requires `--sso`.
83+
7384
## Environment variables
7485

7586
| Variable | CLI equivalent |

metadata-ingestion/src/datahub/cli/sso_cli.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,19 @@ def _check_playwright_ready() -> None:
3434

3535

3636
def browser_sso_login(
37-
frontend_url: str, token_duration: str, timeout_ms: int = 120_000
37+
frontend_url: str,
38+
token_duration: str,
39+
timeout_ms: int = 120_000,
40+
support: bool = False,
3841
) -> Tuple[str, str]:
3942
"""Open browser for SSO login, extract session, generate access token.
4043
4144
Args:
4245
frontend_url: The DataHub frontend URL (e.g. http://localhost:9002).
4346
token_duration: Token validity duration (e.g. ONE_HOUR).
4447
timeout_ms: How long to wait for SSO login to complete, in milliseconds.
48+
support: If True, use /support/authenticate path for DataHub Cloud
49+
support team access to customer instances.
4550
4651
Returns:
4752
Tuple of (token_name, access_token).
@@ -53,15 +58,19 @@ def browser_sso_login(
5358

5459
from playwright.sync_api import sync_playwright
5560

56-
click.echo("Opening browser for SSO login...")
61+
auth_path = "/support/authenticate" if support else "/authenticate"
62+
if support:
63+
click.echo("Opening browser for support SSO login...")
64+
else:
65+
click.echo("Opening browser for SSO login...")
5766
click.echo("Complete the login in your browser.\n")
5867

5968
with sync_playwright() as p:
6069
browser = p.chromium.launch(headless=False)
6170
context = browser.new_context()
6271
page = context.new_page()
6372

64-
page.goto(f"{frontend_url}/authenticate")
73+
page.goto(f"{frontend_url}{auth_path}")
6574

6675
# Wait for the actor cookie, which signals successful SSO login.
6776
actor_urn = None

metadata-ingestion/src/datahub/entrypoints.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,12 @@ def _validate_init_inputs(
280280
default=False,
281281
help="Open browser for SSO login (requires: pip install 'acryl-datahub[sso]' && playwright install chromium)",
282282
)
283+
@click.option(
284+
"--support",
285+
is_flag=True,
286+
default=False,
287+
help="Use support login path for DataHub Cloud customer debugging (use with --sso)",
288+
)
283289
@click.option(
284290
"--agent-context",
285291
is_flag=True,
@@ -295,6 +301,7 @@ def init(
295301
token_duration: Optional[str] = None,
296302
force: bool = False,
297303
sso: bool = False,
304+
support: bool = False,
298305
agent_context: bool = False,
299306
) -> None:
300307
"""Configure which DataHub instance to connect to.
@@ -329,6 +336,11 @@ def init(
329336
datahub init --sso --host https://example.com/gms \\
330337
--token-duration ONE_MONTH
331338
339+
\b
340+
Support Login (DataHub Cloud — customer debugging):
341+
datahub init --sso --support \\
342+
--host https://customer.acryl.io/gms
343+
332344
\b
333345
Environment Variables (for automation):
334346
export DATAHUB_GMS_URL=http://localhost:8080
@@ -356,6 +368,10 @@ def init(
356368
click.echo(text)
357369
return
358370

371+
# Validate: --support requires --sso
372+
if support and not sso:
373+
raise click.UsageError("--support requires --sso")
374+
359375
# Show deprecation warning if --use-password used
360376
if use_password:
361377
click.echo(
@@ -417,7 +433,9 @@ def init(
417433
from datahub.cli.sso_cli import browser_sso_login
418434

419435
frontend_url = guess_frontend_url_from_gms_url(host_value)
420-
_, token_value = browser_sso_login(frontend_url, effective_duration)
436+
_, token_value = browser_sso_login(
437+
frontend_url, effective_duration, support=support
438+
)
421439
click.echo(f"✓ Generated token (expires: {effective_duration})")
422440
elif should_generate_token or use_password:
423441
# Generate token from credentials

metadata-ingestion/tests/unit/cli/test_init_cli.py

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,9 @@ def test_sso_calls_browser_login_and_writes_config(
635635
assert "localhost:8080" in config_content
636636

637637
# Verify frontend URL derivation: 8080 -> 9002
638-
mock_browser_login.assert_called_once_with("http://localhost:9002", "ONE_MONTH")
638+
mock_browser_login.assert_called_once_with(
639+
"http://localhost:9002", "ONE_MONTH", support=False
640+
)
639641

640642
def test_sso_with_custom_duration(self, temp_config: Path, clean_env: None) -> None:
641643
"""Test --sso respects --token-duration."""
@@ -654,7 +656,9 @@ def test_sso_with_custom_duration(self, temp_config: Path, clean_env: None) -> N
654656
)
655657

656658
assert result.exit_code == 0
657-
mock_browser_login.assert_called_once_with("http://localhost:9002", "ONE_MONTH")
659+
mock_browser_login.assert_called_once_with(
660+
"http://localhost:9002", "ONE_MONTH", support=False
661+
)
658662

659663
def test_sso_with_acryl_cloud_url(self, temp_config: Path, clean_env: None) -> None:
660664
"""Test --sso with Acryl Cloud URL derives correct frontend URL."""
@@ -673,9 +677,47 @@ def test_sso_with_acryl_cloud_url(self, temp_config: Path, clean_env: None) -> N
673677
assert result.exit_code == 0
674678
# For acryl.io, frontend URL is the base without /gms
675679
mock_browser_login.assert_called_once_with(
676-
"https://my-instance.acryl.io", "ONE_HOUR"
680+
"https://my-instance.acryl.io", "ONE_HOUR", support=False
677681
)
678682

683+
def test_sso_support_flag(self, temp_config: Path, clean_env: None) -> None:
684+
"""Test --sso --support passes support=True to browser_sso_login."""
685+
runner = CliRunner()
686+
with patch("datahub.cli.sso_cli.browser_sso_login") as mock_browser_login:
687+
mock_browser_login.return_value = ("name", "token-value")
688+
result = runner.invoke(
689+
init,
690+
[
691+
"--sso",
692+
"--support",
693+
"--host",
694+
"https://customer.acryl.io/gms",
695+
],
696+
)
697+
698+
assert result.exit_code == 0
699+
mock_browser_login.assert_called_once_with(
700+
"https://customer.acryl.io", "ONE_HOUR", support=True
701+
)
702+
703+
def test_support_without_sso_errors(
704+
self, temp_config: Path, clean_env: None
705+
) -> None:
706+
"""Test --support without --sso raises an error."""
707+
runner = CliRunner()
708+
result = runner.invoke(
709+
init,
710+
[
711+
"--support",
712+
"--host",
713+
"http://localhost:8080",
714+
"--token",
715+
"my-token",
716+
],
717+
)
718+
assert result.exit_code != 0
719+
assert "--support requires --sso" in result.output
720+
679721

680722
class TestTokenDuration:
681723
"""Tests for configurable token duration."""

0 commit comments

Comments
 (0)