Skip to content

feat(loadbalancing): add an AWS Elastic Load Balancing adapter - #168

Merged
fredpena merged 6 commits into
floci-io:mainfrom
TheSaifZaman:feat/loadbalancing-category
Aug 31, 2026
Merged

feat(loadbalancing): add an AWS Elastic Load Balancing adapter#168
fredpena merged 6 commits into
floci-io:mainfrom
TheSaifZaman:feat/loadbalancing-category

Conversation

@TheSaifZaman

Copy link
Copy Markdown
Contributor

Adds a loadbalancing service under Networking covering ELBv2 load balancers. First
of the ungated AWS categories.

Runtime contract notes

  • Only ELBv2 is implemented. A Version=2012-06-01 request comes back in the
    2015-12-01 namespace, so there is no separate classic ELB to model.
  • State is nested (<State><Code>active</Code></State>). Reading State
    directly yields an object and the column renders blank.
  • A missing load balancer is HTTP 400, not 404 — the fourth service where this
    runtime does that. Worth reading closely: the wire <Code> is
    LoadBalancerNotFound, but the SDK names the modelled error
    LoadBalancerNotFoundException. Matching only the wire code passed every stubbed
    test and still returned 400 against the live runtime
    , because my stub used the
    code I had seen in curl rather than the name the SDK produces. Both names are now
    accepted, both are in awsErrors.ts, and there is a test for each.
  • The runtime returns <NextMarker></NextMarker> at the end of a list, which the
    SDK surfaces as an empty string — paging stops on falsy rather than undefined, or
    it spins forever. A test pins that.
  • create needs at least two subnets in different AZs. They are taken as comma
    separated ids, following awsComputeSchema, which handles security group ids the
    same way — rather than pushing create into a bespoke panel as the AWS networking
    flows needed.

Scope

Target groups are deliberately excluded. They are a second resource kind in this
category and would need the kind facet from ResourceQuery.filters (#162, still
open). Shipping load balancers alone keeps this PR independent of that one; target
groups are a clean follow-up once #162 lands.

Verification

lint, type-check, test and build pass from the repo root. 15 adapter tests.

End to end through the route: nav entry under Networking, create with two real
subnets from the default VPC, list showing the state reaching active, inspect by
encoded ARN, 404 for a missing load balancer, all four validation rejections, and
delete.

Merge note

Adds 'load-balancer' to CloudResource.type in both packages. The frontend
keeps its own union and there is no shared dependency, so a new resource kind is
always a two-package change — #156 makes that permanently safe by opening the
frontend type. Also adds four entries to the not-found Set in awsErrors.ts.

@TheSaifZaman
TheSaifZaman requested a review from hectorvent as a code owner July 28, 2026 17:19
Comment thread packages/api/src/adapter-aws/AwsLoadBalancingAdapter.ts
@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds an AWS ELBv2 load-balancing adapter and wires it through the multi-cloud SPI.

  • New AwsLoadBalancingAdapter with list/create/delete/inspect, nested state handling, dual not-found error names, and empty NextMarker paging stop.
  • Schema, catalog entry, elbv2 client, frontend load-balancer type and icon; target groups deferred.
  • Prior subnet AZ messaging mismatch was fixed by aligning the validation error and tests with the count-only check while documenting ELB’s AZ rule on the field description.

Confidence Score: 5/5

The PR appears safe to merge; the earlier subnet-message mismatch is fixed and no remaining blocking failures were identified.

The prior subnet AZ claim was narrowed to match the count-only check, schema and tests agree, and no incomplete or unsafe fix remains on that path.

Important Files Changed

Filename Overview
packages/api/src/adapter-aws/AwsLoadBalancingAdapter.ts ELBv2 adapter implements list/get/create/delete with correct not-found handling and count-only subnet validation messaging.
packages/api/src/cloud-spi/loadBalancingSchema.ts Create schema fields and columns for load balancers; subnet description states ELB AZ requirement as guidance.
packages/api/src/adapter-aws/AwsLoadBalancingAdapter.test.ts Covers paging, nested state, dual not-found names, create validation, and delete by ARN.
packages/api/src/adapter-aws/awsErrors.ts Maps LoadBalancer and TargetGroup not-found names so routes return 404.
packages/api/src/cloudProxy.ts Registers AwsLoadBalancingAdapter with the ELBv2 client.

