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) {