use crate::error::types::SupervisorError;
use crate::id::types::ChildId;
use crate::policy::task_role_defaults::{SeverityClass, SidecarConfig, TaskRole};
use crate::readiness::signal::ReadinessPolicy;
use crate::spec::child::{
BackoffPolicy, ChildSpec, CommandPermissions, Criticality, EnvVar, HealthCheckConfig,
HealthPolicy, Isolation, RestartPolicy, SecretRef, TaskKind,
};
use crate::spec::shutdown::ShutdownBudget;
use crate::task::factory::TaskFactory;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
struct PolicyDefaults {
restart_policy: RestartPolicy,
shutdown_budget: ShutdownBudget,
health_policy: HealthPolicy,
readiness_policy: ReadinessPolicy,
backoff_policy: BackoffPolicy,
}
fn worker_policy_defaults() -> PolicyDefaults {
PolicyDefaults {
restart_policy: RestartPolicy::Transient,
shutdown_budget: ShutdownBudget::new(Duration::from_secs(5), Duration::from_secs(1)),
health_policy: HealthPolicy::new(Duration::from_secs(1), Duration::from_secs(3)),
readiness_policy: ReadinessPolicy::Immediate,
backoff_policy: BackoffPolicy::new(Duration::from_millis(10), Duration::from_secs(1), 0.0),
}
}
fn baseline_policy_defaults() -> PolicyDefaults {
PolicyDefaults {
restart_policy: RestartPolicy::Permanent,
shutdown_budget: ShutdownBudget::new(Duration::from_secs(5), Duration::from_secs(1)),
health_policy: HealthPolicy::new(Duration::from_secs(10), Duration::from_secs(5)),
readiness_policy: ReadinessPolicy::Immediate,
backoff_policy: BackoffPolicy::new(Duration::from_millis(10), Duration::from_secs(1), 0.0),
}
}
fn apply_policy_defaults(spec: &mut ChildSpec, defaults: PolicyDefaults) {
spec.restart_policy = defaults.restart_policy;
spec.shutdown_budget = defaults.shutdown_budget;
spec.health_policy = defaults.health_policy;
spec.readiness_policy = defaults.readiness_policy;
spec.backoff_policy = defaults.backoff_policy;
}
#[derive(Debug, Clone)]
pub struct ChildSpecBuilder {
spec: ChildSpec,
}
impl ChildSpecBuilder {
pub fn new(id: ChildId, name: impl Into<String>) -> Self {
let spec = ChildSpec {
id,
name: name.into(),
kind: TaskKind::default(),
isolation: Isolation::default(),
factory: None,
factory_key: None,
restart_policy: RestartPolicy::default(),
shutdown_budget: ShutdownBudget::new(Duration::from_secs(5), Duration::from_secs(1)),
health_policy: HealthPolicy::new(Duration::from_secs(10), Duration::from_secs(5)),
readiness_policy: ReadinessPolicy::Immediate,
backoff_policy: BackoffPolicy::new(
Duration::from_millis(10),
Duration::from_secs(1),
0.0,
),
dependencies: Vec::new(),
tags: Vec::new(),
criticality: Criticality::default(),
task_role: None,
sidecar_config: None,
severity: None,
group: None,
health_check: None,
command_permissions: CommandPermissions::default(),
environment: Vec::new(),
secrets: Vec::new(),
cleanup_paths: Vec::new(),
};
Self { spec }.with_policy_defaults(baseline_policy_defaults())
}
pub fn worker(
id: ChildId,
name: impl Into<String>,
kind: TaskKind,
factory: Arc<dyn TaskFactory>,
) -> Self {
Self::new(id, name)
.kind(kind)
.isolation(Isolation::AsyncWorker)
.factory(factory)
.criticality(Criticality::Critical)
.task_role(TaskRole::Worker)
.with_policy_defaults(worker_policy_defaults())
}
pub fn service(
id: ChildId,
name: impl Into<String>,
kind: TaskKind,
factory: Arc<dyn TaskFactory>,
) -> Self {
Self::worker(id, name, kind, factory)
.task_role(TaskRole::Service)
.criticality(Criticality::Critical)
}
pub fn job(
id: ChildId,
name: impl Into<String>,
kind: TaskKind,
factory: Arc<dyn TaskFactory>,
) -> Self {
Self::worker(id, name, kind, factory)
.task_role(TaskRole::Job)
.criticality(Criticality::Optional)
}
pub fn sidecar(
id: ChildId,
name: impl Into<String>,
kind: TaskKind,
factory: Arc<dyn TaskFactory>,
sidecar_config: SidecarConfig,
) -> Self {
let primary_child_id = sidecar_config.primary_child_id.clone();
Self::worker(id, name, kind, factory)
.task_role(TaskRole::Sidecar)
.sidecar_config(sidecar_config)
.dependency(primary_child_id)
.criticality(Criticality::Critical)
}
pub fn supervisor(id: ChildId, name: impl Into<String>) -> Self {
Self::new(id, name)
.kind(TaskKind::Supervisor)
.without_factory()
.criticality(Criticality::Critical)
.task_role(TaskRole::Supervisor)
}
fn with_policy_defaults(mut self, defaults: PolicyDefaults) -> Self {
apply_policy_defaults(&mut self.spec, defaults);
self
}
pub fn kind(mut self, kind: TaskKind) -> Self {
self.spec.kind = kind;
self
}
pub fn isolation(mut self, isolation: Isolation) -> Self {
self.spec.isolation = isolation;
self
}
pub fn factory(mut self, factory: Arc<dyn TaskFactory>) -> Self {
self.spec.factory = Some(factory);
self
}
pub fn factory_key(mut self, factory_key: impl Into<String>) -> Self {
self.spec.factory_key = Some(factory_key.into());
self
}
pub fn without_factory(mut self) -> Self {
self.spec.factory = None;
self
}
pub fn restart_policy(mut self, restart_policy: RestartPolicy) -> Self {
self.spec.restart_policy = restart_policy;
self
}
pub fn shutdown_budget(mut self, shutdown_budget: ShutdownBudget) -> Self {
self.spec.shutdown_budget = shutdown_budget;
self
}
pub fn health_policy(mut self, health_policy: HealthPolicy) -> Self {
self.spec.health_policy = health_policy;
self
}
pub fn readiness_policy(mut self, readiness_policy: ReadinessPolicy) -> Self {
self.spec.readiness_policy = readiness_policy;
self
}
pub fn backoff_policy(mut self, backoff_policy: BackoffPolicy) -> Self {
self.spec.backoff_policy = backoff_policy;
self
}
pub fn dependencies(mut self, dependencies: Vec<ChildId>) -> Self {
self.spec.dependencies = dependencies;
self
}
pub fn dependency(mut self, dependency: ChildId) -> Self {
self.spec.dependencies.push(dependency);
self
}
pub fn tags(mut self, tags: Vec<String>) -> Self {
self.spec.tags = tags;
self
}
pub fn tag(mut self, tag: impl Into<String>) -> Self {
self.spec.tags.push(tag.into());
self
}
pub fn criticality(mut self, criticality: Criticality) -> Self {
self.spec.criticality = criticality;
self
}
pub fn task_role(mut self, task_role: TaskRole) -> Self {
self.spec.task_role = Some(task_role);
self
}
pub fn without_task_role(mut self) -> Self {
self.spec.task_role = None;
self
}
pub fn sidecar_config(mut self, sidecar_config: SidecarConfig) -> Self {
self.spec.sidecar_config = Some(sidecar_config);
self
}
pub fn without_sidecar_config(mut self) -> Self {
self.spec.sidecar_config = None;
self
}
pub fn severity(mut self, severity: SeverityClass) -> Self {
self.spec.severity = Some(severity);
self
}
pub fn without_severity(mut self) -> Self {
self.spec.severity = None;
self
}
pub fn group(mut self, group: impl Into<String>) -> Self {
self.spec.group = Some(group.into());
self
}
pub fn without_group(mut self) -> Self {
self.spec.group = None;
self
}
pub fn health_check(mut self, health_check: HealthCheckConfig) -> Self {
self.spec.health_check = Some(health_check);
self
}
pub fn without_health_check(mut self) -> Self {
self.spec.health_check = None;
self
}
pub fn command_permissions(mut self, command_permissions: CommandPermissions) -> Self {
self.spec.command_permissions = command_permissions;
self
}
pub fn environment(mut self, environment: Vec<EnvVar>) -> Self {
self.spec.environment = environment;
self
}
pub fn env_var(mut self, env_var: EnvVar) -> Self {
self.spec.environment.push(env_var);
self
}
pub fn secrets(mut self, secrets: Vec<SecretRef>) -> Self {
self.spec.secrets = secrets;
self
}
pub fn secret(mut self, secret: SecretRef) -> Self {
self.spec.secrets.push(secret);
self
}
pub fn cleanup_paths(mut self, cleanup_paths: Vec<PathBuf>) -> Self {
self.spec.cleanup_paths = cleanup_paths;
self
}
pub fn cleanup_path(mut self, cleanup_path: impl Into<PathBuf>) -> Self {
self.spec.cleanup_paths.push(cleanup_path.into());
self
}
pub fn build(self) -> Result<ChildSpec, SupervisorError> {
let spec = self.spec;
spec.validate()?;
Ok(spec)
}
}