Skip to content

Commit

Permalink
chore: switch from rusoto to aws-sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
connec committed Oct 30, 2022
1 parent 185cd7f commit e89e4a0
Show file tree
Hide file tree
Showing 9 changed files with 773 additions and 633 deletions.
1,228 changes: 698 additions & 530 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ path = "src/main.rs"
[dependencies]
async_zip = { version = "0.0.9", default-features = false, features = ["deflate"] }
atty = "0.2.14"
aws_sso_flow = { version = "0.2.0", default-features = false, features = ["native-tls", "rusoto"] }
aws-config = { version = "0.49.0", default-features = false, features = ["client-hyper", "native-tls", "rt-tokio"] }
aws-sdk-s3 = { version = "0.19", default-features = false, features = ["native-tls", "rt-tokio"] }
aws-types = "0.49.0"
aws_sso_flow = { version = "0.3.1", default-features = false, features = ["aws-sdk", "native-tls"] }
base64 = "0.13.0"
chrono = { version = "0.4.22", default-features = false }
clap = { version = "3.2.20", features = ["derive", "env"] }
clap_complete = "3.2.4"
cloudformatious = "0.1.0"
cloudformatious = "0.2.0"
colored = "2.0.0"
futures-util = "0.3.24"
hyper = { version = "0.14.20", features = ["stream"] }
md5 = "0.7.0"
rusoto_cloudformation = "0.46.0"
rusoto_core = "0.46.0"
rusoto_credential = "0.46.0"
rusoto_s3 = "0.46.0"
serde_json = "1.0.85"
serde_yaml = "0.9.13"
tempfile = "3.3.0"
tokio = { version = "1.21.0", features = ["fs", "io-std", "io-util", "macros"] }
tokio = { version = "1.21.0", features = ["fs", "io-std", "io-util", "macros", "rt-multi-thread"] }
tokio-util = { version = "0.7.4", features = ["codec"] }
79 changes: 26 additions & 53 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,32 @@
use std::{env, fmt, time::Duration};

use rusoto_core::{HttpClient, Region};
use rusoto_credential::{
AutoRefreshingProvider, ChainProvider, ProfileProvider, ProvideAwsCredentials as _,
};

use crate::Error;

const MISSING_REGION: &str = "Unable CompletionsArgs, DeleteStack
You can set it in your profile, assign `AWS_REGION`, or supply `--region`.";

