Skip to content

Commit

Permalink
feat: add a LocalStack module (#84)
Browse files Browse the repository at this point in the history
This PR adds a module for LocalStack (Community Edition).

Been using testcontainers for my own project, it's a huge help.

---------

Co-authored-by: Artem Medvedev <i@ddtkey.com>
  • Loading branch information
buildwithzephyr and DDtKey authored Jan 8, 2024
1 parent 0605654 commit c5efbc7
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ elastic_search = []
elasticmq = []
google_cloud_sdk_emulators = []
kafka = []
localstack = []
minio = []
mongo = []
mssql_server = []
Expand Down Expand Up @@ -67,6 +68,10 @@ retry = "2.0.0"
name = "postgres"
required-features = ["postgres"]

[[example]]
name = "localstack"
required-features = ["localstack"]

[[example]]
name = "neo4j"
required-features = ["neo4j"]
Expand Down
40 changes: 40 additions & 0 deletions examples/localstack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use aws_config::{meta::region::RegionProviderChain, BehaviorVersion};
use aws_sdk_s3 as s3;
use testcontainers::RunnableImage;
use testcontainers_modules::{localstack::LocalStack, testcontainers::clients::Cli};

#[tokio::main]
async fn main() -> Result<(), s3::Error> {
let docker = Cli::default();
let image: RunnableImage<LocalStack> = LocalStack::default().into();
let image = image.with_env_var(("SERVICES", "s3"));
let container = docker.run(image);
let host_port = container.get_host_port_ipv4(4566);

// Set up AWS client
let endpoint_url = format!("http://127.0.0.1:{host_port}");
let region_provider = RegionProviderChain::default_provider().or_else("us-east-1");
let creds = s3::config::Credentials::new("fake", "fake", None, None, "test");
let config = aws_config::defaults(BehaviorVersion::v2023_11_09())
.region(region_provider)
.credentials_provider(creds)
.endpoint_url(endpoint_url)
.load()
.await;

let client = s3::Client::new(&config);

client
.create_bucket()
.bucket("example-bucket")
.send()
.await?;

let list_buckets_output = client.list_buckets().send().await?;
assert!(list_buckets_output.buckets.is_some());
let buckets_list = list_buckets_output.buckets.unwrap();
assert_eq!(1, buckets_list.len());
assert_eq!("example-bucket", buckets_list[0].name.as_ref().unwrap());

Ok(())
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ pub mod google_cloud_sdk_emulators;
#[cfg(feature = "kafka")]
#[cfg_attr(docsrs, doc(cfg(feature = "kafka")))]
pub mod kafka;
#[cfg(feature = "localstack")]
#[cfg_attr(docsrs, doc(cfg(feature = "localstack")))]
pub mod localstack;
#[cfg(feature = "minio")]
#[cfg_attr(docsrs, doc(cfg(feature = "minio")))]
pub mod minio;
Expand Down
86 changes: 86 additions & 0 deletions src/localstack/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use testcontainers::{core::WaitFor, Image};

const NAME: &str = "localstack/localstack";
const TAG: &str = "3.0";
const DEFAULT_WAIT: u64 = 3000;

/// This module provides [LocalStack](https://www.localstack.cloud/) (Community Edition).
///
/// Currently pinned to [version `3.0`](https://hub.docker.com/layers/localstack/localstack/3.0/images/sha256-73698e485240939490134aadd7e429ac87ff068cd5ad09f5de8ccb76727c13e1?context=explore)
///
/// # Configuration
///
/// For configuration, LocalStack uses environment variables. You can go [here](https://docs.localstack.cloud/references/configuration/)
/// for the full list.
///
/// Testcontainers support setting environment variables with the method
/// `RunnableImage::with_env_var((impl Into<String>, impl Into<String>))`. You will have to convert
/// the Image into a RunnableImage first.
///
/// ```
/// use testcontainers_modules::localstack::LocalStack;
/// use testcontainers::RunnableImage;
///
/// let image: RunnableImage<LocalStack> = LocalStack::default().into();
/// let image = image.with_env_var(("SERVICES", "s3"));
/// ```
///
/// No environment variables are required.
#[derive(Default, Debug)]
pub struct LocalStack;

impl Image for LocalStack {
type Args = ();

fn name(&self) -> String {
NAME.to_owned()
}

fn tag(&self) -> String {
TAG.to_owned()
}

fn ready_conditions(&self) -> Vec<WaitFor> {
vec![
WaitFor::message_on_stdout("Ready."),
WaitFor::millis(DEFAULT_WAIT),
]
}
}

#[cfg(test)]
mod tests {
use super::LocalStack;
use aws_config::meta::region::RegionProviderChain;
use aws_config::BehaviorVersion;
use aws_sdk_sqs as sqs;
use testcontainers::clients;

#[tokio::test]
async fn create_and_list_queue() -> Result<(), sqs::Error> {
let docker = clients::Cli::default();
let node = docker.run(LocalStack::default());
let host_port = node.get_host_port_ipv4(4566);

let region_provider = RegionProviderChain::default_provider().or_else("us-east-1");
let creds = sqs::config::Credentials::new("fake", "fake", None, None, "test");
let config = aws_config::defaults(BehaviorVersion::v2023_11_09())
.region(region_provider)
.credentials_provider(creds)
.endpoint_url(format!("http://localhost:{}", host_port))
.load()
.await;
let client = sqs::Client::new(&config);

client
.create_queue()
.queue_name("example-queue")
.send()
.await?;

let list_result = client.list_queues().send().await?;
assert_eq!(list_result.queue_urls().len(), 1);

Ok(())
}
}

0 comments on commit c5efbc7

Please sign in to comment.