Skip to content

Commit

Permalink
Support monitor upserts
Browse files Browse the repository at this point in the history
  • Loading branch information
szokeasaurusrex committed Nov 8, 2023
1 parent f226a6e commit 8d52559
Showing 1 changed file with 95 additions and 3 deletions.
98 changes: 95 additions & 3 deletions src/commands/monitors/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use anyhow::Result;
use clap::{Arg, ArgAction, ArgMatches, Command};
use console::style;

use sentry::protocol::{MonitorCheckIn, MonitorCheckInStatus};
use sentry::protocol::{MonitorCheckIn, MonitorCheckInStatus, MonitorConfig, MonitorSchedule};
use sentry::types::Dsn;

use crate::api::{Api, ApiCreateMonitorCheckIn, ApiMonitorCheckInStatus, ApiUpdateMonitorCheckIn};
Expand Down Expand Up @@ -45,6 +45,37 @@ pub fn make_command(command: Command) -> Command {
.num_args(1..)
.last(true),
)
.arg(
Arg::new("schedule")
.short('s')
.long("schedule")
.help("Configure the cron monitor with the given schedule (crontab format)"),
)
.arg(
Arg::new("checkin_margin")
.long("check-in-margin")
.value_parser(clap::value_parser!(u64))
.help(
"The allowed margin of minutes after the expected check-in time that the \
monitor will not be considered missed for. This parameter is ignored \
unless a cron schedule is also provided.",
),
)
.arg(
Arg::new("max_runtime")
.long("max-runtime")
.value_parser(clap::value_parser!(u64))
.help(
"The allowed duration in minutes that the monitor may be in progress for \
before being considered failed due to timeout. This parameter is ignored \
unless a cron schedule is also provided.",
),
)
.arg(Arg::new("timezone").long("timezone").help(
"A tz database string (e.g. \"Europe/Vienna\") representing the monitor's \
execution schedule's timezone. This parameter is ignored unless a cron \
schedule is also provided.",
))
}

fn run_program(args: Vec<&String>, monitor_slug: &str) -> (bool, Option<i32>, Duration) {
Expand Down Expand Up @@ -75,6 +106,7 @@ fn dsn_execute(
args: Vec<&String>,
monitor_slug: &str,
environment: &str,
monitor_config: Option<MonitorConfig>,
) -> (bool, Option<i32>) {
let check_in_id = Uuid::new_v4();

Expand All @@ -84,7 +116,7 @@ fn dsn_execute(
status: MonitorCheckInStatus::InProgress,
duration: None,
environment: Some(environment.to_string()),
monitor_config: None,
monitor_config,
};

with_sentry_client(dsn.clone(), |c| c.send_envelope(open_checkin.into()));
Expand Down Expand Up @@ -158,6 +190,58 @@ fn token_execute(
(success, code, None)
}

fn warn_ignored_arguments(matches: &ArgMatches, possibly_ignored: Vec<&str>, reason: &str) {
let mut ignored_arguments: Vec<_> = possibly_ignored
.into_iter()
.filter(|id| matches.contains_id(id))
.map(|id| format!("`{id}`"))
.collect();

if ignored_arguments.len() > 0 {

Check failure on line 200 in src/commands/monitors/run.rs

View workflow job for this annotation

GitHub Actions / Lint

length comparison to zero
let (possible_s, possible_and) = if ignored_arguments.len() > 1 {
("s", "and ")
} else {
("", "")
};

let last = ignored_arguments.len() - 1;
ignored_arguments[last] = format!("{possible_and}{}", ignored_arguments[last]);

let joiner = if ignored_arguments.len() > 2 {
", "
} else {
" "
};

warn!(
"Ignoring {} argument{possible_s} because {reason}.",
ignored_arguments.join(joiner)
)
}
}

fn parse_monitor_config_args(matches: &ArgMatches) -> Option<MonitorConfig> {
match matches.get_one::<String>("schedule") {
Some(schedule) => Some(MonitorConfig {
schedule: MonitorSchedule::Crontab {
value: String::from(schedule),
},
checkin_margin: matches.get_one("checkin_margin").copied(),
max_runtime: matches.get_one("max_runtime").copied(),
timezone: matches.get_one("timezone").cloned(),
}),
None => {
warn_ignored_arguments(
matches,
vec!["checkin_margin", "max_runtime", "timezone"],
"`schedule` argument is missing",
);

None
}
}
}

pub fn execute(matches: &ArgMatches) -> Result<()> {
let config = Config::current();
let dsn = config.get_dsn().ok();
Expand All @@ -172,13 +256,21 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
let args: Vec<_> = matches.get_many::<String>("args").unwrap().collect();
let monitor_slug = matches.get_one::<String>("monitor_slug").unwrap();
let environment = matches.get_one::<String>("environment").unwrap();
let monitor_config = parse_monitor_config_args(&matches);

Check failure on line 259 in src/commands/monitors/run.rs

View workflow job for this annotation

GitHub Actions / Lint

this expression creates a reference which is immediately dereferenced by the compiler

let (success, code) = match dsn {
// Use envelope API when dsn is provided. This is the prefered way to create check-ins,
// and the legacy API will be removed in the next major CLI version.
Some(dsn) => dsn_execute(dsn, args, monitor_slug, environment),
Some(dsn) => dsn_execute(dsn, args, monitor_slug, environment, monitor_config),
// Use legacy API when DSN is not provided
None => {
if monitor_config.is_some() {
warn_ignored_arguments(
matches,
vec!["schedule", "checkin_margin", "max_runtime", "timezone"],
"cron monitor upserts are only supported with DSN auth",
);
}
let (success, code, err) = token_execute(args, monitor_slug, environment);
if let Some(e) = err {
if matches.get_flag("allow_failure") {
Expand Down

0 comments on commit 8d52559

Please sign in to comment.