From 5c2860990b5aee5ca6ea7d70a7cd3bc19b49775e Mon Sep 17 00:00:00 2001 From: Brett Chien Date: Sat, 29 Aug 2026 23:15:09 +0800 Subject: [PATCH] fix(k8s): auto-create the target namespace if it doesn't exist (studio#138 follow-up) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brett hit "namespaces \"openab-studio\" not found" applying the ConfigMap right after #139 landed — the New Fleet wizard's "+ Create new namespace…" field (studio#119) only ever *typed* a name into the deploy request; nothing actually created the k8s Namespace object, so picking it 404'd the instant anything (ConfigMap/Secret/Deployment) tried to apply into it. New `ensure_namespace_k8s` (Patch::Apply, same idempotent shape as `provision_acp_auth_k8s_secret` — a no-op if the namespace already exists), called at the top of both k8s provision paths: `provision_agent_k8s` (the wizard, where Brett hit this) and `provision_from_library_k8s` (the older compose-library path — same gap, fixed for consistency). Verification: could not compile locally (this sandbox reliably OOMs on aws-sdk-ec2). Field types (Namespace/ObjectMeta) verified by reading the pinned k8s-openapi 0.24 source directly. CI is the real gate. Co-Authored-By: Claude Sonnet 5 --- crates/studio-cp/src/lib.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/crates/studio-cp/src/lib.rs b/crates/studio-cp/src/lib.rs index 0879d58..f34ef64 100644 --- a/crates/studio-cp/src/lib.rs +++ b/crates/studio-cp/src/lib.rs @@ -1571,6 +1571,7 @@ pub async fn provision_agent_k8s( directly, or deploy this agent to ECS instead." ); } + ensure_namespace_k8s(context, namespace).await?; // `provision_agent_secrets` only ever touches AWS Secrets Manager when // `input.api_key` is set (chat-platform secrets are unreachable here — // the bail! above already refused those) — a no-op otherwise, so this @@ -1696,6 +1697,34 @@ async fn provision_acp_auth_k8s_secret( Ok(format!("k8s-secret://{secret_name}#OPENAB_ACP_AUTH_KEY")) } +/// Server-side-applies a bare k8s `Namespace` object (Brett, studio#138 +/// follow-up: "if namespace input is not there, we should create it"). The +/// New Fleet wizard's "+ Create new namespace…" field (studio#119) only ever +/// *typed* a name into the deploy request — nothing actually created the +/// namespace, so picking it 404'd the instant anything tried to apply into +/// it (`ConfigMap`/`Secret`/`Deployment` alike). `Patch::Apply`, same +/// idempotent shape as [`provision_acp_auth_k8s_secret`] — a no-op if the +/// namespace already exists. +async fn ensure_namespace_k8s(context: Option<&str>, namespace: &str) -> anyhow::Result<()> { + use k8s_openapi::api::core::v1::Namespace; + use k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta; + use kube::api::{Api, Patch, PatchParams}; + + let client = k8s_client_for(context).await?; + let ns = Namespace { + metadata: ObjectMeta { + name: Some(namespace.to_string()), + ..Default::default() + }, + ..Default::default() + }; + let api: Api = Api::all(client); + api.patch(namespace, &PatchParams::apply("studio-cp"), &Patch::Apply(&ns)) + .await + .map_err(|e| anyhow::anyhow!("failed to create/apply k8s namespace '{namespace}': {e}"))?; + Ok(()) +} + /// Server-side-applies `config.toml` into a k8s `ConfigMap` named /// `{name}-config` in `namespace` (studio#138 — Brett: "k8s does not need to /// fetch from s3"). Mirrors openab's own Helm chart pattern @@ -1823,6 +1852,7 @@ pub async fn provision_from_library_k8s( image_override: Option<&str>, expected_principal: Option<&str>, ) -> anyhow::Result { + ensure_namespace_k8s(context, namespace).await?; let mut bundle = studio_compose::compose_named(library, template, overlay) .map_err(|e| anyhow::anyhow!("compose failed: {e}"))?; let image = image_override