From 0553dc88d7a4fe115e4d079118d8224a6728c939 Mon Sep 17 00:00:00 2001 From: Alon Gubkin Date: Fri, 28 Aug 2026 20:11:12 -0700 Subject: [PATCH] fix: pass setup item through generated deploy CLI --- client-sdks/manager/openapi.json | 7 ++ client-sdks/manager/rust/openapi-3.0.json | 5 ++ crates/alien-deploy-cli/src/commands/up.rs | 83 ++++++++++++++++--- crates/alien-manager/openapi.json | 7 ++ .../alien-manager/src/routes/deployments.rs | 1 + crates/alien-manager/src/routes/sync.rs | 8 +- .../src/traits/deployment_store.rs | 2 + .../tests/registry_proxy_cloud_test.rs | 1 + .../tests/registry_proxy_test.rs | 1 + .../alien-manager/tests/sqlite_store_tests.rs | 2 + crates/alien-manager/tests/stack_import.rs | 1 + 11 files changed, 106 insertions(+), 12 deletions(-) diff --git a/client-sdks/manager/openapi.json b/client-sdks/manager/openapi.json index 2b110343e..b5eec3758 100644 --- a/client-sdks/manager/openapi.json +++ b/client-sdks/manager/openapi.json @@ -9545,6 +9545,13 @@ "null" ] }, + "setupItem": { + "type": [ + "string", + "null" + ], + "description": "Customer setup item selected from the deployment-group contract." + }, "setupMethod": { "type": [ "string", diff --git a/client-sdks/manager/rust/openapi-3.0.json b/client-sdks/manager/rust/openapi-3.0.json index 734021d5a..cb32cd324 100644 --- a/client-sdks/manager/rust/openapi-3.0.json +++ b/client-sdks/manager/rust/openapi-3.0.json @@ -8313,6 +8313,11 @@ "type": "string", "nullable": true }, + "setupItem": { + "type": "string", + "description": "Customer setup item selected from the deployment-group contract.", + "nullable": true + }, "setupMethod": { "type": "string", "description": "Setup method that is registering this deployment, such as `manual` for\nrendered Operator manifests or `helm` for generated Helm installs.", diff --git a/crates/alien-deploy-cli/src/commands/up.rs b/crates/alien-deploy-cli/src/commands/up.rs index ddb15af2c..011299b7c 100644 --- a/crates/alien-deploy-cli/src/commands/up.rs +++ b/crates/alien-deploy-cli/src/commands/up.rs @@ -98,6 +98,10 @@ pub struct UpArgs { #[arg(long)] pub name: Option, + /// Setup item captured by the deployment-group token. + #[arg(long = "setup-item")] + pub setup_item: Option, + /// Encryption key for operator database (required for pull model) #[arg(long, env = "OPERATOR_ENCRYPTION_KEY")] pub encryption_key: Option, @@ -927,6 +931,40 @@ machine = "m8i.xlarge" assert_eq!(error.code, "VALIDATION_ERROR"); } + #[test] + fn deploy_accepts_setup_item_selection() { + let args = UpArgs::parse_from([ + "alien-deploy", + "--platform", + "aws", + "--setup-item", + "deployment", + ]); + + assert_eq!(args.setup_item.as_deref(), Some("deployment")); + } + + #[test] + fn deployment_info_url_includes_setup_item_selection() { + let url = deployment_info_url( + "https://api.example.test/", + Platform::Aws, + Some("deployment"), + ) + .expect("deployment info URL should be valid"); + let query = url.query_pairs().collect::>(); + + assert_eq!(url.path(), "/v1/deployment-info"); + assert_eq!( + query.get("platform").map(|value| value.as_ref()), + Some("aws") + ); + assert_eq!( + query.get("setupItem").map(|value| value.as_ref()), + Some("deployment") + ); + } + fn stack_input(id: &str, kind: StackInputKind, required: bool) -> StackInputDefinition { StackInputDefinition { id: id.to_string(), @@ -1080,7 +1118,14 @@ pub async fn up_command(args: UpArgs, embedded_config: Option<&DeployCliConfig>) let print_progress = should_print_deploy_progress(platform); let base_platform = parse_base_platform(platform, base_platform_str.as_deref())?; let public_endpoints = load_public_endpoints(&args, platform, deploy_config.as_ref())?; - let deployer_inputs = match fetch_deployment_info(&resolved.base_url, &token, platform).await { + let deployer_inputs = match fetch_deployment_info( + &resolved.base_url, + &token, + platform, + args.setup_item.as_deref(), + ) + .await + { Ok(info) => { validate_deployment_readiness(&info, platform)?; deployer_inputs_from_info(&info, platform) @@ -1182,6 +1227,7 @@ pub async fn up_command(args: UpArgs, embedded_config: Option<&DeployCliConfig>) &name, &stack_settings, stack_input_values, + args.setup_item.as_deref(), ) .await?; let deployment_id = init.deployment_id; @@ -2021,6 +2067,7 @@ async fn fetch_deployment_info( base_url: &str, token: &str, platform: Platform, + setup_item: Option<&str>, ) -> Result { let http_client = { use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION, USER_AGENT}; @@ -2045,16 +2092,7 @@ async fn fetch_deployment_info( })? }; - let mut url = reqwest::Url::parse(&format!( - "{}/v1/deployment-info", - base_url.trim_end_matches('/') - )) - .into_alien_error() - .context(ErrorData::ConfigurationError { - message: "Invalid platform API base URL".to_string(), - })?; - url.query_pairs_mut() - .append_pair("platform", platform.as_str()); + let url = deployment_info_url(base_url, platform, setup_item)?; let response = http_client .get(url) .send() @@ -2080,6 +2118,27 @@ async fn fetch_deployment_info( }) } +fn deployment_info_url( + base_url: &str, + platform: Platform, + setup_item: Option<&str>, +) -> Result { + let mut url = reqwest::Url::parse(&format!( + "{}/v1/deployment-info", + base_url.trim_end_matches('/') + )) + .into_alien_error() + .context(ErrorData::ConfigurationError { + message: "Invalid platform API base URL".to_string(), + })?; + url.query_pairs_mut() + .append_pair("platform", platform.as_str()); + if let Some(setup_item) = setup_item { + url.query_pairs_mut().append_pair("setupItem", setup_item); + } + Ok(url) +} + fn deployer_inputs_from_info( info: &DeploymentInfoResponse, platform: Platform, @@ -2605,12 +2664,14 @@ async fn initialize_deployment( name: &str, stack_settings: &StackSettings, input_values: HashMap, + setup_item: Option<&str>, ) -> Result { let body = alien_manager_api::types::InitializeRequest { name: Some(name.to_string()), platform: Some(sdk_platform(platform)), base_platform: base_platform.map(sdk_platform), initial_desired_release: alien_manager_api::types::InitialDesiredRelease::Active, + setup_item: setup_item.map(ToString::to_string), stack_settings: Some(sdk_stack_settings(stack_settings)?), input_values: input_values.into_iter().collect(), scope: None, diff --git a/crates/alien-manager/openapi.json b/crates/alien-manager/openapi.json index 2b110343e..b5eec3758 100644 --- a/crates/alien-manager/openapi.json +++ b/crates/alien-manager/openapi.json @@ -9545,6 +9545,13 @@ "null" ] }, + "setupItem": { + "type": [ + "string", + "null" + ], + "description": "Customer setup item selected from the deployment-group contract." + }, "setupMethod": { "type": [ "string", diff --git a/crates/alien-manager/src/routes/deployments.rs b/crates/alien-manager/src/routes/deployments.rs index 50a1cf492..f070d4368 100644 --- a/crates/alien-manager/src/routes/deployments.rs +++ b/crates/alien-manager/src/routes/deployments.rs @@ -463,6 +463,7 @@ async fn create_deployment( environment_variables: req.environment_variables, public_subdomain: None, input_values: Default::default(), + setup_item: None, deployment_token: Some(raw_token.clone()), }, ) diff --git a/crates/alien-manager/src/routes/sync.rs b/crates/alien-manager/src/routes/sync.rs index 18cb88f19..a0077066d 100644 --- a/crates/alien-manager/src/routes/sync.rs +++ b/crates/alien-manager/src/routes/sync.rs @@ -190,6 +190,9 @@ pub struct InitializeRequest { /// creation intent, not a permanent deployment mode: a later update can /// assign a desired release to a deployment initialized with `none`. pub initial_desired_release: InitialDesiredRelease, + /// Customer setup item selected from the deployment-group contract. + #[serde(skip_serializing_if = "Option::is_none")] + pub setup_item: Option, pub stack_settings: Option, /// Deployer-provided stack inputs. Embedded platform managers resolve /// these before creating the deployment; standalone managers accept the @@ -775,10 +778,12 @@ mod tests { let request: InitializeRequest = serde_json::from_value(json!({ "platform": "kubernetes", "permission": "observe", - "initialDesiredRelease": "none" + "initialDesiredRelease": "none", + "setupItem": "deployment" })) .expect("explicit no-release initialization should deserialize"); assert_eq!(request.initial_desired_release, InitialDesiredRelease::None); + assert_eq!(request.setup_item.as_deref(), Some("deployment")); let missing_selection = serde_json::from_value::(json!({ "platform": "kubernetes", @@ -1855,6 +1860,7 @@ async fn initialize( environment_variables: None, public_subdomain: None, input_values: req.input_values, + setup_item: req.setup_item, deployment_token: dep_token, }, ) diff --git a/crates/alien-manager/src/traits/deployment_store.rs b/crates/alien-manager/src/traits/deployment_store.rs index c9e533f2f..aca69b075 100644 --- a/crates/alien-manager/src/traits/deployment_store.rs +++ b/crates/alien-manager/src/traits/deployment_store.rs @@ -148,6 +148,8 @@ pub struct CreateDeploymentParams { pub public_subdomain: Option, /// Stack input values collected before deployment creation. pub input_values: HashMap, + /// Customer setup item selected from the deployment-group contract. + pub setup_item: Option, /// Raw deployment token for proxy pull auth. pub deployment_token: Option, } diff --git a/crates/alien-manager/tests/registry_proxy_cloud_test.rs b/crates/alien-manager/tests/registry_proxy_cloud_test.rs index ec6fe0ca6..6628d8080 100644 --- a/crates/alien-manager/tests/registry_proxy_cloud_test.rs +++ b/crates/alien-manager/tests/registry_proxy_cloud_test.rs @@ -349,6 +349,7 @@ impl CloudProxyTest { environment_variables: None, public_subdomain: None, input_values: Default::default(), + setup_item: None, deployment_token: Some(deploy_raw.clone()), }, ) diff --git a/crates/alien-manager/tests/registry_proxy_test.rs b/crates/alien-manager/tests/registry_proxy_test.rs index a45e1b5dd..d96917400 100644 --- a/crates/alien-manager/tests/registry_proxy_test.rs +++ b/crates/alien-manager/tests/registry_proxy_test.rs @@ -310,6 +310,7 @@ async fn setup() -> TestSetup { environment_variables: None, public_subdomain: None, input_values: Default::default(), + setup_item: None, deployment_token: Some(deploy_raw.clone()), }, ) diff --git a/crates/alien-manager/tests/sqlite_store_tests.rs b/crates/alien-manager/tests/sqlite_store_tests.rs index 2060e2f9f..dc4d7d774 100644 --- a/crates/alien-manager/tests/sqlite_store_tests.rs +++ b/crates/alien-manager/tests/sqlite_store_tests.rs @@ -96,6 +96,7 @@ async fn create_test_deployment_with_settings( environment_variables: None, public_subdomain: None, input_values: Default::default(), + setup_item: None, deployment_token: None, }, ) @@ -250,6 +251,7 @@ async fn input_values_survive_create_import_and_reimport() { environment_variables: None, public_subdomain: None, input_values: values.clone(), + setup_item: None, deployment_token: None, }, ) diff --git a/crates/alien-manager/tests/stack_import.rs b/crates/alien-manager/tests/stack_import.rs index 726ebd5b4..ca9ed6a04 100644 --- a/crates/alien-manager/tests/stack_import.rs +++ b/crates/alien-manager/tests/stack_import.rs @@ -1068,6 +1068,7 @@ async fn native_deployment_blocks_imported_name() { stack_state: None, environment_variables: None, input_values: Default::default(), + setup_item: None, deployment_token: None, }, )