feat(gateway-api): GEP-713 — Phase 1 (ALB) + Phase 2 (NLB/L4)#9
Open
anngdinh wants to merge 92 commits into
Open
feat(gateway-api): GEP-713 — Phase 1 (ALB) + Phase 2 (NLB/L4)#9anngdinh wants to merge 92 commits into
anngdinh wants to merge 92 commits into
Conversation
Alternative to the AWS-LBC-style design (2026-04-30). All vngcloud customization expressed as Direct Policy Attachment CRDs targeting Gateway / Service / HTTPRoute via spec.targetRefs. No parametersRef, no extensionRef, no LoadBalancerConfig extension. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This branch is a clean GEP-713 implementation; the AWS-style design artifacts are not used here. They remain on feature/gateway-api-phase1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task-by-task plan for the L7 ALB MVP implementing the GEP-713 Direct Policy Attachment design. Four VKS*Policy CRDs, ALB GatewayClass, HTTPRoute support, and four policy validator reconcilers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tures
The v1alpha1 VKSPolicy schemas advertised fields the underlying vngcloud LB
v2 API doesn't expose. Verified against the vngcloud-go-sdk request schemas
(latest available); these are platform limits, not Phase-1 punts.
Removed fields:
VKSGatewayPolicy: SSLPolicy, ALPNPolicy
VKSBackendPolicy: TargetType, EnableProxyProtocol; replaced
SessionAffinity{Type,CookieName,TTL} with Stickiness *bool (API exposes
only an on/off flag, no cookie name or TTL)
VKSHealthCheckPolicy: Port (uses member's monitorPort), HTTPHealthCheck.RequestHeaders
VKSRoutePolicy: AdditionalMatches[Header/QueryParam/Method/SourceIP]
(vngcloud rule types are HOST_NAME and PATH only); FixedResponse action
(vngcloud actions are REJECT, REDIRECT_TO_URL, REDIRECT_TO_POOL only)
Added §1.6 to the design spec documenting the supported feature surface and
the HTTPRoute UnsupportedMatch handling for header/queryParam/method matches.
Regenerated deepcopy and CRD manifests; builds and unit tests pass.
…keleton)
Approach 1A: the Gateway controller is the only writer to a per-Gateway
LoadBalancerConfig CR; the existing LBC controller continues to drive the
cloud LB exactly as it does for Ingress/Service. Owner labels match the
existing convention (vks.vngcloud.vn/owner-resource-{kind,name,uid}) so
the LBC's ownership is greppable.
Phase A — usecase scaffolding:
internal/usecase/contracts.go — ALBGatewayUseCase interface
internal/usecase/gateway_uc/alb_gateway_uc/usecase.go — Init/Ensure/Delete entry points
.../alb_gateway_uc/finalizer.go — finalizer add/remove + delete-owned-LBC
.../alb_gateway_uc/build_lbc.go — defaultGatewayBuildTask, VKSGatewayPolicy
resolver, LBC create/patch with the same
Spec/Status discipline ingress_uc uses
Phase A only translates LB-level fields (Type, SubnetId, ZoneId,
LoadBalancerName, VpcId, ClusterId, Scheme, PackageId, Tags,
LoadBalancerId). Listeners/Pools/Policies/Certificates land in subsequent
phases and will extend buildLoadBalancerConfig in place.
Phase B — controller wiring:
internal/controller/gateway/alb/gateway_controller.go — reconciler with
GatewayClass-name predicate, watches Gateway/HTTPRoute/VKSGatewayPolicy/
LoadBalancerConfig/Service. Async init mirrors networking/ingress.
internal/controller/gateway/alb/lbc_to_gateway.go — handler that
enqueues the owning Gateway when its LBC changes (status-mirror hook).
cmd/main.go — register schemes
(gateway.networking.k8s.io v1 + v1beta1, gateway.vks.vngcloud.vn
v1alpha1), add --disable-alb-gateway-controller flag (defaults to TRUE
while Phase 1 is incomplete), construct + setup the reconciler.
config/rbac/role.yaml — regenerated from
kubebuilder markers on the new reconciler.
Build clean; existing tests pass.
…e C)
For each Gateway.Spec.Listeners[i], emit a v1alpha1.Listener with:
- protocol mapped from gwv1.ProtocolType (HTTP/HTTPS only — TCP/UDP/TLS
rejected for ALB Phase 1)
- port from Gateway listener
- timeouts (Client/Member/Connection) from VKSGatewayPolicy, per-listener
sectionName-scoped policy winning over unscoped policy field-by-field
- allowedCidrs from VKSGatewayPolicy.AllowedCIDRs (joined with ',')
- insertHeaders from VKSGatewayPolicy.InsertHeaders (map → []InsertHeader,
sorted by key for stable Spec equality)
- clientCertificateId from VKSGatewayPolicy.ClientCertificateID
Pool/cert/policy fields stay nil — Phase D adds TLS, Phase E adds Pools+Policies.
helpers.go centralizes the priority-ordered "first non-nil" pickers so each
field's resolution is one line in build_listeners.go.
Build clean.
…ismatch Opt-in (RUN_GATEWAY_E2E) live specs covering the three new behaviors: - cross-ns backendRef reports RefNotPermitted, then ResolvedRefs once a ReferenceGrant is created; - a route in a team=blue namespace attaches via a NamespacesFromSelector listener; - a rule whose backends carry divergent policies reports BackendConfigMismatch. These assert on HTTPRoute status (written on reconcile, no LB provisioning wait). Require the controller to be running the current build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ackendConfigMismatch The full-suite rerun caught the kitchen weighted rule being dropped by the new BackendConfigMismatch fail-closed check (echo had backend/health-check policies, echo-v2 had none). Both weighted backends now share the same policies via multi-targetRefs. New specs (all passing live, 17/17 focused run): - ReferenceGrant revocation -> back to RefNotPermitted - policy Accepted=False/TargetNotFound for a missing target - HTTPRoute PartiallyInvalid/UnsupportedValue for the dropped header rule - VKSRoutePolicy deletion reverts the rule action to REDIRECT_TO_POOL - HTTPRoute deletion drops all listener policies - preferZoneId positive: LBC created in the requested (valid) zone Also: fix stale --enable-gateway-api-alb references (real flag is --disable-alb-gateway-controller) and rename fail*YAML helpers to gwPolicyYAML/plainGatewayYAML now that positive specs reuse them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New docs/guide/gateway-api.md covering: enabling via Helm (gatewayApi.alb.enabled) / --disable-alb-gateway-controller, quick start, HTTPRoute support matrix, the four GEP-713 policy CRDs with full field examples, create-only fields and subnet/zone precedence, BYO-LB adoption, conflict resolution + policy status, route status reasons (incl. RefNotPermitted/BackendConfigMismatch/PartiallyInvalid), ReferenceGrant, TLS termination, and an Ingress-annotation equivalence table. Wire it into mkdocs nav, index/README feature lists, the configuration flag/values tables, and contributing (opt-in gateway e2e). README roadmap: Gateway API Phase 1 shipped. mkdocs build --strict passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the vngcloud-nlb GatewayClass and an L4 reconciler that translates a Gateway + attached TCPRoute/UDPRoute into a Type=Network LoadBalancerConfig, reconciled by the existing LBC controller (same cloud path as Service type=LoadBalancer). Scope: TCP + UDP, modelled on the Service controller's L4 build. TLSRoute (SNI) deferred to a follow-up. Translation (internal/usecase/gateway_uc/nlb_gateway_uc): - Type=Network LBC; one TCP/UDP listener per supported Gateway listener, each with a single default pool from the oldest attached route's backendRefs. - Members via instance/ip target type; weighted backends scaled; health monitor TCP (default) / PING-UDP (UDP) with VKSHealthCheckPolicy override. - Honors VKSGatewayPolicy (LB spec + listener timeouts/CIDRs) and VKSBackendPolicy (algorithm/stickiness/target type). VKSRoutePolicy/certs/headers are L7-only. - Cross-ns backendRefs gated by ReferenceGrant; Gateway + TCP/UDP route status. - HTTP/HTTPS listeners on an nlb Gateway → UnsupportedProtocol (mixed rejected). Controller (internal/controller/gateway/nlb) + wiring: - Thin reconciler mirroring alb; watches TCP/UDP routes, VKS policies, LBC, Svc, ReferenceGrant. New L4 field indexes + eventhandlers in gateway/shared. - cmd/main.go: --disable-nlb-gateway-controller (default TRUE / disabled) and gwv1alpha2 scheme registration. Disabled by default because TCPRoute/UDPRoute are Gateway-API experimental-channel CRDs and a deployment prerequisite. Tests: unit (fake client) for protocol mapping, mixed rejection, pool from route, LBC shape, route attachment/oldest-wins; envtest (experimental CRDs) for GatewayClass Accepted + TCP Gateway→Type=Network LBC + route status. Opt-in live e2e (RUN_GATEWAY_NLB_E2E) for TCP + UDP. ALB envtest still green. Helm: gatewayApi.nlb.enabled (default false) + gatewayclass-nlb.yaml + flag. config/rbac/role.yaml: tcproutes/udproutes (+status). Docs: docs/guide/gateway-nlb.md. Note: the Helm manager-rbac.yaml is regenerated separately ([build] update: helm) and already lacks all gateway RBAC since Phase 1 — config/rbac/role.yaml is the kubebuilder source of truth and carries the new L4 rules. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The live run proved the feature works — the controller created a Type=Layer4
LBC with the TCP/UDP listener+pool — but the spec asserted the wrong string
("Network"). Compare against the SDK constants (LoadBalancerTypeLayer4,
ListenerProtocol{TCP,UDP}, PoolProtocol{TCP,UDP}) instead of hardcoded strings.
Also swap the UDP backend to nginx (coredns crash-loops without a Corefile, which
hung rollout); the spec only asserts LBC shape, not UDP traffic.
Live NLB e2e (RUN_GATEWAY_NLB_E2E): 2/2 pass — TCP (Programmed + address +
TCPRoute Accepted) and UDP (Type=Layer4, UDP listener+pool). Clean teardown.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Unit (coverage_test.go): weighted backendRefs (scaled member weights), zero-weight drop, ip target type (ResolvePodEndpoints), VKSBackendPolicy overlay (algorithm/stickiness), VKSHealthCheckPolicy overlay (port override + thresholds, HTTP probe over a TCP pool), listener policy (timeouts/CIDRs), applyLoadBalancerSpec (scheme/tags/isPOC + nil-clears), resolveSubnetAndZone (default / explicit subnetID / preferZone==default), resolveLoadBalancerName, listAttachedRoutes (TCP+UDP), routeResolvedRefs (Resolved/BackendNotFound/ RefNotPermitted), routeAttaches multi-listener, listenerStatuses mixed protocols (UnsupportedProtocol), buildListenersAndPools UDP happy path and listener-without-route skipped. Envtest (gateway_controller_test.go): UDP Gateway -> Type=Layer4 LBC with UDP listener+pool; Gateway deletion removes the owned LBC via finalizer cleanup. All green: nlb unit + envtest (4 specs, 29s), go build/vet clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…path Brings the applicable, non-duplicate behavioral scenarios from the Service and Ingress controller suites onto the NLB Gateway envtest (mock cloud, same level as the source suites): - listener port change reflected on the LBC (Service "update port") - LB-level attributes from VKSGatewayPolicy -> LBC (Service "all annotations") - subnetId from VKSGatewayPolicy.loadBalancerSpec -> LBC.SubnetId (Ingress "prefer subnet ID"; create-only, policy applied before the Gateway) Dropped as not applicable to the Gateway architecture: NodeSecurityGroup / CNI-tag scenarios (the Gateway path doesn't manage NSGs), "N services share one LB" (1 Gateway = 1 LB), PROXY protocol (not in Phase 2), and TCP+UDP-same-port (a cloud-enforced limit the mock can't exercise). ALB-equivalent LB-attr/ subnet/zone/HTTPS scenarios are already covered by the kitchen live e2e + unit. nlb envtest: 7 specs green (43.9s). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Makefile `run`: add --disable-nlb-gateway-controller=false so local runs exercise the Phase 2 NLB controller alongside ALB. - endpoint_resolver_test.go: switch the one gomega assertion to testify assert.NoError (the file otherwise uses testify); gomega's Expect without a registered fail handler panicked when run via `go test`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds proxyProtocol to VKSBackendPolicy. On the NLB (L4) path, a TCP pool switches to the vngcloud PROXY pool protocol so an L4 backend (HAProxy/nginx ingress controller) can recover the real client IP behind the NLB. TCP-only; ignored for UDP. Mirrors the Service controller's enable-proxy-protocol annotation. Pool protocol is fixed at create time, so set this before the Gateway is created. CRD + deepcopy regenerated (bases + embedded). Tests: unit (TCP->PROXY, UDP untouched) + envtest (VKSBackendPolicy.proxyProtocol -> LBC pool PROXY). nlb envtest green (8 specs, 60.9s). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document VKSBackendPolicy.proxyProtocol in the NLB guide and add an end-to-end example (docs/examples/gateway-nlb-haproxy-realip.md): front an HAProxy ingress controller with a vngcloud-nlb Gateway, enable PROXY protocol on the backend, and have the app recover the real client IP via X-Forwarded-For. Manifests are the ones verified live (whoami showed X-Forwarded-For = the real public egress IP). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the PROXY-protocol real-client-IP walkthrough from Examples into the Guide section and fix the cross-links between it and the NLB guide. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Service controller supports the InterVPC LB scheme (private-subnet-id + private-zone-id). The gateway path already carried scheme + privateSubnetId but had no field for the client-subnet zone, so InterVPC could not be fully expressed. Add PrivateZoneID to the shared VKSLoadBalancerSpec, mapping to LBC.Spec.PrivateZoneId (common.Zone) — identical to the Service controller's private-zone-id annotation. Wire it in both the NLB and ALB applyLoadBalancerSpec (PrivateSubnetID was already wired in both; leaving the new field unwired on ALB would silently no-op). Regenerate deepcopy + both CRD copies. Verified equivalent to the Service controller: scheme "InterVPC" matches v2.InterVpcLoadBalancerScheme, and all three fields land on the same LBC the LBC controller consumes. Tests: - unit: NLB TestApplyLoadBalancerSpec_InterVPC; extend ALB applyLoadBalancerSpec test - envtest: NLB scenario mapping scheme/privateSubnet/privateZone onto the LBC Docs: InterVPC section + example in the NLB guide. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VKSBackendPolicy.TargetNodeLabels was wired into endpoint resolution on both gateway paths but never asserted: existing tests mock the resolver with mock.Anything for the variadic options, so the node selector built from the labels was swallowed. - NLB unit: TargetNodeLabels become an ANDed node selector passed to the resolver; no policy -> empty selector (all nodes). - NLB e2e (opt-in, real cluster): selecting one node by hostname yields exactly one LB pool member with that node's IP. Skips on single-node. - ALB unit: same wiring assertion via synthesizePool (helper alone was already covered by TestResolveTargetNodeLabels). Test-only; no production code changed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… pattern Document the GEP-713 direct-policy-attachment scope of the four CRDs and its key consequence: VKSBackendPolicy/VKSHealthCheckPolicy attach to a Service, so backend config is global to that Service across every Gateway (mirrors upstream BackendTLSPolicy). Add the idiomatic recipe for fronting one app with both an external and internal Gateway, and why differing backend behaviour (e.g. targetType) per Gateway is modelled as two Services rather than per-route backend policy. Cross-link from the NLB guide. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Default gatewayApi.alb.enabled to false and --disable-alb-gateway-controller to true so the Helm chart installs cleanly on clusters that have not installed the Gateway-API CRDs (GatewayClass/Gateway/HTTPRoute). NLB was already disabled. Enabling stays one toggle (gatewayApi.alb.enabled=true) once the CRDs are present; the deployment template already wires the flag from the value. Docs updated to reflect the new defaults. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
….56.0 Bump the Go toolchain 1.25.10 -> 1.25.11 (go.mod, govulncheck workflow, contributing docs). Bump golang.org/x/net v0.54.0 -> v0.56.0 to fix GO-2026-5026 (idna ASCII-only Punycode reject), which govulncheck reported as called from pkg/utils/metadata/metadata_utils.go. go mod tidy pulled the companion x/ modules forward and promoted sigs.k8s.io/gateway-api to a direct require. govulncheck ./... now reports 0 vulnerabilities affecting this code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Route Gateway usecases through pkg/k8s.FinalizerManager, which re-Gets the object before patching and swallows NotFound/Conflict, fixing the StorageError (empty-UID precondition) when the Gateway is already gone. Remove the now-dead internal/controller/gateway/shared finalizer helpers and their tests. Co-Authored-By: Claude <noreply@anthropic.com>
Pool member weights set in LoadBalancerConfig (e.g. 80/20) were never applied to the VNGCloud load balancer; traffic stayed 50/50. Three causes in lbc_uc, all fixed: - checkIfPoolMemberExist ignored weight, so a weight-only change was never detected and UpdatePoolMembers was never called. Weight is now part of member equality (via effectiveWeight, which treats nil/<=1 as the default weight of 1 to avoid a reconcile loop). Identity-only matching moved to a new checkIfPoolMemberExistByAddress, used for de-duplication and delete "can-cover" checks. - Members were built with loadbalancerv2.NewMember, which hardcodes weight to 1. Replaced with buildMemberRequest, constructing the request struct directly with the member's weight (and backup) at all three call sites (deploy_pool create/update, delete_pool). - mergePoolMembers kept the current (cloud) member for in-spec members, discarding the desired weight. It now prefers the spec member. Adds unit tests covering weight-aware equality, identity matching, weight-change detection, and spec-weight preference in merge. Co-Authored-By: Claude <noreply@anthropic.com>
fix(lbc): apply pool member weight to load balancer
…enerics golang/govulncheck-action installs @latest (v1.4.0), which links x/tools@v0.46.0 whose SSA call-graph builder panics on generic code under Go 1.25 ("ForEachElement called on type containing *types.TypeParam"). Invoke govulncheck directly pinned to v1.3.0 (x/tools@v0.44.0) instead. Co-Authored-By: Claude <noreply@anthropic.com>
GitHub is deprecating Node 20 on Actions runners. Bump the SHA-pinned actions that still target node20: - actions/setup-go v6.0.0 -> v6.5.0 - gitleaks/gitleaks-action v2.3.9 -> v3.0.0 - docker/login-action v3.4.0 -> v4.2.0 - docker/build-push-action v6.16.0 -> v7.2.0 All bumps are runtime-only (node20 -> node24); inputs/outputs/behavior are unchanged. The two env vars removed in build-push v7 (DOCKER_BUILD_NO_SUMMARY, DOCKER_BUILD_EXPORT_RETENTION_DAYS) are not used in any workflow. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Update (2026-06-12) — Phase 2: NLB (L4) Gateway + InterVPC
Extends the Gateway path to VNGCloud Network Load Balancers (L4) and closes Service-controller L4 parity gaps. Same model as ALB: the gateway path emits the same
LoadBalancerConfig(LBC) theService type=LoadBalancerpath emits, so the existing LBC controller drives the cloud. Each addition was verified field-by-field against the generated LBC and is unit- + envtest-covered.ec0d95d— Phase 2 NLB (TCP/UDP): newvngcloud-nlbGatewayClass (gateway.vks.vngcloud.vn/nlb);TCPRoute/UDPRouteproduce aLayer 4LBC. HonorsVKSGatewayPolicy(LB spec, listener timeouts, allowed CIDRs),VKSBackendPolicy(pool algorithm, stickiness, target type, node labels),VKSHealthCheckPolicy(TCP / PING-UDP / HTTP probe). TCP→TCP and UDP→PING-UDP health-check defaults mirror the Service controller.VKSRoutePolicyis L7-only and ignored. Disabled by default (--disable-nlb-gateway-controller) becauseTCPRoute/UDPRouteare experimental-channel CRDs the operator must install first; enable withgatewayApi.nlb.enabled=true.ddecb22—VKSBackendPolicy.proxyProtocol: switches a TCP pool to the vngcloudPROXYpool protocol so an L4 backend (e.g. an HAProxy ingress controller) recovers the real client IP behind the NLB. Mirrors the Serviceenable-proxy-protocolannotation. The cloud pool protocol is create-only.c778188— InterVPC scheme: addVKSLoadBalancerSpec.privateZoneId(→LBC.Spec.PrivateZoneId, acommon.Zone) — the missing piece for the InterVPC scheme (scheme+privateSubnetIdwere already wired). Wired on both the NLB and ALB paths. Mirrors the Serviceprivate-zone-idannotation;scheme: InterVPCmatchesv2.InterVpcLoadBalancerScheme.ca7c808,9c79e50); unit + envtest forproxyProtocoland InterVPC.Remaining Service-controller L4 gap: automatic NodeSecurityGroup management (the gateway path creates no NSG) — deferred, same posture as the ALB path.
Update (2026-06-10) — ALB LB-level & health-check parity
Two follow-up commits bring the ALB Gateway path to parity with the Ingress annotations for several
LoadBalancerConfig(LBC) fields. Both paths emit the same LBC, so each was verified field-by-field against the generated LBC and is unit-tested (TDD) inalb_gateway_uc.0c88ad6—VKSHealthCheckPolicy: addedport(→ every pool member'sMonitorPort),httpHealthCheck.method(GET/PUT/POST),httpHealthCheck.httpVersion(1.0/1.1). HTTP-only fields are ignored for TCP probes; mirrors the Ingresshealthcheck-port/healthcheck-http-method/healthcheck-http-versionannotations.f1f955a—VKSGatewayPolicy.loadBalancerSpec: addedloadBalancerNameandpreferZoneId, and wired full BYO-LB adoption vialoadBalancerId—resolveSubnetAndZonenow mirrors the adopted LB's subnet/zone so the LBC matches the LB the LBC controller adopts. Precedence matches Ingress:loadBalancerId(adopt) >subnetId>preferZoneId> cluster defaults. Subnet/zone resolution now runs create-time only (keeps VNGCloud lookups off the per-reconcile hot path).CRD bases + embedded copies regenerated; design-doc decision #6 updated to note BYO-LB adoption is now supported (the controller still never auto-adopts).
Remaining non-parity items are out of scope here:
security-groups/NSG (intentionally deferred) and routing by header/method/query + fixed-response (genuine VNGCloud LB API limits that affect the Ingress path equally).Summary
Lands the foundation for Gateway API GEP-713 Phase 1 — Sections A–E of docs/superpowers/plans/2026-05-08-gateway-api-gep713-phase1.md. No runtime behaviour yet; CRDs install but no controllers are wired up.
What's included:
sigs.k8s.io/gateway-api v1.2.0dep;api/gateway/v1alpha1/API group; finalizer + GatewayClass controller-name constants.VKSGatewayPolicy,VKSBackendPolicy,VKSHealthCheckPolicy,VKSRoutePolicy) with deepcopy generated and embedded into the binary viaregister.go.pkg/gateway/helpers: hostname matching, deterministic synthetic-pool naming + weight scaling, policy target-ref matching. Unit tested.internal/controller/gateway/shared/: protocol classifier, condition + ancestor-status helpers, match-specificity ordering, reverse indexer, finalizer helpers, cross-resource event handlers.internal/usecase/gateway_uc/shared/:ReferenceGrantevaluator, generic Direct-policy resolver (oldest-wins),Matches()adapters on all four policy CRDs.Plan deviations on the record
make sync-embedded-crdsonly matchedvks.vngcloud.vn_*.yaml— extended togateway.vks.vngcloud.vn_*.yaml.register.gohad no path for embedding the new gateway CRDs — added 4//go:embedentries andGetAllCRDs()rows.shared_types.goaliases needed+kubebuilder:object:generate=falsemarkers, otherwise controller-gen emits DeepCopy on external types.derefStrused for both*Groupand*Kind) — split intoderefGroup/derefKind.go get sigs.k8s.io/gateway-api/apis/v1alpha2@v1.2.0was needed in addition to the root module.Why F–J are deferred
Section F (and downstream G/H/I) calls a high-level
vngcloud_repoabstraction (SyncListeners,SyncPools,SyncPolicies,LoadBalancerParams,ImportCertificateFromSecret,EnsureNSGForGateway, …) that doesn't exist — the realVngCloudRepositoryis a thin shell overloadbalancerv2.ICreate*RequestSDK types. The plan's own self-review (line 4747) flags these as "expected to exist." Section J user-docs would describe non-functional features without F–H, so they're held back too.Plan revision needed before F can land.
Test plan
go build ./...cleango test ./pkg/gateway/... ./internal/controller/gateway/shared/... ./internal/usecase/gateway_uc/shared/...— all passmake test(full envtest suite) — reviewer to run; foundation only, no controllers wired up so no envtest specs added in this PRmake manifestsclean re-run produces no diff (CRDs already in tree)🤖 Generated with Claude Code