Cloud Providers Guide
Armature provides unified cloud provider integrations for AWS, GCP, and Azure with dynamic service loading and dependency injection.
Table of Contents
- Overview
- Features
- AWS Integration
- GCP Integration
- Azure Integration
- Dependency Injection
- Best Practices
- Summary
Overview
The cloud provider crates (armature-aws, armature-gcp, armature-azure) provide:
- Dynamic Service Loading: Only compile and load the services you need
- Feature Flags: Fine-grained control over dependencies
- Lazy Initialization: Services are created on-demand
- DI Integration: Register cloud services in your application's DI container
- Unified Configuration: Environment-based and programmatic configuration
Features
- โ AWS: S3, DynamoDB, SQS, SNS, SES, Lambda, Secrets Manager, KMS, Cognito, and more
- โ GCP: Cloud Storage, Pub/Sub, Firestore, Spanner, BigQuery
- โ Azure: Blob Storage, Queue Storage, Cosmos DB, Service Bus, Key Vault
- โ Dynamic service loading based on configuration
- โ Feature flags for each service
- โ DI container integration
AWS Integration
Installation
Add to your Cargo.toml:
[dependencies]
armature-aws = { version = "0.1", features = ["s3", "dynamodb", "sqs"] }
Available Features
| Feature | Services |
|---|---|
s3 |
S3 Object Storage |
dynamodb |
DynamoDB NoSQL Database |
sqs |
Simple Queue Service |
sns |
Simple Notification Service |
ses |
Simple Email Service |
lambda |
Lambda Functions |
secrets-manager |
Secrets Manager |
ssm |
Systems Manager Parameter Store |
cloudwatch |
CloudWatch Metrics/Logs |
kinesis |
Kinesis Data Streams |
kms |
Key Management Service |
cognito |
Cognito User Pools |
storage |
S3 + DynamoDB |
messaging |
SQS + SNS + Kinesis |
all |
All services |
Basic Usage
use armature_aws::{AwsServices, AwsConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Configure services
let config = AwsConfig::builder()
.region("us-east-1")
.enable_s3()
.enable_dynamodb()
.build();
// Initialize (lazy - clients created on first access)
let services = AwsServices::new(config).await?;
// Use S3
let s3 = services.s3()?;
let buckets = s3.list_buckets().send().await?;
println!("Buckets: {:?}", buckets);
// Use DynamoDB
let dynamo = services.dynamodb()?;
let tables = dynamo.list_tables().send().await?;
println!("Tables: {:?}", tables);
Ok(())
}
Environment Configuration
// Reads AWS_REGION, AWS_DEFAULT_REGION, AWS_ENDPOINT_URL
let config = AwsConfig::from_env()
.enable_s3()
.enable_sqs()
.build();
LocalStack Support
let config = AwsConfig::builder()
.region("us-east-1")
.localstack() // Sets endpoint to http://localhost:4566
.enable_s3()
.build();
GCP Integration
Installation
[dependencies]
armature-gcp = { version = "0.1", features = ["storage", "pubsub"] }
Available Features
| Feature | Services |
|---|---|
storage |
Cloud Storage |
pubsub |
Pub/Sub Messaging |
firestore |
Firestore Database |
spanner |
Cloud Spanner |
bigquery |
BigQuery Analytics |
secret-manager |
Secret Manager |
data |
Storage + Firestore + Spanner + BigQuery |
all |
All services |
Basic Usage
use armature_gcp::{GcpServices, GcpConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = GcpConfig::builder()
.project_id("my-project")
.enable_storage()
.enable_pubsub()
.build();
let services = GcpServices::new(config).await?;
// Use Cloud Storage
let storage = services.storage()?;
// Use Pub/Sub
let pubsub = services.pubsub()?;
Ok(())
}
Environment Configuration
// Reads GOOGLE_CLOUD_PROJECT, GOOGLE_APPLICATION_CREDENTIALS
let config = GcpConfig::from_env()
.enable_storage()
.build();
Azure Integration
Installation
[dependencies]
armature-azure = { version = "0.1", features = ["blob", "cosmos"] }
Available Features
| Feature | Services |
|---|---|
blob |
Blob Storage |
queue |
Queue Storage |
cosmos |
Cosmos DB |
servicebus |
Service Bus |
keyvault |
Key Vault |
storage |
Blob + Queue |
all |
All services |
Basic Usage
use armature_azure::{AzureServices, AzureConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = AzureConfig::builder()
.storage_account("mystorageaccount")
.enable_blob()
.enable_cosmos()
.cosmos_endpoint("https://mydb.documents.azure.com:443/")
.build();
let services = AzureServices::new(config).await?;
// Use Blob Storage
let blob = services.blob_service()?;
// Use Cosmos DB
let cosmos = services.cosmos()?;
Ok(())
}
Environment Configuration
// Reads AZURE_STORAGE_ACCOUNT, AZURE_COSMOS_ENDPOINT, etc.
let config = AzureConfig::from_env()
.enable_blob()
.build();
Dependency Injection
The cloud provider crates integrate seamlessly with Armature's DI system.
Registering Cloud Services
use armature_framework::prelude::*;
use armature_aws::{AwsServices, AwsConfig};
#[module]
struct CloudModule;
#[module_impl]
impl CloudModule {
// Register AWS services as a singleton
#[provider(singleton)]
async fn aws_services() -> Arc<AwsServices> {
let config = AwsConfig::from_env()
.enable_s3()
.enable_sqs()
.enable_dynamodb()
.build();
AwsServices::new(config).await.unwrap()
}
// Expose individual clients from the service container
#[provider]
fn s3_client(services: &Arc<AwsServices>) -> aws_sdk_s3::Client {
services.s3().unwrap()
}
#[provider]
fn sqs_client(services: &Arc<AwsServices>) -> aws_sdk_sqs::Client {
services.sqs().unwrap()
}
#[provider]
fn dynamodb_client(services: &Arc<AwsServices>) -> aws_sdk_dynamodb::Client {
services.dynamodb().unwrap()
}
}
Using in Controllers
#[controller("/files")]
struct FileController;
#[controller_impl]
impl FileController {
#[post("/upload")]
async fn upload(
&self,
#[inject] s3: aws_sdk_s3::Client,
body: Bytes,
) -> Result<Json<UploadResponse>, HttpError> {
s3.put_object()
.bucket("my-bucket")
.key("uploaded-file")
.body(body.into())
.send()
.await
.map_err(|e| HttpError::internal(e.to_string()))?;
Ok(Json(UploadResponse { success: true }))
}
}
Multi-Cloud Setup
#[module]
struct MultiCloudModule;
#[module_impl]
impl MultiCloudModule {
#[provider(singleton)]
async fn aws() -> Arc<AwsServices> {
let config = AwsConfig::from_env()
.enable_s3()
.build();
AwsServices::new(config).await.unwrap()
}
#[provider(singleton)]
async fn gcp() -> Arc<GcpServices> {
let config = GcpConfig::from_env()
.enable_storage()
.build();
GcpServices::new(config).await.unwrap()
}
#[provider(singleton)]
async fn azure() -> Arc<AzureServices> {
let config = AzureConfig::from_env()
.enable_blob()
.build();
AzureServices::new(config).await.unwrap()
}
}
Using with Other Armature Crates
Other Armature crates can depend on cloud providers through DI:
// In armature-storage, use S3 from armature-aws
use armature_aws::aws_sdk_s3;
pub struct S3StorageBackend {
client: aws_sdk_s3::Client,
bucket: String,
}
impl S3StorageBackend {
// Client injected from DI container
pub fn new(client: aws_sdk_s3::Client, bucket: String) -> Self {
Self { client, bucket }
}
}
// In your application module
#[module_impl]
impl StorageModule {
#[provider]
fn s3_backend(
client: aws_sdk_s3::Client,
) -> S3StorageBackend {
S3StorageBackend::new(client, "my-bucket".to_string())
}
}
Best Practices
1. Use Feature Flags
Only enable the services you need to minimize compile times and binary size:
# Good - only what you need
armature-aws = { version = "0.1", features = ["s3", "sqs"] }
# Avoid unless you need everything
armature-aws = { version = "0.1", features = ["all"] }
2. Use Environment Configuration
// Prefer environment-based config for flexibility
let config = AwsConfig::from_env()
.enable_s3()
.build();
3. Register Services as Singletons
#[provider(singleton)] // Important!
async fn aws_services() -> Arc<AwsServices> {
// ...
}
4. Handle Missing Services Gracefully
match services.s3() {
Ok(client) => { /* use client */ }
Err(AwsError::ServiceNotConfigured(_)) => {
tracing::warn!("S3 not configured, skipping");
}
Err(e) => return Err(e.into()),
}
5. Use Local Emulators for Testing
#[cfg(test)]
let config = AwsConfig::builder()
.localstack()
.enable_s3()
.build();
#[cfg(test)]
let config = AzureConfig::builder()
.use_emulator()
.enable_blob()
.build();
Summary
Key Concepts:
- Dynamic Loading: Services compile and initialize only when enabled
- Feature Flags: Fine-grained dependency control
- DI Integration: Cloud services live in the application DI container
- Lazy Initialization: Clients created on first access
- Environment Config: Reads from standard cloud environment variables
Crate Feature Groups:
| Crate | Storage | Messaging | Security |
|---|---|---|---|
| AWS | storage |
messaging |
security |
| GCP | data |
messaging |
- |
| Azure | storage |
messaging |
security |