Skip to content
This repository has been archived by the owner on Aug 3, 2023. It is now read-only.

r2 bindings and subcommands #2146

Merged
merged 1 commit into from
Jan 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ binary-install = "0.0.3-alpha.1"
chrome-devtools-rs = { version = "0.0.0-alpha.3", features = ["color"] }
chrono = "0.4.19"
clap = "2.33.3"
cloudflare = "0.8.3"
cloudflare = "0.9.0"
colored_json = "2.1.0"
config = { version = "0.11.0", default-features = false, features = ["toml", "json", "yaml", "ini"] }
console = "0.14.1"
Expand Down
6 changes: 6 additions & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod login;
pub mod logout;
pub mod preview;
pub mod publish;
pub mod r2;
pub mod route;
pub mod secret;
pub mod subdomain;
Expand All @@ -27,6 +28,7 @@ pub mod exec {
pub use super::logout::logout;
pub use super::preview::preview;
pub use super::publish::publish;
pub use super::r2::r2_bucket;
pub use super::route::route;
pub use super::secret::secret;
pub use super::subdomain::subdomain;
Expand Down Expand Up @@ -89,6 +91,10 @@ pub enum Command {
#[structopt(name = "kv:bulk", setting = AppSettings::SubcommandRequiredElseHelp)]
KvBulk(kv::KvBulk),

/// Interact with your Workers R2 Buckets
#[structopt(setting = AppSettings::SubcommandRequiredElseHelp)]
R2(r2::R2),

/// List or delete worker routes.
#[structopt(name = "route", setting = AppSettings::SubcommandRequiredElseHelp)]
Route(route::Route),
Expand Down
45 changes: 45 additions & 0 deletions src/cli/r2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use super::Cli;
use crate::commands;
use crate::settings::{global_user::GlobalUser, toml::Manifest};

use anyhow::Result;
use structopt::StructOpt;

#[derive(Debug, Clone, StructOpt)]
#[structopt(rename_all = "lower")]
pub enum R2 {
/// Interact with your Workers R2 Buckets
Bucket(Bucket),
}

#[derive(Debug, Clone, StructOpt)]
#[structopt(rename_all = "lower")]
pub enum Bucket {
/// List existing buckets
List,
/// Create a new bucket
Create {
/// The name for your new bucket
#[structopt(index = 1)]
name: String,
},
/// Delete an existing bucket
Delete {
/// The name of the bucket to delete
/// Note: bucket must be empty
#[structopt(index = 1)]
name: String,
},
}

pub fn r2_bucket(r2: R2, cli_params: &Cli) -> Result<()> {
let user = GlobalUser::new()?;
let manifest = Manifest::new(&cli_params.config)?;
let env = cli_params.environment.as_deref();

match r2 {
R2::Bucket(Bucket::List) => commands::r2::list(&manifest, env, &user),
R2::Bucket(Bucket::Create { name }) => commands::r2::create(&manifest, env, &user, &name),
R2::Bucket(Bucket::Delete { name }) => commands::r2::delete(&manifest, env, &user, &name),
}
}
1 change: 1 addition & 0 deletions src/commands/kv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ mod tests {
binding: "KV".to_string(),
},
],
r2_buckets: Vec::new(),
durable_objects: None,
migrations: None,
name: "test-target".to_string(),
Expand Down
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod login;
pub mod logout;
mod preview;
pub mod publish;
pub mod r2;
pub mod report;
pub mod route;
pub mod secret;
Expand Down
10 changes: 10 additions & 0 deletions src/commands/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,16 @@ fn validate_target_required_fields_present(target: &Target) -> Result<()> {
}
}

for r2 in &target.r2_buckets {
if r2.binding.is_empty() {
missing_fields.push("r2-bucket binding")
}

if r2.bucket_name.is_empty() {
missing_fields.push("r2-bucket bucket_name")
}
}

