#![cfg_attr(feature = "strict", deny(warnings))]
extern crate text_io;
extern crate tokio;
use std::convert::TryFrom;
use std::env;
use std::path::Path;
use std::str::FromStr;
use clap::{App, AppSettings, Arg, ArgGroup, SubCommand};
use exitfailure::ExitFailure;
use url::Url;
use wrangler::commands;
use wrangler::commands::kv::key::{parse_metadata, KVMetaData};
use wrangler::installer;
use wrangler::preview::{HttpMethod, PreviewOpt};
use wrangler::settings;
use wrangler::settings::global_user::GlobalUser;
use wrangler::settings::toml::TargetType;
use wrangler::terminal::message::{Message, Output, StdOut};
use wrangler::terminal::{emoji, interactive, styles};
use wrangler::version::background_check_for_updates;
fn main() -> Result<(), ExitFailure> {
env_logger::init();
let latest_version_receiver = background_check_for_updates();
if let Ok(me) = env::current_exe() {
if me
.file_stem()
.and_then(|s| s.to_str())
.expect("executable should have a filename")
.starts_with("wrangler-init")
{
installer::install();
}
}
run()?;
if let Ok(latest_version) = latest_version_receiver.try_recv() {
let latest_version = styles::highlight(latest_version.to_string());
let new_version_available = format!(
"A new version of Wrangler ({}) is available!",
latest_version
);
let update_message = "You can learn more about updating here:".to_string();
let update_docs_url = styles::url(
"https://developers.cloudflare.com/workers/cli-wrangler/install-update#update",
);
StdOut::billboard(&format!(
"{}\n{}\n{}",
new_version_available, update_message, update_docs_url
));
}
Ok(())
}
#[allow(clippy::cognitive_complexity)]
fn run() -> Result<(), failure::Error> {
let kv_binding_arg = Arg::with_name("binding")
.help("The binding of the namespace this action applies to")
.short("b")
.long("binding")
.value_name("BINDING NAME")
.takes_value(true);
let kv_namespace_id_arg = Arg::with_name("namespace-id")
.help("The id of the namespace this action applies to")
.short("n")
.long("namespace-id")
.value_name("ID")
.takes_value(true);
let kv_namespace_specifier_group = ArgGroup::with_name("namespace-specifier")
.args(&["binding", "namespace-id"])
.required(true);
let kv_preview_arg = Arg::with_name("preview")
.help("applies the command to the preview namespace when combined with --binding")
.long("preview")
.takes_value(false);
let environment_arg = Arg::with_name("env")
.help("Environment to use")
.short("e")
.long("env")
.takes_value(true)
.value_name("ENVIRONMENT NAME");
let secret_name_arg = Arg::with_name("name")
.help("Name of the secret variable")
.short("n")
.long("name")
.required(true)
.takes_value(true)
.index(1)
.value_name("VAR_NAME");
let verbose_arg = Arg::with_name("verbose")
.long("verbose")
.takes_value(false)
.help("toggle verbose output");
let wrangler_file = Arg::with_name("config")
.short("c")
.takes_value(true)
.long("config")
.help("Path to configuration file. Defaults to `./wrangler.toml`");
let silent_verbose_arg = verbose_arg.clone().hidden(true);
let matches = App::new(format!("{}{} wrangler", emoji::WORKER, emoji::SPARKLES))
.version(env!("CARGO_PKG_VERSION"))
.author("The Wrangler Team <wrangler@cloudflare.com>")
.setting(AppSettings::ArgRequiredElseHelp)
.setting(AppSettings::DeriveDisplayOrder)
.setting(AppSettings::VersionlessSubcommands)
.subcommand(
SubCommand::with_name("kv:namespace")
.about(&*format!(
"{} Interact with your Workers KV Namespaces",
emoji::FILES
))
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("create")
.about("Create a new namespace")
.arg(environment_arg.clone())
.arg(
Arg::with_name("binding")
.help("The binding for your new namespace")
.required(true)
.index(1)
)
.arg(kv_preview_arg.clone())
.arg(silent_verbose_arg.clone())
.arg(wrangler_file.clone())
)
.subcommand(
SubCommand::with_name("delete")
.about("Delete namespace")
.arg(kv_binding_arg.clone())
.arg(kv_namespace_id_arg.clone())
.arg(kv_preview_arg.clone())
.group(kv_namespace_specifier_group.clone())
.arg(environment_arg.clone())
.arg(silent_verbose_arg.clone())
.arg(wrangler_file.clone())
)
.subcommand(
SubCommand::with_name("list")
.about("List all namespaces on your Cloudflare account")
.arg(silent_verbose_arg.clone())
.arg(wrangler_file.clone())
)
.arg(silent_verbose_arg.clone())
)
.subcommand(
SubCommand::with_name("kv:key")
.about(&*format!(
"{} Individually manage Workers KV key-value pairs",
emoji::KEY
))
.setting(AppSettings::SubcommandRequiredElseHelp)
.arg(silent_verbose_arg.clone())
.subcommand(
SubCommand::with_name("put")
.about("Put a key-value pair into a namespace")
.arg(kv_binding_arg.clone())
.arg(kv_namespace_id_arg.clone())
.arg(kv_preview_arg.clone())
.group(kv_namespace_specifier_group.clone())
.arg(environment_arg.clone())
.arg(
Arg::with_name("key")
.help("Key to write value to")
.required(true)
.index(1)
)
.arg(
Arg::with_name("value")
.help("Value for key")
.required(true)
.index(2)
)
.arg(
Arg::with_name("expiration-ttl")
.help("Number of seconds for which the entries should be visible before they expire. At least 60. Takes precedence over 'expiration' option")
.short("t")
.long("ttl")
.value_name("SECONDS")
.takes_value(true)
)
.arg(
Arg::with_name("expiration")
.help("Number of seconds since the UNIX epoch, indicating when the key-value pair should expire")
.short("x")
.long("expiration")
.takes_value(true)
.value_name("SECONDS")
)
.arg(
Arg::with_name("metadata")
.help("Arbitrary JSON to associate with a key-value pair. Must be no more than 1024 bytes.")
.short("m")
.long("metadata")
.takes_value(true)
.value_name("JSON")
)
.arg(
Arg::with_name("path")
.help("The value passed in is a path to a file; open and upload its contents")
.short("p")
.long("path")
.takes_value(false)
)
.arg(silent_verbose_arg.clone())
.arg(wrangler_file.clone())
)
.subcommand(
SubCommand::with_name("get")
.about("Get a key's value from a namespace")
.arg(kv_binding_arg.clone())
.arg(kv_namespace_id_arg.clone())
.arg(kv_preview_arg.clone())
.group(kv_namespace_specifier_group.clone())
.arg(environment_arg.clone())
.arg(
Arg::with_name("key")
.help("Key whose value to get")
.required(true)
.index(1)
)
.arg(silent_verbose_arg.clone())
.arg(wrangler_file.clone())
)
.subcommand(
SubCommand::with_name("delete")
.about("Delete a key and its value from a namespace")
.arg(kv_binding_arg.clone())
.arg(kv_namespace_id_arg.clone())
.arg(kv_preview_arg.clone())
.group(kv_namespace_specifier_group.clone())
.arg(environment_arg.clone())
.arg(
Arg::with_name("key")
.help("Key whose value to delete")
.required(true)
.index(1)
)
.arg(silent_verbose_arg.clone())
.arg(wrangler_file.clone())
)
.subcommand(
SubCommand::with_name("list")
.about("List all keys in a namespace. Produces JSON output")
.arg(kv_binding_arg.clone())
.arg(kv_namespace_id_arg.clone())
.arg(kv_preview_arg.clone())
.group(kv_namespace_specifier_group.clone())
.arg(environment_arg.clone())
.arg(
Arg::with_name("prefix")
.help("The prefix for filtering listed keys")
.short("p")
.long("prefix")
.value_name("STRING")
.takes_value(true),
)
.arg(silent_verbose_arg.clone())
.arg(wrangler_file.clone())
)
)
.subcommand(
SubCommand::with_name("kv:bulk")
.about(&*format!(
"{} Interact with multiple Workers KV key-value pairs at once",
emoji::BICEP
))
.arg(silent_verbose_arg.clone())
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("put")
.about("Upload multiple key-value pairs to a namespace")
.arg(kv_binding_arg.clone())
.arg(kv_namespace_id_arg.clone())
.arg(kv_preview_arg.clone())
.group(kv_namespace_specifier_group.clone())
.arg(environment_arg.clone())
.arg(
Arg::with_name("path")
.help("the JSON file of key-value pairs to upload, in form [{\"key\":..., \"value\":...}\"...]")
.required(true)
.index(1)
)
.arg(wrangler_file.clone())
.arg(silent_verbose_arg.clone())
)
.subcommand(
SubCommand::with_name("delete")
.arg(kv_binding_arg.clone())
.arg(kv_namespace_id_arg.clone())
.group(kv_namespace_specifier_group.clone())
.arg(kv_preview_arg.clone())
.arg(environment_arg.clone())
.about("Delete multiple keys and their values from a namespace")
.arg(
Arg::with_name("path")
.help("the JSON file of key-value pairs to upload, in form [\"<example-key>\", ...]")
.required(true)
.index(1)
)
.arg(wrangler_file.clone())
.arg(silent_verbose_arg.clone())
)
)
.subcommand(
SubCommand::with_name("route")
.about(&*format!(
"{} List or delete worker routes.",
emoji::ROUTE
))
.arg(silent_verbose_arg.clone())
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("list")
.about("List all routes associated with a zone (outputs json)")
.arg(environment_arg.clone())
.arg(wrangler_file.clone())
.arg(silent_verbose_arg.clone())
)
.subcommand(
SubCommand::with_name("delete")
.arg(environment_arg.clone())
.about("Delete a route by id")
.arg(
Arg::with_name("route_id")
.help("the id associated with the route you want to delete (find using `wrangler route list`)")
.required(true)
.index(1)
)
.arg(silent_verbose_arg.clone())
.arg(wrangler_file.clone())
)
)
.subcommand(
SubCommand::with_name("secret")
.about(&*format!(
"{} Generate a secret that can be referenced in the worker script",
emoji::SECRET
))
.arg(silent_verbose_arg.clone())
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("put")
.about("Create or update a secret variable for a script")
.arg(secret_name_arg.clone())
.arg(environment_arg.clone())
.arg(wrangler_file.clone())
.arg(silent_verbose_arg.clone())
)
.subcommand(
SubCommand::with_name("delete")
.about("Delete a secret variable from a script")
.arg(secret_name_arg.clone())
.arg(environment_arg.clone())
.arg(wrangler_file.clone())
.arg(silent_verbose_arg.clone())
)
.subcommand(
SubCommand::with_name("list")
.about("List all secrets for a script")
.arg(environment_arg.clone())
.arg(wrangler_file.clone())
.arg(silent_verbose_arg.clone())
)
)
.subcommand(
SubCommand::with_name("generate")
.about(&*format!(
"{} Generate a new worker project",
emoji::DANCERS
))
.arg(
Arg::with_name("name")
.help("the name of your worker! defaults to 'worker'")
.index(1),
)
.arg(
Arg::with_name("template")
.help("a link to a GitHub template! defaults to https://github.com/cloudflare/worker-template")
.index(2),
)
.arg(
Arg::with_name("type")
.short("t")
.long("type")
.takes_value(true)
.help("the type of project you want generated"),
)
.arg(
Arg::with_name("site")
.short("s")
.long("site")
.takes_value(false)
.help("initializes a Workers Sites project. Overrides `type` and `template`"),
)
.arg(silent_verbose_arg.clone()),
)
.subcommand(
SubCommand::with_name("init")
.about(&*format!(
"{} Create a wrangler.toml for an existing project",
emoji::INBOX
))
.arg(
Arg::with_name("name")
.help("the name of your worker! defaults to 'worker'")
.index(1),
)
.arg(
Arg::with_name("type")
.short("t")
.long("type")
.takes_value(true)
.help("the type of project you want generated"),
)
.arg(
Arg::with_name("site")
.short("s")
.long("site")
.takes_value(false)
.help("initializes a Workers Sites project. Overrides `type` and `template`"),
)
.arg(silent_verbose_arg.clone()),
)
.subcommand(
SubCommand::with_name("build")
.about(&*format!(
"{} Build your worker",
emoji::CRAB
))
.arg(
Arg::with_name("env")
.help("environment to build")
.short("e")
.long("env")
.takes_value(true)
)
.arg(wrangler_file.clone())
.arg(silent_verbose_arg.clone()),
)
.subcommand(
SubCommand::with_name("preview")
.about(&*format!(
"{} Preview your code temporarily on cloudflareworkers.com",
emoji::MICROSCOPE
))
.arg(wrangler_file.clone())
.arg(
Arg::with_name("headless")
.help("Don't open the browser on preview")
.long("headless")
.takes_value(false)
)
.arg(
Arg::with_name("method")
.help("Type of request to preview your worker with (get, post)")
.index(1),
)
.arg(
Arg::with_name("body")
.help("Body string to post to your preview worker request")
.index(2),
)
.arg(
Arg::with_name("url")
.help("URL to open in the worker preview")
.short("u")
.long("url")
.takes_value(true)
)
.arg(
Arg::with_name("env")
.help("Environment to preview")
.short("e")
.long("env")
.takes_value(true)
)
.arg(
Arg::with_name("watch")
.help("Watch your project for changes and update the preview automagically")
.long("watch")
.takes_value(false),
)
.arg(verbose_arg.clone()),
)
.subcommand(
SubCommand::with_name("dev")
.about(&*format!(
"{} Start a local server for developing your worker",
emoji::EAR
))
.arg(
Arg::with_name("env")
.help("environment to build")
.short("e")
.long("env")
.takes_value(true)
)
.arg(
Arg::with_name("port")
.help("port to listen on. defaults to 8787")
.short("p")
.long("port")
.takes_value(true)
)
.arg(
Arg::with_name("host")
.help("Host to forward requests to, defaults to the zone of project or to tutorial.cloudflareworkers.com if unauthenticated.")
.short("h")
.long("host")
.takes_value(true)
)
.arg(
Arg::with_name("ip")
.help("ip to listen on. defaults to 127.0.0.1")
.short("i")
.long("ip")
.takes_value(true)
)
.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)
)
.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)
)
)
.subcommand(
SubCommand::with_name("publish")
.about(&*format!(
"{} Publish your worker to the orange cloud",
emoji::UP
))
.arg(wrangler_file.clone())
.arg(
Arg::with_name("env")
.help("environments to publish to")
.short("e")
.long("env")
.takes_value(true)
)
.arg(silent_verbose_arg.clone())
.arg(
Arg::with_name("release")
.hidden(true)
.long("release")
.takes_value(false)
.help("[deprecated] alias of wrangler publish")
)
.arg(
Arg::with_name("output")
.short("o")
.long("output")
.takes_value(true)
.possible_value("json")
),
)
.subcommand(
SubCommand::with_name("config")
.about(&*format!(
"{} Authenticate Wrangler with a Cloudflare API Token or Global API Key",
emoji::SLEUTH
))
.arg(
Arg::with_name("api-key")
.help("use an email and global API key for authentication. This is not recommended; use API tokens (the default) if possible")
.long("api-key")
.takes_value(false),
)
.arg(
Arg::with_name("no-verify")
.help("do not verify provided credentials before writing out Wrangler config file")
.long("no-verify")
.takes_value(false),
)
.arg(silent_verbose_arg.clone()),
)
.subcommand(
SubCommand::with_name("subdomain")
.about(&*format!(
"{} Configure your workers.dev subdomain",
emoji::WORKER
))
.arg(wrangler_file.clone())
.arg(
Arg::with_name("name")
.help("the subdomain on workers.dev you'd like to reserve")
.index(1),
)
.arg(silent_verbose_arg.clone()),
)
.subcommand(
SubCommand::with_name("whoami")
.about(&*format!(
"{} Retrieve your user info and test your auth config",
emoji::SLEUTH
))
.arg(silent_verbose_arg.clone()),
)
.subcommand(
SubCommand::with_name("tail")
.about(&*format!("{} Aggregate logs from production worker", emoji::TAIL))
.arg(wrangler_file.clone())
.arg(
Arg::with_name("env")
.help("environment to tail logs from")
.short("e")
.long("env")
.takes_value(true)
)
.arg(
Arg::with_name("tunnel_port")
.help("port to accept tail log requests")
.short("p")
.long("port")
.takes_value(true)
)
.arg(
Arg::with_name("metrics_port")
.help("provides endpoint for cloudflared metrics. used to retrieve tunnel url")
.long("metrics")
.takes_value(true)
)
.arg(verbose_arg.clone())
)
.subcommand(
SubCommand::with_name("login")
.about(&*format!("{} Authenticate Wrangler with your Cloudflare username and password", emoji::UNLOCKED)))
.get_matches();
let mut is_preview = false;
let not_recommended_msg = styles::warning("(Not Recommended)");
let recommended_cmd_msg = styles::highlight("`wrangler config --api-key`");
let wrangler_login_msg = styles::highlight("`wrangler login`");
let api_token_url = styles::url("https://dash.cloudflare.com/profile/api-tokens");
let token_support_url = styles::url(
"https://support.cloudflare.com/hc/en-us/articles/200167836-Managing-API-Tokens-and-Keys",
);
if let Some(matches) = matches.subcommand_matches("config") {
let default = !matches.is_present("api-key");
let user: GlobalUser = if default {
StdOut::billboard(&format!("To find your API Token, go to {}\nand create it using the \"Edit Cloudflare Workers\" template.\n\nConsider using {} which only requires your Cloudflare username and password.\n\nIf you are trying to use your Global API Key instead of an API Token\n{}, run {}.", api_token_url, wrangler_login_msg, not_recommended_msg, recommended_cmd_msg));
let api_token: String = interactive::get_user_input("Enter API Token: ");
GlobalUser::TokenAuth { api_token }
} else {
StdOut::billboard(&format!("We don't recommend using your Global API Key!\nPlease consider using an API Token instead.\n\n{}", token_support_url));
let email: String = interactive::get_user_input("Enter Email: ");
let api_key: String = interactive::get_user_input("Enter Global API Key: ");
GlobalUser::GlobalKeyAuth { email, api_key }
};
let verify = !matches.is_present("no-verify");
commands::global_config(&user, verify)?;
} else if let Some(matches) = matches.subcommand_matches("generate") {
let name = matches.value_of("name").unwrap_or("worker");
let site = matches.is_present("site");
let template = matches.value_of("template");
let mut target_type = None;
let template = if site {
if template.is_some() {
failure::bail!("You cannot pass a template and the --site flag to wrangler generate. If you'd like to use the default site boilerplate, run wrangler generate --site. If you'd like to use another site boilerplate, omit --site when running wrangler generate.")
}
"https://github.com/cloudflare/worker-sites-template"
} else {
if let Some(type_value) = matches.value_of("type") {
target_type = Some(TargetType::from_str(&type_value.to_lowercase())?);
}
let default_template = "https://github.com/cloudflare/worker-template";
template.unwrap_or(match target_type {
Some(ref pt) => match pt {
TargetType::Rust => "https://github.com/cloudflare/rustwasm-worker-template",
_ => default_template,
},
_ => default_template,
})
};
log::info!(
"Generate command called with template {}, and name {}",
template,
name
);
commands::generate(name, template, target_type, site)?;
} else if let Some(matches) = matches.subcommand_matches("init") {
let name = matches.value_of("name");
let site = matches.is_present("site");
let target_type = if site {
Some(TargetType::Webpack)
} else {
match matches.value_of("type") {
Some(s) => Some(settings::toml::TargetType::from_str(&s.to_lowercase())?),
None => None,
}
};
commands::init(name, target_type, site)?;
} else if let Some(matches) = matches.subcommand_matches("build") {
commands::build(matches)?;
} else if let Some(matches) = matches.subcommand_matches("preview") {
log::info!("Getting project settings");
let config_path = Path::new(
matches
.value_of("config")
.unwrap_or(commands::DEFAULT_CONFIG_PATH),
);
let manifest = settings::toml::Manifest::new(config_path)?;
let env = matches.value_of("env");
is_preview = true;
let target = manifest.get_target(env, is_preview)?;
let user = settings::global_user::GlobalUser::new().ok();
let method = matches.value_of("method").unwrap_or("get");
let url = matches.value_of("url").unwrap_or("https://example.com");
let body = match matches.value_of("body") {
Some(s) => Some(s.to_string()),
None => None,
};
let livereload = matches.is_present("watch");
let verbose = matches.is_present("verbose");
let headless = matches.is_present("headless");
let method = HttpMethod::from_str(method)?;
let url = Url::parse(url)?;
failure::ensure!(
matches!(url.scheme(), "http" | "https"),
"Invalid URL scheme (use either \"https\" or \"http\")"
);
let options = PreviewOpt {
method,
url,
body,
livereload,
headless,
};
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(
matches
.value_of("config")
.unwrap_or(commands::DEFAULT_CONFIG_PATH),
);
let manifest = settings::toml::Manifest::new(config_path)?;
let host: Option<&str> = matches.value_of("host");
let mut ip: Option<&str> = matches.value_of("ip");
let mut port: Option<u16> = matches
.value_of("port")
.map(|p| p.parse().expect("--port expects a number"));
let mut local_protocol_str: Option<&str> = matches.value_of("local-protocol");
let mut upstream_protocol_str: Option<&str> = matches.value_of("upstream-protocol");
if let Some(d) = &manifest.dev {
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 deployments = manifest.get_deployments(env)?;
is_preview = true;
let target = manifest.get_target(env, is_preview)?;
let user = settings::global_user::GlobalUser::new().ok();
let verbose = matches.is_present("verbose");
let local_protocol = Protocol::try_from(local_protocol_str.unwrap_or("http"))?;
let upstream_protocol = Protocol::try_from(upstream_protocol_str.unwrap_or("https"))?;
let server_config = commands::dev::ServerConfig::new(host, ip, port, upstream_protocol)?;
commands::dev::dev(
target,
deployments,
user,
server_config,
local_protocol,
upstream_protocol,
verbose,
)?;
} else if matches.subcommand_matches("whoami").is_some() {
log::info!("Getting User settings");
let user = settings::global_user::GlobalUser::new()?;
commands::whoami(&user)?;
} else if let Some(matches) = matches.subcommand_matches("publish") {
log::info!("Getting User settings");
let user = settings::global_user::GlobalUser::new()?;
let release = matches.is_present("release");
if release {
let publish_release_msg = styles::highlight("`wrangler publish --release`");
let publish_msg = styles::highlight("`wrangler publish`");
let environments_url = styles::url("https://developers.cloudflare.com/workers/tooling/wrangler/configuration/environments");
StdOut::warn(&format!(
"{} is deprecated and behaves exactly the same as {}.",
publish_release_msg, publish_msg
));
StdOut::warn(&format!("See {} for more information.", environments_url));
}
log::info!("Getting project settings");
let config_path = Path::new(
matches
.value_of("config")
.unwrap_or(commands::DEFAULT_CONFIG_PATH),
);
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.get_deployments(env)?;
if matches.is_present("output") && matches.value_of("output") == Some("json") {
commands::publish(&user, &mut target, deploy_config, Output::Json)?;
} else {
commands::publish(&user, &mut target, deploy_config, Output::PlainText)?;
}
} else if let Some(matches) = matches.subcommand_matches("subdomain") {
log::info!("Getting project settings");
let config_path = Path::new(
matches
.value_of("config")
.unwrap_or(commands::DEFAULT_CONFIG_PATH),
);
let manifest = settings::toml::Manifest::new(config_path)?;
let env = matches.value_of("env");
let target = manifest.get_target(env, is_preview)?;
log::info!("Getting User settings");
let user = settings::global_user::GlobalUser::new()?;
let name = matches.value_of("name");
if let Some(name) = name {
commands::subdomain::set_subdomain(&name, &user, &target)?;
} else {
commands::subdomain::get_subdomain(&user, &target)?;
}
} else if let Some(route_matches) = matches.subcommand_matches("route") {
let user = settings::global_user::GlobalUser::new()?;
let (subcommand, subcommand_matches) = route_matches.subcommand();
let config_path = Path::new(
subcommand_matches
.unwrap()
.value_of("config")
.unwrap_or(commands::DEFAULT_CONFIG_PATH),
);
let manifest = settings::toml::Manifest::new(config_path)?;
let env = subcommand_matches.unwrap().value_of("env");
let env_zone_id = if let Some(environment) = manifest.get_environment(env)? {
environment.zone_id.as_ref()
} else {
None
};
let zone_id: Result<String, failure::Error> = if let Some(zone_id) = env_zone_id {
Ok(zone_id.to_string())
} else if let Some(zone_id) = manifest.zone_id {
Ok(zone_id)
} else {
failure::bail!(
"You must specify a zone_id in your configuration file to use `wrangler route` commands."
)
};
match (subcommand, subcommand_matches) {
("list", Some(_)) => {
commands::route::list(zone_id?, &user)?;
}
("delete", Some(delete_matches)) => {
let route_id = delete_matches.value_of("route_id").unwrap();
commands::route::delete(zone_id?, &user, route_id)?;
}
_ => unreachable!(),
}
} else if let Some(secrets_matches) = matches.subcommand_matches("secret") {
log::info!("Getting User settings");
let user = settings::global_user::GlobalUser::new()?;
log::info!("Getting project settings");
let (subcommand, subcommand_matches) = secrets_matches.subcommand();
let config_path = Path::new(
subcommand_matches
.unwrap()
.value_of("config")
.unwrap_or(commands::DEFAULT_CONFIG_PATH),
);
let manifest = settings::toml::Manifest::new(config_path)?;
match (subcommand, subcommand_matches) {
("put", Some(create_matches)) => {
let name = create_matches.value_of("name");
let env = create_matches.value_of("env");
let target = manifest.get_target(env, is_preview)?;
if let Some(name) = name {
commands::secret::create_secret(&name, &user, &target)?;
}
}
("delete", Some(delete_matches)) => {
let name = delete_matches.value_of("name");
let env = delete_matches.value_of("env");
let target = manifest.get_target(env, is_preview)?;
if let Some(name) = name {
commands::secret::delete_secret(&name, &user, &target)?;
}
}
("list", Some(list_matches)) => {
let env = list_matches.value_of("env");
let target = manifest.get_target(env, is_preview)?;
commands::secret::list_secrets(&user, &target)?;
}
_ => unreachable!(),
}
} else if let Some(kv_matches) = matches.subcommand_matches("kv:namespace") {
let user = settings::global_user::GlobalUser::new()?;
let (subcommand, subcommand_matches) = kv_matches.subcommand();
let config_path = Path::new(
subcommand_matches
.unwrap()
.value_of("config")
.unwrap_or(commands::DEFAULT_CONFIG_PATH),
);
let manifest = settings::toml::Manifest::new(config_path)?;
match (subcommand, subcommand_matches) {
("create", Some(create_matches)) => {
is_preview = create_matches.is_present("preview");
let env = create_matches.value_of("env");
let binding = create_matches.value_of("binding").unwrap();
commands::kv::namespace::create(&manifest, is_preview, env, &user, binding)?;
}
("delete", Some(delete_matches)) => {
is_preview = delete_matches.is_present("preview");
let env = delete_matches.value_of("env");
let target = manifest.get_target(env, is_preview)?;
let namespace_id = match delete_matches.value_of("binding") {
Some(namespace_binding) => {
commands::kv::get_namespace_id(&target, namespace_binding)?
}
None => delete_matches
.value_of("namespace-id")
.unwrap() .to_string(),
};
commands::kv::namespace::delete(&target, &user, &namespace_id)?;
}
("list", Some(list_matches)) => {
let env = list_matches.value_of("env");
let target = manifest.get_target(env, is_preview)?;
commands::kv::namespace::list(&target, &user)?;
}
_ => unreachable!(),
}
} else if let Some(kv_matches) = matches.subcommand_matches("kv:key") {
let user = settings::global_user::GlobalUser::new()?;
let (subcommand, subcommand_matches) = kv_matches.subcommand();
let config_path = Path::new(
subcommand_matches
.unwrap()
.value_of("config")
.unwrap_or(commands::DEFAULT_CONFIG_PATH),
);
let manifest = settings::toml::Manifest::new(config_path)?;
let (target, namespace_id) = match subcommand_matches {
Some(subcommand_matches) => {
is_preview = subcommand_matches.is_present("preview");
let env = subcommand_matches.value_of("env");
let target = manifest.get_target(env, is_preview)?;
let namespace_id = match subcommand_matches.value_of("binding") {
Some(namespace_binding) => {
commands::kv::get_namespace_id(&target, namespace_binding)?
}
None => subcommand_matches
.value_of("namespace-id")
.unwrap() .to_string(),
};
(target, namespace_id)
}
None => unreachable!(), };
match (subcommand, subcommand_matches) {
("get", Some(get_key_matches)) => {
let key = get_key_matches.value_of("key").unwrap();
commands::kv::key::get(&target, &user, &namespace_id, key)?
}
("put", Some(put_key_matches)) => {
let key = put_key_matches.value_of("key").unwrap().to_string();
let value = put_key_matches.value_of("value").unwrap().to_string();
let is_file = put_key_matches.is_present("path");
let expiration = put_key_matches
.value_of("expiration")
.map(|e| e.to_string());
let expiration_ttl = put_key_matches
.value_of("expiration-ttl")
.map(|t| t.to_string());
let metadata =
parse_metadata(put_key_matches.value_of("metadata")).map_err(|e| {
failure::format_err!("--metadata is not valid JSON: {}", e.to_string())
})?;
let kv_metadata = KVMetaData {
namespace_id,
key,
value,
is_file,
expiration,
expiration_ttl,
metadata,
};
commands::kv::key::put(&target, &user, kv_metadata)?
}
("delete", Some(delete_key_matches)) => {
let key = delete_key_matches.value_of("key").unwrap();
commands::kv::key::delete(&target, &user, &namespace_id, key)?
}
("list", Some(list_key_matches)) => {
let prefix = list_key_matches.value_of("prefix");
commands::kv::key::list(&target, &user, &namespace_id, prefix)?
}
_ => unreachable!(),
}
} else if let Some(kv_matches) = matches.subcommand_matches("kv:bulk") {
let (subcommand, subcommand_matches) = kv_matches.subcommand();
let config_path = Path::new(
subcommand_matches
.unwrap()
.value_of("config")
.unwrap_or(commands::DEFAULT_CONFIG_PATH),
);
let manifest = settings::toml::Manifest::new(config_path)?;
let user = settings::global_user::GlobalUser::new()?;
let (target, namespace_id) = match subcommand_matches {
Some(subcommand_matches) => {
is_preview = subcommand_matches.is_present("preview");
let env = subcommand_matches.value_of("env");
let target = manifest.get_target(env, is_preview)?;
let namespace_id = match subcommand_matches.value_of("binding") {
Some(namespace_binding) => {
commands::kv::get_namespace_id(&target, namespace_binding)?
}
None => subcommand_matches
.value_of("namespace-id")
.unwrap() .to_string(),
};
(target, namespace_id)
}
None => unreachable!(), };
match (subcommand, subcommand_matches) {
("put", Some(put_bulk_matches)) => {
let path = put_bulk_matches.value_of("path").unwrap();
commands::kv::bulk::put(&target, &user, &namespace_id, Path::new(path))?
}
("delete", Some(delete_bulk_matches)) => {
let path = delete_bulk_matches.value_of("path").unwrap();
commands::kv::bulk::delete(&target, &user, &namespace_id, Path::new(path))?
}
_ => unreachable!(),
}
} else if let Some(matches) = matches.subcommand_matches("tail") {
let config_path = Path::new(
matches
.value_of("config")
.unwrap_or(commands::DEFAULT_CONFIG_PATH),
);
let manifest = settings::toml::Manifest::new(config_path)?;
let env = matches.value_of("env");
let target = manifest.get_target(env, is_preview)?;
let user = settings::global_user::GlobalUser::new()?;
let tunnel_port: Option<u16> = matches
.value_of("tunnel_port")
.map(|p| p.parse().expect("--port expects a number"));
let metrics_port: Option<u16> = matches
.value_of("metrics_port")
.map(|p| p.parse().expect("--metrics expects a number"));
let verbose = matches.is_present("verbose");
commands::tail::start(&target, &user, tunnel_port, metrics_port, verbose)?;
} else if matches.subcommand_matches("login").is_some() {
commands::login::run()?;
}
Ok(())
}