feat(cloudformation): support AWS::Events::EventBus and EventBusPolicy - #1794
Conversation
… bus on stack teardown
…orm with merge by Sid
…ete so custom-bus rules are cleaned up
|
| Filename | Overview |
|---|---|
| src/main/java/io/github/hectorvent/floci/services/cloudformation/CloudFormationResourceProvisioner.java | Adds provisionEventBus, provisionEventBusPolicy, deleteEventBusSafe, removeEventBusPolicySafe; fixes AWS::Events::Rule deletion to carry EventBusName through the resource-aware delete path; type/physicalId fallback correctly passes null for default-bus rules. |
| src/test/java/io/github/hectorvent/floci/services/cloudformation/CloudFormationIntegrationTest.java | Adds 7 integration tests covering EventBus create/Ref/GetAtt, delete, custom-bus teardown, end-to-end event delivery to SQS, both EventBusPolicy forms, and CREATE-rollback cleanup; all exercise the real wire protocol via RestAssured. |
Sequence Diagram
sequenceDiagram
participant CFN as CloudFormationService
participant P as CloudFormationResourceProvisioner
participant EB as EventBridgeService
Note over CFN,EB: Stack CREATE — EventBus
CFN->>P: provision("AWS::Events::EventBus", props)
P->>EB: createEventBus(name, description, tags, region)
alt ResourceAlreadyExistsException
EB-->>P: throw
P->>EB: describeEventBus(name, region)
EB-->>P: "EventBus{arn}"
else success
EB-->>P: "EventBus{arn}"
end
P-->>CFN: "physicalId=name, attrs{Arn, Name}"
Note over CFN,EB: Stack DELETE — Rule on custom bus
CFN->>P: "delete(StackResource{type=Rule, attrs{EventBusName}}, region)"
P->>P: read attrs.get("EventBusName")
P->>EB: listTargetsByRule(ruleName, busName, region)
EB-->>P: targets
P->>EB: removeTargets + deleteRule(busName)
Note over CFN,EB: Stack DELETE — EventBus
CFN->>P: "delete(StackResource{type=EventBus}, region)"
P->>EB: deleteEventBus(name, region)
alt ValidationException
EB-->>P: throw
P->>P: LOG.warnv(...)
else success
EB-->>P: ok
end
Reviews (3): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile
|
1. Silent leak when bus still has rules at delete time — Addressed in 1e019cd: 2. Non-atomic read-modify-write for the Statement-form merge — Keeping the current approach; the suggested fix doesn't quite apply here. As noted, this is not a live issue: CloudFormation provisions resources sequentially within a stack, so there is no concurrent writer to the same bus policy. The correct atomic fix, should parallel provisioning ever land, would be a new |
|
Hi, Is there a estimate timeline for merging, or anything still blocking it on your side? Context on why I'm asking: we're actively depending on this EventBridge-via-CloudFormation support (AWS::Events::EventBus + AWS::Events::EventBusPolicy) for our local/dev environment. As a stopgap we've built our own Floci image from this branch and are running it from our own ECR, but we'd much rather track an official release than maintain a fork image. Knowing the expected merge/release window would help us plan when we can drop the custom image. |
# Conflicts: # src/main/java/io/github/hectorvent/floci/services/cloudformation/CloudFormationResourceProvisioner.java # src/main/java/io/github/hectorvent/floci/services/cloudformation/CloudFormationService.java
|
Hi @lruizctaima, Thanks for your patience. No timeline, I am the only maintainer looking to this project at the moment. |
hectorvent
left a comment
There was a problem hiding this comment.
Thanks @lruizctaima,
This looks good to me.
Merging...
|
🎉 This PR is included in version 1.6.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
# [1.6.0](floci-io/floci@1.5.34...1.6.0) (2026-08-06) ### Bug Fixes * **1790:** better populate describe alarm responses ([floci-io#1792](floci-io#1792)) ([fcb22be](floci-io@fcb22be)) * **apigateway:** fall through to less specific resources on method mi… ([floci-io#1630](floci-io#1630)) ([96c7aa6](floci-io@96c7aa6)), closes [floci-io#1547](floci-io#1547) * **apigateway:** import authorizers and security requirements from OpenAPI ([floci-io#1798](floci-io#1798)) ([91e06eb](floci-io@91e06eb)) * **apigateway:** use last duplicate header value ([floci-io#1806](floci-io#1806)) ([6a214e8](floci-io@6a214e8)) * **athena:** report the result CSV object key as OutputLocation ([floci-io#1895](floci-io#1895)) ([418ec1b](floci-io@418ec1b)), closes [floci-io#1752](floci-io#1752) * **cloudformation:** AWS::IAM::ManagedPolicy exposes PolicyArn for Fn::GetAtt ([floci-io#2056](floci-io#2056)) ([f719033](floci-io@f719033)) * **cloudformation:** keep uniqueness suffix when truncating generated resource names ([floci-io#1802](floci-io#1802)) ([48b2139](floci-io@48b2139)), closes [floci-io#1825](floci-io#1825) * **cloudformation:** preserve subnet public IP setting ([floci-io#2031](floci-io#2031)) ([b62d1e7](floci-io@b62d1e7)) * **cloudformation:** provision AWS::ApiGatewayV2::Authorizer and wire Route.AuthorizerId ([floci-io#1760](floci-io#1760)) ([7f51c74](floci-io@7f51c74)), closes [floci-io#1773](floci-io#1773) * **cloudformation:** resolve Fn::GetAtt [Vpc, DefaultSecurityGroup] ([floci-io#1977](floci-io#1977)) ([bdad066](floci-io@bdad066)), closes [floci-io#1976](floci-io#1976) * **cloudformation:** treat delete of an already-removed DynamoDB table or Lambda function as idempotent ([floci-io#1803](floci-io#1803)) ([5cb26ff](floci-io@5cb26ff)) * **codebuild:** stabilize start and retry responses ([floci-io#2052](floci-io#2052)) ([fa96b79](floci-io@fa96b79)) * **cognito:** reject unconfirmed users in SRP auth ([floci-io#2027](floci-io#2027)) ([733c72d](floci-io@733c72d)) * **dockerfile:** ensure docker-java compatibility ([floci-io#2096](floci-io#2096)) ([7ade290](floci-io@7ade290)) * **dynamodb:** resolve nested document paths in ADD and DELETE update actions ([floci-io#1908](floci-io#1908)) ([c052259](floci-io@c052259)) * **ec2:** DescribeKeyPairs returns InvalidKeyPair.NotFound for missing names/ids ([floci-io#1932](floci-io#1932)) ([7d96106](floci-io@7d96106)), closes [floci-io#1911](floci-io#1911) * **ec2:** return AWS-parity MissingParameter for CreateSubnet without VpcId ([floci-io#2094](floci-io#2094)) ([88408d8](floci-io@88408d8)), closes [floci-io#2089](floci-io#2089) * **ecs:** resolve Secrets Manager JSON-key selectors in task definition secrets ([floci-io#2133](floci-io#2133)) ([e3c180b](floci-io@e3c180b)), closes [floci-io#1912](floci-io#1912) * **lambda:** carry the owning account into published version snapshots ([floci-io#2041](floci-io#2041)) ([f4e8bd8](floci-io@f4e8bd8)), closes [floci-io#2040](floci-io#2040) * **lambda:** report resolved executed version ([floci-io#2026](floci-io#2026)) ([2889fbd](floci-io@2889fbd)) * **lambda:** reset volatile DynamoDB Streams ESM checkpoints on restart ([floci-io#2077](floci-io#2077)) ([847c30a](floci-io@847c30a)) * **pipes:** register kafka-clients reflection metadata for native image ([floci-io#2068](floci-io#2068)) ([ab73379](floci-io@ab73379)) * **s3:** accept a percent-encoded bucket/key separator in copy sources ([floci-io#2060](floci-io#2060)) ([a8ed218](floci-io@a8ed218)), closes [floci-io#2038](floci-io#2038) * **s3:** apply CreateBucketConfiguration tags on bucket creation ([floci-io#2115](floci-io#2115)) ([79031a2](floci-io@79031a2)) * **s3:** include EventBridgeConfiguration in GetBucketNotification response ([floci-io#2072](floci-io#2072)) ([b8edd37](floci-io@b8edd37)) * **s3:** recognize virtual-hosted-style requests over HTTP/2 ([floci-io#1954](floci-io#1954)) ([5f7d4d2](floci-io@5f7d4d2)), closes [floci-io#1866](floci-io#1866) * **ssm:** report invalid batch parameter names ([floci-io#2075](floci-io#2075)) ([04873bf](floci-io@04873bf)) * **tls:** reuse the persisted self-signed certificate across restarts ([floci-io#1799](floci-io#1799)) ([a4356f2](floci-io@a4356f2)) ### Features * Add AWSCloudFormationReadOnlyAccess as a managed policy. ([floci-io#2057](floci-io#2057)) ([0142dc4](floci-io@0142dc4)) * **apigateway:** support floci:override-id for v1 and v2 ([floci-io#2045](floci-io#2045)) ([02385da](floci-io@02385da)), closes [floci-io#1593](floci-io#1593) * **bedrock:** add proxy backend for real LLM responses via OpenAI-compatible API ([floci-io#1789](floci-io#1789)) ([875e0f2](floci-io@875e0f2)) * **cloudcontrol:** include EC2 resource tags ([floci-io#1933](floci-io#1933)) ([205a8f1](floci-io@205a8f1)) * **cloudformation:** provision AWS::EC2::LaunchTemplate ([floci-io#1973](floci-io#1973)) ([4e0a815](floci-io@4e0a815)), closes [floci-io#1971](floci-io#1971) * **cloudformation:** provision AWS::EC2::VPCGatewayAttachment ([floci-io#1972](floci-io#1972)) ([1a99721](floci-io@1a99721)), closes [floci-io#1970](floci-io#1970) * **cloudformation:** provision AWS::SecretsManager::SecretTargetAttachment ([floci-io#1804](floci-io#1804)) ([62a576c](floci-io@62a576c)) * **cloudformation:** support AWS::Events::EventBus and EventBusPolicy ([floci-io#1794](floci-io#1794)) ([76f602f](floci-io@76f602f)) * **cognito:** implement ResendConfirmationCode ([floci-io#2071](floci-io#2071)) ([bb7c45c](floci-io@bb7c45c)), closes [floci-io#2067](floci-io#2067) * **ec2:** added attach and detach volume support ([floci-io#1787](floci-io#1787)) ([2c74329](floci-io@2c74329)) * **ec2:** return an empty set for DescribeVpnGateways ([floci-io#1975](floci-io#1975)) ([3baf4f4](floci-io@3baf4f4)), closes [floci-io#1974](floci-io#1974) * **iam:** list entities attached to managed policies ([floci-io#1808](floci-io#1808)) ([78ba5b3](floci-io@78ba5b3)) * **iam:** seed Amazon Bedrock managed policies ([floci-io#2034](floci-io#2034)) ([5874348](floci-io@5874348)), closes [floci-io#1559](floci-io#1559) [floci-io#1216](floci-io#1216) * **iam:** seed the managed policies CDK bootstrap and the scenario stacks attach ([floci-io#2064](floci-io#2064)) ([43aea09](floci-io@43aea09)), closes [floci-io#2059](floci-io#2059) [floci-io#2057](floci-io#2057) [floci-io#2057](floci-io#2057) * **kms:** implement ListKeyPolicies ([floci-io#2046](floci-io#2046)) ([760115d](floci-io@760115d)), closes [floci-io#1528](floci-io#1528) * **lambda:** implement the Lambda Extensions API ([floci-io#1773](floci-io#1773)) ([9f0dec9](floci-io@9f0dec9)) * **lambda:** support extra /etc/hosts entries on Lambda launch ([floci-io#2073](floci-io#2073)) ([b543aa4](floci-io@b543aa4)) * **mwaa:** add Amazon MWAA emulation backed by real Airflow (LocalExecutor) ([floci-io#2086](floci-io#2086)) ([7e4e3e8](floci-io@7e4e3e8)) * **release:** one-button release cut from main and ECR Public versioned publishing ([floci-io#2127](floci-io#2127)) ([61011c0](floci-io@61011c0)) * **rum:** add CloudWatch RUM app-monitor service ([floci-io#1797](floci-io#1797)) ([aadc93e](floci-io@aadc93e)) * **sqs:** retain MessageGroupId on standard queue messages ([floci-io#1891](floci-io#1891)) ([a01b707](floci-io@a01b707)), closes [floci-io#1496](floci-io#1496) ### Performance Improvements * **docker:** stop duplicating the native binary into a second image layer ([floci-io#2069](floci-io#2069)) ([9e90e5c](floci-io@9e90e5c))
Summary
AWS CDK stacks that define a custom EventBridge event bus did not work against Floci.
AWS::Events::EventBuswas not handled by the CloudFormation provisioner, so it fell through thedefaultbranch and was only stubbed (a fakearn:aws:stub:::and a random physical id) — the bus was never created inEventBridgeService. As a resultPutEventsto that bus failed withEventBus not found, no rule ever matched, and nothing reached the targets, even thoughcdk deployreportedCREATE_COMPLETEand the bus was absent fromListEventBuses.This wires real CloudFormation provisioning for
AWS::Events::EventBusandAWS::Events::EventBusPolicy, reusing the existingEventBridgeService. No new endpoints, wire protocol, or configuration — this is CloudFormation resource-type coverage on top of the already-complete EventBridge service.AWS::Events::EventBus— provisions viaEventBridgeService.createEventBus(Name,Description,Tags, optional inlinePolicy).Refreturns the bus name;Fn::GetAttresolvesArnandName; re-deploy is idempotent (ResourceAlreadyExistsException→DescribeEventBus).AWS::Events::EventBusPolicy— both the individualAction/Principal/Conditionform and the fullStatementobject form; Statement-form policies are merged bySidso multiple resources on one bus coexist.It also fixes a latent bug the feature surfaces:
AWS::Events::Ruleresources on a custom bus were deleted against thedefaultbus (bus argument hardcoded tonull), so the delete silently no-opped and the rule — and its bus — leaked while the stack recordedDELETE_COMPLETE. The rule'sEventBusNameis now stored as a resource attribute at provision time and read back at delete time, mirroring the existingAWS::EKS::Nodegroupattribute-aware delete. TherollbackCreatedResourcespath is fixed the same way (it used the type-based delete overload instead of the resource-aware one).Type of change
fix:)feat:)feat!:orfix!:)AWS Compatibility
This PR adds CloudFormation resource-type coverage; it does not introduce a new AWS wire action, so there is no new SDK/CLI request shape to pin.
Incorrect behavior fixed:
AWS::Events::EventBusdeployed but the bus was never created in the emulator —ListEventBusesdid not show it andPutEventsto it returnedEventBus not found, so events were never delivered to rule targets. Real CloudFormation creates the bus and CDK stacks route events through it.DELETE_COMPLETE, because the rule was deleted against thedefaultbus. Real CloudFormation removes the rule and the bus.Verification: deployed an EventBus + Rule + SQS-target stack with AWS CDK (
aws-cdk-lib, viacdklocal) against a local Floci —CREATE_COMPLETE, no rollback — thenaws events put-eventsfor a matching event returnedFailedEntryCount 0andaws sqs receive-messagereturned the event, confirming the bus, rule/target, and routing are all real.Ref/Fn::GetAttare exercised via a templateOutputsblock in the integration tests.Checklist
./mvnw testpasses locallyTests: 7 new cases in
CloudFormationIntegrationTest(RestAssured over the real wire protocol — Query for CloudFormation/SQS, JSON 1.1 for EventBridge): EventBus create +Ref/GetAtt, delete, end-to-end delivery to SQS, bothEventBusPolicyforms (individual and two coexisting Statements), custom-bus teardown, and CREATE-rollback cleanup. FullCloudFormationIntegrationTestclass: 99/99 green.Notes for the reviewer
Scope kept deliberately narrow; three things flagged rather than expanded:
describeEventBus→ splice bySid→ write whole policy). It is not atomic, but CloudFormation provisions resources sequentially per stack, so there is no concurrent writer today. It is not routed throughEventBridgeService.putPermission's server-side merge because that path rebuilds a statement from scalaraction/principaland cannot ingest a full IAMStatementobject (Principal:{AWS:[...]}, multiple actions,Resource, arbitraryCondition). The atomic fix, if parallel provisioning ever lands, would be a server-side full-statement merge onEventBridgeService(which would also remove the small duplication).deleteEventBusSafeswallows-and-logs atwarnv. Consistent with the sibling*Safedelete helpers (which log atdebugv), but bumped towarnhere specifically because a bus left alive while the stack saysDELETE_COMPLETEis a state divergence worth surfacing.PolicyandTagsonAWS::Events::EventBusare provisioned but only exercised indirectly by the tests (thePolicybranch reuses the well-coveredputPermissionpolicyJsonpath;Tagsare parsed in the create test). Out of scope and unimplemented:AWS::Events::ApiDestination,AWS::Events::Connection,AWS::Events::Archive,AWS::Scheduler::Schedule(no underlying service support), and enforcing the bus resource policy at delivery time.