let (field_pluralization, is_are) = match missing_fields.len() {
n if n >= 2 => ("fields", "are"),
1 => ("field", "is"),
Expand Down
74 changes: 74 additions & 0 deletions src/commands/r2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use anyhow::Result;

use crate::http;
use crate::settings::global_user::GlobalUser;
use crate::settings::toml::Manifest;
use crate::terminal::message::{Message, StdOut};

use cloudflare::endpoints::r2::{CreateBucket, DeleteBucket, ListBuckets};
use cloudflare::framework::apiclient::ApiClient;

pub fn list(manifest: &Manifest, env: Option<&str>, user: &GlobalUser) -> Result<()> {
let account_id = manifest.get_account_id(env)?;
let client = http::cf_v4_client(user)?;
let result = client.request(&ListBuckets {
account_identifier: &account_id,
});

match result {
Ok(response) => {
let buckets: Vec<String> = response
.result
.buckets
.into_iter()
.map(|b| b.name)
.collect();
println!("{:?}", buckets);
}
Err(e) => println!("{}", e),
}

Ok(())
}

pub fn create(manifest: &Manifest, env: Option<&str>, user: &GlobalUser, name: &str) -> Result<()> {
let account_id = manifest.get_account_id(env)?;
let msg = format!("Creating bucket \"{}\"", name);
StdOut::working(&msg);

let client = http::cf_v4_client(user)?;
let result = client.request(&CreateBucket {
account_identifier: &account_id,
bucket_name: name,
});

match result {
Ok(_) => {
StdOut::success("Success!");
}
Err(e) => print!("{}", e),
}

Ok(())
}

pub fn delete(manifest: &Manifest, env: Option<&str>, user: &GlobalUser, name: &str) -> Result<()> {
let account_id = manifest.get_account_id(env)?;
let msg = format!("Deleting bucket \"{}\"", name);
StdOut::working(&msg);

let client = http::cf_v4_client(user)?;
let result = client.request(&DeleteBucket {
account_identifier: &account_id,
bucket_name: name,
});

match result {
Ok(_) => {
StdOut::success("Success!");
}
Err(e) => print!("{}", e),
}

Ok(())
}
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ fn run() -> Result<()> {
Command::Subdomain { name } => exec::subdomain(name, &cli_params),
Command::Route(route) => exec::route(route, &cli_params),
Command::Secret(secret) => exec::secret(secret, &cli_params),
Command::R2(r2) => exec::r2_bucket(r2, &cli_params),
Command::KvNamespace(namespace) => exec::kv_namespace(namespace, &cli_params),
Command::KvKey(key) => exec::kv_key(key, &cli_params),
Command::KvBulk(bulk) => exec::kv_bulk(bulk, &cli_params),
Expand Down
8 changes: 8 additions & 0 deletions src/settings/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ pub enum Binding {
name: String,
namespace_id: String,
},
R2Bucket {
name: String,
bucket_name: String,
},
#[serde(rename = "durable_object_namespace")]
DurableObjectsClass {
name: String,
Expand All @@ -37,6 +41,10 @@ impl Binding {
Binding::KvNamespace { name, namespace_id }
}

pub fn new_r2_bucket(name: String, bucket_name: String) -> Binding {
Binding::R2Bucket { name, bucket_name }
}

pub fn new_durable_object_namespace(
name: String,
class_name: String,
Expand Down
2 changes: 2 additions & 0 deletions src/settings/toml/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use serde_with::rust::string_empty_as_none;
use crate::settings::toml::builder::Builder;
use crate::settings::toml::durable_objects::DurableObjects;
use crate::settings::toml::kv_namespace::ConfigKvNamespace;
use crate::settings::toml::r2_bucket::ConfigR2Bucket;
use crate::settings::toml::route::RouteConfig;
use crate::settings::toml::site::Site;
use crate::settings::toml::triggers::Triggers;
Expand All @@ -28,6 +29,7 @@ pub struct Environment {
pub site: Option<Site>,
#[serde(alias = "kv-namespaces")]
pub kv_namespaces: Option<Vec<ConfigKvNamespace>>,
pub r2_buckets: Option<Vec<ConfigR2Bucket>>,
pub vars: Option<HashMap<String, String>>,
pub text_blobs: Option<HashMap<String, PathBuf>>,
pub triggers: Option<Triggers>,
Expand Down
37 changes: 37 additions & 0 deletions src/settings/toml/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::settings::toml::dev::Dev;
use crate::settings::toml::durable_objects::DurableObjects;
use crate::settings::toml::environment::Environment;
use crate::settings::toml::kv_namespace::{ConfigKvNamespace, KvNamespace};
use crate::settings::toml::r2_bucket::{ConfigR2Bucket, R2Bucket};
use crate::settings::toml::route::RouteConfig;
use crate::settings::toml::site::Site;
use crate::settings::toml::target_type::TargetType;
Expand Down Expand Up @@ -63,6 +64,7 @@ pub struct Manifest {
pub env: Option<HashMap<String, Environment>>,
#[serde(alias = "kv-namespaces")]
pub kv_namespaces: Option<Vec<ConfigKvNamespace>>,
pub r2_buckets: Option<Vec<ConfigR2Bucket>>,
// TODO: maybe one day, serde toml support will allow us to serialize sites
// as a TOML inline table (this would prevent confusion with environments too!)
pub site: Option<Site>,
Expand Down Expand Up @@ -374,6 +376,7 @@ impl Manifest {
// to include the name of the environment
name: self.name.clone(), // Inherited
kv_namespaces: get_namespaces(self.kv_namespaces.clone(), preview)?, // Not inherited
r2_buckets: get_buckets(self.r2_buckets.clone(), preview)?, // Not inherited
durable_objects: self.durable_objects.clone(), // Not inherited
migrations: match (preview, &self.migrations) {
(false, Some(migrations)) => Some(Migrations::List {
Expand Down Expand Up @@ -408,6 +411,9 @@ impl Manifest {
// don't inherit kv namespaces because it is an anti-pattern to use the same namespaces across multiple environments
target.kv_namespaces = get_namespaces(environment.kv_namespaces.clone(), preview)?;

// don't inherit r2 buckets because it is an anti-pattern to use the same buckets across multiple environments
target.r2_buckets = get_buckets(environment.r2_buckets.clone(), preview)?;

// don't inherit durable object configuration
target.durable_objects = environment.durable_objects.clone();

Expand Down Expand Up @@ -750,6 +756,37 @@ fn get_namespaces(
}
}

fn get_buckets(r2_buckets: Option<Vec<ConfigR2Bucket>>, preview: bool) -> Result<Vec<R2Bucket>> {
if let Some(buckets) = r2_buckets {
buckets.into_iter().map(|ns| {
if preview {
if let Some(preview_bucket_name) = &ns.preview_bucket_name {
if let Some(bucket_name) = &ns.bucket_name {
if preview_bucket_name == bucket_name {
StdOut::warn("Specifying the same r2 bucket_name for both preview and production sessions may cause bugs in your production worker! Proceed with caution.");
}
}
Ok(R2Bucket {
bucket_name: preview_bucket_name.to_string(),
binding: ns.binding.to_string(),
})
} else {
anyhow::bail!("In order to preview a worker with r2 buckets, you must designate a preview_bucket_name in your configuration file for each r2 bucket you'd like to preview.")
}
} else if let Some(bucket_name) = &ns.bucket_name {
Ok(R2Bucket {
bucket_name: bucket_name.to_string(),
binding: ns.binding,
})
} else {
anyhow::bail!("You must specify the bucket name in the bucket_name field for the bucket with binding \"{}\"", &ns.binding)
}
}).collect()
} else {
Ok(Vec::new())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 2 additions & 0 deletions src/settings/toml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod environment;
mod kv_namespace;
mod manifest;
pub mod migrations;
mod r2_bucket;
mod route;
mod site;
pub(crate) mod target;
Expand All @@ -15,6 +16,7 @@ pub use builder::{ModuleRule, UploadFormat};
pub use durable_objects::{DurableObjects, DurableObjectsClass};
pub use kv_namespace::{ConfigKvNamespace, KvNamespace};
pub use manifest::Manifest;
pub use r2_bucket::{ConfigR2Bucket, R2Bucket};
pub use route::{Route, RouteConfig};
pub use site::Site;
pub use target::Target;
Expand Down
34 changes: 34 additions & 0 deletions src/settings/toml/r2_bucket.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use std::fmt;

use serde::{Deserialize, Serialize};

use crate::settings::binding::Binding;

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct ConfigR2Bucket {
pub binding: String,
pub bucket_name: Option<String>,
pub preview_bucket_name: Option<String>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct R2Bucket {
pub binding: String,
pub bucket_name: String,
}

impl fmt::Display for R2Bucket {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"binding: {}, bucket_name: {}",
self.binding, self.bucket_name
)
}
}

impl R2Bucket {
pub fn binding(&self) -> Binding {
Binding::new_r2_bucket(self.binding.clone(), self.bucket_name.clone())
}
}
2 changes: 2 additions & 0 deletions src/settings/toml/target.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::durable_objects::DurableObjects;
use super::kv_namespace::KvNamespace;
pub(crate) use super::manifest::LazyAccountId;
use super::r2_bucket::R2Bucket;
use super::site::Site;
use super::target_type::TargetType;
use super::UsageModel;
Expand All @@ -15,6 +16,7 @@ use std::path::PathBuf;
pub struct Target {
pub account_id: LazyAccountId,
pub kv_namespaces: Vec<KvNamespace>,
pub r2_buckets: Vec<R2Bucket>,
pub durable_objects: Option<DurableObjects>,
pub migrations: Option<Migrations>,
pub name: String,
Expand Down
Loading