Add a new Preprocessor
You are adding logic that runs before CloudFormation DeleteStack is called, to either validate that deletion is allowed or to mutate state to improve the deletion's success rate.
Decide the phase first
CompositePreprocessor runs preprocessors in two phases. Pick the one that fits before writing any code:
- Checker — runs first, in parallel with other checkers. Errors are fatal and abort the whole deletion. All checker errors are collected before returning, so the user sees every problem at once. Use for validation that must pass (e.g. deletion-protection check).
- Modifier — runs after all checkers pass. Errors are logged as warnings only and do not stop deletion. Use for optimisations that improve deletion success but are not required (e.g. Lambda VPC detachment, which lets CloudFormation finish faster but is not strictly necessary).
If your logic must block deletion on failure → Checker. If it is best-effort cleanup → Modifier.
Input
The user provides a name like LambdaVPCDetacher or DeletionProtectionRemover. Derive:
- File name:
internal/preprocessor/<snake_case>.go(e.g.lambda_vpc_detacher.go) - Factory function name:
new<Name>FromConfig(lowercase first letter — package-internal) - Phase: Checker or Modifier (decided above)
Steps
internal/preprocessor/<name>.go— ImplementIPreprocessor.- Add
var _ IPreprocessor = (*<Name>)(nil)for compile-time check. - Reach AWS only through
pkg/clientinterfaces (never the SDK directly). - Operations must be idempotent.
- Concurrency:
errgroup+semaphore.NewWeighted(runtime.NumCPU()). - Errors: client errors are returned as-is (already
ClientError-wrapped). Only wrap locally generated errors (e.g.ctx.Done(), validation). - Checker: collect all errors, do not short-circuit. Modifier: errors propagate but the caller logs them as warnings.
- Add
internal/preprocessor/<name>_test.go— Tests use gomock against theI<Service>interfaces inpkg/client/.pkg/client/— If the preprocessor needs an AWS service the project does not yet wrap:- If the service file exists, extend the interface and struct (do not create a new file).
- If new, create
pkg/client/<service>.gowith//go:generate mockgenon line 1, then runmake mockgen. Public methods wrap errors withClientError. Tests use AWS SDK middleware mocks (sees3_test.go,backup_test.go), NOT gomock.
internal/preprocessor/factory.go— Wire it in:- Add
new<Name>FromConfig(config aws.Config[, forceMode bool])that builds SDK clients withoperation.SDKRetryMaxAttemptsandaws.RetryModeStandard, wraps them viapkg/client, and returns the preprocessor. - In
NewRecursivePreprocessorFromConfig, call the factory and append the result to either thecheckersslice or themodifiersslice in theNewCompositePreprocessor(...)call. Pick the slice that matches the phase you chose.
- Add
internal/resourcetype/resourcetype.go— If the preprocessor reasons over a CloudFormation resource type that is not yet defined (e.g. a new deletion-protection target), add the constant to the appropriate group ("For Deletion Protection Check" or similar). It does not need to be appended toResourceTypes(that slice is for force-delete operators).go.mod/go.sum—go get github.com/aws/aws-sdk-go-v2/service/<service>for any new SDK service, thengo mod tidy.README.md— Add a row to the "Pre-deletion Processing" table.E2E test — Prefer a dedicated
e2e/<name>/directory (e.g.e2e/preprocessor/,e2e/deletion_protection/) over modifyinge2e/full/. When creating a new directory, addcdk/.gitignoreexcludingcdk.context.json(CDK writes account-specific context there at deploy time). Add correspondingtestgen_<name>ande2e_<name>Make targets if applicable. The maintainer runs E2E; the code must compile and synth.Verify locally:
make mockgen(only if you added a new client method)make testmake lint
Conventions reminder
- Code comments: minimal, in English.
- Test naming:
Test[ReceiverType]_[MethodName]. - All public-facing text (README rows, error messages, PR/commit) is English.
- Checker errors are user-visible blockers — error messages should tell the user how to unblock (e.g. "remove TerminationProtection or pass
-f").
Source: go-to-k/delstack — distributed by TomeVault.