Browse Source

acmed/hooks: add option to not run hook after issue failure

pull/173/head
famfo 3 weeks ago
parent
commit
68e25ff0d6
Failed to extract signature
  1. 13
      acmed/src/certificate.rs
  2. 16
      acmed/src/config.rs
  3. 21
      acmed/src/hooks.rs
  4. 8
      acmed/src/storage.rs
  5. 4
      man/en/acmed.toml.5

13
acmed/src/certificate.rs

@ -164,7 +164,7 @@ impl Certificate {
HookType::ChallengeTlsAlpn01Clean,
),
};
hooks::call(self, &self.hooks, &hook_data, hook_type.0).await?;
hooks::call(self, &self.hooks, &hook_data, None, hook_type.0).await?;
Ok((hook_data, hook_type.1))
}
@ -173,7 +173,7 @@ impl Certificate {
data: &ChallengeHookData,
hook_type: HookType,
) -> Result<(), Error> {
hooks::call(self, &self.hooks, data, hook_type).await
hooks::call(self, &self.hooks, data, None, hook_type).await
}
pub async fn call_post_operation_hooks(
@ -196,7 +196,14 @@ impl Certificate {
env: HashMap::new(),
};
hook_data.set_env(&self.env);
hooks::call(self, &self.hooks, &hook_data, HookType::PostOperation).await?;
hooks::call(
self,
&self.hooks,
&hook_data,
Some(is_success),
HookType::PostOperation,
)
.await?;
Ok(())
}
}

16
acmed/src/config.rs

@ -95,6 +95,15 @@ impl Config {
pub fn get_hook(&self, name: &str) -> Result<Vec<hooks::Hook>, Error> {
for hook in self.hook.iter() {
if name == hook.name {
if !hook.hook_type.contains(&HookType::PostOperation)
&& hook.run_after_failure.is_some()
{
return Err(format!(
"{}: run_after_failure can only be used with post-operation hooks", hook.name
)
.into());
}
let h = hooks::Hook {
name: hook.name.to_owned(),
hook_type: hook.hook_type.iter().map(|e| e.to_owned()).collect(),
@ -106,6 +115,7 @@ impl Config {
allow_failure: hook
.allow_failure
.unwrap_or(crate::DEFAULT_HOOK_ALLOW_FAILURE),
run_after_failure: hook.run_after_failure.unwrap_or(true),
};
return Ok(vec![h]);
}
@ -326,6 +336,7 @@ pub struct Hook {
pub stdout: Option<String>,
#[serde(rename = "type")]
pub hook_type: Vec<HookType>,
pub run_after_failure: Option<bool>,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize)]
@ -376,7 +387,10 @@ impl ExternalAccount {
| JwsSignatureAlgorithm::Hs384
| JwsSignatureAlgorithm::Hs512 => {}
_ => {
return Err(format!("{signature_algorithm}: invalid signature algorithm for external account binding").into());
return Err(format!(
"{signature_algorithm}: invalid signature algorithm for external account binding"
)
.into());
}
};
Ok(crate::account::ExternalAccount {

21
acmed/src/hooks.rs

@ -97,6 +97,7 @@ pub struct Hook {
pub stdout: Option<String>,
pub stderr: Option<String>,
pub allow_failure: bool,
pub run_after_failure: bool,
}
impl fmt::Display for Hook {
@ -119,11 +120,26 @@ macro_rules! get_hook_output {
}};
}
async fn call_single<L, T>(logger: &L, data: &T, hook: &Hook) -> Result<(), Error>
async fn call_single<L, T>(
logger: &L,
data: &T,
is_success: Option<bool>,
hook: &Hook,
) -> Result<(), Error>
where
L: HasLogger,
T: Clone + HookEnvData + Serialize,
{
if let Some(success) = is_success {
if !success && !hook.run_after_failure {
logger.debug(&format!(
"skipping hook \"{}\" due to previous failure",
hook.name
));
return Ok(());
}
}
logger.debug(&format!("calling hook \"{}\"", hook.name));
let mut v = vec![];
let args = match &hook.args {
@ -200,6 +216,7 @@ pub async fn call<L, T>(
logger: &L,
hooks: &[Hook],
data: &T,
is_success: Option<bool>,
hook_type: HookType,
) -> Result<(), Error>
where
@ -207,7 +224,7 @@ where
T: Clone + HookEnvData + Serialize,
{
for hook in hooks.iter().filter(|h| h.hook_type.contains(&hook_type)) {
call_single(logger, data, hook)
call_single(logger, data, is_success, hook)
.await
.map_err(|e| e.prefix(&hook.name))?;
}

8
acmed/src/storage.rs

@ -203,9 +203,9 @@ async fn write_file(fm: &FileManager, file_type: FileType, data: &[u8]) -> Resul
let is_new = !path.is_file();
if is_new {
hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePreCreate).await?;
hooks::call(fm, &fm.hooks, &hook_data, None, HookType::FilePreCreate).await?;
} else {
hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePreEdit).await?;
hooks::call(fm, &fm.hooks, &hook_data, None, HookType::FilePreEdit).await?;
}
fm.trace(&format!("writing file {path:?}"));
@ -235,9 +235,9 @@ async fn write_file(fm: &FileManager, file_type: FileType, data: &[u8]) -> Resul
}
if is_new {
hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePostCreate).await?;
hooks::call(fm, &fm.hooks, &hook_data, None, HookType::FilePostCreate).await?;
} else {
hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePostEdit).await?;
hooks::call(fm, &fm.hooks, &hook_data, None, HookType::FilePostEdit).await?;
}
Ok(())
}

4
man/en/acmed.toml.5

@ -355,6 +355,10 @@ String that will be written into the command's standard input. Mutually exclusiv
.Em stdin .
.It Ic stdout Ar string
Path to the file where the command's standard output if written.
.It Cm run_after_failure Ar boolean
Option only applicable to hooks of type
.Em post-operation .
Specifies whether to run the hook after issuing a certificate failed. Default is true.
.It Cm type Ar array
Array of strings. Possible types are:
.Bl -dash -compact

Loading…
Cancel
Save