mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-03 19:28:22 +00:00
Add PrettyDuration, job timeout, job max schedule drift and job retry limit
This commit is contained in:
@@ -152,9 +152,13 @@
|
||||
"properties": {
|
||||
"max_age": {
|
||||
"description": "Maximum age of snapshots.\n\nFormat: 5m, 1h, 2d, ...\n\nAfter the specified time new snapshots will be created, and the old ones discarded.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PrettyDuration"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"requests": {
|
||||
@@ -295,6 +299,17 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"max_schedule_drift": {
|
||||
"description": "Don't start job if past the due time by this amount, instead opting to wait for the next instance of it to be triggered.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PrettyDuration"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"package": {
|
||||
"description": "The package that contains the command to run. Defaults to the app config's package.",
|
||||
"anyOf": [
|
||||
@@ -306,6 +321,24 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"retries": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"timeout": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PrettyDuration"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"volumes": {
|
||||
"type": [
|
||||
"array",
|
||||
@@ -397,9 +430,13 @@
|
||||
},
|
||||
"timeout": {
|
||||
"description": "Request timeout.\n\nFormat: 1s, 5m, 11h, ...",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PrettyDuration"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"unhealthy_threshold": {
|
||||
@@ -492,9 +529,13 @@
|
||||
},
|
||||
"timeout": {
|
||||
"description": "Request timeout.\n\nFormat: 1s, 5m, 11h, ...",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PrettyDuration"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -593,6 +634,9 @@
|
||||
"PackageSource": {
|
||||
"type": "string"
|
||||
},
|
||||
"PrettyDuration": {
|
||||
"type": "string"
|
||||
},
|
||||
"Redirect": {
|
||||
"description": "App redirect configuration.",
|
||||
"type": "object",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use super::pretty_duration::PrettyDuration;
|
||||
|
||||
/// Defines an HTTP request.
|
||||
#[derive(
|
||||
schemars::JsonSchema, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Debug,
|
||||
@@ -24,7 +26,7 @@ pub struct HttpRequest {
|
||||
///
|
||||
/// Format: 1s, 5m, 11h, ...
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub timeout: Option<String>,
|
||||
pub timeout: Option<PrettyDuration>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub expect: Option<HttpRequestExpect>,
|
||||
|
||||
@@ -4,7 +4,7 @@ use serde::{de::Error, Deserialize, Serialize};
|
||||
|
||||
use crate::package::PackageSource;
|
||||
|
||||
use super::{AppConfigCapabilityMemoryV1, AppVolume, HttpRequest};
|
||||
use super::{pretty_duration::PrettyDuration, AppConfigCapabilityMemoryV1, AppVolume, HttpRequest};
|
||||
|
||||
/// Job configuration.
|
||||
#[derive(
|
||||
@@ -69,6 +69,18 @@ pub struct ExecutableJob {
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub volumes: Option<Vec<AppVolume>>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub timeout: Option<PrettyDuration>,
|
||||
|
||||
/// Don't start job if past the due time by this amount,
|
||||
/// instead opting to wait for the next instance of it
|
||||
/// to be triggered.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_schedule_drift: Option<PrettyDuration>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub retries: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
@@ -215,6 +227,9 @@ mod tests {
|
||||
name: "vol".to_owned(),
|
||||
mount: "/path/to/volume".to_owned(),
|
||||
}]),
|
||||
timeout: Some("1m".parse().unwrap()),
|
||||
max_schedule_drift: Some("2h".parse().unwrap()),
|
||||
retries: None,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -234,7 +249,9 @@ execute:
|
||||
limit: '1000.0 MB'
|
||||
volumes:
|
||||
- name: vol
|
||||
mount: /path/to/volume"#;
|
||||
mount: /path/to/volume
|
||||
timeout: '1m'
|
||||
max_schedule_drift: '2h'"#;
|
||||
|
||||
assert_eq!(
|
||||
serialized.trim(),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
mod healthcheck;
|
||||
mod http;
|
||||
mod job;
|
||||
mod pretty_duration;
|
||||
|
||||
pub use self::{healthcheck::*, http::*, job::*};
|
||||
|
||||
@@ -10,6 +11,7 @@ use std::collections::HashMap;
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use bytesize::ByteSize;
|
||||
use pretty_duration::PrettyDuration;
|
||||
|
||||
use crate::package::PackageSource;
|
||||
|
||||
@@ -256,7 +258,7 @@ pub struct AppConfigCapabilityInstaBootV1 {
|
||||
/// After the specified time new snapshots will be created, and the old
|
||||
/// ones discarded.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_age: Option<String>,
|
||||
pub max_age: Option<PrettyDuration>,
|
||||
}
|
||||
|
||||
/// App redirect configuration.
|
||||
|
||||
195
lib/config/src/app/pretty_duration.rs
Normal file
195
lib/config/src/app/pretty_duration.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt::{Debug, Display},
|
||||
str::FromStr,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{de::Error, Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct PrettyDuration {
|
||||
unit: DurationUnit,
|
||||
amount: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum DurationUnit {
|
||||
Seconds,
|
||||
Minutes,
|
||||
Hours,
|
||||
Days,
|
||||
}
|
||||
|
||||
impl PrettyDuration {
|
||||
pub fn as_duration(&self) -> Duration {
|
||||
match self.unit {
|
||||
DurationUnit::Seconds => Duration::from_secs(self.amount),
|
||||
DurationUnit::Minutes => Duration::from_secs(self.amount * 60),
|
||||
DurationUnit::Hours => Duration::from_secs(self.amount * 60 * 60),
|
||||
DurationUnit::Days => Duration::from_secs(self.amount * 60 * 60 * 24),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PrettyDuration {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
unit: DurationUnit::Seconds,
|
||||
amount: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for PrettyDuration {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for PrettyDuration {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.as_duration().cmp(&other.as_duration())
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonSchema for PrettyDuration {
|
||||
fn schema_name() -> String {
|
||||
"PrettyDuration".to_owned()
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
||||
String::json_schema(gen)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DurationUnit {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DurationUnit::Seconds => write!(f, "s"),
|
||||
DurationUnit::Minutes => write!(f, "m"),
|
||||
DurationUnit::Hours => write!(f, "h"),
|
||||
DurationUnit::Days => write!(f, "d"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for DurationUnit {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"s" | "S" => Ok(Self::Seconds),
|
||||
"m" | "M" => Ok(Self::Minutes),
|
||||
"h" | "H" => Ok(Self::Hours),
|
||||
"d" | "D" => Ok(Self::Days),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PrettyDuration {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}{}", self.amount, self.unit)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PrettyDuration {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
<Self as Display>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PrettyDuration {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (amount_str, unit_str) = s.split_at_checked(s.len() - 1).ok_or(())?;
|
||||
Ok(Self {
|
||||
unit: unit_str.parse()?,
|
||||
amount: amount_str.parse().map_err(|_| ())?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for PrettyDuration {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for PrettyDuration {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let repr: Cow<'de, str> = Cow::deserialize(deserializer)?;
|
||||
repr.parse()
|
||||
.map_err(|()| D::Error::custom("Failed to parse value as a duration"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn pretty_duration_serialize() {
|
||||
assert_eq!(
|
||||
PrettyDuration {
|
||||
unit: DurationUnit::Seconds,
|
||||
amount: 1234
|
||||
}
|
||||
.to_string(),
|
||||
"1234s"
|
||||
);
|
||||
assert_eq!(
|
||||
PrettyDuration {
|
||||
unit: DurationUnit::Minutes,
|
||||
amount: 345
|
||||
}
|
||||
.to_string(),
|
||||
"345m"
|
||||
);
|
||||
assert_eq!(
|
||||
PrettyDuration {
|
||||
unit: DurationUnit::Hours,
|
||||
amount: 56
|
||||
}
|
||||
.to_string(),
|
||||
"56h"
|
||||
);
|
||||
assert_eq!(
|
||||
PrettyDuration {
|
||||
unit: DurationUnit::Days,
|
||||
amount: 7
|
||||
}
|
||||
.to_string(),
|
||||
"7d"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn pretty_duration_deserialize() {
|
||||
fn assert_deserializes_to(repr1: &str, repr2: &str, unit: DurationUnit, amount: u64) {
|
||||
let duration = PrettyDuration { unit, amount };
|
||||
assert_eq!(duration, repr1.parse().unwrap());
|
||||
assert_eq!(duration, repr2.parse().unwrap());
|
||||
}
|
||||
|
||||
assert_deserializes_to("12s", "12S", DurationUnit::Seconds, 12);
|
||||
assert_deserializes_to("34m", "34M", DurationUnit::Minutes, 34);
|
||||
assert_deserializes_to("56h", "56H", DurationUnit::Hours, 56);
|
||||
assert_deserializes_to("7d", "7D", DurationUnit::Days, 7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
pub fn cant_parse_nagative_duration() {
|
||||
_ = "-12s".parse::<PrettyDuration>().unwrap();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user