Begin teasing out general process specification schema
This commit is contained in:
parent
b373d3440a
commit
ea7e13b0c0
|
@ -2,4 +2,4 @@
|
||||||
ProcessDir´³orµµ±present´³dict·³dir´³named³dir´³atom³String„„„„„µ±invalid´³dict·³dir´³named³dir³any„„„„µ±absent´³dict·„„„„„³
|
ProcessDir´³orµµ±present´³dict·³dir´³named³dir´³atom³String„„„„„µ±invalid´³dict·³dir´³named³dir³any„„„„µ±absent´³dict·„„„„„³
|
||||||
ProcessEnv´³orµµ±present´³dict·³env´³named³env´³dictof´³refµ„³EnvVariable„´³refµ„³EnvValue„„„„„„µ±invalid´³dict·³env´³named³env³any„„„„µ±absent´³dict·„„„„„³CommandLine´³orµµ±shell´³atom³String„„µ±full´³refµ„³FullCommandLine„„„„³EnvVariable´³orµµ±string´³atom³String„„µ±symbol´³atom³Symbol„„µ±invalid³any„„„³FullProcess´³andµ´³dict·³argv´³named³argv´³refµ„³CommandLine„„„„´³named³env´³refµ„³
|
ProcessEnv´³orµµ±present´³dict·³env´³named³env´³dictof´³refµ„³EnvVariable„´³refµ„³EnvValue„„„„„„µ±invalid´³dict·³env´³named³env³any„„„„µ±absent´³dict·„„„„„³CommandLine´³orµµ±shell´³atom³String„„µ±full´³refµ„³FullCommandLine„„„„³EnvVariable´³orµµ±string´³atom³String„„µ±symbol´³atom³Symbol„„µ±invalid³any„„„³FullProcess´³andµ´³dict·³argv´³named³argv´³refµ„³CommandLine„„„„´³named³env´³refµ„³
|
||||||
ProcessEnv„„´³named³dir´³refµ„³
|
ProcessEnv„„´³named³dir´³refµ„³
|
||||||
ProcessDir„„´³named³clearEnv´³refµ„³ClearEnv„„´³named³readyOnStart´³refµ„³ReadyOnStart„„´³named³restart´³refµ„³RestartField„„„„³ReadyOnStart´³orµµ±present´³dict·³readyOnStart´³named³readyOnStart´³atom³Boolean„„„„„µ±invalid´³dict·³readyOnStart´³named³readyOnStart³any„„„„µ±absent´³dict·„„„„„³RestartField´³orµµ±present´³dict·³restart´³named³restart´³refµ„³
RestartPolicy„„„„„µ±invalid´³dict·³restart´³named³restart³any„„„„µ±absent´³dict·„„„„„³
DaemonProcess´³rec´³lit³daemon„´³tupleµ´³named³id³any„´³named³config´³refµ„³Process„„„„„³
DaemonService´³rec´³lit³daemon„´³tupleµ´³named³id³any„„„„³
RestartPolicy´³orµµ±always´³lit³always„„µ±onError´³lit³on-error„„µ±all´³lit³all„„„„³FullCommandLine´³tuplePrefixµ´³named³program´³atom³String„„„´³named³args´³seqof´³atom³String„„„„„³embeddedType´³refµ³ EntityRef„³Cap„„„µ³internalServices„´³schema·³version‘³definitions·³ Milestone´³rec´³lit³ milestone„´³tupleµ´³named³name³any„„„„³DebtReporter´³lit³
debt-reporter„³
ConfigWatcher´³rec´³lit³config-watcher„´³tupleµ´³named³path´³atom³String„„„„„³TcpRelayListener´³rec´³lit³relay-listener„´³tupleµ´³named³addr´³refµ³TransportAddress„³Tcp„„„„„³UnixRelayListener´³rec´³lit³relay-listener„´³tupleµ´³named³addr´³refµ³TransportAddress„³Unix„„„„„„³embeddedType´³refµ³ EntityRef„³Cap„„„„„
|
ProcessDir„„´³named³clearEnv´³refµ„³ClearEnv„„„„³ReadyOnStart´³orµµ±present´³dict·³readyOnStart´³named³readyOnStart´³atom³Boolean„„„„„µ±invalid´³dict·³readyOnStart´³named³readyOnStart³any„„„„µ±absent´³dict·„„„„„³RestartField´³orµµ±present´³dict·³restart´³named³restart´³refµ„³
RestartPolicy„„„„„µ±invalid´³dict·³restart´³named³restart³any„„„„µ±absent´³dict·„„„„„³
DaemonProcess´³rec´³lit³daemon„´³tupleµ´³named³id³any„´³named³config´³refµ„³DaemonProcessSpec„„„„„³
DaemonService´³rec´³lit³daemon„´³tupleµ´³named³id³any„„„„³
RestartPolicy´³orµµ±always´³lit³always„„µ±onError´³lit³on-error„„µ±all´³lit³all„„„„³FullCommandLine´³tuplePrefixµ´³named³program´³atom³String„„„´³named³args´³seqof´³atom³String„„„„³DaemonProcessSpec´³orµµ±simple´³refµ„³CommandLine„„µ±full´³refµ„³FullDaemonProcess„„„„³FullDaemonProcess´³andµ´³named³process´³refµ„³FullProcess„„´³named³readyOnStart´³refµ„³ReadyOnStart„„´³named³restart´³refµ„³RestartField„„„„„³embeddedType´³refµ³ EntityRef„³Cap„„„µ³internalServices„´³schema·³version‘³definitions·³ Milestone´³rec´³lit³ milestone„´³tupleµ´³named³name³any„„„„³DebtReporter´³lit³
debt-reporter„³
ConfigWatcher´³rec´³lit³config-watcher„´³tupleµ´³named³path´³atom³String„„„„„³TcpRelayListener´³rec´³lit³relay-listener„´³tupleµ´³named³addr´³refµ³TransportAddress„³Tcp„„„„„³UnixRelayListener´³rec´³lit³relay-listener„´³tupleµ´³named³addr´³refµ³TransportAddress„³Unix„„„„„„³embeddedType´³refµ³ EntityRef„³Cap„„„„„
|
|
@ -4,7 +4,12 @@ embeddedType EntityRef.Cap .
|
||||||
Service = DaemonService .
|
Service = DaemonService .
|
||||||
|
|
||||||
DaemonService = <daemon @id any> .
|
DaemonService = <daemon @id any> .
|
||||||
DaemonProcess = <daemon @id any @config Process>.
|
DaemonProcess = <daemon @id any @config DaemonProcessSpec>.
|
||||||
|
|
||||||
|
DaemonProcessSpec = @simple CommandLine / @full FullDaemonProcess .
|
||||||
|
FullDaemonProcess = @process FullProcess & @readyOnStart ReadyOnStart & @restart RestartField .
|
||||||
|
ReadyOnStart = @present { readyOnStart: bool } / @invalid { readyOnStart: any } / @absent {} .
|
||||||
|
RestartField = @present { restart: RestartPolicy } / @invalid { restart: any } / @absent {} .
|
||||||
|
|
||||||
Process = @simple CommandLine / @full FullProcess .
|
Process = @simple CommandLine / @full FullProcess .
|
||||||
FullProcess =
|
FullProcess =
|
||||||
|
@ -12,14 +17,10 @@ FullProcess =
|
||||||
& @env ProcessEnv
|
& @env ProcessEnv
|
||||||
& @dir ProcessDir
|
& @dir ProcessDir
|
||||||
& @clearEnv ClearEnv
|
& @clearEnv ClearEnv
|
||||||
& @readyOnStart ReadyOnStart
|
|
||||||
& @restart RestartField
|
|
||||||
.
|
.
|
||||||
ProcessEnv = @present { env: { EnvVariable: EnvValue ...:... } } / @invalid { env: any } / @absent {} .
|
ProcessEnv = @present { env: { EnvVariable: EnvValue ...:... } } / @invalid { env: any } / @absent {} .
|
||||||
ProcessDir = @present { dir: string } / @invalid { dir: any } / @absent {} .
|
ProcessDir = @present { dir: string } / @invalid { dir: any } / @absent {} .
|
||||||
ClearEnv = @present { clearEnv: bool } / @invalid { clearEnv: any } / @absent {} .
|
ClearEnv = @present { clearEnv: bool } / @invalid { clearEnv: any } / @absent {} .
|
||||||
ReadyOnStart = @present { readyOnStart: bool } / @invalid { readyOnStart: any } / @absent {} .
|
|
||||||
RestartField = @present { restart: RestartPolicy } / @invalid { restart: any } / @absent {} .
|
|
||||||
|
|
||||||
CommandLine = @shell string / @full FullCommandLine .
|
CommandLine = @shell string / @full FullCommandLine .
|
||||||
FullCommandLine = [@program string, @args string ...] .
|
FullCommandLine = [@program string, @args string ...] .
|
||||||
|
|
|
@ -4,7 +4,6 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use syndicate::actor::*;
|
use syndicate::actor::*;
|
||||||
use syndicate::enclose;
|
use syndicate::enclose;
|
||||||
use syndicate::error::Error;
|
|
||||||
use syndicate::supervise::{Supervisor, SupervisorConfiguration};
|
use syndicate::supervise::{Supervisor, SupervisorConfiguration};
|
||||||
|
|
||||||
use tokio::process;
|
use tokio::process;
|
||||||
|
@ -30,10 +29,6 @@ pub fn on_demand(t: &mut Activation, config_ds: Arc<Cap>, root_ds: Arc<Cap>) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cannot_start<R>() -> Result<R, Error> {
|
|
||||||
Err("Cannot start daemon process")?
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
fn elaborate(self) -> FullProcess {
|
fn elaborate(self) -> FullProcess {
|
||||||
match self {
|
match self {
|
||||||
|
@ -42,10 +37,83 @@ impl Process {
|
||||||
env: ProcessEnv::Absent,
|
env: ProcessEnv::Absent,
|
||||||
dir: ProcessDir::Absent,
|
dir: ProcessDir::Absent,
|
||||||
clear_env: ClearEnv::Absent,
|
clear_env: ClearEnv::Absent,
|
||||||
|
},
|
||||||
|
Process::Full(spec) => *spec,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FullProcess {
|
||||||
|
fn build_command(&self) -> Option<process::Command> {
|
||||||
|
let argv = self.argv.clone().elaborate();
|
||||||
|
let mut cmd = process::Command::new(argv.program);
|
||||||
|
cmd.args(argv.args);
|
||||||
|
match &self.dir {
|
||||||
|
ProcessDir::Present { dir } => { cmd.current_dir(dir); () },
|
||||||
|
ProcessDir::Absent => (),
|
||||||
|
ProcessDir::Invalid { dir } => {
|
||||||
|
tracing::error!(?dir, "Invalid working directory");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &self.clear_env {
|
||||||
|
ClearEnv::Present { clear_env: true } => { cmd.env_clear(); () },
|
||||||
|
ClearEnv::Present { clear_env: false } => (),
|
||||||
|
ClearEnv::Absent => (),
|
||||||
|
ClearEnv::Invalid { clear_env } => {
|
||||||
|
tracing::error!(?clear_env, "Invalid clearEnv setting");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &self.env {
|
||||||
|
ProcessEnv::Present { env } => {
|
||||||
|
for (k, v) in env {
|
||||||
|
if let Some(env_variable) = match k {
|
||||||
|
EnvVariable::String(k) => Some(k),
|
||||||
|
EnvVariable::Symbol(k) => Some(k),
|
||||||
|
EnvVariable::Invalid(env_variable) => {
|
||||||
|
tracing::error!(?env_variable,
|
||||||
|
"Invalid environment variable name");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
match v {
|
||||||
|
EnvValue::Set(value) => { cmd.env(env_variable, value); () }
|
||||||
|
EnvValue::Remove => { cmd.env_remove(env_variable); () }
|
||||||
|
EnvValue::Invalid(value) => {
|
||||||
|
tracing::error!(?env_variable, ?value,
|
||||||
|
"Invalid environment variable value");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProcessEnv::Absent => (),
|
||||||
|
ProcessEnv::Invalid { env } => {
|
||||||
|
tracing::error!(?env, "Invalid daemon environment");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.stdin(std::process::Stdio::null());
|
||||||
|
cmd.stdout(std::process::Stdio::inherit());
|
||||||
|
cmd.stderr(std::process::Stdio::inherit());
|
||||||
|
cmd.kill_on_drop(true);
|
||||||
|
|
||||||
|
Some(cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DaemonProcessSpec {
|
||||||
|
fn elaborate(self) -> FullDaemonProcess {
|
||||||
|
match self {
|
||||||
|
DaemonProcessSpec::Simple(command_line) => FullDaemonProcess {
|
||||||
|
process: Process::Simple(command_line).elaborate(),
|
||||||
ready_on_start: ReadyOnStart::Absent,
|
ready_on_start: ReadyOnStart::Absent,
|
||||||
restart: RestartField::Absent,
|
restart: RestartField::Absent,
|
||||||
},
|
},
|
||||||
Process::Full(spec) => *spec,
|
DaemonProcessSpec::Full(spec) => *spec,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +130,7 @@ impl CommandLine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProcessInstance {
|
struct DaemonInstance {
|
||||||
name: tracing::Span,
|
name: tracing::Span,
|
||||||
cmd: process::Command,
|
cmd: process::Command,
|
||||||
announce_presumed_readiness: bool,
|
announce_presumed_readiness: bool,
|
||||||
|
@ -71,7 +139,7 @@ struct ProcessInstance {
|
||||||
restart_policy: RestartPolicy,
|
restart_policy: RestartPolicy,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProcessInstance {
|
impl DaemonInstance {
|
||||||
fn handle_exit(self, t: &mut Activation, error_message: Option<String>) -> ActorResult {
|
fn handle_exit(self, t: &mut Activation, error_message: Option<String>) -> ActorResult {
|
||||||
let delay =
|
let delay =
|
||||||
std::time::Duration::from_millis(if let None = error_message { 200 } else { 1000 });
|
std::time::Duration::from_millis(if let None = error_message { 200 } else { 1000 });
|
||||||
|
@ -184,68 +252,20 @@ fn run(
|
||||||
counter::adjust(t, &unready_configs, 1);
|
counter::adjust(t, &unready_configs, 1);
|
||||||
counter::adjust(t, &total_configs, 1);
|
counter::adjust(t, &total_configs, 1);
|
||||||
|
|
||||||
match language().parse::<Process>(&config) {
|
match language().parse::<DaemonProcessSpec>(&config) {
|
||||||
Ok(config) => {
|
Ok(config) => {
|
||||||
tracing::info!(?config);
|
tracing::info!(?config);
|
||||||
let config = config.elaborate();
|
let config = config.elaborate();
|
||||||
let facet = t.facet.clone();
|
let facet = t.facet.clone();
|
||||||
t.linked_task(syndicate::name!("subprocess"), async move {
|
t.linked_task(syndicate::name!("subprocess"), async move {
|
||||||
let argv = config.argv.elaborate();
|
let cmd = config.process.build_command().ok_or("Cannot start daemon process")?;
|
||||||
let mut cmd = process::Command::new(argv.program);
|
|
||||||
cmd.args(argv.args);
|
|
||||||
match config.dir {
|
|
||||||
ProcessDir::Present { dir } => { cmd.current_dir(dir); () },
|
|
||||||
ProcessDir::Absent => (),
|
|
||||||
ProcessDir::Invalid { dir } => {
|
|
||||||
tracing::error!(?dir, "Invalid working directory");
|
|
||||||
return cannot_start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match config.clear_env {
|
|
||||||
ClearEnv::Present { clear_env: true } => { cmd.env_clear(); () },
|
|
||||||
ClearEnv::Present { clear_env: false } => (),
|
|
||||||
ClearEnv::Absent => (),
|
|
||||||
ClearEnv::Invalid { clear_env } => {
|
|
||||||
tracing::error!(?clear_env, "Invalid clearEnv setting");
|
|
||||||
return cannot_start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match config.env {
|
|
||||||
ProcessEnv::Present { env } => {
|
|
||||||
for (k, v) in env {
|
|
||||||
if let Some(env_variable) = match k {
|
|
||||||
EnvVariable::String(k) => Some(k),
|
|
||||||
EnvVariable::Symbol(k) => Some(k),
|
|
||||||
EnvVariable::Invalid(env_variable) => {
|
|
||||||
tracing::error!(?env_variable,
|
|
||||||
"Invalid environment variable name");
|
|
||||||
return cannot_start();
|
|
||||||
}
|
|
||||||
} {
|
|
||||||
match v {
|
|
||||||
EnvValue::Set(value) => { cmd.env(env_variable, value); () }
|
|
||||||
EnvValue::Remove => { cmd.env_remove(env_variable); () }
|
|
||||||
EnvValue::Invalid(value) => {
|
|
||||||
tracing::error!(?env_variable, ?value,
|
|
||||||
"Invalid environment variable value");
|
|
||||||
return cannot_start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProcessEnv::Absent => (),
|
|
||||||
ProcessEnv::Invalid { env } => {
|
|
||||||
tracing::error!(?env, "Invalid daemon environment");
|
|
||||||
return cannot_start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let announce_presumed_readiness = match config.ready_on_start {
|
let announce_presumed_readiness = match config.ready_on_start {
|
||||||
ReadyOnStart::Present { ready_on_start } => ready_on_start,
|
ReadyOnStart::Present { ready_on_start } => ready_on_start,
|
||||||
ReadyOnStart::Absent => true,
|
ReadyOnStart::Absent => true,
|
||||||
ReadyOnStart::Invalid { ready_on_start } => {
|
ReadyOnStart::Invalid { ready_on_start } => {
|
||||||
tracing::error!(?ready_on_start, "Invalid readyOnStart value");
|
tracing::error!(?ready_on_start, "Invalid readyOnStart value");
|
||||||
return cannot_start();
|
Err("Invalid readyOnStart value")?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let restart_policy = match config.restart {
|
let restart_policy = match config.restart {
|
||||||
|
@ -253,16 +273,11 @@ fn run(
|
||||||
RestartField::Absent => RestartPolicy::All,
|
RestartField::Absent => RestartPolicy::All,
|
||||||
RestartField::Invalid { restart } => {
|
RestartField::Invalid { restart } => {
|
||||||
tracing::error!(?restart, "Invalid restart value");
|
tracing::error!(?restart, "Invalid restart value");
|
||||||
return cannot_start();
|
Err("Invalid restart value")?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
cmd.stdin(std::process::Stdio::null());
|
let daemon_instance = DaemonInstance {
|
||||||
cmd.stdout(std::process::Stdio::inherit());
|
|
||||||
cmd.stderr(std::process::Stdio::inherit());
|
|
||||||
cmd.kill_on_drop(true);
|
|
||||||
|
|
||||||
let process_instance = ProcessInstance {
|
|
||||||
name: tracing::Span::current(),
|
name: tracing::Span::current(),
|
||||||
cmd,
|
cmd,
|
||||||
announce_presumed_readiness,
|
announce_presumed_readiness,
|
||||||
|
@ -272,7 +287,7 @@ fn run(
|
||||||
};
|
};
|
||||||
|
|
||||||
facet.activate(Account::new(syndicate::name!("instance-startup")), |t| {
|
facet.activate(Account::new(syndicate::name!("instance-startup")), |t| {
|
||||||
process_instance.start(t)
|
daemon_instance.start(t)
|
||||||
})?;
|
})?;
|
||||||
Ok(LinkedTaskTermination::KeepFacet)
|
Ok(LinkedTaskTermination::KeepFacet)
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue