Skip to content

fix(cd): adopt an existing CD state bucket instead of creating a new one#2194

Merged
lionello merged 4 commits into
DefangLabs:mainfrom
defangdevs:fix/codebuild-cd-reuse-bucket
Jul 22, 2026
Merged

fix(cd): adopt an existing CD state bucket instead of creating a new one#2194
lionello merged 4 commits into
DefangLabs:mainfrom
defangdevs:fix/codebuild-cd-reuse-bucket

Conversation

@defangdevs

@defangdevs defangdevs commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Problem

The CodeBuild CD driver (src/pkg/clouds/aws/codebuild/cfn/) always creates a stack-managed, CFN-auto-named S3 bucket for Pulumi state, with RetainExceptOnCreate. Because the bucket is retained on stack delete but named per-stack-instance:

  • defang cd teardown deletes the defang-cd stack → the bucket is retained (orphaned);
  • the next connect/bootstrap creates a fresh stack → CFN mints a new defang-cd-bucket-* and the old one (plus its Pulumi state) is stranded.

Repeated over time this leaves many orphaned buckets in an account (observed: 11 defang-cd-bucket-* in one account, only 3 backed by a live stack). This is the same problem as #358, which #422 fixed for the old ECS CD — but that logic (findExistingBucket + a skip-bucket template override) did not survive the ECS→CodeBuild CD rewrite.

Fix

Before creating the stack, look for a retained CD bucket in the same account+region — buckets named <stack>-bucket-* carrying the defang:Prefix tag equal to the stack name. If exactly one exists, generate the template without a managed Bucket resource and emit that bucket as the bucketName output; otherwise create a managed bucket as before. >1 match errors rather than guessing.

RetainExceptOnCreate makes the one-time managed→adopted transition safe: when the Bucket resource leaves the template on the next update, CloudFormation retains the physical bucket, and the stack now simply references it.

  • template.go: CreateTemplate(stack, existingBucket string) — skips the managed bucket and outputs the existing name when adopting.
  • setup.go: findAdoptableBucket() (ListBuckets → prefix + region + defang:Prefix tag) wired into SetUp.
  • The published/quick-create template (byoc/aws/byoc.go) is unchanged — it always creates a managed bucket (existingBucket == "").

Tests / verification

  • New TestFindAdoptableBucket (mocked S3): none / one-match / wrong-region / wrong-tag / ambiguous.
  • New TestCreateTemplateBucketAdoption: managed variant creates the bucket; adopt variant has no Bucket resource and outputs the existing name.
  • Golden template test unchanged (managed path is byte-identical).
  • go build ./..., go test -short ./pkg/clouds/aws/codebuild/... ./pkg/cli/client/byoc/aws/..., go vet, gofmt, and golangci-lint on the changed packages all pass (no new findings).

Notes for reviewers

Fixes #2192. Re-fixes #358 for the CodeBuild CD (regressed since #422).

Summary by CodeRabbit

  • New Features

    • Deployment setup can now reuse eligible retained AWS S3 state buckets.
    • Existing buckets are validated by region and ownership tags before adoption.
    • CloudFormation templates omit creation of a new bucket when an eligible bucket is reused.
  • Bug Fixes

    • Ambiguous or incorrectly matched buckets are rejected to prevent unintended adoption.
  • Tests

    • Added coverage for bucket discovery, validation, adoption, and rejection scenarios.

The CodeBuild CD (codebuild/cfn) always created a stack-managed, CFN
auto-named S3 bucket with RetainExceptOnCreate. So `defang cd teardown`
retained the bucket and the next bootstrap minted a fresh one, orphaning
the old bucket (and its Pulumi state) on every teardown/re-bootstrap
cycle. This re-introduced DefangLabs#358, which DefangLabs#422 had fixed for the old ECS CD
before the CodeBuild rewrite dropped the logic.