pub async fn get_client<C>(
ctor: impl FnOnce(HttpClient, AutoRefreshingProvider<aws_sso_flow::ChainProvider>, Region) -> C,
region: Option<Region>,
) -> Result<C, Error> {
let region = region
.map(Ok)
.or_else(get_region)
.ok_or_else(|| Error::other(MISSING_REGION))?
.map_err(Error::other)?;
let client = HttpClient::new().map_err(Error::other)?;

let mut credentials = ChainProvider::new();
credentials.set_timeout(Duration::from_secs(1));

let credentials =
AutoRefreshingProvider::new(aws_sso_flow::ChainProvider::new().push(credentials).push(
aws_sso_flow::SsoFlow::builder().verification_prompt(|url| async move {
if atty::is(atty::Stream::Stdin) && atty::is(atty::Stream::Stdout) {
eprintln!("Using SSO an profile – go to {url} to authenticate");
Ok(())
} else {
Err(NonInteractiveSsoError)
}
}),
))
.map_err(Error::other)?;

// Proactively fetch credentials so we get earlier errors.
credentials.credentials().await.map_err(Error::other)?;
use std::fmt;

use aws_config::{meta::credentials::CredentialsProviderChain, SdkConfig};
use aws_types::region::Region;

pub async fn get_config(region: Option<Region>) -> SdkConfig {
let sso = aws_sso_flow::SsoFlow::builder().verification_prompt(|url| async move {
if atty::is(atty::Stream::Stdin) && atty::is(atty::Stream::Stdout) {
eprintln!("Using SSO an profile – go to {url} to authenticate");
Ok(())
} else {
Err(NonInteractiveSsoError)
}
});
let credentials = CredentialsProviderChain::first_try("SsoFlow", sso)
.or_default_provider()
.await;

let mut config = aws_config::from_env().credentials_provider(credentials);
if let Some(region) = region {
config = config.region(region);
}

Ok(ctor(client, credentials, region))
config.load().await
}

fn get_region() -> Option<Result<Region, Box<dyn std::error::Error>>> {
// rusoto_cloudformation::Region::default implements a similar algorithm but falls back to
// us-east-1, which we don't want.
match env::var("AWS_DEFAULT_REGION").or_else(|_| env::var("AWS_REGION")) {
Ok(region) => Some(region.parse().map_err(Into::into)),
Err(_) => match ProfileProvider::region() {
Ok(Some(region)) => Some(region.parse().map_err(Into::into)),
Ok(None) => None,
Err(error) => Some(Err(error.into())),
},
}
pub async fn get_client<C>(ctor: impl FnOnce(&SdkConfig) -> C, region: Option<Region>) -> C {
let config = get_config(region).await;
ctor(&config)
}

#[derive(Debug)]
Expand Down
2 changes: 1 addition & 1 deletion src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod completions;
mod delete_stack;
mod package;

use rusoto_core::Region;
use aws_types::region::Region;

use crate::Error;

Expand Down
11 changes: 6 additions & 5 deletions src/command/apply_stack.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::{collections::HashMap, convert::TryInto, ffi::OsStr, fmt, path::PathBuf, str::FromStr};

use aws_types::region::Region;
use cloudformatious::{
ApplyStackError, ApplyStackInput, Capability, CloudFormatious as _, Parameter, TemplateSource,
self, ApplyStackError, ApplyStackInput, Capability, Parameter, Tag, TemplateSource,
};
use rusoto_cloudformation::{CloudFormationClient, Tag};
use rusoto_core::Region;

use crate::{
client::get_config,
fmt::{print_events, Sizing},
package, s3, template, Error, Template,
};
Expand Down Expand Up @@ -145,7 +145,8 @@ pub async fn main(region: Option<Region>, args: Args) -> Result<(), Error> {
let mut template = Template::open(args.template_path.clone()).await?;
preprocess(region.as_ref(), &args, &mut template).await?;

let client = crate::get_client(CloudFormationClient::new_with, region).await?;
let config = get_config(region).await;
let client = cloudformatious::Client::new(&config);
let mut apply = client.apply_stack(args.into_input(&template));

let change_set = apply.change_set().await.map_err(Error::other)?;
Expand Down Expand Up @@ -200,7 +201,7 @@ async fn preprocess(
)));
};

let client = s3::Client::new(region.cloned()).await?;
let client = s3::Client::new(region.cloned()).await;

