Design¶
Goals¶
Converge an S3 bucket to a desired spec for the supported features:
tags
versioning
public access block
Stay declarative: Desired → Current → Plan → Apply → Result
Keep the module primitive and composable: no “SDK wrapper” surface
Future scope (not implemented yet): encryption, lifecycle rules, bucket policy templates (e.g., TLS-only), CloudFront-origin/OAC policy convergence.
Architecture¶
The purpose of this module is to provide a convergent management interface to Amazon S3 buckets.
We do not expose the full boto3 surface area. Instead, we converge a pragmatic subset of bucket features that are commonly needed by dxaws primitives. This keeps the public contract small, testable, and stable.
Declarative Convergence¶
This module follows a declarative convergence model.
Instead of issuing imperative AWS calls directly, callers describe the desired state. The module then:
Plans what needs to change to reach that state
Applies the plan using provider-backed AWS operations
This separation makes behavior predictable, testable, and idempotent, while keeping AWS-specific logic isolated in providers and making plans easy to unit test.
Key Concepts¶
Providers¶
Providers encapsulate AWS SDK (boto3) calls. The rest of the module treats the provider as an interface so that planning/execution can be unit tested without AWS.
Manager (public facade)¶
manager.py is the public entrypoint. It:
Validates the desired state
Reads the current state from the provider
Calls the planner to compute a plan
Applies the plan via the executor
Returns a result object
Planner / Executor¶
planner.py computes a deterministic plan (a list of actions) by comparing desired vs current state.
executor.py applies the plan using provider-backed operations.
planner.py¶
Planner rules are deterministic and include purpose-driven invariants:
If bucket doesn’t exist →
CREATE_BUCKETTags drift →
PUT_TAGSPublic access block drift →
PUT_PUBLIC_ACCESS_BLOCKVersioning drift →
PUT_VERSIONING
These four rules define the current contract that higher-level modules can rely on.
Future planner rules (not implemented yet)¶
Encryption drift →
PUT_ENCRYPTIONBucket policy convergence:
TLS-only deny statement
CloudFront OAC allow-read statement
Lifecycle rules / intelligent-tiering / replication templates
executor.py¶
The executor applies actions in a safe order:
Create bucket (if needed)
Public access block
Versioning
Tags
Returns S3BucketResult from a provider read-back.
providers/base.py (interface)¶
boto3 methods that will be encapsulated:
get_current(name) -> S3BucketCurrentcreate_bucket(desired)put_tags(name, tags)put_public_access_block(name, config)put_versioning(name, enabled: bool)destroy_bucket(name)get_outputs(name) -> S3BucketResult
AWS implementation in providers/aws.py uses boto3 S3.
constants.py / exceptions.py¶
Defaults:
DEFAULT_ENCRYPTION = "SSE-S3"DEFAULT_BLOCK_PUBLIC_ACCESS = TrueDEFAULT_ENFORCE_TLS = True
Exceptions:
InvalidDesiredStateErrorBucketNameErrorMissingCloudFrontInfoError(only if strict mode is enabled)
CLI Surface¶
The CLI orchestration for S3 lives in dxaws-cli.
This module remains Python-first and focuses on providing a stable manager/provider contract that higher-level tooling can call.
Tradeoffs¶
Eventual Consistency¶
Some S3 operations (especially create/delete and feature reads immediately after) can be eventually consistent.
Acceptance tests intentionally include:
a visibility wait after create
drift + converge checks
a delete waiter with small grace delays
This keeps the contract stable while acknowledging AWS propagation behavior.
CloudFront / Policy Convergence¶
CloudFront-origin and policy convergence (TLS-only, OAC statements, etc.) are intentionally deferred. The current module stays focused on the primitive features we already rely on.