1use std::path::PathBuf;
2
3use crate::config::{DeployerConfig, DeployerRequest, OutputFormat, Provider};
4use crate::contract::DeployerCapability;
5use crate::error::{DeployerError, Result};
6use crate::multi_target;
7use crate::plan::PlanContext;
8
9#[derive(Debug, Clone)]
10pub struct JujuMachineRequest {
11 pub capability: DeployerCapability,
12 pub tenant: String,
13 pub pack_path: PathBuf,
14 pub provider_pack: Option<PathBuf>,
15 pub deploy_pack_id_override: Option<String>,
16 pub deploy_flow_id_override: Option<String>,
17 pub environment: Option<String>,
18 pub pack_id: Option<String>,
19 pub pack_version: Option<String>,
20 pub pack_digest: Option<String>,
21 pub distributor_url: Option<String>,
22 pub distributor_token: Option<String>,
23 pub preview: bool,
24 pub dry_run: bool,
25 pub execute_local: bool,
26 pub output: OutputFormat,
27 pub config_path: Option<PathBuf>,
28 pub allow_remote_in_offline: bool,
29 pub providers_dir: PathBuf,
30 pub packs_dir: PathBuf,
31}
32
33impl JujuMachineRequest {
34 pub fn new(
35 capability: DeployerCapability,
36 tenant: impl Into<String>,
37 pack_path: PathBuf,
38 ) -> Self {
39 Self {
40 capability,
41 tenant: tenant.into(),
42 pack_path,
43 provider_pack: None,
44 deploy_pack_id_override: None,
45 deploy_flow_id_override: None,
46 environment: None,
47 pack_id: None,
48 pack_version: None,
49 pack_digest: None,
50 distributor_url: None,
51 distributor_token: None,
52 preview: false,
53 dry_run: false,
54 execute_local: false,
55 output: OutputFormat::Text,
56 config_path: None,
57 allow_remote_in_offline: false,
58 providers_dir: PathBuf::from("providers/deployer"),
59 packs_dir: PathBuf::from("packs"),
60 }
61 }
62
63 pub fn into_deployer_request(self) -> DeployerRequest {
64 DeployerRequest {
65 capability: self.capability,
66 provider: Provider::Local,
67 strategy: "juju-machine".to_string(),
68 tenant: self.tenant,
69 environment: self.environment,
70 pack_path: self.pack_path,
71 bundle_root: None,
72 providers_dir: self.providers_dir,
73 packs_dir: self.packs_dir,
74 provider_pack: self.provider_pack,
75 pack_id: self.pack_id,
76 pack_version: self.pack_version,
77 pack_digest: self.pack_digest,
78 distributor_url: self.distributor_url,
79 distributor_token: self.distributor_token,
80 preview: self.preview,
81 dry_run: self.dry_run,
82 execute_local: self.execute_local,
83 output: self.output,
84 config_path: self.config_path,
85 allow_remote_in_offline: self.allow_remote_in_offline,
86 deploy_pack_id_override: self.deploy_pack_id_override,
87 deploy_flow_id_override: self.deploy_flow_id_override,
88 bundle_source: None,
89 bundle_digest: None,
90 repo_registry_base: None,
91 store_registry_base: None,
92 }
93 }
94}
95
96pub fn resolve_config(request: JujuMachineRequest) -> Result<DeployerConfig> {
97 DeployerConfig::resolve(request.into_deployer_request())
98}
99
100pub fn ensure_juju_machine_config(config: &DeployerConfig) -> Result<()> {
101 if config.provider != Provider::Local || config.strategy != "juju-machine" {
102 return Err(DeployerError::Config(format!(
103 "juju-machine adapter requires provider=local strategy=juju-machine, got provider={} strategy={}",
104 config.provider.as_str(),
105 config.strategy
106 )));
107 }
108 Ok(())
109}
110
111pub async fn run(request: JujuMachineRequest) -> Result<multi_target::OperationResult> {
112 let config = resolve_config(request)?;
113 run_config(config).await
114}
115
116pub async fn run_config(config: DeployerConfig) -> Result<multi_target::OperationResult> {
117 ensure_juju_machine_config(&config)?;
118 multi_target::run(config).await
119}
120
121pub async fn run_with_plan(
122 request: JujuMachineRequest,
123 plan: PlanContext,
124) -> Result<multi_target::OperationResult> {
125 let config = resolve_config(request)?;
126 run_config_with_plan(config, plan).await
127}
128
129pub async fn run_config_with_plan(
130 config: DeployerConfig,
131 plan: PlanContext,
132) -> Result<multi_target::OperationResult> {
133 ensure_juju_machine_config(&config)?;
134 multi_target::run_with_plan(config, plan).await
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn juju_machine_request_defaults_to_local_juju_machine_target() {
143 let request =
144 JujuMachineRequest::new(DeployerCapability::Plan, "acme", PathBuf::from("pack-dir"))
145 .into_deployer_request();
146
147 assert_eq!(request.provider, Provider::Local);
148 assert_eq!(request.strategy, "juju-machine");
149 }
150}