feat(loadbalancing): add an AWS Elastic Load Balancing adapter - #168
Conversation
|
| 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
left a comment
There was a problem hiding this comment.
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:
-
Could you add a
loadbalancingentry toSERVICE_ICONSinpackages/frontend/src/components/serviceIcons.ts? Unknown keys degrade to the fallback circle by design, so nothing breaks, but a real icon (lucide'sScaleorWaypointsboth read well) is a one line win for the nav. -
loadBalancingSchemaFor()inloadBalancingSchema.tsappears 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.
4d29a32 to
e693e7c
Compare
…dispatcher loadBalancingSchemaFor had no callers since schemas resolve from the registered adapter; keeping it would copy a dead pattern into a new file.
|
@hectorvent Both taken, thank you. 1. 2. Also rebased onto 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 Gate green: lint, type-check, 441 tests, build. |
|
🎉 This PR is included in version 0.4.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
# [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)
Adds a
loadbalancingservice under Networking covering ELBv2 load balancers. Firstof the ungated AWS categories.
Runtime contract notes
Version=2012-06-01request comes back in the2015-12-01namespace, so there is no separate classic ELB to model.Stateis nested (<State><Code>active</Code></State>). ReadingStatedirectly yields an object and the column renders blank.
runtime does that. Worth reading closely: the wire
<Code>isLoadBalancerNotFound, but the SDK names the modelled errorLoadBalancerNotFoundException. Matching only the wire code passed every stubbedtest and still returned 400 against the live runtime, because my stub used the
code I had seen in
curlrather than the name the SDK produces. Both names are nowaccepted, both are in
awsErrors.ts, and there is a test for each.<NextMarker></NextMarker>at the end of a list, which theSDK surfaces as an empty string — paging stops on falsy rather than
undefined, orit spins forever. A test pins that.
createneeds at least two subnets in different AZs. They are taken as commaseparated ids, following
awsComputeSchema, which handles security group ids thesame 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
kindfacet fromResourceQuery.filters(#162, stillopen). 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,testandbuildpass 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 byencoded ARN, 404 for a missing load balancer, all four validation rejections, and
delete.
Merge note
Adds
'load-balancer'toCloudResource.typein both packages. The frontendkeeps 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
SetinawsErrors.ts.