Before creating the stack, look for a retained `<stack>-bucket-*` in the
same account+region (matched by the defang:Prefix tag). If exactly one
exists, generate the template without a managed bucket and emit the
existing bucket name as the bucketName output; RetainExceptOnCreate makes
the one-time managed->adopted transition safe (the physical bucket is
retained when the resource leaves the template). Ambiguous (>1) matches
error rather than guess.

Adds unit tests for findAdoptableBucket and the template bucket-adoption
branch. The published/quick-create template is unchanged (managed bucket).

Fixes DefangLabs#2192. Re-fixes DefangLabs#358 for the CodeBuild CD (regressed since DefangLabs#422).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sw7j9FYnbChZJbYArxbhuo
@defangdevs
defangdevs requested a review from lionello as a code owner July 22, 2026 10:29
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@defangdevs, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b51fe615-eba4-4071-a3f6-0cd60e39c520

📥 Commits

Reviewing files that changed from the base of the PR and between b5b1c3c and d129633.

📒 Files selected for processing (6)
  • src/cmd/cli/command/cd.go
  • src/cmd/cli/command/commands.go
  • src/pkg/cli/client/byoc/aws/byoc.go
  • src/pkg/cli/client/byoc/do/byoc.go
  • src/pkg/cli/client/byoc/gcp/byoc.go
  • src/pkg/clouds/aws/codebuild/cfn/setup.go
📝 Walkthrough

Walkthrough

Changes

Retained bucket adoption

Layer / File(s) Summary
Conditional bucket template generation
src/pkg/clouds/aws/codebuild/cfn/template.go, src/pkg/clouds/aws/codebuild/cfn/template_test.go, src/pkg/cli/client/byoc/aws/byoc.go
CreateTemplate accepts an optional existing bucket, omits the managed bucket resource when provided, and reports the selected bucket in outputs. Callers and tests pass the new argument.
Bucket discovery and setup reuse
src/pkg/clouds/aws/codebuild/cfn/setup.go, src/pkg/clouds/aws/codebuild/cfn/bucket_test.go
Setup finds buckets matching the prefix, region, and stack tag, rejects ambiguous matches, and reuses a valid bucket. Table-driven tests cover these cases.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AwsCfn.SetUp
  participant S3
  participant findAdoptableBucket
  participant CreateTemplate
  AwsCfn.SetUp->>S3: List CD state buckets
  S3-->>findAdoptableBucket: Return locations and tags
  findAdoptableBucket-->>AwsCfn.SetUp: Return eligible bucket
  AwsCfn.SetUp->>CreateTemplate: Pass existing bucket
  CreateTemplate-->>AwsCfn.SetUp: Return adoption template
Loading

Possibly related PRs