package::process(
&client,
Expand Down
10 changes: 5 additions & 5 deletions src/command/delete_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ use std::{
path::PathBuf,
};

use cloudformatious::{CloudFormatious as _, DeleteStackError, DeleteStackInput};
use rusoto_cloudformation::CloudFormationClient;
use rusoto_core::Region;
use aws_types::region::Region;
use cloudformatious::{self, DeleteStackError, DeleteStackInput};

use crate::{
client::get_client,
client::get_config,
fmt::{print_events, Sizing},
Error,
};
Expand Down Expand Up @@ -101,7 +100,8 @@ impl TryFrom<Args> for DeleteStackInput {
pub async fn main(region: Option<Region>, args: Args) -> Result<(), Error> {
let quiet = args.quiet;

let client = get_client(CloudFormationClient::new_with, region).await?;
let config = get_config(region).await;
let client = cloudformatious::Client::new(&config);
let mut delete = client.delete_stack(args.try_into()?);
let sizing = Sizing::default();

Expand Down
4 changes: 2 additions & 2 deletions src/command/package.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::path::PathBuf;

use rusoto_core::Region;
use aws_types::region::Region;

use crate::{package, s3, Error, Template};

Expand Down Expand Up @@ -30,7 +30,7 @@ pub struct Args {
}

pub async fn main(region: Option<Region>, args: Args) -> Result<(), Error> {
let client = s3::Client::new(region).await?;
let client = s3::Client::new(region).await;
let mut template = Template::open(args.template_path).await?;

let targets = package::targets(&mut template);
Expand Down
12 changes: 8 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ mod package;
mod s3;
mod template;

use std::process;
use std::{convert::Infallible, process};

use aws_types::region::Region;
use clap::Parser;
use rusoto_core::Region;

use self::{client::get_client, error::Error, template::Template};
use self::{error::Error, template::Template};

/// A CloudFormation CLI that won't make you cry.
///
Expand All @@ -23,7 +23,7 @@ use self::{client::get_client, error::Error, template::Template};
#[clap(name = "cloudformatious")]
struct Args {
/// The region to use. Overrides config/env settings.
#[clap(long, env = "AWS_REGION")]
#[clap(long, env = "AWS_REGION", value_parser = parse_region)]
region: Option<Region>,

#[clap(subcommand)]
Expand All @@ -43,3 +43,7 @@ async fn main() {
});
}
}

fn parse_region(region: &str) -> Result<Region, Infallible> {
Ok(Region::new(region.to_owned()))
}
46 changes: 20 additions & 26 deletions src/s3.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::{convert::TryInto, path::Path};

use aws_types::region::Region;
use futures_util::{StreamExt, TryStreamExt};
use rusoto_core::Region;
use rusoto_s3::{HeadObjectRequest, PutObjectRequest, S3Client, S3};
use tokio::{
fs::File,
io::{AsyncSeekExt, BufReader},
Expand All @@ -12,13 +11,13 @@ use tokio_util::codec::{BytesCodec, FramedRead};
use crate::{client::get_client, Error};

pub struct Client {
inner: rusoto_s3::S3Client,
inner: aws_sdk_s3::Client,
}

impl Client {
pub async fn new(region: Option<Region>) -> Result<Self, Error> {
let inner = get_client(S3Client::new_with, region).await?;
Ok(Self { inner })
pub async fn new(region: Option<Region>) -> Self {
let inner = get_client(aws_sdk_s3::Client::new, region).await;
Self { inner }
}

pub async fn upload<'a>(&self, request: UploadRequest<'a>) -> Result<UploadOutput, Error> {
Expand Down Expand Up @@ -47,18 +46,17 @@ impl Client {

let exists = self
.inner
.head_object(HeadObjectRequest {
bucket: request.bucket.to_owned(),
key: key.clone(),
..Default::default()
})
.head_object()
.bucket(request.bucket)
.key(&key)
.send()
.await
.map(|_| true)
.or_else({
let bucket = &request.bucket;
let key = &key;
move |error| match error {
rusoto_core::RusotoError::Unknown(res) if res.status.as_u16() == 404 => {
aws_sdk_s3::types::SdkError::ServiceError { err, .. } if err.is_not_found() => {
Ok(false)
}
error => Err(Error::other(format!(
Expand All @@ -75,21 +73,17 @@ impl Client {
.await
.map_err(|error| Error::other(format!("couldn't read upload package: {error}")))?;

let body =
hyper::Body::wrap_stream(FramedRead::new(BufReader::new(file), BytesCodec::new()));

self.inner
.put_object(PutObjectRequest {
body: Some(rusoto_s3::StreamingBody::new_with_size(
FramedRead::new(BufReader::new(file), BytesCodec::new())
.map_ok(|chunk| chunk.freeze()),
meta.len()
.try_into()
.expect("file is too large for platform"),
)),
bucket: request.bucket.to_owned(),
content_length: Some(meta.len().try_into().expect("file is insanely large")),
content_md5: Some(base64::encode(&content_md5.0)),
key: key.clone(),
..Default::default()
})
.put_object()
.body(body.into())
.bucket(request.bucket)
.content_length(meta.len().try_into().expect("file is insanely large"))
.content_md5(base64::encode(&content_md5.0))
.key(&key)
.send()
.await
.map_err(|error| {
Error::other(format!(
Expand Down

0 comments on commit e89e4a0

Please sign in to comment.