Skip to main content

greentic_deployer/
juju_machine.rs

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}