Skip to main content

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