mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 09:01:31 +00:00
fix: route the EKS update reads so they refuse instead of reading as a typo (#1084)
* fix: route the EKS update reads so they refuse instead of reading as a typo
UpdateClusterVersion was registered at POST /clusters/{name}/update-version, which is the nodegroup pattern and not a path the SDK ever calls; AWS puts it at POST /clusters/{name}/updates. The refusal written for it was therefore unreachable, and the router answered InvalidAction, which tells a caller they typed something that does not exist.
ListUpdates and DescribeUpdate had no route at all and answered the same way. Both now refuse with NotImplemented at the gateway rather than through a service method: the update surface they would read is absent, so a round-trip to reach a constant refusal is surface for its own sake.
The absence of the cluster-level update surface is unchanged. This only stops the platform blaming the caller's spelling for its own routing.
* test: account for the two EKS update reads in the cross-tenant table
Both name their cluster by a path segment that carries no account segment, so
neither can name another tenant's resource and neither needs a case of its own.
This commit is contained in:
+13
-1
@@ -53,10 +53,22 @@ var eksRoutes = []eksRoute{
|
||||
func(ctx context.Context, gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) {
|
||||
return gateway_eks.UpdateClusterConfig(ctx, gw.NATSConn, acct, p[0], b)
|
||||
}},
|
||||
{"POST", "/clusters/{clusterName}/update-version", "UpdateClusterVersion",
|
||||
{"POST", "/clusters/{clusterName}/updates", "UpdateClusterVersion",
|
||||
func(ctx context.Context, gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) {
|
||||
return gateway_eks.UpdateClusterVersion(ctx, gw.NATSConn, acct, p[0], b)
|
||||
}},
|
||||
// The update surface these two read is absent, so they refuse here rather
|
||||
// than through a service method: a round-trip to reach a constant refusal
|
||||
// is surface for its own sake. Left unregistered they would answer
|
||||
// InvalidAction, which blames the caller's spelling for a routing gap.
|
||||
{"GET", "/clusters/{clusterName}/updates", "ListUpdates",
|
||||
func(ctx context.Context, gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) {
|
||||
return nil, errors.New(awserrors.ErrorNotImplemented)
|
||||
}},
|
||||
{"GET", "/clusters/{clusterName}/updates/{updateId}", "DescribeUpdate",
|
||||
func(ctx context.Context, gw *GatewayConfig, acct, callerARN string, p []string, b []byte) (any, error) {
|
||||
return nil, errors.New(awserrors.ErrorNotImplemented)
|
||||
}},
|
||||
// Control-plane VM broker: relays bootstrap/state POSTs onto eks.bus.*/eks.state.* NATS subjects.
|
||||
// acct and callerARN are ignored; cluster account comes from the body.
|
||||
{"POST", "/clusters/{clusterName}/internal-publish", "PublishInternal",
|
||||
|
||||
@@ -49,6 +49,8 @@ var eksScopes = map[string][]resourceSource{
|
||||
"DeleteCluster": {sourceCluster},
|
||||
"UpdateClusterConfig": {sourceCluster},
|
||||
"UpdateClusterVersion": {sourceCluster},
|
||||
"ListUpdates": {sourceCluster},
|
||||
"DescribeUpdate": {sourceCluster},
|
||||
"ListClusters": {sourceAny},
|
||||
|
||||
// Internal control-plane routes. The cluster's owning account is a path
|
||||
|
||||
@@ -25,7 +25,9 @@ func TestLookupEKSAction_ResolvesKnownRoutes(t *testing.T) {
|
||||
{"GET", "/clusters/alpha", "DescribeCluster", []string{"alpha"}},
|
||||
{"DELETE", "/clusters/alpha", "DeleteCluster", []string{"alpha"}},
|
||||
{"POST", "/clusters/alpha/update-config", "UpdateClusterConfig", []string{"alpha"}},
|
||||
{"POST", "/clusters/alpha/update-version", "UpdateClusterVersion", []string{"alpha"}},
|
||||
{"POST", "/clusters/alpha/updates", "UpdateClusterVersion", []string{"alpha"}},
|
||||
{"GET", "/clusters/alpha/updates", "ListUpdates", []string{"alpha"}},
|
||||
{"GET", "/clusters/alpha/updates/upd1", "DescribeUpdate", []string{"alpha", "upd1"}},
|
||||
{"POST", "/clusters/alpha/node-groups", "CreateNodegroup", []string{"alpha"}},
|
||||
{"GET", "/clusters/alpha/node-groups", "ListNodegroups", []string{"alpha"}},
|
||||
{"GET", "/clusters/alpha/node-groups/ng1", "DescribeNodegroup", []string{"alpha", "ng1"}},
|
||||
@@ -124,6 +126,8 @@ func TestLookupEKSAction_CoversAllActions(t *testing.T) {
|
||||
"ListClusters": false,
|
||||
"UpdateClusterConfig": false,
|
||||
"UpdateClusterVersion": false,
|
||||
"ListUpdates": false,
|
||||
"DescribeUpdate": false,
|
||||
"DeleteCluster": false,
|
||||
"CreateNodegroup": false,
|
||||
"DescribeNodegroup": false,
|
||||
@@ -226,3 +230,27 @@ type awsCodeError struct{ code string }
|
||||
|
||||
func (e *awsCodeError) Error() string { return e.code }
|
||||
func errAWS(code string) error { return &awsCodeError{code: code} }
|
||||
|
||||
// The update surface is absent, and the honest answer for an action that exists
|
||||
// and is not served is NotImplemented. Unregistered, these two answered
|
||||
// InvalidAction, which tells the caller they typed something that does not exist.
|
||||
func TestLookupEKSAction_UpdateReadsRefuseAsNotImplemented(t *testing.T) {
|
||||
for _, path := range []string{"/clusters/alpha/updates", "/clusters/alpha/updates/upd1"} {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
_, params, handler, ok := eksRouter.lookup(http.MethodGet, path)
|
||||
require.True(t, ok)
|
||||
out, err := handler(context.Background(), nil, "000000000001", "", params, nil)
|
||||
assert.Nil(t, out)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, awserrors.ErrorNotImplemented, err.Error())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// update-version is the nodegroup pattern. The cluster action has never been
|
||||
// reachable at it on AWS, so keeping it registered would serve a path no caller
|
||||
// sends and hide the one they do.
|
||||
func TestLookupEKSAction_ClusterUpdateVersionIsNotAtTheNodegroupPath(t *testing.T) {
|
||||
_, _, _, ok := eksRouter.lookup(http.MethodPost, "/clusters/alpha/update-version")
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
@@ -428,11 +428,11 @@ func eksUncased() map[string]string {
|
||||
"AssociateAccessPolicy", "AssociateIdentityProviderConfig", "CreateAccessEntry", "CreateAddon",
|
||||
"CreateCluster", "CreateNodegroup", "DeleteAccessEntry", "DeleteAddon", "DeleteCluster",
|
||||
"DeleteNodegroup", "DescribeAccessEntry", "DescribeAddon", "DescribeAddonVersions",
|
||||
"DescribeCluster", "DescribeIdentityProviderConfig", "DescribeNodegroup",
|
||||
"DescribeCluster", "DescribeIdentityProviderConfig", "DescribeNodegroup", "DescribeUpdate",
|
||||
"DisassociateAccessPolicy", "DisassociateIdentityProviderConfig", "GetRecoveryDirective",
|
||||
"ListAccessEntries", "ListAccessPolicies", "ListAddons", "ListAssociatedAccessPolicies",
|
||||
"ListClusters", "ListIdentityProviderConfigs", "ListInternalAddons", "ListNodegroups",
|
||||
"PublishInternal", "UpdateAccessEntry", "UpdateAddon", "UpdateClusterConfig",
|
||||
"ListUpdates", "PublishInternal", "UpdateAccessEntry", "UpdateAddon", "UpdateClusterConfig",
|
||||
"UpdateClusterVersion", "UpdateNodegroupConfig", "UpdateNodegroupVersion", "WebhookTokenReview",
|
||||
} {
|
||||
uncased[action] = byName
|
||||
|
||||
Reference in New Issue
Block a user