From 2265aee120ec51028c75d0ce3ee715baff9cc0d4 Mon Sep 17 00:00:00 2001
From: Julian Sommer <115516918+juliansommer@users.noreply.github.com>
Date: Mon, 14 Sep 2026 11:30:55 +1000
Subject: [PATCH] feat: add eks to aws coverage
---
docs/aws-model-operation-coverage.md | 50 ++++++++++++++++++++++++++++
internal/awsmodel/embed.go | 1 +
internal/awsmodel/model.go | 1 +
internal/awsmodel/model_test.go | 3 +-
spinifex/gateway/eks.go | 12 +++++++
spinifex/gateway/operations.go | 3 ++
spinifex/gateway/operations_test.go | 35 +++++++++++++++++++
7 files changed, 104 insertions(+), 1 deletion(-)
diff --git a/docs/aws-model-operation-coverage.md b/docs/aws-model-operation-coverage.md
index a6c5e024a..25013f81d 100644
--- a/docs/aws-model-operation-coverage.md
+++ b/docs/aws-model-operation-coverage.md
@@ -10,6 +10,7 @@ Implemented means a modelled operation is registered to a real handler. Stub and
| ec2 | 2016-11-15 | 625 | 121 | 121 | 0 | 0 | 504 | 0 |
| ecr | 2015-09-21 | 47 | 39 | 19 | 13 | 7 | 11 | 3 |
| ecs | 2014-11-13 | 56 | 39 | 31 | 5 | 0 | 20 | 3 |
+| eks | 2017-11-01 | 56 | 38 | 34 | 0 | 0 | 22 | 4 |
| elasticloadbalancingv2 | 2015-12-01 | 46 | 37 | 33 | 0 | 0 | 13 | 4 |
| iam | 2010-05-08 | 159 | 75 | 75 | 0 | 0 | 84 | 0 |
| rds | 2014-10-31 | 162 | 43 | 26 | 0 | 12 | 124 | 5 |
@@ -337,6 +338,55 @@ None.
`PollAssignments`, `ProvisionCapacity`, `ReportTaskGPU`
+
+
+## eks
+
+Implements **34 of 56** modelled operations (60.7%).
+
+Implemented (34)
+
+`AssociateAccessPolicy`, `AssociateIdentityProviderConfig`, `CreateAccessEntry`, `CreateAddon`, `CreateCluster`, `CreateNodegroup`, `DeleteAccessEntry`, `DeleteAddon`
+
+`DeleteCluster`, `DeleteNodegroup`, `DescribeAccessEntry`, `DescribeAddon`, `DescribeAddonVersions`, `DescribeCluster`, `DescribeIdentityProviderConfig`, `DescribeNodegroup`
+
+`DisassociateAccessPolicy`, `DisassociateIdentityProviderConfig`, `ListAccessEntries`, `ListAccessPolicies`, `ListAddons`, `ListAssociatedAccessPolicies`, `ListClusters`, `ListIdentityProviderConfigs`
+
+`ListNodegroups`, `ListTagsForResource`, `TagResource`, `UntagResource`, `UpdateAccessEntry`, `UpdateAddon`, `UpdateClusterConfig`, `UpdateClusterVersion`
+
+`UpdateNodegroupConfig`, `UpdateNodegroupVersion`
+
+
+
+
+Missing from dispatch (22)
+
+`AssociateEncryptionConfig`, `CreateEksAnywhereSubscription`, `CreateFargateProfile`, `CreatePodIdentityAssociation`, `DeleteEksAnywhereSubscription`, `DeleteFargateProfile`, `DeletePodIdentityAssociation`, `DeregisterCluster`
+
+`DescribeAddonConfiguration`, `DescribeEksAnywhereSubscription`, `DescribeFargateProfile`, `DescribeInsight`, `DescribePodIdentityAssociation`, `DescribeUpdate`, `ListEksAnywhereSubscriptions`, `ListFargateProfiles`
+
+`ListInsights`, `ListPodIdentityAssociations`, `ListUpdates`, `RegisterCluster`, `UpdateEksAnywhereSubscription`, `UpdatePodIdentityAssociation`
+
+
+
+
+Registered stubs (0)
+
+None.
+
+
+
+Deliberately unsupported (0)
+
+None.
+
+
+
+Registered outside the pinned model (4)
+
+`GetRecoveryDirective`, `ListInternalAddons`, `PublishInternal`, `WebhookTokenReview`
+
+
## elasticloadbalancingv2
diff --git a/internal/awsmodel/embed.go b/internal/awsmodel/embed.go
index de8fdf341..5a090da79 100644
--- a/internal/awsmodel/embed.go
+++ b/internal/awsmodel/embed.go
@@ -14,6 +14,7 @@ var modelFiles = map[Service]string{
EC2: "models/apis/ec2/2016-11-15/api-2.json",
ECR: "models/apis/ecr/2015-09-21/api-2.json",
ECS: "models/apis/ecs/2014-11-13/api-2.json",
+ EKS: "models/apis/eks/2017-11-01/api-2.json",
ElasticLoadBalancingV2: "models/apis/elasticloadbalancingv2/2015-12-01/api-2.json",
IAM: "models/apis/iam/2010-05-08/api-2.json",
RDS: "models/apis/rds/2014-10-31/api-2.json",
diff --git a/internal/awsmodel/model.go b/internal/awsmodel/model.go
index fda687a74..deab8cd8f 100644
--- a/internal/awsmodel/model.go
+++ b/internal/awsmodel/model.go
@@ -28,6 +28,7 @@ const (
EC2 Service = "ec2"
ECR Service = "ecr"
ECS Service = "ecs"
+ EKS Service = "eks"
ElasticLoadBalancingV2 Service = "elasticloadbalancingv2"
IAM Service = "iam"
RDS Service = "rds"
diff --git a/internal/awsmodel/model_test.go b/internal/awsmodel/model_test.go
index 7c2854794..ca41d03e4 100644
--- a/internal/awsmodel/model_test.go
+++ b/internal/awsmodel/model_test.go
@@ -8,7 +8,7 @@ import (
)
func TestServices(t *testing.T) {
- want := []Service{ACM, EC2, ECR, ECS, ElasticLoadBalancingV2, IAM, RDS, S3, STS}
+ want := []Service{ACM, EC2, ECR, ECS, EKS, ElasticLoadBalancingV2, IAM, RDS, S3, STS}
if got := Services(); !reflect.DeepEqual(got, want) {
t.Fatalf("Services() = %v, want %v", got, want)
}
@@ -24,6 +24,7 @@ func TestLoadAllModels(t *testing.T) {
EC2: {"2016-11-15", 625, 3198},
ECR: {"2015-09-21", 47, 330},
ECS: {"2014-11-13", 56, 400},
+ EKS: {"2017-11-01", 56, 298},
ElasticLoadBalancingV2: {"2015-12-01", 46, 357},
IAM: {"2010-05-08", 159, 497},
RDS: {"2014-10-31", 162, 745},
diff --git a/spinifex/gateway/eks.go b/spinifex/gateway/eks.go
index 04ba3f522..4678b533b 100644
--- a/spinifex/gateway/eks.go
+++ b/spinifex/gateway/eks.go
@@ -8,6 +8,7 @@ import (
"net/http"
"net/url"
"regexp"
+ "slices"
"github.com/mulgadc/bluebottle/pkg/auth"
"github.com/mulgadc/spinifex/spinifex/awserrors"
@@ -216,6 +217,17 @@ var eksRoutes = []eksRoute{
}},
}
+// eksActionNames returns the distinct actions in eksRoutes in stable order.
+// Deduplicated because an action may be reachable by more than one route.
+func eksActionNames() []string {
+ names := make([]string, 0, len(eksRoutes))
+ for _, route := range eksRoutes {
+ names = append(names, route.action)
+ }
+ slices.Sort(names)
+ return slices.Compact(names)
+}
+
// lookupEKSAction matches method+path against eksRoutes, returning the action,
// path params, and handler, or ("", nil, nil, false) on no match.
// path must be r.URL.EscapedPath(): ARNs in path segments are percent-encoded
diff --git a/spinifex/gateway/operations.go b/spinifex/gateway/operations.go
index 8e1f211f5..bedcc5b6b 100644
--- a/spinifex/gateway/operations.go
+++ b/spinifex/gateway/operations.go
@@ -41,6 +41,9 @@ func AWSOperationInventory() map[string]ServiceOperationInventory {
Registered: mapKeys(gateway_ecs.Actions),
Stubbed: gateway_ecs.StubbedActionNames(),
},
+ "eks": {
+ Registered: eksActionNames(),
+ },
"elasticloadbalancingv2": {
Registered: mapKeys(elbv2Actions),
},
diff --git a/spinifex/gateway/operations_test.go b/spinifex/gateway/operations_test.go
index 91969fc9e..653094530 100644
--- a/spinifex/gateway/operations_test.go
+++ b/spinifex/gateway/operations_test.go
@@ -1,8 +1,10 @@
package gateway
import (
+ "slices"
"testing"
+ "github.com/mulgadc/spinifex/internal/awsmodel"
"github.com/stretchr/testify/require"
)
@@ -23,6 +25,9 @@ func TestAWSOperationInventoryClassifiesDispatchers(t *testing.T) {
require.NotContains(t, inventory["ecr"].Stubbed, "DescribeRepositories")
require.Contains(t, inventory["ecr"].Unsupported, "StartImageScan")
+ require.Contains(t, inventory["eks"].Registered, "DescribeCluster")
+ require.NotContains(t, inventory["eks"].Stubbed, "DescribeCluster")
+
require.Contains(t, inventory["rds"].Registered, "CreateDBInstance")
require.NotContains(t, inventory["rds"].Unsupported, "CreateDBInstance")
require.Contains(t, inventory["rds"].Unsupported, "CreateDBCluster")
@@ -31,6 +36,36 @@ func TestAWSOperationInventoryClassifiesDispatchers(t *testing.T) {
require.False(t, hasS3, "S3 is delegated to Predastore, not dispatched here")
}
+func TestAWSOperationInventoryMeasuresEKSAgainstTheModel(t *testing.T) {
+ inventory := AWSOperationInventory()
+
+ eks, err := awsmodel.CompareOperations(awsmodel.EKS, awsmodel.DispatchInventory{
+ Registered: inventory["eks"].Registered,
+ Stubbed: inventory["eks"].Stubbed,
+ Unsupported: inventory["eks"].Unsupported,
+ })
+ require.NoError(t, err)
+ require.Contains(t, eks.Implemented, "DescribeCluster")
+ // Spinifex-internal control-plane routes have no AWS model entry, so they
+ // must be reported as registered outside the pinned model.
+ require.ElementsMatch(t, []string{
+ "GetRecoveryDirective",
+ "ListInternalAddons",
+ "PublishInternal",
+ "WebhookTokenReview",
+ }, eks.Extra)
+ for _, action := range eks.Extra {
+ require.NotContains(t, eks.Implemented, action)
+ }
+}
+
+func TestEKSActionNamesAreSortedAndDistinct(t *testing.T) {
+ names := eksActionNames()
+
+ require.True(t, slices.IsSorted(names), "actions are not sorted: %v", names)
+ require.Len(t, slices.Compact(slices.Clone(names)), len(names), "actions contain duplicates: %v", names)
+}
+
func TestAWSOperationInventoryStatusesAreDisjointSubsets(t *testing.T) {
for service, inventory := range AWSOperationInventory() {
t.Run(service, func(t *testing.T) {