From b1a9fa5d139c9341b84f12fa98523fe04742718e Mon Sep 17 00:00:00 2001 From: Sydney Acksman Date: Thu, 8 Oct 2020 20:22:56 -0500 Subject: [PATCH 1/5] Add support for Cron Triggers * This also refactors deployments to accept multiple deploy targets so you aren't limited to zoned, zoneless, or scheduled for one script. * It also removes the constraint that only route or routes can be used. If both are specified, routes will have route inserted at the beginning --- src/commands/dev/edge/mod.rs | 11 +- src/commands/dev/edge/setup.rs | 40 +- src/commands/dev/edge/watch.rs | 10 +- src/commands/dev/mod.rs | 29 +- src/commands/publish.rs | 89 ++-- src/deploy/mod.rs | 96 ++-- src/deploy/schedule.rs | 63 +++ src/deploy/{route.rs => zoned.rs} | 63 ++- src/deploy/zoneless.rs | 69 +++ src/fixtures/mod.rs | 2 +- src/fixtures/wrangler_toml.rs | 7 + src/main.rs | 47 +- src/settings/toml/deploy_config.rs | 160 ------- src/settings/toml/environment.rs | 17 +- src/settings/toml/manifest.rs | 99 ++++- src/settings/toml/mod.rs | 5 +- src/settings/toml/route.rs | 41 ++ .../{deploy_config.rs => deployments.rs} | 414 ++++++++++++------ src/settings/toml/tests/mod.rs | 2 +- src/settings/toml/triggers.rs | 6 + src/terminal/message.rs | 1 + 21 files changed, 746 insertions(+), 525 deletions(-) create mode 100644 src/deploy/schedule.rs rename src/deploy/{route.rs => zoned.rs} (70%) create mode 100644 src/deploy/zoneless.rs delete mode 100644 src/settings/toml/deploy_config.rs rename src/settings/toml/tests/{deploy_config.rs => deployments.rs} (68%) create mode 100644 src/settings/toml/triggers.rs diff --git a/src/commands/dev/edge/mod.rs b/src/commands/dev/edge/mod.rs index d5d3444e1..f26cecfab 100644 --- a/src/commands/dev/edge/mod.rs +++ b/src/commands/dev/edge/mod.rs @@ -6,8 +6,9 @@ use setup::{upload, Session}; use watch::watch_for_changes; use crate::commands::dev::{socket, Protocol, ServerConfig}; +use crate::deploy::DeployTarget; use crate::settings::global_user::GlobalUser; -use crate::settings::toml::{DeployConfig, Target}; +use crate::settings::toml::Target; use tokio::runtime::Runtime as TokioRuntime; @@ -18,17 +19,17 @@ pub fn dev( target: Target, user: GlobalUser, server_config: ServerConfig, - deploy_config: DeployConfig, + deploy_target: DeployTarget, local_protocol: Protocol, upstream_protocol: Protocol, verbose: bool, ) -> Result<(), failure::Error> { - let session = Session::new(&target, &user, &deploy_config)?; + let session = Session::new(&target, &user, &deploy_target)?; let mut target = target; let preview_token = upload( &mut target, - &deploy_config, + &deploy_target, &user, session.preview_token.clone(), verbose, @@ -43,7 +44,7 @@ pub fn dev( thread::spawn(move || { watch_for_changes( target, - &deploy_config, + &deploy_target, &user, Arc::clone(&preview_token), session_token, diff --git a/src/commands/dev/edge/setup.rs b/src/commands/dev/edge/setup.rs index 096dbc295..128fcc9a8 100644 --- a/src/commands/dev/edge/setup.rs +++ b/src/commands/dev/edge/setup.rs @@ -1,8 +1,9 @@ use std::path::Path; +use crate::deploy::DeployTarget; use crate::kv::bulk; use crate::settings::global_user::GlobalUser; -use crate::settings::toml::{DeployConfig, Target}; +use crate::settings::toml::Target; use crate::sites::{add_namespace, sync}; use crate::terminal::message::{Message, StdOut}; use crate::upload; @@ -13,7 +14,7 @@ use serde_json::json; pub(super) fn upload( target: &mut Target, - deploy_config: &DeployConfig, + deploy_target: &DeployTarget, user: &GlobalUser, session_token: String, verbose: bool, @@ -38,7 +39,7 @@ pub(super) fn upload( (Vec::new(), None, None) }; - let session_config = get_session_config(deploy_config); + let session_config = get_session_config(deploy_target); let address = get_upload_address(target); let script_upload_form = upload::form::build(target, asset_manifest, Some(session_config))?; @@ -76,9 +77,9 @@ impl Session { pub fn new( target: &Target, user: &GlobalUser, - deploy_config: &DeployConfig, + deploy_target: &DeployTarget, ) -> Result { - let exchange_url = get_exchange_url(deploy_config, user)?; + let exchange_url = get_exchange_url(deploy_target, user)?; let host = match exchange_url.host_str() { Some(host) => Ok(host.to_string()), None => Err(failure::format_err!( @@ -86,13 +87,14 @@ impl Session { )), }?; - let host = match deploy_config { - DeployConfig::Zoned(_) => host, - DeployConfig::Zoneless(_) => { + let host = match deploy_target { + DeployTarget::Zoned(_) => host, + DeployTarget::Zoneless(_) => { let namespaces: Vec<&str> = host.as_str().split('.').collect(); let subdomain = namespaces[1]; format!("{}.{}.workers.dev", target.name, subdomain) } + _ => unreachable!(), }; let client = crate::http::legacy_auth_client(&user); @@ -114,30 +116,32 @@ impl Session { } } -fn get_session_config(deploy_config: &DeployConfig) -> serde_json::Value { - match deploy_config { - DeployConfig::Zoned(config) => { +fn get_session_config(target: &DeployTarget) -> serde_json::Value { + match target { + DeployTarget::Zoned(config) => { let mut routes: Vec = Vec::new(); for route in &config.routes { routes.push(route.pattern.clone()); } json!({ "routes": routes }) } - DeployConfig::Zoneless(_) => json!({"workers_dev": true}), + DeployTarget::Zoneless(_) => json!({"workers_dev": true}), + _ => unreachable!(), } } -fn get_session_address(deploy_config: &DeployConfig) -> String { - match deploy_config { - DeployConfig::Zoned(config) => format!( +fn get_session_address(target: &DeployTarget) -> String { + match target { + DeployTarget::Zoned(config) => format!( "https://api.cloudflare.com/client/v4/zones/{}/workers/edge-preview", config.zone_id ), // TODO: zoneless is probably wrong - DeployConfig::Zoneless(config) => format!( + DeployTarget::Zoneless(config) => format!( "https://api.cloudflare.com/client/v4/accounts/{}/workers/subdomain/edge-preview", config.account_id ), + _ => unreachable!(), } } @@ -149,11 +153,11 @@ fn get_upload_address(target: &mut Target) -> String { } fn get_exchange_url( - deploy_config: &DeployConfig, + deploy_target: &DeployTarget, user: &GlobalUser, ) -> Result { let client = crate::http::legacy_auth_client(&user); - let address = get_session_address(deploy_config); + let address = get_session_address(deploy_target); let url = Url::parse(&address)?; let response = client.get(url).send()?.error_for_status()?; let text = &response.text()?; diff --git a/src/commands/dev/edge/watch.rs b/src/commands/dev/edge/watch.rs index c7b8f36ae..9e5964b39 100644 --- a/src/commands/dev/edge/watch.rs +++ b/src/commands/dev/edge/watch.rs @@ -1,14 +1,14 @@ use std::sync::{mpsc, Arc, Mutex}; use crate::commands::dev::edge::setup; - +use crate::deploy::DeployTarget; use crate::settings::global_user::GlobalUser; -use crate::settings::toml::{DeployConfig, Target}; +use crate::settings::toml::Target; use crate::watch::watch_and_build; pub fn watch_for_changes( target: Target, - deploy_config: &DeployConfig, + deploy_target: &DeployTarget, user: &GlobalUser, preview_token: Arc>, session_token: String, @@ -20,7 +20,7 @@ pub fn watch_for_changes( while receiver.recv().is_ok() { let user = user.clone(); let target = target.clone(); - let deploy_config = deploy_config.clone(); + let deploy_target = deploy_target.clone(); let session_token = session_token.clone(); let mut target = target; @@ -32,7 +32,7 @@ pub fn watch_for_changes( // // this allows the server to route subsequent requests // to the proper script - *preview_token = setup::upload(&mut target, &deploy_config, &user, session_token, verbose)?; + *preview_token = setup::upload(&mut target, &deploy_target, &user, session_token, verbose)?; } Ok(()) diff --git a/src/commands/dev/mod.rs b/src/commands/dev/mod.rs index b79ee7007..0750fed6e 100644 --- a/src/commands/dev/mod.rs +++ b/src/commands/dev/mod.rs @@ -9,8 +9,9 @@ pub use server_config::Protocol; pub use server_config::ServerConfig; use crate::build::build_target; +use crate::deploy::{DeployTarget, DeploymentSet}; use crate::settings::global_user::GlobalUser; -use crate::settings::toml::{DeployConfig, Target}; +use crate::settings::toml::Target; use crate::terminal::message::{Message, StdOut}; use crate::terminal::styles; @@ -18,7 +19,7 @@ use crate::terminal::styles; /// to a Cloudflare Workers runtime and returns HTTP responses pub fn dev( target: Target, - deploy_config: DeployConfig, + deployments: DeploymentSet, user: Option, server_config: ServerConfig, local_protocol: Protocol, @@ -28,6 +29,28 @@ pub fn dev( // before serving requests we must first build the Worker build_target(&target)?; + let deploy_target = { + let valid_targets = deployments + .into_iter() + .filter(|t| matches!(t, DeployTarget::Zoned(_) | DeployTarget::Zoneless(_))) + .collect::>(); + + let valid_target = valid_targets + .iter() + .find(|&t| matches!(t, DeployTarget::Zoned(_))) + .or_else(|| { + valid_targets + .iter() + .find(|&t| matches!(t, DeployTarget::Zoneless(_))) + }); + + if let Some(target) = valid_target { + target.clone() + } else { + failure::bail!("No valid deployment targets: `wrangler dev` can only be used to develop zoned and zoneless deployments") + } + }; + let host_str = styles::highlight("--host"); let local_str = styles::highlight("--local-protocol"); let upstream_str = styles::highlight("--upstream-protocol"); @@ -48,7 +71,7 @@ pub fn dev( target, user, server_config, - deploy_config, + deploy_target, local_protocol, upstream_protocol, verbose, diff --git a/src/commands/publish.rs b/src/commands/publish.rs index d1b6983a8..91d47b032 100644 --- a/src/commands/publish.rs +++ b/src/commands/publish.rs @@ -5,11 +5,11 @@ use indicatif::{ProgressBar, ProgressStyle}; use serde::{Deserialize, Serialize}; use crate::build::build_target; -use crate::deploy; +use crate::deploy::{self, DeploymentSet}; use crate::http::{self, Feature}; use crate::kv::bulk; use crate::settings::global_user::GlobalUser; -use crate::settings::toml::{DeployConfig, Target}; +use crate::settings::toml::Target; use crate::sites; use crate::terminal::emoji; use crate::terminal::message::{Message, Output, StdErr, StdOut}; @@ -25,11 +25,31 @@ pub struct PublishOutput { pub fn publish( user: &GlobalUser, target: &mut Target, - deploy_config: DeployConfig, + deployments: DeploymentSet, out: Output, ) -> Result<(), failure::Error> { validate_target_required_fields_present(target)?; + let deploy = |target: &Target| match deploy::worker(&user, &deployments) { + Ok(urls) => { + let result_msg = if !urls.is_empty() { + format!("Successfully published your script to {}", urls[0]) + } else { + "Successfully published your script".to_owned() + }; + StdErr::success(&result_msg); + if out == Output::Json { + StdOut::as_json(&PublishOutput { + success: true, + name: target.name.clone(), + urls, + }); + } + Ok(()) + } + Err(e) => Err(e), + }; + // Build the script before uploading and log build result let build_result = build_target(&target); match build_result { @@ -42,7 +62,6 @@ pub fn publish( if let Some(site_config) = &target.site { let path = &site_config.bucket.clone(); validate_bucket_location(path)?; - warn_site_incompatible_route(&deploy_config); let site_namespace = sites::add_namespace(user, target, false)?; @@ -77,26 +96,7 @@ pub fn publish( // Next, upload and deploy the worker with the updated asset_manifest upload::script(&upload_client, &target, Some(asset_manifest))?; - match deploy::worker(&user, &deploy_config) { - Ok(urls) => { - let result_msg = format!("Successfully published your script to {}", urls[0]); - match out { - Output::Json => { - let mut jsonout = PublishOutput::default(); - jsonout.success = true; - jsonout.name = target.name.clone(); - jsonout.urls = urls; - StdErr::success(&result_msg); - StdOut::as_json(&jsonout); - } - Output::PlainText => { - StdOut::success(&result_msg); - } - } - Ok(()) - } - Err(e) => Err(e), - }?; + deploy(target)?; // Finally, remove any stale files if !to_delete.is_empty() { @@ -128,51 +128,12 @@ pub fn publish( let upload_client = http::legacy_auth_client(user); upload::script(&upload_client, &target, None)?; - match deploy::worker(&user, &deploy_config) { - Ok(urls) => { - let result_msg = format!("Successfully published your script to {}", urls[0]); - match out { - Output::Json => { - let mut jsonout = PublishOutput::default(); - jsonout.success = true; - jsonout.name = target.name.clone(); - jsonout.urls = urls; - StdErr::success(&result_msg); - StdOut::as_json(&jsonout); - } - Output::PlainText => { - StdOut::success(&result_msg); - } - } - Ok(()) - } - Err(e) => Err(e), - }?; + deploy(target)?; } Ok(()) } -// This checks all of the configured routes for the wildcard ending and warns -// the user that their site may not work as expected without it. -fn warn_site_incompatible_route(deploy_config: &DeployConfig) { - if let DeployConfig::Zoned(zoned) = &deploy_config { - let mut no_star_routes = Vec::new(); - for route in &zoned.routes { - if !route.pattern.ends_with('*') { - no_star_routes.push(route.pattern.to_string()); - } - } - - if !no_star_routes.is_empty() { - StdOut::warn(&format!( - "The following routes in your configuration file should have a trailing * to apply the Worker on every path, otherwise your site will not behave as expected.\n{}", - no_star_routes.join("\n")) - ); - } - } -} - // We don't want folks setting their bucket to the top level directory, // which is where wrangler commands are always called from. pub fn validate_bucket_location(bucket: &PathBuf) -> Result<(), failure::Error> { diff --git a/src/deploy/mod.rs b/src/deploy/mod.rs index 65992e691..6fedd3b84 100644 --- a/src/deploy/mod.rs +++ b/src/deploy/mod.rs @@ -1,75 +1,41 @@ -mod route; -use route::publish_routes; +mod schedule; +mod zoned; +mod zoneless; -use crate::commands::subdomain::Subdomain; -use crate::http; -use crate::settings::global_user::GlobalUser; -use crate::settings::toml::{DeployConfig, Zoneless}; +pub use schedule::ScheduleTarget; +pub use zoned::ZonedTarget; +pub use zoneless::ZonelessTarget; -pub fn worker( - user: &GlobalUser, - deploy_config: &DeployConfig, -) -> Result, failure::Error> { - match deploy_config { - DeployConfig::Zoneless(zoneless_config) => { - // this is a zoneless deploy - log::info!("publishing to workers.dev subdomain"); - let deploy_address = publish_zoneless(user, zoneless_config)?; - let addresses = vec![deploy_address]; - Ok(addresses) - } - DeployConfig::Zoned(zoned_config) => { - // this is a zoned deploy - log::info!("publishing to zone {}", zoned_config.zone_id); - - let published_routes = publish_routes(&user, zoned_config)?; +use crate::settings::global_user::GlobalUser; - let addresses: Vec = - published_routes.iter().map(|r| format!("{}", r)).collect(); +/// A set of deploy targets. +pub type DeploymentSet = Vec; - Ok(addresses) - } - } +#[derive(Debug, PartialEq, Clone)] +pub enum DeployTarget { + Zoned(ZonedTarget), + Zoneless(ZonelessTarget), + Schedule(ScheduleTarget), } -fn publish_zoneless( +pub fn worker( user: &GlobalUser, - zoneless_config: &Zoneless, -) -> Result { - log::info!("checking that subdomain is registered"); - let subdomain = match Subdomain::get(&zoneless_config.account_id, user)? { - Some(subdomain) => subdomain, - None => failure::bail!("Before publishing to workers.dev, you must register a subdomain. Please choose a name for your subdomain and run `wrangler subdomain `.") - }; - - let sd_worker_addr = format!( - "https://api.cloudflare.com/client/v4/accounts/{}/workers/scripts/{}/subdomain", - zoneless_config.account_id, zoneless_config.script_name, - ); - - let client = http::legacy_auth_client(user); - - log::info!("Making public on subdomain..."); - let res = client - .post(&sd_worker_addr) - .header("Content-type", "application/json") - .body(build_subdomain_request()) - .send()?; - - if !res.status().is_success() { - failure::bail!( - "Something went wrong! Status: {}, Details {}", - res.status(), - res.text()? - ) + deployment: &[DeployTarget], +) -> Result, failure::Error> { + let mut urls = Vec::new(); + for target in deployment { + match target { + DeployTarget::Zoned(zoned) => { + let route_urls = zoned.deploy(user)?; + urls.extend_from_slice(&route_urls); + } + DeployTarget::Zoneless(zoneless) => { + let worker_dev = zoneless.deploy(user)?; + urls.push(worker_dev); + } + DeployTarget::Schedule(schedule) => schedule.deploy(user)?, + } } - Ok(format!( - "https://{}.{}.workers.dev", - zoneless_config.script_name, subdomain - )) -} - -fn build_subdomain_request() -> String { - serde_json::json!({ "enabled": true }).to_string() + Ok(urls) } diff --git a/src/deploy/schedule.rs b/src/deploy/schedule.rs new file mode 100644 index 000000000..80049d801 --- /dev/null +++ b/src/deploy/schedule.rs @@ -0,0 +1,63 @@ +use crate::http; +use crate::settings::global_user::GlobalUser; +use crate::terminal::message::{Message, StdOut}; + +#[derive(Clone, Debug, PartialEq)] +pub struct ScheduleTarget { + pub account_id: String, + pub script_name: String, + pub crons: Vec, +} + +impl ScheduleTarget { + pub fn build( + account_id: String, + script_name: String, + crons: Vec, + ) -> Result { + // TODO: add validation for expressions before pushing them to the API + // we can do this once the cron parser is open sourced + Ok(Self { + account_id, + script_name, + crons, + }) + } + + pub fn deploy(&self, user: &GlobalUser) -> Result<(), failure::Error> { + log::info!("publishing schedules"); + let schedule_worker_addr = format!( + "https://api.cloudflare.com/client/v4/accounts/{}/workers/scripts/{}/schedules", + self.account_id, self.script_name, + ); + + let client = http::legacy_auth_client(user); + + log::info!("Pushing {} schedule(s)...", self.crons.len()); + let res = client + .put(&schedule_worker_addr) + .header("Content-Type", "application/json") + .body(build_schedules_request(&self.crons)) + .send()?; + + if !res.status().is_success() { + failure::bail!( + "Something went wrong! Status: {}, Details {}", + res.status(), + res.text()? + ) + } + + StdOut::success("Uploaded schedules"); + + Ok(()) + } +} + +fn build_schedules_request(crons: &[String]) -> String { + let values = crons + .iter() + .map(|s| serde_json::json!({ "cron": s })) + .collect(); + serde_json::Value::Array(values).to_string() +} diff --git a/src/deploy/route.rs b/src/deploy/zoned.rs similarity index 70% rename from src/deploy/route.rs rename to src/deploy/zoned.rs index 2e64a8bea..616a790a7 100644 --- a/src/deploy/route.rs +++ b/src/deploy/zoned.rs @@ -7,11 +7,70 @@ use cloudflare::framework::apiclient::ApiClient; use crate::http; use crate::settings::global_user::GlobalUser; -use crate::settings::toml::{Route, Zoned}; +use crate::settings::toml::{Route, RouteConfig}; +use crate::terminal::message::{Message, StdOut}; + +#[derive(Clone, Debug, PartialEq)] +pub struct ZonedTarget { + pub zone_id: String, + pub routes: Vec, +} + +impl ZonedTarget { + pub fn build(script_name: &str, route_config: &RouteConfig) -> Result { + match route_config.zone_id.as_ref() { + Some(zone_id) if !zone_id.is_empty() => { + let new_route = |route: &String| Route { + id: None, + script: Some(script_name.to_string()), + pattern: route.to_string(), + }; + let routes: Vec = route_config + .route + .iter() + .map(new_route) + .chain(route_config.routes.iter().flatten().filter_map(|route| { + if route.is_empty() { + StdOut::warn("your configuration file contains an empty route"); + None + } else { + Some(new_route(route)) + } + })) + .collect(); + + if routes.is_empty() { + failure::bail!("No routes specified"); + } + + Ok(Self { + zone_id: zone_id.to_owned(), + routes, + }) + } + _ => failure::bail!("field `zone_id` is required to deploy to routes"), + } + } + + pub fn deploy(&self, user: &GlobalUser) -> Result, failure::Error> { + log::info!("publishing to zone {}", self.zone_id); + + let published_routes = publish_routes(&user, self)?; + + let display_results: Vec = published_routes.iter().map(|r| r.to_string()).collect(); + + StdOut::success(&format!( + "Deployed to the following routes:\n{}", + display_results.join("\n") + )); + + Ok(display_results) + } +} pub fn publish_routes( user: &GlobalUser, - zoned_config: &Zoned, + zoned_config: &ZonedTarget, ) -> Result, failure::Error> { // For the moment, we'll just make this call once and make all our decisions based on the response. // There is a possibility of race conditions, but we just report back the results and allow the diff --git a/src/deploy/zoneless.rs b/src/deploy/zoneless.rs new file mode 100644 index 000000000..b54d1dedb --- /dev/null +++ b/src/deploy/zoneless.rs @@ -0,0 +1,69 @@ +use crate::commands::subdomain::Subdomain; +use crate::http; +use crate::settings::global_user::GlobalUser; +use crate::settings::toml::RouteConfig; +use crate::terminal::message::{Message, StdOut}; + +#[derive(Clone, Debug, PartialEq)] +pub struct ZonelessTarget { + pub account_id: String, + pub script_name: String, +} + +impl ZonelessTarget { + pub fn build(script_name: &str, route_config: &RouteConfig) -> Result { + match route_config.account_id.as_ref() { + // TODO: Deserialize empty strings to None; cannot do this for account id + // yet without a large refactor. + Some(account_id) if !account_id.is_empty() => Ok(Self { + script_name: script_name.to_string(), + account_id: account_id.to_string(), + }), + _ => failure::bail!("field `account_id` is required to deploy to workers.dev"), + } + } + + pub fn deploy(&self, user: &GlobalUser) -> Result { + log::info!("publishing to workers.dev subdomain"); + log::info!("checking that subdomain is registered"); + let subdomain = match Subdomain::get(&self.account_id, user)? { + Some(subdomain) => subdomain, + None => failure::bail!("Before publishing to workers.dev, you must register a subdomain. Please choose a name for your subdomain and run `wrangler subdomain `.") + }; + + let sd_worker_addr = format!( + "https://api.cloudflare.com/client/v4/accounts/{}/workers/scripts/{}/subdomain", + self.account_id, self.script_name, + ); + + let client = http::legacy_auth_client(user); + + log::info!("Making public on subdomain..."); + let res = client + .post(&sd_worker_addr) + .header("Content-type", "application/json") + .body(build_subdomain_request()) + .send()?; + + if !res.status().is_success() { + failure::bail!( + "Something went wrong! Status: {}, Details {}", + res.status(), + res.text()? + ) + } + + let deploy_address = format!("https://{}.{}.workers.dev", self.script_name, subdomain); + + StdOut::success(&format!( + "Successfully published your script to {}", + deploy_address + )); + + Ok(deploy_address) + } +} + +fn build_subdomain_request() -> String { + serde_json::json!({ "enabled": true }).to_string() +} diff --git a/src/fixtures/mod.rs b/src/fixtures/mod.rs index 4e396c14c..e745bfe6e 100644 --- a/src/fixtures/mod.rs +++ b/src/fixtures/mod.rs @@ -1,5 +1,5 @@ mod wrangler_toml; -pub use wrangler_toml::{EnvConfig, KvConfig, SiteConfig, WranglerToml, TEST_ENV_NAME}; +pub use wrangler_toml::{EnvConfig, KvConfig, SiteConfig, Triggers, WranglerToml, TEST_ENV_NAME}; use std::fs; use std::fs::File; diff --git a/src/fixtures/wrangler_toml.rs b/src/fixtures/wrangler_toml.rs index e4610dc89..0a2a48c96 100644 --- a/src/fixtures/wrangler_toml.rs +++ b/src/fixtures/wrangler_toml.rs @@ -16,6 +16,11 @@ pub struct KvConfig { pub id: Option<&'static str>, } +#[derive(Clone, Debug, Default, Serialize)] +pub struct Triggers { + pub crons: Option>, +} + #[derive(Clone, Debug, Default, Serialize)] pub struct SiteConfig { pub bucket: Option<&'static str>, @@ -39,6 +44,7 @@ pub struct EnvConfig { #[serde(alias = "kv-namespaces")] pub kv_namespaces: Option>, pub vars: Option>, + pub triggers: Option, } impl EnvConfig { @@ -98,6 +104,7 @@ pub struct WranglerToml { pub kv_namespaces: Option>, pub site: Option, pub vars: Option>, + pub triggers: Option, } impl WranglerToml { diff --git a/src/main.rs b/src/main.rs index e311faf06..12bd63eb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -525,18 +525,16 @@ fn run() -> Result<(), failure::Error> { ) .arg( Arg::with_name("local-protocol") - .help("sets the protocol on which the wrangler dev listens, by default this is http but can be set to https") - .long("local-protocol") - .takes_value(true) - ) + .help("sets the protocol on which the wrangler dev listens, by default this is http but can be set to https") + .long("local-protocol") + .takes_value(true) + ) .arg( Arg::with_name("upstream-protocol") - .help("sets the protocol on which requests are sent to the host, by default this is https but can be set to http") - .long("upstream-protocol") - .takes_value(true) - ) - .arg(verbose_arg.clone()) - .arg(wrangler_file.clone()) + .help("sets the protocol on which requests are sent to the host, by default this is https but can be set to http") + .long("upstream-protocol") + .takes_value(true) + ) ) .subcommand( SubCommand::with_name("publish") @@ -772,6 +770,8 @@ fn run() -> Result<(), failure::Error> { commands::preview(target, user, options, verbose)?; } else if let Some(matches) = matches.subcommand_matches("dev") { + use commands::dev::Protocol; + log::info!("Starting dev server"); let config_path = Path::new( @@ -787,31 +787,20 @@ fn run() -> Result<(), failure::Error> { .value_of("port") .map(|p| p.parse().expect("--port expects a number")); - type Protocol = commands::dev::Protocol; let mut local_protocol_str: Option<&str> = matches.value_of("local-protocol"); let mut upstream_protocol_str: Option<&str> = matches.value_of("upstream-protocol"); // Check if arg not given but present in wrangler.toml if let Some(d) = &manifest.dev { - if ip.is_none() && d.ip.is_some() { - ip = d.ip.as_deref(); - } - - if port.is_none() && d.port.is_some() { - port = d.port; - } - - if local_protocol_str.is_none() && d.local_protocol.is_some() { - local_protocol_str = d.local_protocol.as_deref(); - } - - if upstream_protocol_str.is_none() && d.upstream_protocol.is_some() { - upstream_protocol_str = d.upstream_protocol.as_deref(); - } + ip = ip.or_else(|| d.ip.as_deref()); + port = port.or(d.port); + local_protocol_str = local_protocol_str.or_else(|| d.local_protocol.as_deref()); + upstream_protocol_str = + upstream_protocol_str.or_else(|| d.upstream_protocol.as_deref()); } let env = matches.value_of("env"); - let deploy_config = manifest.deploy_config(env)?; + let deployments = manifest.get_deployments(env)?; is_preview = true; let target = manifest.get_target(env, is_preview)?; let user = settings::global_user::GlobalUser::new().ok(); @@ -824,7 +813,7 @@ fn run() -> Result<(), failure::Error> { commands::dev::dev( target, - deploy_config, + deployments, user, server_config, local_protocol, @@ -861,7 +850,7 @@ fn run() -> Result<(), failure::Error> { let manifest = settings::toml::Manifest::new(config_path)?; let env = matches.value_of("env"); let mut target = manifest.get_target(env, is_preview)?; - let deploy_config = manifest.deploy_config(env)?; + let deploy_config = manifest.get_deployments(env)?; if matches.is_present("output") && matches.value_of("output") == Some("json") { commands::publish(&user, &mut target, deploy_config, Output::Json)?; } else { diff --git a/src/settings/toml/deploy_config.rs b/src/settings/toml/deploy_config.rs deleted file mode 100644 index c9bbaab68..000000000 --- a/src/settings/toml/deploy_config.rs +++ /dev/null @@ -1,160 +0,0 @@ -use crate::settings::toml::Route; -use crate::terminal::message::{Message, StdOut}; -#[derive(Clone, Debug, PartialEq)] -pub enum DeployConfig { - Zoneless(Zoneless), - Zoned(Zoned), -} - -impl DeployConfig { - pub fn build( - script_name: &str, - route_config: &RouteConfig, - ) -> Result { - if route_config.is_valid() { - failure::bail!( - "you must set EITHER workers_dev = true OR provide a zone_id and route/routes." - ) - } - - if route_config.is_zoneless() { - DeployConfig::build_zoneless(script_name, route_config) - } else if route_config.is_zoned() { - DeployConfig::build_zoned(script_name, route_config) - } else { - failure::bail!("No deploy target specified"); - } - } - - fn build_zoneless( - script_name: &str, - route_config: &RouteConfig, - ) -> Result { - if let Some(account_id) = &route_config.account_id { - // TODO: Deserialize empty strings to None; cannot do this for account id - // yet without a large refactor. - if account_id.is_empty() { - failure::bail!("field `account_id` is required to deploy to workers.dev"); - } - let zoneless = Zoneless { - script_name: script_name.to_string(), - account_id: account_id.to_string(), - }; - - Ok(DeployConfig::Zoneless(zoneless)) - } else { - failure::bail!("field `account_id` is required to deploy to workers.dev"); - } - } - - fn build_zoned( - script_name: &str, - route_config: &RouteConfig, - ) -> Result { - if let Some(zone_id) = &route_config.zone_id { - if zone_id.is_empty() { - failure::bail!("field `zone_id` is required to deploy to routes"); - } - - if route_config.has_conflicting_targets() { - failure::bail!("specify either `route` or `routes`"); - } - - let mut zoned = Zoned { - zone_id: zone_id.to_owned(), - routes: Vec::new(), - }; - - if let Some(route) = &route_config.route { - zoned.routes.push(Route { - id: None, - script: Some(script_name.to_string()), - pattern: route.to_string(), - }); - } else if let Some(routes) = &route_config.routes { - for route in routes { - if route.is_empty() { - StdOut::warn("your configuration file contains an empty route") - } else { - zoned.routes.push(Route { - id: None, - script: Some(script_name.to_string()), - pattern: route.to_string(), - }) - } - } - } - - if zoned.routes.is_empty() { - failure::bail!("No routes specified"); - } - - Ok(DeployConfig::Zoned(zoned)) - } else { - failure::bail!("field `zone_id` is required to deploy to routes"); - } - } -} - -#[derive(Clone, Debug, PartialEq)] -pub struct Zoneless { - pub account_id: String, - pub script_name: String, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct Zoned { - pub zone_id: String, - pub routes: Vec, -} - -#[derive(Debug)] -pub struct RouteConfig { - pub workers_dev: Option, - pub route: Option, - pub routes: Option>, - pub zone_id: Option, - pub account_id: Option, -} - -impl RouteConfig { - fn is_valid(&self) -> bool { - self.workers_dev_false_by_itself() || self.has_conflicting_targets() - } - - fn has_conflicting_targets(&self) -> bool { - if self.is_zoneless() { - self.has_routes_defined() - } else if let Some(routes) = &self.routes { - !routes.is_empty() && self.route.is_some() - } else { - false - } - } - - fn has_routes_defined(&self) -> bool { - if self.route.is_some() { - true - } else if let Some(routes) = &self.routes { - !routes.is_empty() - } else { - false - } - } - - fn is_zoneless(&self) -> bool { - self.workers_dev.unwrap_or_default() - } - - fn is_zoned(&self) -> bool { - self.has_routes_defined() || self.zone_id.is_some() - } - - fn workers_dev_false_by_itself(&self) -> bool { - if let Some(workers_dev) = self.workers_dev { - !workers_dev && !self.has_routes_defined() - } else { - false - } - } -} diff --git a/src/settings/toml/environment.rs b/src/settings/toml/environment.rs index ac7db8cbd..8703579a0 100644 --- a/src/settings/toml/environment.rs +++ b/src/settings/toml/environment.rs @@ -3,9 +3,10 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use serde_with::rust::string_empty_as_none; -use crate::settings::toml::deploy_config::RouteConfig; use crate::settings::toml::kv_namespace::ConfigKvNamespace; +use crate::settings::toml::route::RouteConfig; use crate::settings::toml::site::Site; +use crate::settings::toml::triggers::Triggers; #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct Environment { @@ -24,6 +25,7 @@ pub struct Environment { #[serde(alias = "kv-namespaces")] pub kv_namespaces: Option>, pub vars: Option>, + pub triggers: Option, } impl Environment { @@ -32,17 +34,8 @@ impl Environment { top_level_account_id: String, top_level_zone_id: Option, ) -> Option { - let account_id = if self.account_id.is_none() { - Some(top_level_account_id) - } else { - self.account_id.clone() - }; - - let zone_id = if self.zone_id.is_none() { - top_level_zone_id - } else { - self.zone_id.clone() - }; + let account_id = self.account_id.clone().or(Some(top_level_account_id)); + let zone_id = self.zone_id.clone().or(top_level_zone_id); if self.workers_dev.is_none() && self.route.is_none() && self.routes.is_none() { None diff --git a/src/settings/toml/manifest.rs b/src/settings/toml/manifest.rs index 35da0a30a..1b9bd96c8 100644 --- a/src/settings/toml/manifest.rs +++ b/src/settings/toml/manifest.rs @@ -10,15 +10,21 @@ use serde::{Deserialize, Serialize}; use serde_with::rust::string_empty_as_none; use crate::commands::{validate_worker_name, DEFAULT_CONFIG_PATH}; -use crate::settings::toml::deploy_config::{DeployConfig, RouteConfig}; +use crate::deploy::{self, DeployTarget, DeploymentSet}; use crate::settings::toml::dev::Dev; use crate::settings::toml::environment::Environment; use crate::settings::toml::kv_namespace::{ConfigKvNamespace, KvNamespace}; +use crate::settings::toml::route::RouteConfig; use crate::settings::toml::site::Site; use crate::settings::toml::target_type::TargetType; +use crate::settings::toml::triggers::Triggers; use crate::settings::toml::Target; -use crate::terminal::message::{Message, StdOut}; -use crate::terminal::{emoji, styles}; +use crate::terminal::{ + emoji, + message::{Message, StdOut}, + styles, +}; + #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] pub struct Manifest { #[serde(default)] @@ -43,6 +49,7 @@ pub struct Manifest { pub kv_namespaces: Option>, pub env: Option>, pub vars: Option>, + pub triggers: Option, } impl Manifest { @@ -156,28 +163,86 @@ impl Manifest { } } - pub fn deploy_config(&self, env: Option<&str>) -> Result { + pub fn get_deployments(&self, env: Option<&str>) -> Result { let script = self.worker_name(env); validate_worker_name(&script)?; - if let Some(environment) = self.get_environment(env)? { - // if there is an environment level deploy target, try to return that - if let Some(env_route_config) = - environment.route_config(self.account_id.clone(), self.zone_id.clone()) + let mut deployments = DeploymentSet::new(); + + let env = self.get_environment(env)?; + + let mut add_routed_deployments = + |route_config: &RouteConfig| -> Result<(), failure::Error> { + if route_config.is_zoned() { + let zoned = deploy::ZonedTarget::build(&script, route_config)?; + // This checks all of the configured routes for the wildcard ending and warns + // the user that their site may not work as expected without it. + if self.site.is_some() { + let no_star_routes = zoned + .routes + .iter() + .filter(|r| !r.pattern.ends_with('*')) + .map(|r| r.pattern.as_str()) + .collect::>(); + if !no_star_routes.is_empty() { + StdOut::warn(&format!( + "The following routes in your configuration file should have a trailing * to apply the Worker on every path, otherwise your site will not behave as expected.\n{}", + no_star_routes.join("\n")) + ); + } + } + + deployments.push(DeployTarget::Zoned(zoned)); + } + + if route_config.is_zoneless() { + let zoneless = deploy::ZonelessTarget::build(&script, route_config)?; + deployments.push(DeployTarget::Zoneless(zoneless)); + } + + Ok(()) + }; + + if let Some(env) = env { + if let Some(env_route_cfg) = + env.route_config(self.account_id.clone(), self.zone_id.clone()) { - DeployConfig::build(&script, &env_route_config) + add_routed_deployments(&env_route_cfg) } else { - // If the top level config is Zoned, the user needs to specify new route config - let top_level_config = DeployConfig::build(&script, &self.route_config())?; - match top_level_config { - DeployConfig::Zoned(_) => failure::bail!( - "you must specify route(s) per environment for zoned deploys." - ), - DeployConfig::Zoneless(_) => Ok(top_level_config), + let config = self.route_config(); + if config.is_zoned() { + failure::bail!("you must specify route(s) per environment for zoned deploys."); + } else { + add_routed_deployments(&config) } } } else { - DeployConfig::build(&script, &self.route_config()) + add_routed_deployments(&self.route_config()) + }?; + + let crons = match env { + Some(e) => { + let account_id = e.account_id.as_ref().unwrap_or(&self.account_id); + e.triggers + .as_ref() + .map(|t| (t.crons.as_slice(), account_id)) + } + None => self + .triggers + .as_ref() + .map(|t| (t.crons.as_slice(), &self.account_id)), + }; + + if let Some((crons, account)) = crons { + let scheduled = + deploy::ScheduleTarget::build(account.clone(), script.clone(), crons.to_vec())?; + deployments.push(DeployTarget::Schedule(scheduled)); + } + + if !deployments.is_empty() { + Ok(deployments) + } else { + failure::bail!("No deployments specified!") } } diff --git a/src/settings/toml/mod.rs b/src/settings/toml/mod.rs index 72b945bbd..0528328cc 100644 --- a/src/settings/toml/mod.rs +++ b/src/settings/toml/mod.rs @@ -1,4 +1,3 @@ -mod deploy_config; mod dev; mod environment; mod kv_namespace; @@ -7,12 +6,12 @@ mod route; mod site; mod target; mod target_type; +mod triggers; -pub use deploy_config::{DeployConfig, Zoned, Zoneless}; pub use environment::Environment; pub use kv_namespace::{ConfigKvNamespace, KvNamespace}; pub use manifest::Manifest; -pub use route::Route; +pub use route::{Route, RouteConfig}; pub use site::Site; pub use target::Target; pub use target_type::TargetType; diff --git a/src/settings/toml/route.rs b/src/settings/toml/route.rs index 6dbba1561..45505cb76 100644 --- a/src/settings/toml/route.rs +++ b/src/settings/toml/route.rs @@ -18,3 +18,44 @@ impl From<&WorkersRoute> for Route { } } } + +#[derive(Debug)] +pub struct RouteConfig { + pub workers_dev: Option, + pub route: Option, + pub routes: Option>, + pub zone_id: Option, + pub account_id: Option, +} + +impl RouteConfig { + pub fn has_routes_defined(&self) -> bool { + if self.route.is_some() { + true + } else if let Some(routes) = &self.routes { + !routes.is_empty() + } else { + false + } + } + + pub fn routes(&self) -> impl Iterator { + self.route.iter().chain(self.routes.iter().flatten()) + } + + pub fn is_zoneless(&self) -> bool { + self.workers_dev.unwrap_or_default() + } + + pub fn is_zoned(&self) -> bool { + self.has_routes_defined() && self.zone_id.is_some() + } + + pub fn workers_dev_false_by_itself(&self) -> bool { + if let Some(workers_dev) = self.workers_dev { + !workers_dev && !self.has_routes_defined() + } else { + false + } + } +} diff --git a/src/settings/toml/tests/deploy_config.rs b/src/settings/toml/tests/deployments.rs similarity index 68% rename from src/settings/toml/tests/deploy_config.rs rename to src/settings/toml/tests/deployments.rs index 672ed33d2..2e9dd4f22 100644 --- a/src/settings/toml/tests/deploy_config.rs +++ b/src/settings/toml/tests/deployments.rs @@ -1,10 +1,10 @@ use std::str::FromStr; +use crate::deploy::{DeployTarget, ScheduleTarget, ZonedTarget, ZonelessTarget}; use crate::settings::toml::route::Route; use crate::settings::toml::Manifest; -use crate::settings::toml::{DeployConfig, Zoned, Zoneless}; -use crate::fixtures::{EnvConfig, WranglerToml, TEST_ENV_NAME}; +use crate::fixtures::{EnvConfig, Triggers, WranglerToml, TEST_ENV_NAME}; // Test consts const ZONE_ID: &str = "samplezoneid"; @@ -13,18 +13,18 @@ const ACCOUNT_ID: &str = "fakeaccountid"; // TOP LEVEL TESTS #[test] -fn it_errors_on_empty_deploy_config() { +fn it_errors_on_empty_get_deployments() { let test_toml = WranglerToml::webpack("empty"); let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_errors_on_conflicting_deploy_configs() { +fn it_errors_on_conflicting_get_deploymentss() { let script_name = "workers_dev_true_and_zoned_config"; let workers_dev = true; @@ -34,13 +34,28 @@ fn it_errors_on_conflicting_deploy_configs() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); + let expected_deployments = vec![ + DeployTarget::Zoned(ZonedTarget { + routes: vec![Route { + script: Some(script_name.to_owned()), + pattern: PATTERN.to_owned(), + id: None, + }], + zone_id: ZONE_ID.to_owned(), + }), + DeployTarget::Zoneless(ZonelessTarget { + account_id: ACCOUNT_ID.to_owned(), + script_name: script_name.to_owned(), + }), + ]; let environment = None; + let actual_deployments = manifest.get_deployments(environment).unwrap(); - assert!(manifest.deploy_config(environment).is_err()); + assert_eq!(actual_deployments, expected_deployments); } #[test] -fn it_can_get_a_top_level_zoneless_deploy_config() { +fn it_can_get_a_top_level_zoneless_get_deployments() { let script_name = "zoneless"; let workers_dev = true; let test_toml = WranglerToml::zoneless(script_name, ACCOUNT_ID, workers_dev); @@ -48,17 +63,17 @@ fn it_can_get_a_top_level_zoneless_deploy_config() { let manifest = Manifest::from_str(&toml_string).unwrap(); let environment = None; - let actual_deploy_config = manifest.deploy_config(environment).unwrap(); - let expected_deploy_config = DeployConfig::Zoneless(Zoneless { + let actual_deployments = manifest.get_deployments(environment).unwrap(); + let expected_deployments = vec![DeployTarget::Zoneless(ZonelessTarget { script_name: script_name.to_string(), account_id: ACCOUNT_ID.to_string(), - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] -fn it_errors_on_deploy_config_missing_name() { +fn it_errors_on_get_deployments_missing_name() { let script_name = ""; let workers_dev = true; let test_toml = WranglerToml::zoneless(script_name, ACCOUNT_ID, workers_dev); @@ -67,11 +82,11 @@ fn it_errors_on_deploy_config_missing_name() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_errors_on_deploy_config_missing_account_id() { +fn it_errors_on_get_deployments_missing_account_id() { let script_name = "zoneless_no_account_id"; let workers_dev = true; let mut test_toml = WranglerToml::zoneless(script_name, ACCOUNT_ID, workers_dev); @@ -81,11 +96,11 @@ fn it_errors_on_deploy_config_missing_account_id() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_errors_on_zoneless_deploy_config_workers_dev_false() { +fn it_errors_on_zoneless_get_deployments_workers_dev_false() { let script_name = "zoneless_false"; let workers_dev = false; let test_toml = WranglerToml::zoneless(script_name, ACCOUNT_ID, workers_dev); @@ -94,11 +109,11 @@ fn it_errors_on_zoneless_deploy_config_workers_dev_false() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_can_get_a_single_route_zoned_deploy_config() { +fn it_can_get_a_single_route_zoned_get_deployments() { let script_name = "single_route_zoned"; let test_toml = WranglerToml::zoned_single_route(script_name, ZONE_ID, PATTERN); @@ -106,22 +121,22 @@ fn it_can_get_a_single_route_zoned_deploy_config() { let manifest = Manifest::from_str(&toml_string).unwrap(); let environment = None; - let actual_deploy_config = manifest.deploy_config(environment).unwrap(); + let actual_deployments = manifest.get_deployments(environment).unwrap(); let expected_routes = vec![Route { script: Some(script_name.to_string()), pattern: PATTERN.to_string(), id: None, }]; - let expected_deploy_config = DeployConfig::Zoned(Zoned { + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { zone_id: ZONE_ID.to_string(), routes: expected_routes, - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] -fn it_can_get_a_single_route_zoned_deploy_config_workers_dev_false() { +fn it_can_get_a_single_route_zoned_get_deployments_workers_dev_false() { let script_name = "single_route_zoned_workers_dev_false"; let mut test_toml = WranglerToml::zoned_single_route(script_name, ZONE_ID, PATTERN); @@ -129,23 +144,112 @@ fn it_can_get_a_single_route_zoned_deploy_config_workers_dev_false() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let environment = None; - let actual_deploy_config = manifest.deploy_config(environment).unwrap(); let expected_routes = vec![Route { script: Some(script_name.to_string()), pattern: PATTERN.to_string(), id: None, }]; - let expected_deploy_config = DeployConfig::Zoned(Zoned { + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { zone_id: ZONE_ID.to_string(), routes: expected_routes, + })]; + let environment = None; + let actual_deployments = manifest.get_deployments(environment).unwrap(); + + assert_eq!(actual_deployments, expected_deployments); +} + +#[test] +fn it_can_get_a_scheduled_no_workers_dev_no_zoned() { + let script_name = "single_schedule"; + + let crons = vec!["0 * * * *".to_owned()]; + + let mut test_toml = WranglerToml::webpack(script_name); + test_toml.account_id = Some(ACCOUNT_ID); + test_toml.triggers = Some(Triggers { + crons: Some(crons.clone()), }); - assert_eq!(actual_deploy_config, expected_deploy_config); + let toml_string = toml::to_string(&test_toml).unwrap(); + let manifest = Manifest::from_str(&toml_string).unwrap(); + + let expected_deployments = vec![DeployTarget::Schedule(ScheduleTarget { + account_id: ACCOUNT_ID.to_owned(), + script_name: script_name.to_owned(), + crons: crons, + })]; + let environment = None; + let actual_deployments = manifest.get_deployments(environment).unwrap(); + + assert_eq!(actual_deployments, expected_deployments); } #[test] -fn it_errors_on_single_route_deploy_config_empty_zone_id() { +fn it_can_get_a_scheduled_in_env_no_workers_dev_no_zoned() { + let script_name = "single_schedule"; + + let env_crons = vec!["0 * * * *".to_owned()]; + + let env_config = EnvConfig { + triggers: Some(Triggers { + crons: Some(env_crons.clone()), + }), + ..EnvConfig::default() + }; + let mut test_toml = WranglerToml { + account_id: Some(ACCOUNT_ID), + triggers: Some(Triggers { + crons: Some(vec!["0 * * * *".to_owned()]), + }), + ..WranglerToml::webpack(script_name) + }; + test_toml + .env + .get_or_insert_with(Default::default) + .insert("b", env_config); + + let toml_string = toml::to_string(&test_toml).unwrap(); + let manifest = Manifest::from_str(&toml_string).unwrap(); + + let environment = Some("b"); + let worker_name = manifest.worker_name(Some("b")); + let expected_deployments = vec![DeployTarget::Schedule(ScheduleTarget { + account_id: ACCOUNT_ID.to_owned(), + script_name: worker_name, + crons: env_crons, + })]; + let actual_deployments = manifest.get_deployments(environment).unwrap(); + + assert_eq!(actual_deployments, expected_deployments); +} +#[test] +fn it_errors_on_no_schedules_in_env() { + // with no zoned, zoneless, or schedule targets in environment, we error + let script_name = "single_schedule"; + + let crons = vec!["0 * * * *".to_owned()]; + let env = EnvConfig::custom_script_name("no_schedule"); + + let mut test_toml = WranglerToml::webpack(script_name); + test_toml.account_id = Some(ACCOUNT_ID); + test_toml.triggers = Some(Triggers { + crons: Some(crons.clone()), + }); + test_toml + .env + .get_or_insert_with(Default::default) + .insert("b", env); + + let toml_string = toml::to_string(&test_toml).unwrap(); + let manifest = Manifest::from_str(&toml_string).unwrap(); + + let environment = Some("b"); + assert!(manifest.get_deployments(environment).is_err()); +} + +#[test] +fn it_errors_on_single_route_get_deployments_empty_zone_id() { let script_name = "single_route_empty_zone_id"; let empty_zone_id = ""; @@ -155,11 +259,11 @@ fn it_errors_on_single_route_deploy_config_empty_zone_id() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_errors_on_single_route_deploy_config_missing_zone_id() { +fn it_errors_on_single_route_get_deployments_missing_zone_id() { let script_name = "single_route_empty_zone_id"; let empty_zone_id = ""; @@ -170,11 +274,11 @@ fn it_errors_on_single_route_deploy_config_missing_zone_id() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_errors_on_single_route_deploy_config_empty_route() { +fn it_errors_on_single_route_get_deployments_empty_route() { let script_name = "single_route_empty_route"; let pattern = ""; @@ -184,11 +288,11 @@ fn it_errors_on_single_route_deploy_config_empty_route() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_errors_on_single_route_deploy_config_missing_route() { +fn it_errors_on_single_route_get_deployments_missing_route() { let script_name = "single_route_missing_route"; let mut test_toml = WranglerToml::zoned_single_route(script_name, ZONE_ID, ""); @@ -198,11 +302,11 @@ fn it_errors_on_single_route_deploy_config_missing_route() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_can_get_a_multi_route_zoned_deploy_config() { +fn it_can_get_a_multi_route_zoned_get_deployments() { let script_name = "multi_route_zoned"; let patterns = [PATTERN, "blog.hostname.tld/*"]; @@ -220,19 +324,19 @@ fn it_can_get_a_multi_route_zoned_deploy_config() { id: None, }) .collect(); - let expected_deploy_config = DeployConfig::Zoned(Zoned { + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { zone_id: ZONE_ID.to_string(), routes: expected_routes, - }); + })]; let environment = None; - let actual_deploy_config = manifest.deploy_config(environment).unwrap(); + let actual_deployments = manifest.get_deployments(environment).unwrap(); - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] -fn it_can_get_a_multi_route_zoned_deploy_config_workers_dev_false() { +fn it_can_get_a_multi_route_zoned_get_deployments_workers_dev_false() { let script_name = "multi_route_zoned_workers_dev_false"; let patterns = [PATTERN, "blog.hostname.tld/*"]; @@ -251,19 +355,19 @@ fn it_can_get_a_multi_route_zoned_deploy_config_workers_dev_false() { id: None, }) .collect(); - let expected_deploy_config = DeployConfig::Zoned(Zoned { + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { zone_id: ZONE_ID.to_string(), routes: expected_routes, - }); + })]; let environment = None; - let actual_deploy_config = manifest.deploy_config(environment).unwrap(); + let actual_deployments = manifest.get_deployments(environment).unwrap(); - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] -fn it_errors_on_multi_route_deploy_config_empty_zone_id() { +fn it_errors_on_multi_route_get_deployments_empty_zone_id() { let script_name = "multi_route_empty_zone_id"; let patterns = [PATTERN, "blog.hostname.tld/*"]; let empty_zone_id = ""; @@ -274,11 +378,11 @@ fn it_errors_on_multi_route_deploy_config_empty_zone_id() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_errors_on_multi_route_deploy_config_missing_zone_id() { +fn it_errors_on_multi_route_get_deployments_missing_zone_id() { let script_name = "multi_route_missing_zone_id"; let patterns = [PATTERN, "blog.hostname.tld/*"]; let empty_zone_id = ""; @@ -291,11 +395,11 @@ fn it_errors_on_multi_route_deploy_config_missing_zone_id() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_errors_on_multi_route_deploy_config_empty_routes_list() { +fn it_errors_on_multi_route_get_deployments_empty_routes_list() { let script_name = "multi_route_empty_routes_list"; let patterns = []; @@ -305,11 +409,11 @@ fn it_errors_on_multi_route_deploy_config_empty_routes_list() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_errors_on_multi_route_deploy_config_empty_route() { +fn it_errors_on_multi_route_get_deployments_empty_route() { let script_name = "multi_route_empty_route"; let patterns = [""]; @@ -319,11 +423,11 @@ fn it_errors_on_multi_route_deploy_config_empty_route() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_errors_on_deploy_config_route_and_routes() { +fn it_errors_on_get_deployments_route_and_routes() { let script_name = "route_and_routes"; let patterns = ["blog.hostname.tld/*"]; @@ -332,13 +436,27 @@ fn it_errors_on_deploy_config_route_and_routes() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); + let expected_routes = std::iter::once(PATTERN) + .chain(patterns.iter().copied()) + .map(|p| Route { + script: Some(script_name.to_string()), + pattern: (*p).to_string(), + id: None, + }) + .collect(); + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { + routes: expected_routes, + zone_id: ZONE_ID.to_owned(), + })]; + let environment = None; + let actual_deployments = manifest.get_deployments(environment).unwrap(); - assert!(manifest.deploy_config(environment).is_err()); + assert_eq!(actual_deployments, expected_deployments); } #[test] -fn it_errors_on_deploy_config_route_and_workers_dev_true() { +fn it_errors_on_get_deployments_route_and_workers_dev_true() { let script_name = "route_and_workers_dev"; let mut test_toml = WranglerToml::zoned_single_route(script_name, ZONE_ID, PATTERN); @@ -348,11 +466,11 @@ fn it_errors_on_deploy_config_route_and_workers_dev_true() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } #[test] -fn it_errors_on_deploy_config_routes_and_workers_dev_true() { +fn it_errors_on_get_deployments_routes_and_workers_dev_true() { let script_name = "routes_and_workers_dev"; let patterns = [PATTERN]; @@ -363,7 +481,7 @@ fn it_errors_on_deploy_config_routes_and_workers_dev_true() { let environment = None; - assert!(manifest.deploy_config(environment).is_err()); + assert!(manifest.get_deployments(environment).is_err()); } // ENVIRONMENT TESTS @@ -377,9 +495,9 @@ fn when_top_level_empty_env_empty() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -393,9 +511,9 @@ fn when_top_level_empty_env_has_zone_id() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -410,9 +528,9 @@ fn when_top_level_empty_env_workers_dev_false() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -427,13 +545,13 @@ fn when_top_level_empty_env_workers_dev_true() { let manifest = Manifest::from_str(&toml_string).unwrap(); let environment = Some(TEST_ENV_NAME); - let actual_deploy_config = manifest.deploy_config(environment).unwrap(); - let expected_deploy_config = DeployConfig::Zoneless(Zoneless { + let actual_deployments = manifest.get_deployments(environment).unwrap(); + let expected_deployments = vec![DeployTarget::Zoneless(ZonelessTarget { script_name: manifest.worker_name(environment), account_id: account_id.to_string(), - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] @@ -447,9 +565,9 @@ fn when_top_level_empty_zoned_single_route_env() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -462,9 +580,9 @@ fn when_top_level_empty_env_zoned_single_route_no_zone_id() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -477,9 +595,9 @@ fn when_top_level_empty_env_zoned_single_route_zone_id_only() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -491,7 +609,7 @@ fn when_top_level_empty_env_zoned_single_route() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)).unwrap(); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)).unwrap(); let expected_name = manifest.worker_name(Some(TEST_ENV_NAME)); @@ -500,12 +618,12 @@ fn when_top_level_empty_env_zoned_single_route() { pattern: PATTERN.to_string(), id: None, }]; - let expected_deploy_config = DeployConfig::Zoned(Zoned { + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { zone_id: ZONE_ID.to_string(), routes: expected_routes, - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] @@ -519,9 +637,9 @@ fn when_top_level_empty_zoned_multi_route_env_routes_empty() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -535,9 +653,9 @@ fn when_top_level_empty_zoned_multi_route_env_route_empty() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -551,9 +669,9 @@ fn when_top_level_empty_zoned_multi_route_env_zone_id_missing() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -567,7 +685,7 @@ fn when_top_level_empty_zoned_multi_route_env() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)).unwrap(); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)).unwrap(); let expected_name = manifest.worker_name(Some(TEST_ENV_NAME)); @@ -580,12 +698,12 @@ fn when_top_level_empty_zoned_multi_route_env() { }) .collect(); - let expected_deploy_config = DeployConfig::Zoned(Zoned { + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { zone_id: ZONE_ID.to_string(), routes: expected_routes, - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] @@ -600,13 +718,13 @@ fn when_top_level_zoneless_env_empty() { let manifest = Manifest::from_str(&toml_string).unwrap(); let environment = Some(TEST_ENV_NAME); - let actual_deploy_config = manifest.deploy_config(environment).unwrap(); - let expected_deploy_config = DeployConfig::Zoneless(Zoneless { + let actual_deployments = manifest.get_deployments(environment).unwrap(); + let expected_deployments = vec![DeployTarget::Zoneless(ZonelessTarget { script_name: manifest.worker_name(environment), account_id: ACCOUNT_ID.to_string(), - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] @@ -620,9 +738,9 @@ fn when_top_level_zoneless_env_zoneless_workers_dev_false() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -638,13 +756,13 @@ fn when_top_level_zoneless_env_zoneless_workers_dev_true() { let manifest = Manifest::from_str(&toml_string).unwrap(); let environment = Some(TEST_ENV_NAME); - let actual_deploy_config = manifest.deploy_config(environment).unwrap(); - let expected_deploy_config = DeployConfig::Zoneless(Zoneless { + let actual_deployments = manifest.get_deployments(environment).unwrap(); + let expected_deployments = vec![DeployTarget::Zoneless(ZonelessTarget { account_id: ACCOUNT_ID.to_string(), script_name: manifest.worker_name(environment), - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] @@ -661,13 +779,13 @@ fn when_top_level_zoneless_env_zoned_single_route_empty() { let manifest = Manifest::from_str(&toml_string).unwrap(); let environment = Some(TEST_ENV_NAME); - let actual_deploy_config = manifest.deploy_config(environment).unwrap(); - let expected_deploy_config = DeployConfig::Zoneless(Zoneless { + let actual_deployments = manifest.get_deployments(environment).unwrap(); + let expected_deployments = vec![DeployTarget::Zoneless(ZonelessTarget { account_id: ACCOUNT_ID.to_string(), script_name: manifest.worker_name(environment), - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] @@ -683,9 +801,9 @@ fn when_top_level_zoneless_env_zoned_single_route_zone_id_missing() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -700,7 +818,7 @@ fn when_top_level_zoneless_env_zoned_single_route() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)).unwrap(); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)).unwrap(); let expected_name = manifest.worker_name(Some(TEST_ENV_NAME)); @@ -709,12 +827,12 @@ fn when_top_level_zoneless_env_zoned_single_route() { pattern: PATTERN.to_string(), id: None, }]; - let expected_deploy_config = DeployConfig::Zoned(Zoned { + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { zone_id: ZONE_ID.to_string(), routes: expected_routes, - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] @@ -730,9 +848,9 @@ fn when_top_level_zoneless_env_zoned_multi_route_routes_list_empty() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -748,14 +866,14 @@ fn when_top_level_zoneless_env_zoned_multi_route_route_empty() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] fn when_top_level_zoneless_env_zoned_multi_route_route_key_present() { - // when route key also present, error + // when route key also present, append routes to route let patterns = [PATTERN]; let mut env_config = EnvConfig::zoned_multi_route(ZONE_ID, patterns.to_vec()); env_config.route = Some("blog.hostname.tld/*"); @@ -767,9 +885,25 @@ fn when_top_level_zoneless_env_zoned_multi_route_route_key_present() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let expected_name = manifest.worker_name(Some(TEST_ENV_NAME)); + let expected_routes = std::iter::once("blog.hostname.tld/*") + .into_iter() + .chain(patterns.iter().copied()) + .map(|p| Route { + script: Some(expected_name.clone()), + pattern: p.to_owned(), + id: None, + }) + .collect(); + + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { + routes: expected_routes, + zone_id: ZONE_ID.to_owned(), + })]; + + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)).unwrap(); - assert!(actual_deploy_config.is_err()); + assert_eq!(expected_deployments, actual_deployments); } #[test] @@ -785,9 +919,9 @@ fn when_top_level_zoneless_env_zoned_multi_route_zone_id_missing() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -803,7 +937,7 @@ fn when_top_level_zoneless_env_zoned_multi_route() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)).unwrap(); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)).unwrap(); let expected_name = manifest.worker_name(Some(TEST_ENV_NAME)); @@ -816,12 +950,12 @@ fn when_top_level_zoneless_env_zoned_multi_route() { }) .collect(); - let expected_deploy_config = DeployConfig::Zoned(Zoned { + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { zone_id: ZONE_ID.to_string(), routes: expected_routes, - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] @@ -834,9 +968,9 @@ fn when_top_level_zoned_env_empty() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -851,9 +985,9 @@ fn when_top_level_zoned_env_zoneless_workers_dev_false() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -869,13 +1003,13 @@ fn when_top_level_zoned_env_zoneless_workers_dev_true() { let manifest = Manifest::from_str(&toml_string).unwrap(); let environment = Some(TEST_ENV_NAME); - let actual_deploy_config = manifest.deploy_config(environment).unwrap(); - let expected_deploy_config = DeployConfig::Zoneless(Zoneless { + let actual_deployments = manifest.get_deployments(environment).unwrap(); + let expected_deployments = vec![DeployTarget::Zoneless(ZonelessTarget { account_id: ACCOUNT_ID.to_string(), script_name: manifest.worker_name(environment), - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] @@ -890,9 +1024,9 @@ fn when_top_level_zoned_env_zoned_single_route_route_empty() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)); - assert!(actual_deploy_config.is_err()); + assert!(actual_deployments.is_err()); } #[test] @@ -907,7 +1041,7 @@ fn when_top_level_zoned_env_zoned_single_route_zone_id_missing() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)).unwrap(); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)).unwrap(); let expected_name = manifest.worker_name(Some(TEST_ENV_NAME)); @@ -916,12 +1050,12 @@ fn when_top_level_zoned_env_zoned_single_route_zone_id_missing() { pattern: env_pattern.to_string(), id: None, }]; - let expected_deploy_config = DeployConfig::Zoned(Zoned { + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { zone_id: ZONE_ID.to_string(), routes: expected_routes, - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } #[test] @@ -937,7 +1071,7 @@ fn when_top_level_zoned_env_zoned_single_route() { let toml_string = toml::to_string(&test_toml).unwrap(); let manifest = Manifest::from_str(&toml_string).unwrap(); - let actual_deploy_config = manifest.deploy_config(Some(TEST_ENV_NAME)).unwrap(); + let actual_deployments = manifest.get_deployments(Some(TEST_ENV_NAME)).unwrap(); let expected_name = manifest.worker_name(Some(TEST_ENV_NAME)); @@ -946,10 +1080,10 @@ fn when_top_level_zoned_env_zoned_single_route() { pattern: PATTERN.to_string(), id: None, }]; - let expected_deploy_config = DeployConfig::Zoned(Zoned { + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { zone_id: env_zone_id.to_string(), routes: expected_routes, - }); + })]; - assert_eq!(actual_deploy_config, expected_deploy_config); + assert_eq!(actual_deployments, expected_deployments); } diff --git a/src/settings/toml/tests/mod.rs b/src/settings/toml/tests/mod.rs index bce2cc402..ad8ffb8bc 100644 --- a/src/settings/toml/tests/mod.rs +++ b/src/settings/toml/tests/mod.rs @@ -1,4 +1,4 @@ -mod deploy_config; +mod deployments; use super::*; diff --git a/src/settings/toml/triggers.rs b/src/settings/toml/triggers.rs new file mode 100644 index 000000000..ac99ef4d5 --- /dev/null +++ b/src/settings/toml/triggers.rs @@ -0,0 +1,6 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +pub struct Triggers { + pub crons: Vec, +} diff --git a/src/terminal/message.rs b/src/terminal/message.rs index ddb9a5a07..8d3f4254d 100644 --- a/src/terminal/message.rs +++ b/src/terminal/message.rs @@ -3,6 +3,7 @@ use super::emoji; use billboard::{Billboard, BorderColor, BorderStyle}; use serde::Serialize; +#[derive(Clone, Copy, PartialEq, Debug)] pub enum Output { Json, PlainText, From 3bdebad2835c99ae9bf3254337b30fd484d652b6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 23 Oct 2020 15:07:33 +0000 Subject: [PATCH 2/5] Bump chrono from 0.4.15 to 0.4.19 (#1578) --- Cargo.lock | 6 ++++-- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5cfbb3308..490529dbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -294,14 +294,16 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.15" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ + "libc", "num-integer", "num-traits 0.2.12", "serde 1.0.116", "time", + "winapi 0.3.9", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7cd1f1c81..cc9385947 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ base64 = "0.12.3" billboard = "0.1.0" binary-install = "0.0.3-alpha.1" chrome-devtools-rs = { version = "0.0.0-alpha.1", features = ["color"] } -chrono = "0.4.15" +chrono = "0.4.19" clap = "2.33.3" cloudflare = "0.6.6" config = "0.10.1" From 69e2e2ec4f00c1e4f6aa89d9ed8b1d6e028b202f Mon Sep 17 00:00:00 2001 From: Sydney Acksman Date: Fri, 23 Oct 2020 10:44:38 -0500 Subject: [PATCH 3/5] Feedback changes --- src/commands/publish.rs | 23 +++++++++++---- src/deploy/mod.rs | 25 ++++++++++------ src/deploy/schedule.rs | 7 ++--- src/deploy/zoned.rs | 5 ---- src/deploy/zoneless.rs | 6 ---- src/settings/toml/manifest.rs | 6 ++-- src/settings/toml/tests/deployments.rs | 40 +++++++++++++++++++++++--- 7 files changed, 76 insertions(+), 36 deletions(-) diff --git a/src/commands/publish.rs b/src/commands/publish.rs index 91d47b032..fa1640db4 100644 --- a/src/commands/publish.rs +++ b/src/commands/publish.rs @@ -20,6 +20,7 @@ pub struct PublishOutput { pub success: bool, pub name: String, pub urls: Vec, + pub schedules: Vec, } pub fn publish( @@ -31,11 +32,22 @@ pub fn publish( validate_target_required_fields_present(target)?; let deploy = |target: &Target| match deploy::worker(&user, &deployments) { - Ok(urls) => { - let result_msg = if !urls.is_empty() { - format!("Successfully published your script to {}", urls[0]) - } else { - "Successfully published your script".to_owned() + Ok(deploy::DeployResults { urls, schedules }) => { + let result_msg = match (urls.as_slice(), schedules.as_slice()) { + ([], []) => "Successfully published your script".to_owned(), + ([], schedules) => format!( + "Successfully published your script with this schedule\n {}", + schedules.join("\n ") + ), + (urls, []) => format!( + "Successfully published your script to\n {}", + urls.join("\n ") + ), + (urls, schedules) => format!( + "Successfully published your script to\n {}\nwith this schedule\n {}", + urls.join("\n "), + schedules.join("\n ") + ), }; StdErr::success(&result_msg); if out == Output::Json { @@ -43,6 +55,7 @@ pub fn publish( success: true, name: target.name.clone(), urls, + schedules, }); } Ok(()) diff --git a/src/deploy/mod.rs b/src/deploy/mod.rs index 6fedd3b84..fa3d4d07a 100644 --- a/src/deploy/mod.rs +++ b/src/deploy/mod.rs @@ -20,22 +20,31 @@ pub enum DeployTarget { pub fn worker( user: &GlobalUser, - deployment: &[DeployTarget], -) -> Result, failure::Error> { - let mut urls = Vec::new(); - for target in deployment { + deploy_targets: &[DeployTarget], +) -> Result { + let mut results = DeployResults::default(); + for target in deploy_targets { match target { DeployTarget::Zoned(zoned) => { let route_urls = zoned.deploy(user)?; - urls.extend_from_slice(&route_urls); + results.urls.extend(route_urls); } DeployTarget::Zoneless(zoneless) => { let worker_dev = zoneless.deploy(user)?; - urls.push(worker_dev); + results.urls.push(worker_dev); + } + DeployTarget::Schedule(schedule) => { + let schedules = schedule.deploy(user)?; + results.schedules.extend(schedules); } - DeployTarget::Schedule(schedule) => schedule.deploy(user)?, } } - Ok(urls) + Ok(results) +} + +#[derive(Default)] +pub struct DeployResults { + pub urls: Vec, + pub schedules: Vec, } diff --git a/src/deploy/schedule.rs b/src/deploy/schedule.rs index 80049d801..a4b08491c 100644 --- a/src/deploy/schedule.rs +++ b/src/deploy/schedule.rs @@ -1,6 +1,5 @@ use crate::http; use crate::settings::global_user::GlobalUser; -use crate::terminal::message::{Message, StdOut}; #[derive(Clone, Debug, PartialEq)] pub struct ScheduleTarget { @@ -24,7 +23,7 @@ impl ScheduleTarget { }) } - pub fn deploy(&self, user: &GlobalUser) -> Result<(), failure::Error> { + pub fn deploy(&self, user: &GlobalUser) -> Result, failure::Error> { log::info!("publishing schedules"); let schedule_worker_addr = format!( "https://api.cloudflare.com/client/v4/accounts/{}/workers/scripts/{}/schedules", @@ -48,9 +47,7 @@ impl ScheduleTarget { ) } - StdOut::success("Uploaded schedules"); - - Ok(()) + Ok(self.crons.clone()) } } diff --git a/src/deploy/zoned.rs b/src/deploy/zoned.rs index 616a790a7..248468183 100644 --- a/src/deploy/zoned.rs +++ b/src/deploy/zoned.rs @@ -59,11 +59,6 @@ impl ZonedTarget { let display_results: Vec = published_routes.iter().map(|r| r.to_string()).collect(); - StdOut::success(&format!( - "Deployed to the following routes:\n{}", - display_results.join("\n") - )); - Ok(display_results) } } diff --git a/src/deploy/zoneless.rs b/src/deploy/zoneless.rs index b54d1dedb..a66cb04cc 100644 --- a/src/deploy/zoneless.rs +++ b/src/deploy/zoneless.rs @@ -2,7 +2,6 @@ use crate::commands::subdomain::Subdomain; use crate::http; use crate::settings::global_user::GlobalUser; use crate::settings::toml::RouteConfig; -use crate::terminal::message::{Message, StdOut}; #[derive(Clone, Debug, PartialEq)] pub struct ZonelessTarget { @@ -55,11 +54,6 @@ impl ZonelessTarget { let deploy_address = format!("https://{}.{}.workers.dev", self.script_name, subdomain); - StdOut::success(&format!( - "Successfully published your script to {}", - deploy_address - )); - Ok(deploy_address) } } diff --git a/src/settings/toml/manifest.rs b/src/settings/toml/manifest.rs index 1b9bd96c8..d8b820b41 100644 --- a/src/settings/toml/manifest.rs +++ b/src/settings/toml/manifest.rs @@ -239,11 +239,11 @@ impl Manifest { deployments.push(DeployTarget::Schedule(scheduled)); } - if !deployments.is_empty() { - Ok(deployments) - } else { + if deployments.is_empty() { failure::bail!("No deployments specified!") } + + Ok(deployments) } pub fn get_account_id(&self, environment_name: Option<&str>) -> Result { diff --git a/src/settings/toml/tests/deployments.rs b/src/settings/toml/tests/deployments.rs index 2e9dd4f22..02ba2c242 100644 --- a/src/settings/toml/tests/deployments.rs +++ b/src/settings/toml/tests/deployments.rs @@ -24,7 +24,7 @@ fn it_errors_on_empty_get_deployments() { } #[test] -fn it_errors_on_conflicting_get_deploymentss() { +fn it_can_get_multiple_deployments() { let script_name = "workers_dev_true_and_zoned_config"; let workers_dev = true; @@ -366,6 +366,38 @@ fn it_can_get_a_multi_route_zoned_get_deployments_workers_dev_false() { assert_eq!(actual_deployments, expected_deployments); } +#[test] +fn it_can_get_multi_route_with_route() { + let script_name = "multi_route_with_route"; + let patterns = [PATTERN]; + + let mut test_toml = WranglerToml::webpack(script_name); + test_toml.workers_dev = Some(false); + test_toml.routes = Some(patterns.to_vec()); + test_toml.route = Some("blog.hostname.tld/*"); + test_toml.zone_id = Some(ZONE_ID); + let toml_string = toml::to_string(&test_toml).unwrap(); + let manifest = Manifest::from_str(&toml_string).unwrap(); + + let expected_routes = std::iter::once("blog.hostname.tld/*") + .chain(patterns.iter().copied()) + .map(|p| Route { + script: Some(script_name.to_string()), + pattern: (*p).to_string(), + id: None, + }) + .collect(); + let expected_deployments = vec![DeployTarget::Zoned(ZonedTarget { + zone_id: ZONE_ID.to_string(), + routes: expected_routes, + })]; + + let environment = None; + let actual_deployments = manifest.get_deployments(environment).unwrap(); + + assert_eq!(actual_deployments, expected_deployments); +} + #[test] fn it_errors_on_multi_route_get_deployments_empty_zone_id() { let script_name = "multi_route_empty_zone_id"; @@ -427,7 +459,7 @@ fn it_errors_on_multi_route_get_deployments_empty_route() { } #[test] -fn it_errors_on_get_deployments_route_and_routes() { +fn it_can_get_a_route_with_routes() { let script_name = "route_and_routes"; let patterns = ["blog.hostname.tld/*"]; @@ -456,7 +488,7 @@ fn it_errors_on_get_deployments_route_and_routes() { } #[test] -fn it_errors_on_get_deployments_route_and_workers_dev_true() { +fn it_gets_deployments_with_route_and_workers_dev_true() { let script_name = "route_and_workers_dev"; let mut test_toml = WranglerToml::zoned_single_route(script_name, ZONE_ID, PATTERN); @@ -470,7 +502,7 @@ fn it_errors_on_get_deployments_route_and_workers_dev_true() { } #[test] -fn it_errors_on_get_deployments_routes_and_workers_dev_true() { +fn it_gets_deployments_with_routes_and_workers_dev_true() { let script_name = "routes_and_workers_dev"; let patterns = [PATTERN]; From e31a6201ced849ba5ee12f6018cee60296d4b5d1 Mon Sep 17 00:00:00 2001 From: Sydney Acksman Date: Fri, 23 Oct 2020 13:02:27 -0500 Subject: [PATCH 4/5] Inherit triggers when deploying environment with no specified triggers --- src/settings/toml/manifest.rs | 1 + src/settings/toml/tests/deployments.rs | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/settings/toml/manifest.rs b/src/settings/toml/manifest.rs index d8b820b41..61be2603e 100644 --- a/src/settings/toml/manifest.rs +++ b/src/settings/toml/manifest.rs @@ -225,6 +225,7 @@ impl Manifest { let account_id = e.account_id.as_ref().unwrap_or(&self.account_id); e.triggers .as_ref() + .or_else(|| self.triggers.as_ref()) .map(|t| (t.crons.as_slice(), account_id)) } None => self diff --git a/src/settings/toml/tests/deployments.rs b/src/settings/toml/tests/deployments.rs index 02ba2c242..21e182945 100644 --- a/src/settings/toml/tests/deployments.rs +++ b/src/settings/toml/tests/deployments.rs @@ -224,12 +224,12 @@ fn it_can_get_a_scheduled_in_env_no_workers_dev_no_zoned() { assert_eq!(actual_deployments, expected_deployments); } #[test] -fn it_errors_on_no_schedules_in_env() { +fn it_cat_get_inherited_env_schedules() { // with no zoned, zoneless, or schedule targets in environment, we error let script_name = "single_schedule"; let crons = vec!["0 * * * *".to_owned()]; - let env = EnvConfig::custom_script_name("no_schedule"); + let env = EnvConfig::custom_script_name("inherited_schedule"); let mut test_toml = WranglerToml::webpack(script_name); test_toml.account_id = Some(ACCOUNT_ID); @@ -245,7 +245,14 @@ fn it_errors_on_no_schedules_in_env() { let manifest = Manifest::from_str(&toml_string).unwrap(); let environment = Some("b"); - assert!(manifest.get_deployments(environment).is_err()); + let expected_deployments = vec![DeployTarget::Schedule(ScheduleTarget { + account_id: ACCOUNT_ID.to_owned(), + script_name: "inherited_schedule".to_owned(), + crons, + })]; + let actual_deployments = manifest.get_deployments(environment).unwrap(); + + assert_eq!(actual_deployments, expected_deployments); } #[test] From 7400d7527a592e049432ce944b6d23d055fc3b93 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 23 Oct 2020 18:54:00 +0000 Subject: [PATCH 5/5] Bump futures-util from 0.3.5 to 0.3.7 (#1613) --- Cargo.lock | 60 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 490529dbc..97351b685 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -831,21 +831,21 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" +checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46" [[package]] name = "futures-io" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" +checksum = "6e1798854a4727ff944a7b12aa999f58ce7aa81db80d2dfaaf2ba06f065ddd2b" [[package]] name = "futures-macro" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" +checksum = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -855,24 +855,24 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" +checksum = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11" [[package]] name = "futures-task" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +checksum = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c" dependencies = [ "once_cell", ] [[package]] name = "futures-util" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" +checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34" dependencies = [ "futures-core", "futures-io", @@ -880,7 +880,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project", + "pin-project 1.0.1", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -1037,7 +1037,7 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project", + "pin-project 0.4.23", "socket2", "tokio", "tower-service", @@ -1655,7 +1655,16 @@ version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa" dependencies = [ - "pin-project-internal", + "pin-project-internal 0.4.23", +] + +[[package]] +name = "pin-project" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841" +dependencies = [ + "pin-project-internal 1.0.1", ] [[package]] @@ -1669,6 +1678,17 @@ dependencies = [ "syn", ] +[[package]] +name = "pin-project-internal" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.1.7" @@ -1756,9 +1776,9 @@ checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" [[package]] name = "proc-macro2" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] @@ -2345,9 +2365,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.39" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9" +checksum = "5ad5de3220ea04da322618ded2c42233d02baca219d6f160a3e9c87cda16c942" dependencies = [ "proc-macro2", "quote", @@ -2603,7 +2623,7 @@ dependencies = [ "futures-util", "log 0.4.11", "native-tls", - "pin-project", + "pin-project 0.4.23", "tokio", "tokio-native-tls", "tungstenite",