Skip to content

Commit 966aa2c

Browse files
bsquizzclaude
andauthored
feat: improve 'namespace describe' CLI output (#555)
* feat: improve 'namespace describe' CLI output with tabulate tables Use tabulate to format namespace info and credentials into clean aligned tables. Hide empty fields (gateway route, zero deploy counts, missing credentials) and display ROSA cluster kubeconfig instructions when a cluster is detected. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Run pre-commit --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fa9b582 commit 966aa2c

3 files changed

Lines changed: 71 additions & 18 deletions

File tree

bonfire/bonfire.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
wait_for_db_resources,
3737
wait_on_cji,
3838
whoami,
39-
get_console_url,
4039
get_pool_size_limit,
4140
get_reserved_namespace_quantity,
4241
log_namespace_events,
@@ -1637,16 +1636,13 @@ def _cmd_config_deploy(
16371636
else:
16381637
log.info("successfully deployed to namespace %s", ns)
16391638
es_telemetry.send_telemetry("successful deployment")
1640-
url = get_console_url()
1641-
if url:
1642-
ns_url = f"{url}/k8s/cluster/projects/{ns}"
1643-
log.info("namespace url: %s", ns_url)
1644-
log.info(
1645-
"resource usage dashboard for namespace '%s': %s",
1646-
ns,
1647-
conf.RESOURCE_DASHBOARD_URL.format(namespace=ns),
1648-
)
1639+
log.info(
1640+
"resource usage dashboard for namespace '%s': %s",
1641+
ns,
1642+
conf.RESOURCE_DASHBOARD_URL.format(namespace=ns),
1643+
)
16491644
click.echo(ns)
1645+
log.info("for namespace access information, run 'bonfire namespace describe %s'", ns)
16501646

16511647

16521648
def _process_clowdenv(namespace, quay_user, clowd_env, template_file, local):

bonfire/namespaces.py

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import json
55
import logging
66

7+
from tabulate import tabulate
8+
79
from ocviapy import get_all_namespaces, get_json, on_k8s, set_current_namespace
810
from wait_for import TimedOutError
911

@@ -380,15 +382,58 @@ def describe_namespace(project_name: str, output: str):
380382
if output == "json":
381383
return json.dumps(info, indent=2)
382384

383-
data = f"\nCurrent project: {project_name}\n"
385+
rows = [("Namespace", project_name)]
384386
if info.get("console_namespace_route"):
385-
data += f"Project URL: {info['console_namespace_route']}\n"
386-
data += f"Keycloak admin route: {info['keycloak_admin_route']}\n"
387-
data += f"Keycloak admin login: {info['keycloak_admin_username']} | {info['keycloak_admin_password']}\n"
388-
data += f"{info['clowdapps_deployed']} ClowdApp(s), {info['frontends_deployed']} Frontend(s) deployed\n"
389-
data += f"Gateway route: {info['gateway_route']}\n"
390-
data += f"Default user login: {info['default_username']} | {info['default_password']}\n"
391-
return data
387+
rows.append(("Project URL", info["console_namespace_route"]))
388+
if info.get("gateway_route"):
389+
rows.append(("Gateway route", info["gateway_route"]))
390+
if info.get("clowdapps_deployed"):
391+
rows.append(("ClowdApps deployed", info["clowdapps_deployed"]))
392+
if info.get("frontends_deployed"):
393+
rows.append(("Frontends deployed", info["frontends_deployed"]))
394+
395+
def _has_cred(user, pw):
396+
return user not in ("", "N/A") or pw not in ("", "N/A")
397+
398+
cred_rows = []
399+
if _has_cred(info["keycloak_admin_username"], info["keycloak_admin_password"]):
400+
cred_rows.append(
401+
(
402+
"Keycloak admin",
403+
info["keycloak_admin_route"],
404+
info["keycloak_admin_username"],
405+
info["keycloak_admin_password"],
406+
)
407+
)
408+
if _has_cred(info["default_username"], info["default_password"]):
409+
cred_rows.append(("Default user", "", info["default_username"], info["default_password"]))
410+
411+
lines = [tabulate(rows, tablefmt="simple")]
412+
413+
if cred_rows:
414+
lines.append("\nCredentials:")
415+
for name, route, user, pw in cred_rows:
416+
cred_info = [
417+
(f"{name} username", user),
418+
(f"{name} password", pw),
419+
]
420+
if route:
421+
cred_info.append((f"{name} route", route))
422+
lines.append(tabulate(cred_info, tablefmt="simple"))
423+
lines.append("")
424+
425+
if info.get("has_cluster"):
426+
ns = project_name
427+
lines.append(
428+
"ROSA Cluster configuration detected! To access it, run:\n"
429+
"\n"
430+
f" oc get secret {ns}-cluster-kubeconfig \\\n"
431+
f" -n {ns} \\\n"
432+
f" -o jsonpath='{{.data.value}}' | base64 -d > /tmp/{ns}-kubeconfig\n"
433+
f" KUBECONFIG=/tmp/{ns}-kubeconfig oc whoami"
434+
)
435+
436+
return "\n" + "\n".join(lines) + "\n"
392437

393438

394439
def parse_fe_env(project_name):

bonfire_lib/status.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ def describe_namespace(client: EphemeralK8sClient, namespace: str) -> dict:
190190
console_url = get_console_url(client)
191191
ns_url = f"{console_url}/k8s/cluster/projects/{namespace}" if console_url else ""
192192

193+
has_cluster = _has_cluster_kubeconfig(client, namespace)
194+
193195
return {
194196
"namespace": namespace,
195197
"console_namespace_route": ns_url,
@@ -201,9 +203,19 @@ def describe_namespace(client: EphemeralK8sClient, namespace: str) -> dict:
201203
"default_username": kc_creds.get("defaultUsername", "N/A"),
202204
"default_password": kc_creds.get("defaultPassword", "N/A"),
203205
"gateway_route": f"https://{fe_host}" if fe_host else "",
206+
"has_cluster": has_cluster,
204207
}
205208

206209

210+
def _has_cluster_kubeconfig(client: EphemeralK8sClient, namespace: str) -> bool:
211+
"""Check if a cluster kubeconfig secret exists in the namespace."""
212+
try:
213+
secret = client.get_secret(f"{namespace}-cluster-kubeconfig", namespace)
214+
return secret is not None
215+
except Exception:
216+
return False
217+
218+
207219
def _get_keycloak_creds(client: EphemeralK8sClient, namespace: str) -> dict:
208220
"""Get keycloak credentials from the namespace's keycloak secret."""
209221
secret = client.get_secret(f"env-{namespace}-keycloak", namespace)

0 commit comments

Comments
 (0)