use super::*;
impl ResourceProvisioner {
pub(super) fn get_att_s3_bucket(&self, physical_id: &str, attribute: &str) -> Option<String> {
let mut accounts = self.s3_state.write();
let state = accounts.get_or_create(&self.account_id);
let bucket = state.buckets.get(physical_id)?;
match attribute {
"Arn" => Some(Arn::s3(&bucket.name).to_string()),
"DomainName" => Some(format!("{}.s3.amazonaws.com", bucket.name)),
"RegionalDomainName" => {
Some(format!("{}.s3.{}.amazonaws.com", bucket.name, self.region))
}
"DualStackDomainName" => Some(format!(
"{}.s3.dualstack.{}.amazonaws.com",
bucket.name, self.region
)),
"WebsiteURL" => Some(format!(
"http://{}.s3-website-{}.amazonaws.com",
bucket.name, self.region
)),
_ => None,
}
}
pub(super) fn create_s3_bucket(
&self,
resource: &ResourceDefinition,
) -> Result<ProvisionResult, String> {
let props = &resource.properties;
let bucket_name = props
.get("BucketName")
.and_then(|v| v.as_str())
.unwrap_or(&resource.logical_id);
let mut __s3_mas = self.s3_state.write();
let state = __s3_mas.get_or_create(&self.account_id);
let region = state.region.clone();
let bucket = S3Bucket::new(bucket_name, &state.region, &state.account_id);
let meta = bucket_meta_snapshot(&bucket);
state.buckets.insert(bucket_name.to_string(), bucket);
self.s3_store
.put_bucket_meta(bucket_name, &meta)
.map_err(|e| format!("failed to persist bucket {bucket_name}: {e}"))?;
let arn = Arn::s3(bucket_name).to_string();
let domain_name = format!("{bucket_name}.s3.amazonaws.com");
let regional_domain_name = format!("{bucket_name}.s3.{region}.amazonaws.com");
let dual_stack_domain_name = format!("{bucket_name}.s3.dualstack.{region}.amazonaws.com");
let website_url = format!("http://{bucket_name}.s3-website-{region}.amazonaws.com");
Ok(ProvisionResult::new(bucket_name)
.with("Arn", arn)
.with("DomainName", domain_name)
.with("RegionalDomainName", regional_domain_name)
.with("DualStackDomainName", dual_stack_domain_name)
.with("WebsiteURL", website_url))
}
pub(super) fn delete_s3_bucket(&self, physical_id: &str) -> Result<(), String> {
let mut __s3_mas = self.s3_state.write();
let state = __s3_mas.get_or_create(&self.account_id);
state.buckets.remove(physical_id);
self.s3_store
.delete_bucket(physical_id)
.map_err(|e| format!("failed to remove bucket {physical_id}: {e}"))?;
Ok(())
}
pub(super) fn create_s3_bucket_policy(
&self,
resource: &ResourceDefinition,
) -> Result<ProvisionResult, String> {
let bucket_name = s3_policy_bucket_name(&resource.properties)?;
let policy = policy_document_string(&resource.properties)?;
let mut __s3_mas = self.s3_state.write();
let state = __s3_mas.get_or_create(&self.account_id);
let bucket = state
.buckets
.get_mut(&bucket_name)
.ok_or_else(|| format!("Bucket {bucket_name} not yet provisioned"))?;
bucket.policy = Some(policy.clone());
self.s3_store
.put_bucket_subresource(&bucket_name, BucketSubresource::Policy, &policy)
.map_err(|e| format!("failed to persist bucket policy for {bucket_name}: {e}"))?;
Ok(ProvisionResult::new(format!("{bucket_name}-policy")))
}
pub(super) fn update_s3_bucket_policy(
&self,
existing: &StackResource,
resource: &ResourceDefinition,
) -> Result<ProvisionResult, String> {
let bucket_name = s3_policy_bucket_name(&resource.properties)?;
let policy = policy_document_string(&resource.properties)?;
let mut __s3_mas = self.s3_state.write();
let state = __s3_mas.get_or_create(&self.account_id);
let old_bucket = existing
.physical_id
.strip_suffix("-policy")
.unwrap_or(&existing.physical_id);
if old_bucket != bucket_name {
if let Some(bucket) = state.buckets.get_mut(old_bucket) {
bucket.policy = None;
}
self.s3_store
.delete_bucket_subresource(old_bucket, BucketSubresource::Policy)
.map_err(|e| format!("failed to clear bucket policy for {old_bucket}: {e}"))?;
}
let bucket = state
.buckets
.get_mut(&bucket_name)
.ok_or_else(|| format!("Bucket {bucket_name} not yet provisioned"))?;
bucket.policy = Some(policy.clone());
self.s3_store
.put_bucket_subresource(&bucket_name, BucketSubresource::Policy, &policy)
.map_err(|e| format!("failed to persist bucket policy for {bucket_name}: {e}"))?;
Ok(ProvisionResult::new(format!("{bucket_name}-policy")))
}
pub(super) fn delete_s3_bucket_policy(&self, physical_id: &str) -> Result<(), String> {
let bucket_name = physical_id.strip_suffix("-policy").unwrap_or(physical_id);
let mut __s3_mas = self.s3_state.write();
let state = __s3_mas.get_or_create(&self.account_id);
if let Some(bucket) = state.buckets.get_mut(bucket_name) {
bucket.policy = None;
}
self.s3_store
.delete_bucket_subresource(bucket_name, BucketSubresource::Policy)
.map_err(|e| format!("failed to remove bucket policy for {bucket_name}: {e}"))?;
Ok(())
}
}
fn s3_policy_bucket_name(props: &serde_json::Value) -> Result<String, String> {
props
.get("Bucket")
.and_then(|v| v.as_str())
.map(String::from)
.ok_or_else(|| "Bucket is required".to_string())
}