use super::{
AllowedHostToml, BlockingStrategyConfigToml, ComponentCommon, ComponentLocationToml,
ComponentStdOutputToml, ConfigName, DeploymentResolved, DurationConfig, ExecConfigToml,
JsParamToml, LogLevelToml, OCI_SCHEMA_PREFIX, WebhookRoute, default_external_server_name,
default_lock_extension, default_lock_extension_leeway, default_max_output_bytes,
default_max_retries, default_retry_exp_backoff, resolve_local_refs,
sanitize_deployment_relative_path, strip_deployment_dir_prefix,
};
use crate::args::TomlComponentType;
use crate::config::env_var::EnvVarConfig;
use anyhow::{Context, anyhow, bail};
use concepts::cas::Cas;
use concepts::component_id::ComponentDigest;
use concepts::{ContentDigest, FunctionFqn};
use hashbrown::HashMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_with::{DeserializeFromStr, SerializeDisplay};
use std::collections::BTreeMap;
use std::fmt::Display;
use std::str::FromStr;
#[derive(Deserialize, Serialize, JsonSchema, Default, Clone)]
#[serde(deny_unknown_fields)]
pub(crate) struct DeploymentToml {
#[serde(default, rename = "activity_wasm")]
pub(crate) activities_wasm: Vec<ActivityWasmComponentConfigToml>,
#[serde(default, rename = "activity_stub")]
pub(crate) activities_stub: Vec<ActivityStubComponentConfigToml>,
#[serde(default, rename = "activity_external")]
pub(crate) activities_external: Vec<ActivityExternalComponentConfigToml>,
#[serde(default, rename = "activity_js")]
pub(crate) activities_js: Vec<ActivityJsComponentConfigToml>,
#[serde(default, rename = "activity_exec")]
pub(crate) activities_exec: Vec<ActivityExecComponentConfigToml>,
#[serde(default, rename = "workflow_wasm")]
pub(crate) workflows_wasm: Vec<WorkflowWasmComponentConfigToml>,
#[serde(default, rename = "workflow_js")]
pub(crate) workflows_js: Vec<WorkflowJsComponentConfigToml>,
#[serde(default, rename = "webhook_endpoint_wasm")]
pub(crate) webhooks_wasm: Vec<WebhookWasmComponentConfigToml>,
#[serde(default, rename = "webhook_endpoint_js")]
pub(crate) webhooks_js: Vec<WebhookJsComponentConfigToml>,
#[serde(default, rename = "cron")]
pub(crate) crons: Vec<CronComponentConfigToml>,
}
#[derive(Default)]
pub(crate) struct DeploymentTomlValidated {
pub(crate) activities_exec: Vec<(ActivityExecComponentConfigToml, ConfigName)>,
pub(crate) activities_external: Vec<(ActivityExternalComponentConfigToml, ConfigName)>,
pub(crate) activities_js: Vec<(ActivityJsComponentConfigToml, ConfigName)>,
pub(crate) activities_stub: Vec<(ActivityStubComponentConfigToml, ConfigName)>,
pub(crate) activities_wasm: Vec<ActivityWasmComponentConfigToml>,
pub(crate) workflows_js: Vec<(WorkflowJsComponentConfigToml, ConfigName)>,
pub(crate) workflows_wasm: Vec<WorkflowWasmComponentConfigToml>,
pub(crate) webhooks_js: Vec<WebhookJsComponentConfigToml>,
pub(crate) webhooks_wasm: Vec<WebhookWasmComponentConfigToml>,
pub(crate) crons: Vec<CronComponentConfigToml>,
pub(crate) component_names_to_types: hashbrown::HashMap<String, crate::args::TomlComponentType>,
}
impl DeploymentTomlValidated {
pub(crate) async fn resolve(self, cas: &dyn Cas) -> Result<DeploymentResolved, anyhow::Error> {
resolve_local_refs(self, cas).await
}
}
impl DeploymentToml {
pub(crate) fn validate(
mut self,
deployment_dir: &std::path::Path,
) -> Result<DeploymentTomlValidated, anyhow::Error> {
self.expand_deployment_dir_prefix(deployment_dir)?;
self.normalize_oci_locations()?;
self.validate_wit_sources()?;
let mut component_names_to_types = hashbrown::HashMap::new();
let iter = self
.activities_wasm
.iter()
.map(|c| (c.common.name.as_str(), TomlComponentType::ActivityWasm))
.chain(
self.workflows_wasm
.iter()
.map(|c| (c.common.name.as_str(), TomlComponentType::WorkflowWasm)),
)
.chain(self.webhooks_wasm.iter().map(|c| {
(
c.common.name.as_str(),
TomlComponentType::WebhookEndpointWasm,
)
}))
.chain(
self.webhooks_js
.iter()
.map(|c| (c.name.as_str(), TomlComponentType::WebhookEndpointJs)),
)
.chain(
self.crons
.iter()
.map(|c| (c.name.as_str(), TomlComponentType::Cron)),
);
for (name, component_type) in iter {
if component_names_to_types
.insert(name.to_string(), component_type)
.is_some()
{
bail!("duplicate component name `{name}` in deployment");
}
}
let activities_js = Self::resolve_names(self.activities_js);
let activities_exec = Self::resolve_names(self.activities_exec);
let activities_stub = Self::resolve_stub_names(self.activities_stub);
let activities_external = Self::resolve_external_names(self.activities_external);
let workflows_js = Self::resolve_names(self.workflows_js);
for (_, name) in &activities_js {
if component_names_to_types
.insert(name.to_string(), TomlComponentType::ActivityJs)
.is_some()
{
bail!("duplicate component name `{name}` in deployment");
}
}
for (_, name) in &activities_exec {
if component_names_to_types
.insert(name.to_string(), TomlComponentType::ActivityExec)
.is_some()
{
bail!("duplicate component name `{name}` in deployment");
}
}
for (_, name) in &activities_stub {
if component_names_to_types
.insert(name.to_string(), TomlComponentType::ActivityStub)
.is_some()
{
bail!("duplicate component name `{name}` in deployment");
}
}
for (_, name) in &activities_external {
if component_names_to_types
.insert(name.to_string(), TomlComponentType::ActivityExternal)
.is_some()
{
bail!("duplicate component name `{name}` in deployment");
}
}
for (_, name) in &workflows_js {
if component_names_to_types
.insert(name.to_string(), TomlComponentType::WorkflowJs)
.is_some()
{
bail!("duplicate component name `{name}` in deployment");
}
}
Ok(DeploymentTomlValidated {
activities_exec,
activities_external,
activities_js,
activities_stub,
activities_wasm: self.activities_wasm,
workflows_js,
workflows_wasm: self.workflows_wasm,
webhooks_js: self.webhooks_js,
webhooks_wasm: self.webhooks_wasm,
crons: self.crons,
component_names_to_types,
})
}
fn validate_wit_sources(&self) -> anyhow::Result<()> {
fn validate(section: &str, interface: &FunctionInterfaceToml) -> anyhow::Result<()> {
if let FunctionInterfaceToml::Authored(AuthoredFunctionInterfaceToml { wit }) =
interface
{
sanitize_deployment_relative_path(wit)
.with_context(|| format!("invalid `{section}.wit`"))?;
}
Ok(())
}
for config in &self.activities_js {
validate("activity_js", &config.interface)?;
}
for config in &self.activities_exec {
validate("activity_exec", &config.interface)?;
}
for config in &self.workflows_js {
validate("workflow_js", &config.interface)?;
}
for config in &self.activities_stub {
if let ActivityStubComponentConfigToml::Inline(inline) = config {
validate("activity_stub", &inline.interface)?;
}
}
for config in &self.activities_external {
if let ActivityExternalComponentConfigToml::Inline(inline) = config {
validate("activity_external", &inline.interface)?;
}
}
Ok(())
}
fn resolve_names<T: HasOptionalNameAndFfqn>(configs: Vec<T>) -> Vec<(T, ConfigName)> {
configs
.into_iter()
.map(|c| {
let name = c
.config_name()
.cloned()
.unwrap_or_else(|| ConfigName::from_ffqn(c.ffqn()));
(c, name)
})
.collect()
}
fn resolve_stub_names(
configs: Vec<ActivityStubComponentConfigToml>,
) -> Vec<(ActivityStubComponentConfigToml, ConfigName)> {
configs
.into_iter()
.map(|c| {
let name = match &c {
ActivityStubComponentConfigToml::File(f) => f.common.name.clone(),
ActivityStubComponentConfigToml::Inline(i) => i
.name
.clone()
.unwrap_or_else(|| ConfigName::from_ffqn(&i.ffqn)),
};
(c, name)
})
.collect()
}
fn resolve_external_names(
configs: Vec<ActivityExternalComponentConfigToml>,
) -> Vec<(ActivityExternalComponentConfigToml, ConfigName)> {
configs
.into_iter()
.map(|c| {
let name = match &c {
ActivityExternalComponentConfigToml::File(f) => f.common.name.clone(),
ActivityExternalComponentConfigToml::Inline(i) => i
.name
.clone()
.unwrap_or_else(|| ConfigName::from_ffqn(&i.ffqn)),
};
(c, name)
})
.collect()
}
pub(crate) fn expand_deployment_dir(
s: &mut String,
deployment_dir: &std::path::Path,
) -> anyhow::Result<()> {
let candidate = strip_deployment_dir_prefix(s).unwrap_or(s.as_str());
if std::path::Path::new(candidate).is_absolute() {
bail!("absolute local paths are not allowed in deployment manifests: `{s}`");
}
let rel = sanitize_deployment_relative_path(candidate)
.with_context(|| format!("invalid deployment-relative path `{s}`"))?;
*s = deployment_dir.join(rel).to_string_lossy().into_owned();
Ok(())
}
fn normalize_oci_locations(&mut self) -> Result<(), anyhow::Error> {
fn normalize(loc: &mut ComponentLocationToml) -> Result<(), anyhow::Error> {
if let ComponentLocationToml::Oci(image) = loc {
let reference = oci_client::Reference::from_str(image)
.map_err(|e| anyhow!("invalid OCI reference `{image}`: {e}"))?;
*image = reference.to_string();
}
Ok(())
}
for c in &mut self.activities_wasm {
normalize(&mut c.common.location)?;
}
for c in &mut self.activities_stub {
if let ActivityStubComponentConfigToml::File(c) = c {
normalize(&mut c.common.location)?;
}
}
for c in &mut self.activities_external {
if let ActivityExternalComponentConfigToml::File(c) = c {
normalize(&mut c.common.location)?;
}
}
for c in &mut self.workflows_wasm {
normalize(&mut c.common.location)?;
}
for c in &mut self.webhooks_wasm {
normalize(&mut c.common.location)?;
}
Ok(())
}
fn expand_deployment_dir_prefix(
&mut self,
deployment_dir: &std::path::Path,
) -> anyhow::Result<()> {
fn expand_loc(
loc: &mut ComponentLocationToml,
deployment_dir: &std::path::Path,
) -> anyhow::Result<()> {
if let ComponentLocationToml::Path(p) = loc {
DeploymentToml::expand_deployment_dir(p, deployment_dir)?;
}
Ok(())
}
for c in &mut self.activities_wasm {
expand_loc(&mut c.common.location, deployment_dir)?;
}
for c in &mut self.activities_stub {
if let ActivityStubComponentConfigToml::File(c) = c {
expand_loc(&mut c.common.location, deployment_dir)?;
}
}
for c in &mut self.activities_external {
if let ActivityExternalComponentConfigToml::File(c) = c {
expand_loc(&mut c.common.location, deployment_dir)?;
}
}
for c in &mut self.workflows_wasm {
expand_loc(&mut c.common.location, deployment_dir)?;
}
for c in &mut self.webhooks_wasm {
expand_loc(&mut c.common.location, deployment_dir)?;
}
Ok(())
}
}
trait HasOptionalNameAndFfqn {
fn config_name(&self) -> Option<&ConfigName>;
fn ffqn(&self) -> &FunctionFqn;
}
impl HasOptionalNameAndFfqn for ActivityJsComponentConfigToml {
fn config_name(&self) -> Option<&ConfigName> {
self.name.as_ref()
}
fn ffqn(&self) -> &FunctionFqn {
&self.ffqn
}
}
impl HasOptionalNameAndFfqn for ActivityExecComponentConfigToml {
fn config_name(&self) -> Option<&ConfigName> {
self.name.as_ref()
}
fn ffqn(&self) -> &FunctionFqn {
&self.ffqn
}
}
impl HasOptionalNameAndFfqn for WorkflowJsComponentConfigToml {
fn config_name(&self) -> Option<&ConfigName> {
self.name.as_ref()
}
fn ffqn(&self) -> &FunctionFqn {
&self.ffqn
}
}
impl HasOptionalNameAndFfqn for ActivityStubExtInlineConfigToml {
fn config_name(&self) -> Option<&ConfigName> {
self.name.as_ref()
}
fn ffqn(&self) -> &FunctionFqn {
&self.ffqn
}
}
#[derive(Debug, Clone, Hash, JsonSchema, SerializeDisplay, DeserializeFromStr)]
#[schemars(with = "String")]
pub(crate) enum ScriptLocationPathOrOci {
Path(String),
Oci(oci_client::Reference),
}
impl Display for ScriptLocationPathOrOci {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ScriptLocationPathOrOci::Path(p) => write!(f, "{p}"),
ScriptLocationPathOrOci::Oci(r) => write!(f, "{OCI_SCHEMA_PREFIX}{r}"),
}
}
}
impl FromStr for ScriptLocationPathOrOci {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(location) = s.strip_prefix(OCI_SCHEMA_PREFIX) {
Ok(ScriptLocationPathOrOci::Oci(
oci_client::Reference::from_str(location)
.map_err(|e| anyhow::anyhow!("invalid OCI reference: {e}"))?,
))
} else {
Ok(ScriptLocationPathOrOci::Path(s.to_string()))
}
}
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
pub(crate) struct ActivityStubExtInlineConfigToml {
#[serde(default)]
pub(crate) name: Option<ConfigName>,
#[schemars(with = "String")]
pub(crate) ffqn: FunctionFqn,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
#[schemars(skip)]
pub(crate) component_files: BTreeMap<String, ContentDigest>,
#[serde(flatten)]
pub(crate) interface: FunctionInterfaceToml,
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(deny_unknown_fields)]
pub(crate) struct AuthoredFunctionInterfaceToml {
pub(crate) wit: String,
}
#[derive(Debug, Default, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(deny_unknown_fields)]
pub(crate) struct InlineFunctionInterfaceToml {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) params: Option<Vec<JsParamToml>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) return_type: Option<String>,
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(untagged)]
pub(crate) enum FunctionInterfaceToml {
Authored(AuthoredFunctionInterfaceToml),
Inline(InlineFunctionInterfaceToml),
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(untagged)]
pub(crate) enum ActivityStubComponentConfigToml {
File(ActivityStubFileConfigToml),
Inline(ActivityStubExtInlineConfigToml),
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(untagged)]
pub(crate) enum ActivityExternalComponentConfigToml {
File(ActivityExternalFileConfigToml),
Inline(ActivityStubExtInlineConfigToml),
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
pub(crate) struct ActivityJsComponentConfigToml {
#[serde(default)]
pub(crate) name: Option<ConfigName>,
#[serde(default)]
pub(crate) location: Option<ScriptLocationPathOrOci>,
#[serde(default)]
pub(crate) content: Option<String>,
#[serde(default)]
#[schemars(with = "Option<String>")]
pub(crate) content_digest: Option<ContentDigest>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
#[schemars(skip)]
pub(crate) component_files: BTreeMap<String, ContentDigest>,
#[serde(default)]
#[schemars(with = "Option<String>")]
pub(crate) component_digest: Option<ComponentDigest>,
#[schemars(with = "String")]
pub(crate) ffqn: FunctionFqn,
#[serde(flatten)]
pub(crate) interface: FunctionInterfaceToml,
#[serde(default)]
pub(crate) exec: ExecConfigToml,
#[serde(default = "default_max_retries")]
pub(crate) max_retries: u32,
#[serde(default = "default_retry_exp_backoff")]
pub(crate) retry_exp_backoff: DurationConfig,
#[serde(default)]
pub(crate) forward_stdout: ComponentStdOutputToml,
#[serde(default)]
pub(crate) forward_stderr: ComponentStdOutputToml,
#[serde(default)]
pub(crate) logs_store_min_level: LogLevelToml,
#[serde(default)]
pub(crate) env_vars: Vec<EnvVarConfig>,
#[serde(default, rename = "allowed_host")]
pub(crate) allowed_hosts: Vec<AllowedHostToml>,
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
pub(crate) struct ActivityExecComponentConfigToml {
#[serde(default)]
pub(crate) name: Option<ConfigName>,
#[serde(default)]
pub(crate) location: Option<ScriptLocationPathOrOci>,
#[serde(default)]
pub(crate) content: Option<String>,
#[serde(default)]
#[schemars(with = "Option<String>")]
pub(crate) content_digest: Option<ContentDigest>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
#[schemars(skip)]
pub(crate) component_files: BTreeMap<String, ContentDigest>,
#[schemars(with = "String")]
pub(crate) ffqn: FunctionFqn,
#[serde(flatten)]
pub(crate) interface: FunctionInterfaceToml,
#[serde(default)]
#[schemars(with = "Option<String>")]
pub(crate) component_digest: Option<ComponentDigest>,
#[serde(default)]
pub(crate) exec: ExecConfigToml,
#[serde(default = "default_max_retries")]
pub(crate) max_retries: u32,
#[serde(default = "default_retry_exp_backoff")]
pub(crate) retry_exp_backoff: DurationConfig,
#[serde(default)]
pub(crate) forward_stdout: ComponentStdOutputToml,
#[serde(default)]
pub(crate) forward_stderr: ComponentStdOutputToml,
#[serde(default)]
pub(crate) logs_store_min_level: LogLevelToml,
#[serde(default)]
pub(crate) env_vars: Vec<EnvVarConfig>,
#[serde(default = "default_max_output_bytes")]
pub(crate) max_output_bytes: u64,
#[serde(default)]
pub(crate) secrets: Vec<String>,
#[serde(default)]
pub(crate) params_via_stdin: bool,
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
pub(crate) struct WorkflowJsComponentConfigToml {
#[serde(default)]
pub(crate) name: Option<ConfigName>,
#[serde(default)]
pub(crate) location: Option<ScriptLocationPathOrOci>,
#[serde(default)]
pub(crate) content: Option<String>,
#[serde(default)]
#[schemars(with = "Option<String>")]
pub(crate) content_digest: Option<ContentDigest>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
#[schemars(skip)]
pub(crate) component_files: BTreeMap<String, ContentDigest>,
#[serde(default)]
#[schemars(with = "Option<String>")]
pub(crate) component_digest: Option<ComponentDigest>,
#[schemars(with = "String")]
pub(crate) ffqn: FunctionFqn,
#[serde(flatten)]
pub(crate) interface: FunctionInterfaceToml,
#[serde(default)]
pub(crate) exec: ExecConfigToml,
#[serde(default = "default_retry_exp_backoff")]
pub(crate) retry_exp_backoff: DurationConfig,
#[serde(default)]
pub(crate) blocking_strategy: BlockingStrategyConfigToml,
#[serde(default = "default_lock_extension")]
pub(crate) lock_extension: bool,
#[serde(default = "default_lock_extension_leeway")]
pub(crate) lock_extension_leeway: DurationConfig,
#[serde(default)]
pub(crate) logs_store_min_level: LogLevelToml,
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(deny_unknown_fields)]
pub(crate) struct WorkflowWasmComponentConfigToml {
#[serde(flatten)]
pub(crate) common: ComponentCommon,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schemars(with = "Option<String>")]
pub(crate) content_digest: Option<ContentDigest>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
#[schemars(skip)]
pub(crate) component_files: BTreeMap<String, ContentDigest>,
#[serde(default)]
#[schemars(with = "Option<String>")]
pub(crate) component_digest: Option<ComponentDigest>,
#[serde(default)]
pub(crate) exec: ExecConfigToml,
#[serde(default = "default_retry_exp_backoff")]
pub(crate) retry_exp_backoff: DurationConfig,
#[serde(default)]
pub(crate) blocking_strategy: BlockingStrategyConfigToml,
#[serde(default)]
pub(crate) backtrace: ComponentBacktraceConfig,
#[serde(default)]
pub(crate) stub_wasi: bool,
#[serde(default = "default_lock_extension")]
pub(crate) lock_extension: bool,
#[serde(default = "default_lock_extension_leeway")]
pub(crate) lock_extension_leeway: DurationConfig,
#[serde(default)]
pub(crate) logs_store_min_level: LogLevelToml,
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Default, Clone)]
#[serde(deny_unknown_fields)]
pub(crate) struct ComponentBacktraceConfig {
#[serde(rename = "sources")]
#[schemars(with = "std::collections::HashMap<String, String>")]
pub(crate) frame_files_to_sources: HashMap<String, String>,
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(deny_unknown_fields)]
pub(crate) struct WebhookWasmComponentConfigToml {
#[serde(flatten)]
pub(crate) common: ComponentCommon,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schemars(with = "Option<String>")]
pub(crate) content_digest: Option<ContentDigest>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
#[schemars(skip)]
pub(crate) component_files: BTreeMap<String, ContentDigest>,
#[serde(default = "default_external_server_name")]
pub(crate) http_server: ConfigName,
pub(crate) routes: Vec<WebhookRoute>,
#[serde(default)]
pub(crate) forward_stdout: ComponentStdOutputToml,
#[serde(default)]
pub(crate) forward_stderr: ComponentStdOutputToml,
#[serde(default)]
pub(crate) env_vars: Vec<EnvVarConfig>,
#[serde(default)]
pub(crate) backtrace: ComponentBacktraceConfig,
#[serde(default)]
pub(crate) backtrace_persist: bool,
#[serde(default)]
pub(crate) logs_store_min_level: LogLevelToml,
#[serde(default, rename = "allowed_host")]
pub(crate) allowed_hosts: Vec<AllowedHostToml>,
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(deny_unknown_fields)]
pub(crate) struct WebhookJsComponentConfigToml {
pub(crate) name: ConfigName,
#[serde(default)]
pub(crate) location: Option<ScriptLocationPathOrOci>,
#[serde(default)]
pub(crate) content: Option<String>,
#[serde(default)]
#[schemars(with = "Option<String>")]
pub(crate) content_digest: Option<ContentDigest>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
#[schemars(skip)]
pub(crate) component_files: BTreeMap<String, ContentDigest>,
#[serde(default = "default_external_server_name")]
pub(crate) http_server: ConfigName,
pub(crate) routes: Vec<WebhookRoute>,
#[serde(default)]
pub(crate) forward_stdout: ComponentStdOutputToml,
#[serde(default)]
pub(crate) forward_stderr: ComponentStdOutputToml,
#[serde(default)]
pub(crate) logs_store_min_level: LogLevelToml,
#[serde(default)]
pub(crate) env_vars: Vec<EnvVarConfig>,
#[serde(default)]
pub(crate) backtrace_persist: bool,
#[serde(default, rename = "allowed_host")]
pub(crate) allowed_hosts: Vec<AllowedHostToml>,
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(deny_unknown_fields)]
pub struct ActivityWasmComponentConfigToml {
#[serde(flatten)]
pub common: ComponentCommon,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schemars(with = "Option<String>")]
pub content_digest: Option<ContentDigest>,
#[serde(default)]
#[schemars(with = "Option<String>")]
pub component_digest: Option<ComponentDigest>,
#[serde(default)]
pub exec: ExecConfigToml,
#[serde(default = "default_max_retries")]
pub max_retries: u32,
#[serde(default = "default_retry_exp_backoff")]
pub retry_exp_backoff: DurationConfig,
#[serde(default)]
pub forward_stdout: ComponentStdOutputToml,
#[serde(default)]
pub forward_stderr: ComponentStdOutputToml,
#[serde(default)]
pub env_vars: Vec<EnvVarConfig>,
#[serde(default)]
pub logs_store_min_level: LogLevelToml,
#[serde(default, rename = "allowed_host")]
pub allowed_hosts: Vec<AllowedHostToml>,
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(deny_unknown_fields)]
pub struct ActivityStubFileConfigToml {
#[serde(flatten)]
pub common: ComponentCommon,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schemars(with = "Option<String>")]
pub content_digest: Option<ContentDigest>,
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(deny_unknown_fields)]
pub struct ActivityExternalFileConfigToml {
#[serde(flatten)]
pub common: ComponentCommon,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schemars(with = "Option<String>")]
pub content_digest: Option<ContentDigest>,
#[serde(default)]
#[schemars(with = "Option<String>")]
pub component_digest: Option<ComponentDigest>,
}
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(deny_unknown_fields)]
pub struct CronComponentConfigToml {
pub name: ConfigName,
#[schemars(with = "String")]
pub ffqn: FunctionFqn,
#[serde(default = "default_schedule_params")]
pub params: String,
pub schedule: String,
#[serde(default)]
pub exec: ExecConfigToml,
}
#[must_use]
pub fn default_schedule_params() -> String {
"[]".to_string()
}