Suggested reviewers: lionello

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: reusing an existing CD state bucket instead of creating a new one.
Linked Issues check ✅ Passed The changes implement bucket reuse on re-bootstrap and previous teardown, matching the intent of issues #2192 and #358.
Out of Scope Changes check ✅ Passed All code changes support bucket adoption and template updates; no unrelated behavior appears introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pkg/clouds/aws/codebuild/cfn/setup.go`:
- Around line 215-223: Update AwsCfn.SetUp to determine whether the managed
stack already exists before calling findExistingBucket. Only perform
retained-bucket discovery and pass the discovered bucket to CreateTemplate on
the stack-creation path; for existing stacks, preserve the current bucket
configuration in the update template. Add coverage for updating a pre-existing
managed stack.
- Around line 178-183: Normalize the bucketRegion value in the bucket-retention
comparison before checking it against region: map S3’s “EU” LocationConstraint
to “eu-west-1”, while preserving the existing us-east-1 empty-value handling.
Add a test covering an EU response that matches eu-west-1 and confirms the
retained bucket is not duplicated.
- Around line 163-188: Update bucket discovery in setup.go around ListBuckets,
GetBucketLocation, and GetBucketTagging: return any GetBucketLocation error
instead of skipping the bucket, and treat only the S3 NoSuchTagSet error from
GetBucketTagging as a non-match while propagating all other tagging errors. In
bucket_test.go, update mockBucketLister to inject location and tagging errors
and add coverage for both failure paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 320d91e0-c165-4fa0-94bf-76d7286cab1a

📥 Commits

Reviewing files that changed from the base of the PR and between 9079c22 and b5b1c3c.

📒 Files selected for processing (5)
  • src/pkg/cli/client/byoc/aws/byoc.go
  • src/pkg/clouds/aws/codebuild/cfn/bucket_test.go
  • src/pkg/clouds/aws/codebuild/cfn/setup.go
  • src/pkg/clouds/aws/codebuild/cfn/template.go
  • src/pkg/clouds/aws/codebuild/cfn/template_test.go

Comment thread src/pkg/clouds/aws/codebuild/cfn/setup.go Outdated
Comment thread src/pkg/clouds/aws/codebuild/cfn/setup.go Outdated
Comment thread src/pkg/clouds/aws/codebuild/cfn/setup.go Outdated
Comment thread src/pkg/clouds/aws/codebuild/cfn/setup.go Outdated
defangdevs and others added 2 commits July 22, 2026 03:45
…flag

Per review, drop the automatic bucket discovery (ListBuckets + tag match):
that approach mirrored DefangLabs#422, which was reverted, and relies on account-wide
list/tagging permissions and unambiguous matching. Instead adopt a bucket
only when the operator passes it explicitly:

- add a `--bucket` flag to `defang cd install`;
- ByocAws.SetCDBucket sets AwsCfn.ExistingBucket before SetUpCD;
- SetUp passes ExistingBucket to CreateTemplate (which already skips the
  managed bucket and outputs the given name when adopting).

Removes findAdoptableBucket and its unit test. The template-level
adoption logic and its test are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sw7j9FYnbChZJbYArxbhuo
Implement SetCDBucket on the GCP and DigitalOcean BYOC providers so the
explicit adopt-bucket flag works for every object-storage CD backend:

- GCP: ByocGcp adopts the given GCS bucket (skips the find-or-create in
  SetUpCD); CD service-account bucket roles are still applied to it.
- DigitalOcean: ByocDo sets DoApp.BucketName, which GetBucketName already
  honors ahead of prefix-discovery/create.

Azure is intentionally not supported: its CD state lives in a Storage
Account + Blob Container, not a bucket, so `--bucket` doesn't map. The
flag now errors with a clear message for unsupported providers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sw7j9FYnbChZJbYArxbhuo
@defangdevs

Copy link
Copy Markdown
Contributor Author

Addressed the review:

  • Dropped the automatic ListBuckets discovery (that mirrored reuse cd bucket #422, which was reverted). Adoption is now opt-in only, via a new defang cd install --bucket <name> flag.
  • Extended it to all object-storage CD backends: AWS (S3), GCP (GCS), and DigitalOcean (Spaces) each implement SetCDBucket. The command type-asserts the provider, so unsupported providers get a clear error.
  • Azure is intentionally excluded: its CD state lives in a Storage Account + Blob Container, not a bucket, so --bucket doesn't map. The flag errors with a message saying so.

The template-level adoption logic (skip the managed Bucket resource, output the given name) is unchanged. build / test -short / vet / gofmt / golangci-lint all pass on the touched packages.

DigitalOcean already read DEFANG_CD_BUCKET at construction, so the env var
worked for every command there. AWS and GCP only honored the `cd install
--bucket` flag. Read DEFANG_CD_BUCKET at construction for AWS and GCP too,
so the env var adopts an existing CD bucket across all commands (up, cd
down, etc.), and default the `--bucket` flag to $DEFANG_CD_BUCKET (matching
the --allow-upgrade convention). The flag still overrides the env on
`cd install`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sw7j9FYnbChZJbYArxbhuo
@defangdevs

Copy link
Copy Markdown
Contributor Author

Follow-ups pushed:

@lionello
lionello merged commit 6444051 into DefangLabs:main Jul 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BYOC AWS: re-bootstrapping mints a new defang-cd CD bucket each time and orphans the old one (bucket leak) Defang cd init with previous s3 bucket

2 participants