diff --git a/docs/compatibility/ec2-api-coverage/README.md b/docs/compatibility/ec2-api-coverage/README.md index 74bba539a..9f2115a8e 100644 --- a/docs/compatibility/ec2-api-coverage/README.md +++ b/docs/compatibility/ec2-api-coverage/README.md @@ -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. diff --git a/docs/compatibility/ec2-api-coverage/intro.md b/docs/compatibility/ec2-api-coverage/intro.md index 08d8945d0..c59acdbcb 100644 --- a/docs/compatibility/ec2-api-coverage/intro.md +++ b/docs/compatibility/ec2-api-coverage/intro.md @@ -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. diff --git a/docs/compatibility/ecr-api-coverage/README.md b/docs/compatibility/ecr-api-coverage/README.md index a03f2af34..bbcde45f3 100644 --- a/docs/compatibility/ecr-api-coverage/README.md +++ b/docs/compatibility/ecr-api-coverage/README.md @@ -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. diff --git a/docs/compatibility/ecr-api-coverage/intro.md b/docs/compatibility/ecr-api-coverage/intro.md index 5bef24aa5..d8c849e47 100644 --- a/docs/compatibility/ecr-api-coverage/intro.md +++ b/docs/compatibility/ecr-api-coverage/intro.md @@ -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. diff --git a/docs/compatibility/ecs-api-coverage/README.md b/docs/compatibility/ecs-api-coverage/README.md index 9bc2084d6..41413acf8 100644 --- a/docs/compatibility/ecs-api-coverage/README.md +++ b/docs/compatibility/ecs-api-coverage/README.md @@ -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 | diff --git a/docs/compatibility/eks-api-coverage/README.md b/docs/compatibility/eks-api-coverage/README.md index e6d4e2462..0a3201ca6 100644 --- a/docs/compatibility/eks-api-coverage/README.md +++ b/docs/compatibility/eks-api-coverage/README.md @@ -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 | diff --git a/docs/compatibility/eks-api-coverage/intro.md b/docs/compatibility/eks-api-coverage/intro.md deleted file mode 100644 index 0d0475945..000000000 --- a/docs/compatibility/eks-api-coverage/intro.md +++ /dev/null @@ -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. diff --git a/docs/compatibility/elbv2-api-coverage/README.md b/docs/compatibility/elbv2-api-coverage/README.md index d82cb29bf..9de868328 100644 --- a/docs/compatibility/elbv2-api-coverage/README.md +++ b/docs/compatibility/elbv2-api-coverage/README.md @@ -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 | diff --git a/docs/compatibility/pages.json b/docs/compatibility/pages.json index e00581fe3..ca36864e7 100644 --- a/docs/compatibility/pages.json +++ b/docs/compatibility/pages.json @@ -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", diff --git a/docs/compatibility/rds-api-coverage/README.md b/docs/compatibility/rds-api-coverage/README.md index c9a4d3e59..c74ee09b8 100644 --- a/docs/compatibility/rds-api-coverage/README.md +++ b/docs/compatibility/rds-api-coverage/README.md @@ -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. diff --git a/docs/compatibility/rds-api-coverage/intro.md b/docs/compatibility/rds-api-coverage/intro.md index 03e2d8c35..f56508dc0 100644 --- a/docs/compatibility/rds-api-coverage/intro.md +++ b/docs/compatibility/rds-api-coverage/intro.md @@ -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 | |-----------|--------------------| diff --git a/internal/awsmodel/page.go b/internal/awsmodel/page.go index 2434add26..1e4fe3419 100644 --- a/internal/awsmodel/page.go +++ b/internal/awsmodel/page.go @@ -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 } diff --git a/internal/awsmodel/page_test.go b/internal/awsmodel/page_test.go index 532d40f40..f93162d20 100644 --- a/internal/awsmodel/page_test.go +++ b/internal/awsmodel/page_test.go @@ -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) {