Skip to content

feat(cloudformation): stack policies, async stacks, CancelUpdateStack and RollbackStack (CFN-6) - #1380

Draft
NitinKumar004 wants to merge 1 commit into
developmentfrom
feat/aws-cfn-stack-policy-async
Draft

NitinKumar004 wants to merge 1 commit into
developmentfrom
feat/aws-cfn-stack-policy-async

Conversation

@NitinKumar004

Copy link
Copy Markdown
Collaborator

What

CFN-6: stack policies, asynchronous stack operations under --async-settle, CancelUpdateStack, and the RollbackStack / TokenAlreadyExistsException part of CFN-X9. Closes the rest of CFN-06 and CFN-17.

Stack policy

  • SetStackPolicy / GetStackPolicy, plus StackPolicyBody/StackPolicyURL on CreateStack and UpdateStack. URLs are read from the emulated S3, like TemplateURL.
  • The policy is validated against the documented grammar: Effect Allow/Deny, Action or NotAction from Update:Modify|Replace|Delete|*, Principal "*", Resource or NotResource as * or LogicalResourceId/<glob>, and Condition StringEquals/StringLike on ResourceType. Bad JSON or a bad grammar is a ValidationError, and the 16384 length limit is checked.
  • Once a stack has a policy, anything no statement allows is denied, and an explicit Deny wins. As the AWS guide warns, an Allow with NotResource and no type condition does not protect the excluded resource.
  • The update executor checks each in-place change (Update:Modify), replacement (Update:Replace) and dropped resource (Update:Delete) before touching it. A denied resource ends UPDATE_FAILED with Action denied by stack policy: Statement [#N] does not allow [Update:Replace] for resource [LogicalResourceId/X], and the stack rolls back. Nothing is created or deleted for it.
  • StackPolicyDuringUpdateBody/URL overrides the policy for that one update only. A policy can be replaced but not removed: SetStackPolicy needs a body or a URL.
  • ExecuteChangeSet is held to the stored policy.

Async stacks (config.AsyncSettle / serve --async-settle)

  • Create, update, delete, ContinueUpdateRollback and RollbackStack stay *_IN_PROGRESS for the settle window. The resource work is done at once, but the last phase waits: completion, rollback, and the update's cleanup phase, where old resources of replacements are deleted. It completes lazily on the next API call or on the serve ticker (CloudFormation is now a registered Tickable).
  • Events are stamped a step apart, and DescribeStackEvents and DescribeStackResources only show what has arrived by now, so clients see them come in one by one.
  • CancelUpdateStack works only in UPDATE_IN_PROGRESS. Otherwise it returns ValidationError: CancelUpdateStack cannot be called from current stack status. It drops the events that had not arrived yet, fails the resources caught in progress, and goes UPDATE_ROLLBACK_IN_PROGRESS ("Stack update cancelled") then UPDATE_ROLLBACK_COMPLETE. The cleanup phase had not run yet, so the rollback puts the old resources of replacements back and deletes the new ones. No data is lost.
  • With settle off, every operation is synchronous as before. The existing tests pass unchanged.

RollbackStack and client request tokens

  • RollbackStack rolls a CREATE_FAILED stack back to ROLLBACK_COMPLETE, deleting what it made. It takes an UPDATE_FAILED stack back to the template, parameters and resources it had before the first failed update, and puts back the old resources of replacements that update kept. It ends UPDATE_ROLLBACK_COMPLETE. Any other status is a ValidationError.
  • ClientRequestToken on CreateStack, UpdateStack, DeleteStack, ContinueUpdateRollback, CancelUpdateStack, RollbackStack and ExecuteChangeSet. A retry with the same token of the same action returns without running again, so a retried CreateStack returns the stack instead of AlreadyExists. A token used by another action is TokenAlreadyExistsException. Stack events carry the operation's token.

Persistence

The stack policy, the pending async phase, the tokens and the last stable state are all in the snapshot. A restore mid-update can still settle it or cancel it.

Coverage docs

coveragegen dropped every Set* method of a native mock as a wiring setter, so SetStackPolicy was missing. A Set* method whose first parameter is a context.Context is now counted as an operation, and Tick is excluded like Snapshot/Restore. This also brings back ten real GKE Set* operations that were hidden.

Notes

  • The implicit-deny reason (Action denied by stack policy: No statement allows [...]), the RollbackStack bad-status text and the TokenAlreadyExists message are close guesses. I could not find the exact AWS strings in the docs or the botocore model.
  • DynamoDB still has no in-place Updater (CFN-7), so an in-place Modify of a table only records the new properties.

Testing

  • Unit tests first (provider and service), and SDK round-trip tests on the wire. They did not compile against origin/development. Compat suite: SetStackPolicy, GetStackPolicy, CancelUpdateStack and RollbackStack are green, and compatgen exits 0.
  • Gates: go build ./..., vet, and go test -race on cloudformation (provider, service, server), providers/aws, serverkit, settle, persist, coveragegen and compat/aws. golangci-lint --new-from-rev=origin/development reports 0 issues.
  • e2e against cloudemu serve on :61166 with the aws CLI, with and without --async-settle. A policy denies Update:Replace on a table. The denied rename rolls back with the item intact and no new table. The override update replaces the table and the stored policy is unchanged. Bad JSON gets a ValidationError. Mid-update cancel-update-stack goes UPDATE_ROLLBACK_IN_PROGRESS and then COMPLETE, keeps the old table and its item, deletes the new one and reverts the template. Without settle, cancel returns the ValidationError.
  • Terraform (aws provider 6.66.0) runs an aws_cloudformation_stack with policy_body through apply, a clean plan, an update of both the template and the policy, another clean plan, and destroy, in both modes.

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.

1 participant