Reviews (5): Last reviewed commit: "chore: merge main into load balancing ex..." | Re-trigger Greptile

@hectorvent hectorvent 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.

Thank you for this one. The runtime contract notes are the best part: catching that the wire <Code> is LoadBalancerNotFound while the SDK models LoadBalancerNotFoundException, and that a stub using only the wire code passed every test while the live runtime still returned 400, is exactly the kind of finding that saves the next adapter author an afternoon. The empty NextMarker pagination pin and the honest validation message (enforcing only the subnet count, since the AZ rule belongs to ELB) are also much appreciated. Registration is fully on the new catalog pattern, both lockfiles are regenerated, all checks are green, and the README matrix row plus verification notes are in place.

Two small optional things, neither blocking:

  1. Could you add a loadbalancing entry to SERVICE_ICONS in packages/frontend/src/components/serviceIcons.ts? Unknown keys degrade to the fallback circle by design, so nothing breaks, but a real icon (lucide's Scale or Waypoints both read well) is a one line win for the nav.

  2. loadBalancingSchemaFor() in loadBalancingSchema.ts appears to have no callers: since the catalog change, the schema is resolved from the registered adapter. The older schema files carry the same vestigial export, so keeping it for symmetry is fine, but if you are touching the file anyway, would you consider dropping it so the new file does not copy the dead pattern forward?

Deferring target groups until the kind facet from #162 lands is the right call, and calling that out in the code comment makes the follow-up easy to find. Nice work.

Both lockfiles updated with the manifest, since CI installs the workspace
with pnpm and the api package with bun, both frozen.
Adds a `loadbalancing` service under Networking covering ELBv2 load
balancers. First of the ungated AWS categories.

Runtime contract notes:

- Only ELBv2 is implemented. A Version=2012-06-01 request comes back in
  the 2015-12-01 namespace, so there is no separate classic ELB to model.
- `State` is nested (<State><Code>active</Code></State>). Reading State
  directly yields an object and the column renders blank.
- A missing load balancer is reported with HTTP 400, not 404. The wire
  <Code> is LoadBalancerNotFound but the SDK names the modelled error
  LoadBalancerNotFoundException — matching only the wire code passed every
  stubbed test and still returned 400 against the live runtime, so both
  names are accepted and both are in awsErrors.ts.
- The runtime returns <NextMarker></NextMarker> at the end of a list,
  which the SDK surfaces as an empty string, so paging stops on falsy
  rather than undefined or it spins forever. A test pins that.
- create needs at least two subnets in different availability zones. They
  are taken as comma separated ids, following awsComputeSchema which
  handles security group ids the same way, rather than pushing create into
  a bespoke panel.

Target groups are deliberately excluded: they are a second resource kind
in this category and would need the `kind` facet from ResourceQuery.filters
(floci-io#162). Shipping load balancers alone keeps this independent of that PR.

Adds 'load-balancer' to CloudResource.type in both packages — the frontend
keeps its own union and there is no shared dependency, so a new resource
kind is always a two-package change.

Verified end to end through the route: nav entry under Networking, create
with two real subnets, list showing the state reaching active, inspect by
encoded ARN, 404 for a missing load balancer, all four validation
rejections, and delete.
The create error said subnets had to be "in different availability
zones", but the check only counts ids. Two subnets in the same zone passed
locally and failed at ELB with a provider error, so the adapter advertised
a check it does not perform.

Enforcing the real rule would mean calling EC2 DescribeSubnets from the
load balancing adapter — coupling two services for a validation nicety, and
a new failure mode if EC2 permissions differ. So the message now claims
only the count, and the schema field carries the availability zone
requirement as ELB's rule rather than ours.
@TheSaifZaman

Copy link
Copy Markdown
Contributor Author

Rebased onto fd3bd2f after #147, #152 and #155 merged. Conflict was the CloudResource.type union in both packages against 'secret'; both members kept. README regenerated to add the Load Balancing row alongside main's Key Vault column.

Gate green after the rebase: lint, type-check, 441 tests, build.

…dispatcher

loadBalancingSchemaFor had no callers since schemas resolve from the registered adapter; keeping it would copy a dead pattern into a new file.
@TheSaifZaman

Copy link
Copy Markdown
Contributor Author

@hectorvent Both taken, thank you.

1. loadbalancing icon added, mapped to lucide's Scale.

2. loadBalancingSchemaFor() removed. Confirmed no callers anywhere in packages/api before deleting, and dropped the now-unused CloudProvider import with it. You are right that keeping it for symmetry would copy the dead pattern forward — the older files carrying it is an argument for cleaning those up later, not for adding a fifth.

Also rebased onto fd3bd2f after #147, #152 and #155 merged. The CloudResource.type union collided with the 'secret' member #147 added; both kept.

One thing worth passing on, since you singled out the wire-code-versus-SDK-name finding: the same failure mode turned up twice more while building #170 (CloudWatch Logs). The SDK models the log group timestamp as creationTime while the runtime sends createdTime, and it models no eventId on log events at all though the runtime sends one — and crucially the SDK deserializer discards fields it does not model, so a compatibility fallback reading the runtime's spelling is dead code that only ever passes against hand-built stubs. The general rule seems to be: the SDK's model is the contract, and stubs built from curl output test shapes it can never produce. Both were caught by live verification and tsc, not by the test suite.

Gate green: lint, type-check, 441 tests, build.

@fredpena
fredpena self-requested a review as a code owner August 31, 2026 21:34
@fredpena
fredpena merged commit 10d4298 into floci-io:main Aug 31, 2026
6 checks passed
@hectorvent

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 0.4.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

fredpena pushed a commit to TheSaifZaman/floci-ui that referenced this pull request Sep 1, 2026
# [0.4.0](floci-io/floci-ui@0.3.0...0.4.0) (2026-09-01)

### Bug Fixes

* **ec2:** include catalog AMIs in launch selector ([floci-io#191](floci-io#191)) ([b72135d](floci-io@b72135d))

### Features

* **aws:** add CloudFormation adapter to Cloud Explorer ([floci-io#184](floci-io#184)) ([f3d6105](floci-io@f3d6105)), closes [floci-io#81](floci-io#81) [floci-io#75](floci-io#75) [floci-io#81](floci-io#81) [floci-io#75](floci-io#75) [floci-io#81](floci-io#81)
* **aws:** add EventBridge explorer ([floci-io#146](floci-io#146)) ([42944e9](floci-io@42944e9)), closes [floci-io#85](floci-io#85)
* **aws:** add IAM to Cloud Explorer ([floci-io#145](floci-io#145)) ([fc50d19](floci-io@fc50d19)), closes [floci-io#79](floci-io#79)
* **aws:** add Secrets Manager resource adapter ([floci-io#193](floci-io#193)) ([4811866](floci-io@4811866))
* **azure:** add databases and split Cosmos NoSQL ([floci-io#149](floci-io#149)) ([2e704f4](floci-io@2e704f4)), closes [floci-io#92](floci-io#92) [floci-io#67](floci-io#67) [floci-io/floci-az#138](floci-io/floci-az#138) [floci-io#143](floci-io#143)
* **azure:** add Service Bus explorer ([floci-io#144](floci-io#144)) ([a9f0d06](floci-io@a9f0d06)), closes [floci-io#89](floci-io#89)
* **eks:** Manage nodegroups and Fargate profiles via Cloud Proxy ([floci-io#194](floci-io#194)) ([3091cc9](floci-io@3091cc9)), closes [floci-io#106](floci-io#106)
* **loadbalancing:** add an AWS Elastic Load Balancing adapter ([floci-io#168](floci-io#168)) ([10d4298](floci-io@10d4298)), closes [floci-io#162](floci-io#162) [floci-io#162](floci-io#162) [floci-io#156](floci-io#156)
* **messaging:** add a messaging category with SQS and Pub/Sub ([floci-io#157](floci-io#157)) ([dfa6d1c](floci-io@dfa6d1c)), closes [floci-io#155](floci-io#155) [floci-io#155](floci-io#155) [floci-io#156](floci-io#156) [floci-io#156](floci-io#156)
* **secretsmanager:** add JSON key-value editor for secret values ([floci-io#195](floci-io#195)) ([8e88961](floci-io@8e88961)), closes [floci-io#151](floci-io#151)
* **ses:** Add AWS SES mailbox to Cloud Explorer ([floci-io#196](floci-io#196)) ([6389a56](floci-io@6389a56)), closes [floci-io#130](floci-io#130)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants