feat: hide internal commands from compatibility page

This commit is contained in:
Julian Sommer
2026-09-14 12:13:32 +10:00
parent 19a047bccb
commit 613231fd18
13 changed files with 161 additions and 83 deletions
@@ -20,7 +20,7 @@ tags:
Spinifex implements **121 of the 625** operations (**19.4%**) in the EC2 `2016-11-15` API model.
### Spot instances are a mock
### Spot Instances Are a Mock
Spot Instance Requests are a mock over the on-demand `RunInstances` path. A request synchronously launches real VMs on the operator's own compute and is then reported `active` and `fulfilled`. There is no spot market: no bidding, no price rejection, no interruption and no reclamation, and instances are never taken back.
+1 -1
View File
@@ -1,3 +1,3 @@
### Spot instances are a mock
### Spot Instances Are a Mock
Spot Instance Requests are a mock over the on-demand `RunInstances` path. A request synchronously launches real VMs on the operator's own compute and is then reported `active` and `fulfilled`. There is no spot market: no bidding, no price rejection, no interruption and no reclamation, and instances are never taken back.
+11 -7
View File
@@ -26,7 +26,7 @@ Repository metadata is served over the AWS API on the gateway endpoint. Image da
The split explains the largest group of unimplemented operations below. The layer-transfer operations — `BatchCheckLayerAvailability`, `InitiateLayerUpload`, `UploadLayerPart`, `CompleteLayerUpload` and `GetDownloadUrlForLayer` — are registered stubs because the `/v2/` endpoint carries that traffic instead. A client using `docker` or any OCI-compatible tool never calls them.
Vulnerability scanning is not offered, so the image-scanning operations refuse deliberately rather than pretending to have found nothing. Registry replication is likewise absent: a deployment is a single registry, with no cross-region peer to replicate to.
Registry replication is absent as well: a deployment is a single registry, with no cross-region peer to replicate to.
### Operations
@@ -35,7 +35,7 @@ Vulnerability scanning is not offered, so the image-scanning operations refuse d
| `BatchCheckLayerAvailability` | 🟡 Stub |
| `BatchDeleteImage` | ✅ Implemented |
| `BatchGetImage` | ✅ Implemented |
| `BatchGetRepositoryScanningConfiguration` | ⛔ Not applicable |
| `BatchGetRepositoryScanningConfiguration` | ⛔ Not applicable [1](#notes) |
| `CompleteLayerUpload` | 🟡 Stub |
| `CreatePullThroughCacheRule` | ❌ Not implemented |
| `CreateRepository` | ✅ Implemented |
@@ -47,7 +47,7 @@ Vulnerability scanning is not offered, so the image-scanning operations refuse d
| `DeleteRepositoryCreationTemplate` | ❌ Not implemented |
| `DeleteRepositoryPolicy` | ✅ Implemented |
| `DescribeImageReplicationStatus` | ❌ Not implemented |
| `DescribeImageScanFindings` | ⛔ Not applicable |
| `DescribeImageScanFindings` | ⛔ Not applicable [1](#notes) |
| `DescribeImages` | ✅ Implemented |
| `DescribePullThroughCacheRules` | ❌ Not implemented |
| `DescribeRegistry` | 🟡 Stub |
@@ -58,20 +58,20 @@ Vulnerability scanning is not offered, so the image-scanning operations refuse d
| `GetLifecyclePolicy` | ✅ Implemented |
| `GetLifecyclePolicyPreview` | ✅ Implemented |
| `GetRegistryPolicy` | 🟡 Stub |
| `GetRegistryScanningConfiguration` | ⛔ Not applicable |
| `GetRegistryScanningConfiguration` | ⛔ Not applicable [1](#notes) |
| `GetRepositoryPolicy` | ✅ Implemented |
| `InitiateLayerUpload` | 🟡 Stub |
| `ListImages` | ✅ Implemented |
| `ListTagsForResource` | ✅ Implemented |
| `PutImage` | ✅ Implemented |
| `PutImageScanningConfiguration` | ⛔ Not applicable |
| `PutImageScanningConfiguration` | ⛔ Not applicable [1](#notes) |
| `PutImageTagMutability` | ✅ Implemented |
| `PutLifecyclePolicy` | ✅ Implemented |
| `PutRegistryPolicy` | 🟡 Stub |
| `PutRegistryScanningConfiguration` | ⛔ Not applicable |
| `PutRegistryScanningConfiguration` | ⛔ Not applicable [1](#notes) |
| `PutReplicationConfiguration` | 🟡 Stub |
| `SetRepositoryPolicy` | ✅ Implemented |
| `StartImageScan` | ⛔ Not applicable |
| `StartImageScan` | ⛔ Not applicable [1](#notes) |
| `StartLifecyclePolicyPreview` | ✅ Implemented |
| `TagResource` | 🟡 Stub |
| `UntagResource` | 🟡 Stub |
@@ -82,3 +82,7 @@ Vulnerability scanning is not offered, so the image-scanning operations refuse d
| `GetImageScanningConfiguration` | 🔒 Outside the pinned model |
| `ListRepositories` | 🔒 Outside the pinned model |
| `ReplicateImage` | 🔒 Outside the pinned model |
### Notes
1. Image scanning is an AWS-hosted vulnerability service with no equivalent here, so a finding set would always be empty.
+1 -1
View File
@@ -4,4 +4,4 @@ Repository metadata is served over the AWS API on the gateway endpoint. Image da
The split explains the largest group of unimplemented operations below. The layer-transfer operations — `BatchCheckLayerAvailability`, `InitiateLayerUpload`, `UploadLayerPart`, `CompleteLayerUpload` and `GetDownloadUrlForLayer` — are registered stubs because the `/v2/` endpoint carries that traffic instead. A client using `docker` or any OCI-compatible tool never calls them.
Vulnerability scanning is not offered, so the image-scanning operations refuse deliberately rather than pretending to have found nothing. Registry replication is likewise absent: a deployment is a single registry, with no cross-region peer to replicate to.
Registry replication is absent as well: a deployment is a single registry, with no cross-region peer to replicate to.
@@ -84,6 +84,3 @@ Clusters, services and tasks run on EC2 container instances. There is no Fargate
| `UpdateServicePrimaryTaskSet` | ❌ Not implemented |
| `UpdateTaskProtection` | ❌ Not implemented |
| `UpdateTaskSet` | ❌ Not implemented |
| `PollAssignments` | 🔒 Outside the pinned model |
| `ProvisionCapacity` | 🔒 Outside the pinned model |
| `ReportTaskGPU` | 🔒 Outside the pinned model |
@@ -20,21 +20,6 @@ tags:
Spinifex implements **34 of the 56** operations (**60.7%**) in the EKS `2017-11-01` API model.
### Routing
EKS is REST-JSON, dispatched by HTTP method and path rather than by an action parameter, so the gateway's routing table is the authoritative list of what it serves. Clusters run K3s on control-plane VMs; nodegroups are EC2 instances joined to them.
### Internal routes
Four routes on this surface are internal, not AWS actions, and appear in the table below as registered outside the pinned model:
- `PublishInternal` — the control-plane VM broker, relaying bootstrap and state posts onto the message bus.
- `WebhookTokenReview` — the token webhook posting bearer tokens for host-side resolution.
- `ListInternalAddons` — the on-VM add-on sync agent fetching its staged manifests.
- `GetRecoveryDirective` — the on-VM recovery agent reading its per-member directive at boot.
None is callable with an ordinary tenant credential. Each names the target account in the path rather than taking it from the credential, so each requires a control-plane instance-role session bound to the named cluster; a broad `eks:*` grant does not reach them.
### Operations
| Operation | Status |
@@ -95,7 +80,3 @@ None is callable with an ordinary tenant credential. Each names the target accou
| `UpdateNodegroupConfig` | ✅ Implemented |
| `UpdateNodegroupVersion` | ✅ Implemented |
| `UpdatePodIdentityAssociation` | ❌ Not implemented |
| `GetRecoveryDirective` | 🔒 Outside the pinned model |
| `ListInternalAddons` | 🔒 Outside the pinned model |
| `PublishInternal` | 🔒 Outside the pinned model |
| `WebhookTokenReview` | 🔒 Outside the pinned model |
@@ -1,14 +0,0 @@
### Routing
EKS is REST-JSON, dispatched by HTTP method and path rather than by an action parameter, so the gateway's routing table is the authoritative list of what it serves. Clusters run K3s on control-plane VMs; nodegroups are EC2 instances joined to them.
### Internal routes
Four routes on this surface are internal, not AWS actions, and appear in the table below as registered outside the pinned model:
- `PublishInternal` — the control-plane VM broker, relaying bootstrap and state posts onto the message bus.
- `WebhookTokenReview` — the token webhook posting bearer tokens for host-side resolution.
- `ListInternalAddons` — the on-VM add-on sync agent fetching its staged manifests.
- `GetRecoveryDirective` — the on-VM recovery agent reading its per-member directive at boot.
None is callable with an ordinary tenant credential. Each names the target account in the path rather than taking it from the credential, so each requires a control-plane instance-role session bound to the named cluster; a broad `eks:*` grant does not reach them.
@@ -75,6 +75,4 @@ The data plane is a system-managed load balancer VM, launched automatically duri
| `SetSecurityGroups` | ✅ Implemented |
| `SetSubnets` | ✅ Implemented |
| `DescribeListenerAttributes` | 🔒 Outside the pinned model |
| `GetLBConfig` | 🔒 Outside the pinned model |
| `LBAgentHeartbeat` | 🔒 Outside the pinned model |
| `ModifyListenerAttributes` | 🔒 Outside the pinned model |
+55 -2
View File
@@ -64,7 +64,18 @@
"ecr",
"containers",
"registry"
]
],
"notApplicable": {
"BatchGetRepositoryScanningConfiguration": "image-scanning",
"DescribeImageScanFindings": "image-scanning",
"GetRegistryScanningConfiguration": "image-scanning",
"PutImageScanningConfiguration": "image-scanning",
"PutRegistryScanningConfiguration": "image-scanning",
"StartImageScan": "image-scanning"
},
"notes": {
"image-scanning": "Image scanning is an AWS-hosted vulnerability service with no equivalent here, so a finding set would always be empty."
}
},
"ecs": {
"slug": "ecs-api-coverage",
@@ -79,6 +90,11 @@
"ecs",
"containers",
"orchestration"
],
"internal": [
"PollAssignments",
"ProvisionCapacity",
"ReportTaskGPU"
]
},
"eks": {
@@ -94,6 +110,12 @@
"eks",
"kubernetes",
"containers"
],
"internal": [
"GetRecoveryDirective",
"ListInternalAddons",
"PublishInternal",
"WebhookTokenReview"
]
},
"elasticloadbalancingv2": {
@@ -109,6 +131,10 @@
"elbv2",
"load balancing",
"networking"
],
"internal": [
"GetLBConfig",
"LBAgentHeartbeat"
]
},
"iam": {
@@ -167,7 +193,34 @@
"databases",
"postgresql",
"mariadb"
]
],
"internal": [
"AcknowledgeDBBootstrap",
"GetDBBootstrapConfig",
"PollDBCommands",
"RegisterDBInstance",
"SubmitDBStateChange"
],
"notApplicable": {
"CreateDBCluster": "clusters",
"CreateDBInstanceReadReplica": "read-replicas",
"CreateOptionGroup": "option-groups",
"DeleteDBCluster": "clusters",
"DeleteOptionGroup": "option-groups",
"DescribeDBClusters": "clusters",
"DescribeOptionGroups": "option-groups",
"FailoverDBCluster": "clusters",
"ModifyDBCluster": "clusters",
"ModifyOptionGroup": "option-groups",
"PromoteReadReplica": "read-replicas",
"RestoreDBInstanceToPointInTime": "point-in-time-restore"
},
"notes": {
"clusters": "Aurora and Multi-AZ clusters are not offered; a DB instance here is a single VM running the engine directly.",
"option-groups": "Option groups configure engine add-ons for engines this platform does not offer, such as Oracle and SQL Server.",
"point-in-time-restore": "Point-in-time restore needs continuous transaction-log archival, which the backup path does not keep.",
"read-replicas": "Replication between instances is not offered, so there is no replica to create or promote."
}
},
"sts": {
"slug": "sts-api-coverage",
+21 -21
View File
@@ -25,7 +25,7 @@ Spinifex implements **26 of the 162** operations (**16.0%**) in the RDS `2014-10
Spinifex offers PostgreSQL and MariaDB. Each DB instance is one dedicated system-owned VM running the engine directly, launched from a platform AMI and hidden from the customer's EC2 API. `Engine` is fixed at create: there is no in-place engine change, no cross-engine snapshot restore and no migration between the two.
`mysql` is not an accepted engine and is not an alias for `mariadb`. MariaDB is offered under its own AWS engine name, exactly as AWS RDS offers it, so a client — including Terraform's `aws_db_instance` — must set `engine = "mariadb"`. Aliasing would report an engine and a version the instance is not running, and the discrepancy would propagate into `DescribeDBInstances`, parameter-group families and snapshot metadata.
`mysql` is not an accepted engine and is not an alias for `mariadb`. MariaDB is offered under its own AWS engine name, exactly as AWS RDS offers it, so a client — including Terraform's `aws_db_instance` — must set `engine = "mariadb"`.
Engine versions are pinned per engine. An `EngineVersion` naming anything but the pin is rejected, including a narrower minor version, because the AMI makes no promise about which minor it carries.
@@ -37,11 +37,9 @@ The engine is reached over a customer-account ENI injected into a subnet of the
Both engines enforce encrypted connections by default: `rds.force_ssl` on PostgreSQL and `require_secure_transport` on MariaDB. For MariaDB this is a deliberate divergence from AWS, which leaves it off. Both are boolean, modifiable and dynamic, so setting either to `0` in a parameter group restores plaintext without a reboot.
Enforcement requires that the connection is encrypted, not that the client validates the certificate. The engine serves a per-instance certificate signed by the cluster CA carrying both the ENI address and the DNS name, so a client that wants full verification can have it. A deployment holding no cluster CA cannot serve TLS at all, and a parameter group asking for enforcement there is refused rather than quietly ignored.
### Rejected parameters
A parameter whose omission would create a false safety, security or availability guarantee is rejected with `InvalidParameterValue` rather than silently dropped. Parameters that are merely inert — `AutoMinorVersionUpgrade`, `CopyTagsToSnapshot`, `DeleteAutomatedBackups`, Performance Insights and Enhanced Monitoring fields — are accepted as no-ops.
A parameter whose omission would create a false safety, security or availability guarantee is rejected with `InvalidParameterValue` rather than silently dropped.
| Parameter | Why it is rejected |
|-----------|--------------------|
@@ -89,12 +87,12 @@ A parameter whose omission would create a false safety, security or availability
| `CopyOptionGroup` | ❌ Not implemented |
| `CreateBlueGreenDeployment` | ❌ Not implemented |
| `CreateCustomDBEngineVersion` | ❌ Not implemented |
| `CreateDBCluster` | ⛔ Not applicable |
| `CreateDBCluster` | ⛔ Not applicable [1](#notes) |
| `CreateDBClusterEndpoint` | ❌ Not implemented |
| `CreateDBClusterParameterGroup` | ❌ Not implemented |
| `CreateDBClusterSnapshot` | ❌ Not implemented |
| `CreateDBInstance` | ✅ Implemented |
| `CreateDBInstanceReadReplica` | ⛔ Not applicable |
| `CreateDBInstanceReadReplica` | ⛔ Not applicable [4](#notes) |
| `CreateDBParameterGroup` | ✅ Implemented |
| `CreateDBProxy` | ❌ Not implemented |
| `CreateDBProxyEndpoint` | ❌ Not implemented |
@@ -105,11 +103,11 @@ A parameter whose omission would create a false safety, security or availability
| `CreateEventSubscription` | ❌ Not implemented |
| `CreateGlobalCluster` | ❌ Not implemented |
| `CreateIntegration` | ❌ Not implemented |
| `CreateOptionGroup` | ⛔ Not applicable |
| `CreateOptionGroup` | ⛔ Not applicable [2](#notes) |
| `CreateTenantDatabase` | ❌ Not implemented |
| `DeleteBlueGreenDeployment` | ❌ Not implemented |
| `DeleteCustomDBEngineVersion` | ❌ Not implemented |
| `DeleteDBCluster` | ⛔ Not applicable |
| `DeleteDBCluster` | ⛔ Not applicable [1](#notes) |
| `DeleteDBClusterAutomatedBackup` | ❌ Not implemented |
| `DeleteDBClusterEndpoint` | ❌ Not implemented |
| `DeleteDBClusterParameterGroup` | ❌ Not implemented |
@@ -126,7 +124,7 @@ A parameter whose omission would create a false safety, security or availability
| `DeleteEventSubscription` | ❌ Not implemented |
| `DeleteGlobalCluster` | ❌ Not implemented |
| `DeleteIntegration` | ❌ Not implemented |
| `DeleteOptionGroup` | ⛔ Not applicable |
| `DeleteOptionGroup` | ⛔ Not applicable [2](#notes) |
| `DeleteTenantDatabase` | ❌ Not implemented |
| `DeregisterDBProxyTargets` | ❌ Not implemented |
| `DescribeAccountAttributes` | ❌ Not implemented |
@@ -139,7 +137,7 @@ A parameter whose omission would create a false safety, security or availability
| `DescribeDBClusterParameters` | ❌ Not implemented |
| `DescribeDBClusterSnapshotAttributes` | ❌ Not implemented |
| `DescribeDBClusterSnapshots` | ❌ Not implemented |
| `DescribeDBClusters` | ⛔ Not applicable |
| `DescribeDBClusters` | ⛔ Not applicable [1](#notes) |
| `DescribeDBEngineVersions` | ✅ Implemented |
| `DescribeDBInstanceAutomatedBackups` | ✅ Implemented |
| `DescribeDBInstances` | ✅ Implemented |
@@ -166,7 +164,7 @@ A parameter whose omission would create a false safety, security or availability
| `DescribeGlobalClusters` | ❌ Not implemented |
| `DescribeIntegrations` | ❌ Not implemented |
| `DescribeOptionGroupOptions` | ❌ Not implemented |
| `DescribeOptionGroups` | ⛔ Not applicable |
| `DescribeOptionGroups` | ⛔ Not applicable [2](#notes) |
| `DescribeOrderableDBInstanceOptions` | ✅ Implemented |
| `DescribePendingMaintenanceActions` | ❌ Not implemented |
| `DescribeReservedDBInstances` | ❌ Not implemented |
@@ -177,14 +175,14 @@ A parameter whose omission would create a false safety, security or availability
| `DisableHttpEndpoint` | ❌ Not implemented |
| `DownloadDBLogFilePortion` | ❌ Not implemented |
| `EnableHttpEndpoint` | ❌ Not implemented |
| `FailoverDBCluster` | ⛔ Not applicable |
| `FailoverDBCluster` | ⛔ Not applicable [1](#notes) |
| `FailoverGlobalCluster` | ❌ Not implemented |
| `ListTagsForResource` | ✅ Implemented |
| `ModifyActivityStream` | ❌ Not implemented |
| `ModifyCertificates` | ❌ Not implemented |
| `ModifyCurrentDBClusterCapacity` | ❌ Not implemented |
| `ModifyCustomDBEngineVersion` | ❌ Not implemented |
| `ModifyDBCluster` | ⛔ Not applicable |
| `ModifyDBCluster` | ⛔ Not applicable [1](#notes) |
| `ModifyDBClusterEndpoint` | ❌ Not implemented |
| `ModifyDBClusterParameterGroup` | ❌ Not implemented |
| `ModifyDBClusterSnapshotAttribute` | ❌ Not implemented |
@@ -201,9 +199,9 @@ A parameter whose omission would create a false safety, security or availability
| `ModifyEventSubscription` | ❌ Not implemented |
| `ModifyGlobalCluster` | ❌ Not implemented |
| `ModifyIntegration` | ❌ Not implemented |
| `ModifyOptionGroup` | ⛔ Not applicable |
| `ModifyOptionGroup` | ⛔ Not applicable [2](#notes) |
| `ModifyTenantDatabase` | ❌ Not implemented |
| `PromoteReadReplica` | ⛔ Not applicable |
| `PromoteReadReplica` | ⛔ Not applicable [4](#notes) |
| `PromoteReadReplicaDBCluster` | ❌ Not implemented |
| `PurchaseReservedDBInstancesOffering` | ❌ Not implemented |
| `RebootDBCluster` | ❌ Not implemented |
@@ -222,7 +220,7 @@ A parameter whose omission would create a false safety, security or availability
| `RestoreDBClusterToPointInTime` | ❌ Not implemented |
| `RestoreDBInstanceFromDBSnapshot` | ✅ Implemented |
| `RestoreDBInstanceFromS3` | ❌ Not implemented |
| `RestoreDBInstanceToPointInTime` | ⛔ Not applicable |
| `RestoreDBInstanceToPointInTime` | ⛔ Not applicable [3](#notes) |
| `RevokeDBSecurityGroupIngress` | ❌ Not implemented |
| `StartActivityStream` | ❌ Not implemented |
| `StartDBCluster` | ❌ Not implemented |
@@ -236,8 +234,10 @@ A parameter whose omission would create a false safety, security or availability
| `SwitchoverBlueGreenDeployment` | ❌ Not implemented |
| `SwitchoverGlobalCluster` | ❌ Not implemented |
| `SwitchoverReadReplica` | ❌ Not implemented |
| `AcknowledgeDBBootstrap` | 🔒 Outside the pinned model |
| `GetDBBootstrapConfig` | 🔒 Outside the pinned model |
| `PollDBCommands` | 🔒 Outside the pinned model |
| `RegisterDBInstance` | 🔒 Outside the pinned model |
| `SubmitDBStateChange` | 🔒 Outside the pinned model |
### Notes
1. Aurora and Multi-AZ clusters are not offered; a DB instance here is a single VM running the engine directly.
2. Option groups configure engine add-ons for engines this platform does not offer, such as Oracle and SQL Server.
3. Point-in-time restore needs continuous transaction-log archival, which the backup path does not keep.
4. Replication between instances is not offered, so there is no replica to create or promote.
+2 -4
View File
@@ -2,7 +2,7 @@
Spinifex offers PostgreSQL and MariaDB. Each DB instance is one dedicated system-owned VM running the engine directly, launched from a platform AMI and hidden from the customer's EC2 API. `Engine` is fixed at create: there is no in-place engine change, no cross-engine snapshot restore and no migration between the two.
`mysql` is not an accepted engine and is not an alias for `mariadb`. MariaDB is offered under its own AWS engine name, exactly as AWS RDS offers it, so a client — including Terraform's `aws_db_instance` — must set `engine = "mariadb"`. Aliasing would report an engine and a version the instance is not running, and the discrepancy would propagate into `DescribeDBInstances`, parameter-group families and snapshot metadata.
`mysql` is not an accepted engine and is not an alias for `mariadb`. MariaDB is offered under its own AWS engine name, exactly as AWS RDS offers it, so a client — including Terraform's `aws_db_instance` — must set `engine = "mariadb"`.
Engine versions are pinned per engine. An `EngineVersion` naming anything but the pin is rejected, including a narrower minor version, because the AMI makes no promise about which minor it carries.
@@ -14,11 +14,9 @@ The engine is reached over a customer-account ENI injected into a subnet of the
Both engines enforce encrypted connections by default: `rds.force_ssl` on PostgreSQL and `require_secure_transport` on MariaDB. For MariaDB this is a deliberate divergence from AWS, which leaves it off. Both are boolean, modifiable and dynamic, so setting either to `0` in a parameter group restores plaintext without a reboot.
Enforcement requires that the connection is encrypted, not that the client validates the certificate. The engine serves a per-instance certificate signed by the cluster CA carrying both the ENI address and the DNS name, so a client that wants full verification can have it. A deployment holding no cluster CA cannot serve TLS at all, and a parameter group asking for enforcement there is refused rather than quietly ignored.
### Rejected parameters
A parameter whose omission would create a false safety, security or availability guarantee is rejected with `InvalidParameterValue` rather than silently dropped. Parameters that are merely inert — `AutoMinorVersionUpgrade`, `CopyTagsToSnapshot`, `DeleteAutomatedBackups`, Performance Insights and Enhanced Monitoring fields — are accepted as no-ops.
A parameter whose omission would create a false safety, security or availability guarantee is rejected with `InvalidParameterValue` rather than silently dropped.
| Parameter | Why it is rejected |
|-----------|--------------------|
+26 -6
View File
@@ -50,16 +50,19 @@ func (c OperationCoverage) ImplementedPercent() float64 {
}
// OperationStatuses returns every modelled operation with its dispatch state,
// followed by the registered operations the pinned model does not describe.
// followed by the registered operations the pinned model does not describe,
// less the internal routes the page declares.
//
// notApplicable declares operations the platform will never serve, mapped to
// the note key explaining why. A refusing handler is the same fact reached
// mechanically, so both publish as StatusNotApplicable.
func (c OperationCoverage) OperationStatuses(notApplicable map[string]string) ([]OperationStatus, error) {
// The page's NotApplicable declares operations the platform will never serve,
// mapped to the note key explaining why. A refusing handler is the same fact
// reached mechanically, so both publish as StatusNotApplicable.
func (c OperationCoverage) OperationStatuses(page PageMetadata) ([]OperationStatus, error) {
registered := toSet(c.Registered)
stubbed := toSet(c.Stubbed)
unsupported := toSet(c.Unsupported)
modelled := toSet(c.Modelled)
extra := toSet(c.Extra)
notApplicable := page.NotApplicable
for operation, noteKey := range notApplicable {
switch {
@@ -72,6 +75,14 @@ func (c OperationCoverage) OperationStatuses(notApplicable map[string]string) ([
}
}
internal := map[string]bool{}
for _, operation := range page.Internal {
if !extra[operation] {
return nil, fmt.Errorf("awsmodel: %s route %q is declared internal but is not a registered route outside the pinned model", c.Service, operation)
}
internal[operation] = true
}
statuses := make([]OperationStatus, 0, len(c.Modelled)+len(c.Extra))
for _, operation := range c.Modelled {
status := StatusNotImplemented
@@ -90,6 +101,9 @@ func (c OperationCoverage) OperationStatuses(notApplicable map[string]string) ([
statuses = append(statuses, OperationStatus{Operation: operation, Status: status})
}
for _, operation := range c.Extra {
if internal[operation] {
continue
}
statuses = append(statuses, OperationStatus{Operation: operation, Status: StatusOutsideModel})
}
return statuses, nil
@@ -115,6 +129,12 @@ type PageMetadata struct {
// dispatch tables at render, so a typo or a later implementation fails the
// build rather than publishing a claim that has quietly become false.
NotApplicable map[string]string `json:"notApplicable,omitempty"`
// Internal names registered routes that carry platform plumbing rather than
// a tenant-callable AWS action, which the page leaves out. A route outside
// the pinned model that is not declared here is published, so a real AWS
// operation newer than the pin surfaces instead of disappearing quietly.
Internal []string `json:"internal,omitempty"`
}
// PageSet is the metadata for the index page and every service page.
@@ -253,7 +273,7 @@ func RenderServicePage(coverage OperationCoverage, pages PageSet, intro string)
body.WriteString(strings.TrimSpace(intro) + "\n\n")
}
statuses, err := coverage.OperationStatuses(page.NotApplicable)
statuses, err := coverage.OperationStatuses(page)
if err != nil {
return "", err
}
+43 -2
View File
@@ -3,6 +3,7 @@ package awsmodel_test
import (
"os"
"path/filepath"
"slices"
"strings"
"testing"
@@ -157,7 +158,7 @@ func TestOperationStatusesRejectsStaleNotApplicableClaims(t *testing.T) {
},
} {
t.Run(name, func(t *testing.T) {
if _, err := coverage.OperationStatuses(declared); err == nil {
if _, err := coverage.OperationStatuses(PageMetadata{NotApplicable: declared}); err == nil {
t.Fatal("OperationStatuses accepted a claim that cannot hold")
}
})
@@ -170,7 +171,9 @@ func TestOperationStatusesCarriesTheNoteKey(t *testing.T) {
t.Fatal(err)
}
statuses, err := coverage.OperationStatuses(map[string]string{"GetFederationToken": "no-broker"})
statuses, err := coverage.OperationStatuses(PageMetadata{
NotApplicable: map[string]string{"GetFederationToken": "no-broker"},
})
if err != nil {
t.Fatal(err)
}
@@ -186,6 +189,44 @@ func TestOperationStatusesCarriesTheNoteKey(t *testing.T) {
t.Fatal("declared operation is missing from the table")
}
// Platform plumbing is left out of a page about the AWS API, but only where it
// is declared: an undeclared route stays visible, so a real AWS operation newer
// than the pinned model cannot disappear by being registered.
func TestOperationStatusesHidesDeclaredInternalRoutes(t *testing.T) {
coverage, err := CompareOperations(STS, DispatchInventory{
Registered: []string{"AssumeRole", "PublishInternal", "AheadOfThePin"},
})
if err != nil {
t.Fatal(err)
}
statuses, err := coverage.OperationStatuses(PageMetadata{Internal: []string{"PublishInternal"}})
if err != nil {
t.Fatal(err)
}
for _, status := range statuses {
if status.Operation == "PublishInternal" {
t.Error("a declared internal route is published")
}
}
if !slices.ContainsFunc(statuses, func(s OperationStatus) bool {
return s.Operation == "AheadOfThePin" && s.Status == StatusOutsideModel
}) {
t.Error("an undeclared route outside the pinned model is not published")
}
}
func TestOperationStatusesRejectsAnInternalRouteThatIsModelled(t *testing.T) {
coverage, err := CompareOperations(STS, DispatchInventory{Registered: []string{"AssumeRole"}})
if err != nil {
t.Fatal(err)
}
if _, err := coverage.OperationStatuses(PageMetadata{Internal: []string{"AssumeRole"}}); err == nil {
t.Fatal("OperationStatuses hid a modelled AWS operation as internal plumbing")
}
}
// A footnote reused across operations is written once, and both directions of
// the reference are checked so neither side can rot.
func TestRenderServicePageFootnotesSharedNotes(t *testing.T) {