Skip to content

feat(cloudformation): support AWS::Events::EventBus and EventBusPolicy - #1794

Merged
hectorvent merged 8 commits into
floci-io:mainfrom
lruizctaima:feat/cfn-events-eventbus
Aug 5, 2026
Merged

hectorvent merged 8 commits into
floci-io:mainfrom
lruizctaima:feat/cfn-events-eventbus

Conversation

@lruizctaima

@lruizctaima lruizctaima commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

AWS CDK stacks that define a custom EventBridge event bus did not work against Floci. AWS::Events::EventBus was not handled by the CloudFormation provisioner, so it fell through the default branch and was only stubbed (a fake arn:aws:stub::: and a random physical id) — the bus was never created in EventBridgeService. As a result PutEvents to that bus failed with EventBus not found, no rule ever matched, and nothing reached the targets, even though cdk deploy reported CREATE_COMPLETE and the bus was absent from ListEventBuses.

This wires real CloudFormation provisioning for AWS::Events::EventBus and AWS::Events::EventBusPolicy, reusing the existing EventBridgeService. 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 via EventBridgeService.createEventBus (Name, Description, Tags, optional inline Policy). Ref returns the bus name; Fn::GetAtt resolves Arn and Name; re-deploy is idempotent (ResourceAlreadyExistsExceptionDescribeEventBus).
  • AWS::Events::EventBusPolicy — both the individual Action/Principal/Condition form and the full Statement object form; Statement-form policies are merged by Sid so multiple resources on one bus coexist.

It also fixes a latent bug the feature surfaces: AWS::Events::Rule resources on a custom bus were deleted against the default bus (bus argument hardcoded to null), so the delete silently no-opped and the rule — and its bus — leaked while the stack recorded DELETE_COMPLETE. The rule's EventBusName is now stored as a resource attribute at provision time and read back at delete time, mirroring the existing AWS::EKS::Nodegroup attribute-aware delete. The rollbackCreatedResources path is fixed the same way (it used the type-based delete overload instead of the resource-aware one).

Type of change

  • Bug fix (fix:)
  • New feature (feat:)
  • Breaking change (feat!: or fix!:)
  • Docs / chore

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:

  • A CDK/CloudFormation stack with a custom AWS::Events::EventBus deployed but the bus was never created in the emulator — ListEventBuses did not show it and PutEvents to it returned EventBus not found, so events were never delivered to rule targets. Real CloudFormation creates the bus and CDK stacks route events through it.
  • Deleting (teardown or CREATE rollback) a stack with a custom bus + a rule left both resources live while reporting DELETE_COMPLETE, because the rule was deleted against the default bus. Real CloudFormation removes the rule and the bus.

Verification: deployed an EventBus + Rule + SQS-target stack with AWS CDK (aws-cdk-lib, via cdklocal) against a local Floci — CREATE_COMPLETE, no rollback — then aws events put-events for a matching event returned FailedEntryCount 0 and aws sqs receive-message returned the event, confirming the bus, rule/target, and routing are all real. Ref/Fn::GetAtt are exercised via a template Outputs block in the integration tests.

Checklist

  • ./mvnw test passes locally
  • New or updated integration test added
  • Commit messages follow Conventional Commits

Tests: 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, both EventBusPolicy forms (individual and two coexisting Statements), custom-bus teardown, and CREATE-rollback cleanup. Full CloudFormationIntegrationTest class: 99/99 green.

Notes for the reviewer

