Implement daemon service
This commit is contained in:
parent
c87bfd8a2d
commit
9f316ac659
|
@ -1397,6 +1397,15 @@ dependencies = [
|
|||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.4"
|
||||
|
@ -1596,7 +1605,9 @@ dependencies = [
|
|||
"memchr",
|
||||
"mio 0.7.13",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"tokio-macros",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
|
|
@ -26,7 +26,7 @@ structopt = "0.3"
|
|||
tungstenite = "0.13"
|
||||
tokio-tungstenite = "0.14"
|
||||
|
||||
tokio = { version = "1.10", features = ["io-std", "time"] }
|
||||
tokio = { version = "1.10", features = ["io-std", "time", "process"] }
|
||||
tokio-util = "0.6"
|
||||
|
||||
tracing = "0.1"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
´³bundle·µ³externalServices„´³schema·³version‘³definitions·³Service´³refµ„³
DaemonService„³ClearEnv´³orµµ±present´³dict·³clearEnv´³named³clearEnv´³atom³Boolean„„„„„µ±absent´³dict·„„„„„³DaemonId³any³EnvValue´³orµµ±set´³atom³String„„µ±remove´³lit€„„„„³ DaemonDir´³orµµ±present´³dict·³dir´³named³dir´³atom³String„„„„„µ±absent´³dict·„„„„„³ DaemonEnv´³orµµ±present´³dict·³env´³named³env´³dictof´³refµ„³EnvVariable„´³refµ„³EnvValue„„„„„„µ±absent´³dict·„„„„„³
|
||||
DaemonSpec´³andµ´³dict·³argv´³named³argv´³seqof´³atom³String„„„„„´³named³env´³refµ„³ DaemonEnv„„´³named³dir´³refµ„³ DaemonDir„„´³named³clearEnv´³refµ„³ClearEnv„„„„³EnvVariable´³orµµ±string´³atom³String„„µ±symbol´³atom³Symbol„„„„³
DaemonProcess´³rec´³lit³daemon„´³tupleµ´³named³id´³refµ„³DaemonId„„´³named³config´³refµ„³
|
||||
DaemonSpec„„„„„³
DaemonService´³rec´³lit³daemon„´³tupleµ´³named³id´³refµ„³DaemonId„„„„„³ServiceDependency´³rec´³lit³
|
||||
´³bundle·µ³externalServices„´³schema·³version‘³definitions·³Service´³refµ„³
DaemonService„³ClearEnv´³orµµ±present´³dict·³clearEnv´³named³clearEnv´³atom³Boolean„„„„„µ±invalid´³dict·³clearEnv´³named³clearEnv³any„„„„µ±absent´³dict·„„„„„³DaemonId³any³EnvValue´³orµµ±set´³atom³String„„µ±remove´³lit€„„µ±invalid³any„„„³ DaemonDir´³orµµ±present´³dict·³dir´³named³dir´³atom³String„„„„„µ±invalid´³dict·³dir´³named³dir³any„„„„µ±absent´³dict·„„„„„³ DaemonEnv´³orµµ±present´³dict·³env´³named³env´³dictof´³refµ„³EnvVariable„´³refµ„³EnvValue„„„„„„µ±invalid´³dict·³env´³named³env³any„„„„µ±absent´³dict·„„„„„³
|
||||
DaemonSpec´³orµµ±simple´³refµ„³CommandLine„„µ±full´³refµ„³FullDaemonSpec„„„„³CommandLine´³orµµ±shell´³atom³String„„µ±full´³refµ„³FullCommandLine„„„„³EnvVariable´³orµµ±string´³atom³String„„µ±symbol´³atom³Symbol„„µ±invalid³any„„„³
DaemonProcess´³rec´³lit³daemon„´³tupleµ´³named³id´³refµ„³DaemonId„„´³named³config´³refµ„³
|
||||
DaemonSpec„„„„„³
DaemonService´³rec´³lit³daemon„´³tupleµ´³named³id´³refµ„³DaemonId„„„„„³FullDaemonSpec´³andµ´³dict·³argv´³named³argv´³refµ„³CommandLine„„„„´³named³env´³refµ„³ DaemonEnv„„´³named³dir´³refµ„³ DaemonDir„„´³named³clearEnv´³refµ„³ClearEnv„„„„³FullCommandLine´³tuplePrefixµ´³named³program´³atom³String„„„´³named³args´³seqof´³atom³String„„„„³ServiceDependency´³rec´³lit³
|
||||
depends-on„´³tupleµ´³named³depender³any„´³named³dependee³any„„„„„³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„„„„„
|
|
@ -9,10 +9,14 @@ ServiceDependency = <depends-on @depender any @dependee any> .
|
|||
|
||||
DaemonProcess = <daemon @id DaemonId @config DaemonSpec>.
|
||||
DaemonId = any .
|
||||
DaemonSpec = { argv: [string ...] } & @env DaemonEnv & @dir DaemonDir & @clearEnv ClearEnv .
|
||||
DaemonEnv = @present { env: { EnvVariable: EnvValue ...:... } } / @absent {} .
|
||||
DaemonDir = @present { dir: string } / @absent {} .
|
||||
ClearEnv = @present { clearEnv: bool } / @absent {} .
|
||||
DaemonSpec = @simple CommandLine / @full FullDaemonSpec .
|
||||
FullDaemonSpec = { argv: CommandLine } & @env DaemonEnv & @dir DaemonDir & @clearEnv ClearEnv .
|
||||
DaemonEnv = @present { env: { EnvVariable: EnvValue ...:... } } / @invalid { env: any } / @absent {} .
|
||||
DaemonDir = @present { dir: string } / @invalid { dir: any } / @absent {} .
|
||||
ClearEnv = @present { clearEnv: bool } / @invalid { clearEnv: any } / @absent {} .
|
||||
|
||||
EnvVariable = @string string / @symbol symbol .
|
||||
EnvValue = @set string / @remove #f .
|
||||
CommandLine = @shell string / @full FullCommandLine .
|
||||
FullCommandLine = [@program string, @args string ...] .
|
||||
|
||||
EnvVariable = @string string / @symbol symbol / @invalid any .
|
||||
EnvValue = @set string / @remove #f / @invalid any .
|
||||
|
|
|
@ -5,8 +5,10 @@ use std::sync::Arc;
|
|||
use syndicate::actor::*;
|
||||
use syndicate::supervise::{Supervisor, SupervisorConfiguration};
|
||||
|
||||
use tokio::process;
|
||||
|
||||
use crate::language::language;
|
||||
use crate::schemas::external_services::{DaemonService, DaemonSpec};
|
||||
use crate::schemas::external_services::*;
|
||||
|
||||
use syndicate_macros::during;
|
||||
|
||||
|
@ -24,6 +26,36 @@ pub fn on_demand(t: &mut Activation, config_ds: Arc<Cap>, root_ds: Arc<Cap>) {
|
|||
});
|
||||
}
|
||||
|
||||
fn cannot_start() -> ActorResult {
|
||||
Err("Cannot start daemon process")?
|
||||
}
|
||||
|
||||
impl DaemonSpec {
|
||||
fn elaborate(self) -> FullDaemonSpec {
|
||||
match self {
|
||||
DaemonSpec::Simple(command_line) => FullDaemonSpec {
|
||||
argv: *command_line,
|
||||
env: DaemonEnv::Absent,
|
||||
dir: DaemonDir::Absent,
|
||||
clear_env: ClearEnv::Absent,
|
||||
},
|
||||
DaemonSpec::Full(spec) => *spec,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandLine {
|
||||
fn elaborate(self) -> FullCommandLine {
|
||||
match self {
|
||||
CommandLine::Shell(s) => FullCommandLine {
|
||||
program: "sh".to_owned(),
|
||||
args: vec!["-c".to_owned(), s],
|
||||
},
|
||||
CommandLine::Full(command_line) => *command_line,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
t: &mut Activation,
|
||||
config_ds: Arc<Cap>,
|
||||
|
@ -35,11 +67,79 @@ fn run(
|
|||
config_ds.assert(t, &(), &syndicate_macros::template!("<service-running =spec>"));
|
||||
}
|
||||
|
||||
let id = service.id.0.clone();
|
||||
Ok(during!(t, config_ds, language(), <daemon #(service.id.0) $config>, |t: &mut Activation| {
|
||||
match language().parse::<DaemonSpec>(&config) {
|
||||
Ok(config) => {
|
||||
let config = config.elaborate();
|
||||
t.linked_task(syndicate::name!("subprocess"), async move {
|
||||
tracing::info!(?config);
|
||||
let argv = config.argv.elaborate();
|
||||
let mut cmd = process::Command::new(argv.program);
|
||||
cmd.args(argv.args);
|
||||
match config.dir {
|
||||
DaemonDir::Present { dir } => { cmd.current_dir(dir); () },
|
||||
DaemonDir::Absent => (),
|
||||
DaemonDir::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 {
|
||||
DaemonEnv::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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DaemonEnv::Absent => (),
|
||||
DaemonEnv::Invalid { env } => {
|
||||
tracing::error!(?env, "Invalid daemon environment");
|
||||
return cannot_start();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(during!(t, config_ds, language(), <daemon #(id) $config>, |_t| {
|
||||
if let Ok(config) = language().parse::<DaemonSpec>(&config) {
|
||||
tracing::info!("daemon {:?} {:?}", &service, &config);
|
||||
cmd.stdin(std::process::Stdio::null());
|
||||
cmd.stdout(std::process::Stdio::inherit());
|
||||
cmd.stderr(std::process::Stdio::inherit());
|
||||
cmd.kill_on_drop(true);
|
||||
|
||||
tracing::info!(?cmd);
|
||||
let mut child = cmd.spawn()?;
|
||||
tracing::info!(status = ?child.wait().await);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::error!(?config, "Invalid DaemonSpec");
|
||||
return cannot_start();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}))
|
||||
|
|
Loading…
Reference in New Issue