A few more schema tweaks

This commit is contained in:
Arshia Ghafoori
2025-01-14 13:20:39 +04:00
parent 1f6ced514c
commit 00c25681e7
2 changed files with 70 additions and 63 deletions

View File

@@ -299,17 +299,6 @@
"type": "string" "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": { "package": {
"description": "The package that contains the command to run. Defaults to the app config's package.", "description": "The package that contains the command to run. Defaults to the app config's package.",
"anyOf": [ "anyOf": [
@@ -321,24 +310,6 @@
} }
] ]
}, },
"retries": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"timeout": {
"anyOf": [
{
"$ref": "#/definitions/PrettyDuration"
},
{
"type": "null"
}
]
},
"volumes": { "volumes": {
"type": [ "type": [
"array", "array",
@@ -606,9 +577,38 @@
"trigger" "trigger"
], ],
"properties": { "properties": {
"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"
}
]
},
"name": { "name": {
"type": "string" "type": "string"
}, },
"retries": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"timeout": {
"anyOf": [
{
"$ref": "#/definitions/PrettyDuration"
},
{
"type": "null"
}
]
},
"trigger": { "trigger": {
"$ref": "#/definitions/JobTrigger" "$ref": "#/definitions/JobTrigger"
} }

View File

@@ -13,6 +13,19 @@ use super::{pretty_duration::PrettyDuration, AppConfigCapabilityMemoryV1, AppVol
pub struct Job { pub struct Job {
name: String, name: String,
trigger: JobTrigger, trigger: JobTrigger,
#[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>,
#[serde(flatten)] #[serde(flatten)]
action: JobAction, action: JobAction,
} }
@@ -29,9 +42,9 @@ pub enum JobAction {
#[derive( #[derive(
serde::Serialize, serde::Deserialize, schemars::JsonSchema, Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema, Clone, Debug, PartialEq, Eq,
)] )]
pub enum CronType { pub enum CronSchedule {
Daily,
Hourly, Hourly,
Daily,
Weekly, Weekly,
CronExpression(String), CronExpression(String),
} }
@@ -40,7 +53,7 @@ pub enum CronType {
pub enum JobTrigger { pub enum JobTrigger {
PreDeployment, PreDeployment,
PostDeployment, PostDeployment,
Cron(CronType), Cron(CronSchedule),
} }
#[derive( #[derive(
@@ -69,18 +82,6 @@ pub struct ExecutableJob {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub volumes: Option<Vec<AppVolume>>, 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( #[derive(
@@ -123,10 +124,10 @@ impl Display for JobTrigger {
match self { match self {
Self::PreDeployment => write!(f, "pre-deployment"), Self::PreDeployment => write!(f, "pre-deployment"),
Self::PostDeployment => write!(f, "post-deployment"), Self::PostDeployment => write!(f, "post-deployment"),
Self::Cron(CronType::Hourly) => write!(f, "@hourly"), Self::Cron(CronSchedule::Hourly) => write!(f, "@hourly"),
Self::Cron(CronType::Daily) => write!(f, "@daily"), Self::Cron(CronSchedule::Daily) => write!(f, "@daily"),
Self::Cron(CronType::Weekly) => write!(f, "@weekly"), Self::Cron(CronSchedule::Weekly) => write!(f, "@weekly"),
Self::Cron(CronType::CronExpression(sched)) => write!(f, "{}", sched), Self::Cron(CronSchedule::CronExpression(sched)) => write!(f, "{}", sched),
} }
} }
} }
@@ -141,15 +142,15 @@ impl FromStr for JobTrigger {
Ok(Self::PostDeployment) Ok(Self::PostDeployment)
} else if let Some(predefined_sched) = s.strip_prefix('@') { } else if let Some(predefined_sched) = s.strip_prefix('@') {
match predefined_sched { match predefined_sched {
"hourly" => Ok(Self::Cron(CronType::Hourly)), "hourly" => Ok(Self::Cron(CronSchedule::Hourly)),
"daily" => Ok(Self::Cron(CronType::Daily)), "daily" => Ok(Self::Cron(CronSchedule::Daily)),
"weekly" => Ok(Self::Cron(CronType::Weekly)), "weekly" => Ok(Self::Cron(CronSchedule::Weekly)),
_ => Err(format!("Invalid cron expression {s}").into()), _ => Err(format!("Invalid cron expression {s}").into()),
} }
} else { } else {
// Let's make sure the input string is valid... // Let's make sure the input string is valid...
match cron::Schedule::from_str(s) { match cron::Schedule::from_str(s) {
Ok(sched) => Ok(Self::Cron(CronType::CronExpression( Ok(sched) => Ok(Self::Cron(CronSchedule::CronExpression(
sched.source().to_owned(), sched.source().to_owned(),
))), ))),
Err(_) => Err(format!("Invalid cron expression {s}").into()), Err(_) => Err(format!("Invalid cron expression {s}").into()),
@@ -184,15 +185,21 @@ mod tests {
assert_roundtrip("pre-deployment", JobTrigger::PreDeployment); assert_roundtrip("pre-deployment", JobTrigger::PreDeployment);
assert_roundtrip("post-deployment", JobTrigger::PostDeployment); assert_roundtrip("post-deployment", JobTrigger::PostDeployment);
assert_roundtrip("@hourly", JobTrigger::Cron(crate::app::CronType::Hourly)); assert_roundtrip(
assert_roundtrip("@daily", JobTrigger::Cron(crate::app::CronType::Daily)); "@hourly",
assert_roundtrip("@weekly", JobTrigger::Cron(crate::app::CronType::Weekly)); JobTrigger::Cron(crate::app::CronSchedule::Hourly),
);
assert_roundtrip("@daily", JobTrigger::Cron(crate::app::CronSchedule::Daily));
assert_roundtrip(
"@weekly",
JobTrigger::Cron(crate::app::CronSchedule::Weekly),
);
// Note: the parsing code should keep the formatting of the source string. // Note: the parsing code should keep the formatting of the source string.
// This is tested in assert_roundtrip. // This is tested in assert_roundtrip.
assert_roundtrip( assert_roundtrip(
"0 0/2 12 ? JAN-APR 2", "0 0/2 12 ? JAN-APR 2",
JobTrigger::Cron(crate::app::CronType::CronExpression( JobTrigger::Cron(crate::app::CronSchedule::CronExpression(
"0 0/2 12 ? JAN-APR 2".to_owned(), "0 0/2 12 ? JAN-APR 2".to_owned(),
)), )),
); );
@@ -202,9 +209,12 @@ mod tests {
pub fn job_serialization_roundtrip() { pub fn job_serialization_roundtrip() {
let job = Job { let job = Job {
name: "my-job".to_owned(), name: "my-job".to_owned(),
trigger: JobTrigger::Cron(super::CronType::CronExpression( trigger: JobTrigger::Cron(super::CronSchedule::CronExpression(
"0 0/2 12 ? JAN-APR 2".to_owned(), "0 0/2 12 ? JAN-APR 2".to_owned(),
)), )),
timeout: Some("1m".parse().unwrap()),
max_schedule_drift: Some("2h".parse().unwrap()),
retries: None,
action: JobAction::Execute(super::ExecutableJob { action: JobAction::Execute(super::ExecutableJob {
package: Some(crate::package::PackageSource::Ident( package: Some(crate::package::PackageSource::Ident(
crate::package::PackageIdent::Named(crate::package::NamedPackageIdent { crate::package::PackageIdent::Named(crate::package::NamedPackageIdent {
@@ -227,15 +237,14 @@ mod tests {
name: "vol".to_owned(), name: "vol".to_owned(),
mount: "/path/to/volume".to_owned(), mount: "/path/to/volume".to_owned(),
}]), }]),
timeout: Some("1m".parse().unwrap()),
max_schedule_drift: Some("2h".parse().unwrap()),
retries: None,
}), }),
}; };
let serialized = r#" let serialized = r#"
name: my-job name: my-job
trigger: '0 0/2 12 ? JAN-APR 2' trigger: '0 0/2 12 ? JAN-APR 2'
timeout: '1m'
max_schedule_drift: '2h'
execute: execute:
package: ns/pkg package: ns/pkg
command: cmd command: cmd
@@ -249,9 +258,7 @@ execute:
limit: '1000.0 MB' limit: '1000.0 MB'
volumes: volumes:
- name: vol - name: vol
mount: /path/to/volume mount: /path/to/volume"#;
timeout: '1m'
max_schedule_drift: '2h'"#;
assert_eq!( assert_eq!(
serialized.trim(), serialized.trim(),