Scope kept deliberately narrow; three things flagged rather than expanded:

  • Statement-form policy merge is a client-side read-modify-write (describeEventBus → splice by Sid → 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 through EventBridgeService.putPermission's server-side merge because that path rebuilds a statement from scalar action/principal and cannot ingest a full IAM Statement object (Principal:{AWS:[...]}, multiple actions, Resource, arbitrary Condition). The atomic fix, if parallel provisioning ever lands, would be a server-side full-statement merge on EventBridgeService (which would also remove the small duplication).
  • deleteEventBusSafe swallows-and-logs at warnv. Consistent with the sibling *Safe delete helpers (which log at debugv), but bumped to warn here specifically because a bus left alive while the stack says DELETE_COMPLETE is a state divergence worth surfacing.
  • Inline Policy and Tags on AWS::Events::EventBus are provisioned but only exercised indirectly by the tests (the Policy branch reuses the well-covered putPermission policyJson path; Tags are 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.

@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds real CloudFormation provisioning for AWS::Events::EventBus and AWS::Events::EventBusPolicy, and fixes a latent bug where deleting an AWS::Events::Rule on a custom bus would silently target the default bus instead, leaving both the rule and the bus alive while the stack reported DELETE_COMPLETE.

  • AWS::Events::EventBus — provisions via EventBridgeService.createEventBus, stores Arn/Name attributes, and handles idempotent re-create via ResourceAlreadyExistsExceptiondescribeEventBus. Ref returns the bus name; Fn::GetAtt resolves Arn and Name.
  • AWS::Events::EventBusPolicy — both the scalar Action/Principal form and the full Statement object form (merged by Sid via read-modify-write so multiple resources on the same bus coexist).
  • Rule delete fixEventBusName is now stored as a resource attribute at provision time and read back at delete time via the resource-aware delete(StackResource, region) path, which is correctly invoked by both deleteStackResources and rollbackCreatedResources; the type-based fallback path passes null (default bus) only for rules that predate this change.

Confidence Score: 5/5

Safe to merge. The change is narrowly scoped to CloudFormation provisioning for two new EventBridge resource types and a targeted fix for custom-bus rule deletion; no existing behavior is changed, no new endpoints are introduced, and seven integration tests exercise every new code path end-to-end.

The provisioners follow established patterns in the file exactly, the resource-aware delete path correctly uses stored attributes, and rollbackCreatedResources calls the resource-aware delete so the EventBusName fix applies during rollback as well. The two concerns raised in the previous review round (non-atomic policy merge and silent-divergence logging level) are both addressed — the merge is an acknowledged sequential-provisioning trade-off and the log is now warnv.

Files Needing Attention: No files require special attention.

Important Files Changed

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
Loading

Reviews (3): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

@lruizctaima

lruizctaima commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

1. Silent leak when bus still has rules at delete time — Addressed in 1e019cd: deleteEventBusSafe now logs at warnv instead of debugv, so a bus left alive while the stack records DELETE_COMPLETE (e.g. a rule that hardcodes EventBusName with no Ref/DependsOn, giving no dependency edge to order the deletes) is diagnosable, per the AGENTS.md guideline.

2. Non-atomic read-modify-write for the Statement-form merge — Keeping the current approach; the suggested fix doesn't quite apply here. EventBridgeService.putPermission's server-side merge (the policyJson == null path) rebuilds the statement from scalar action/principal/conditionJson, so it can't ingest a full IAM Statement object (Principal: {AWS: [...]}, multiple Actions, Resource, arbitrary Condition) — routing the Statement form through it would drop those fields. That's why the merge lives in the provisioner and pushes the whole document via the policyJson branch.

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 EventBridgeService method that merges a full statement by Sid server-side (which would also remove the small duplication of merge logic). Deferring that to keep this PR narrow.

@lruizctaima

Copy link
Copy Markdown
Contributor Author

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
@hectorvent hectorvent self-assigned this Aug 1, 2026
@hectorvent

Copy link
Copy Markdown
Collaborator

Hi @lruizctaima,

Thanks for your patience. No timeline, I am the only maintainer looking to this project at the moment.

@hectorvent hectorvent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks @lruizctaima,

This looks good to me.
Merging...

@hectorvent
hectorvent merged commit 76f602f into floci-io:main Aug 5, 2026
13 checks passed
@hectorvent

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.6.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

abanna pushed a commit to abanna/floci that referenced this pull request Aug 6, 2026
# [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))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cloudformation AWS CloudFormation eventbridge Amazon EventBridge feature released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants