Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ DevCloud is an **on-ramp to the cloud**, not a replacement for it. The goal is t

## Features

- **205 AWS services registered, 201 serving at least one operation** — the remaining 4 are routed and decline with a clean AWS error rather than letting the call bill a real account. See [coverage.md](docs/coverage.md) for what the numbers do and do not promise.
- **boto3-compatible** — a 1,144-test suite runs in CI (`make test-compat`) across every registered service. Unsupported operations return a clean AWS error, never a false success.
- **213 AWS services registered, 209 serving at least one operation** — the remaining 4 are routed and decline with a clean AWS error rather than letting the call bill a real account. See [coverage.md](docs/coverage.md) for what the numbers do and do not promise.
- **boto3-compatible** — a 1,156-test suite runs in CI (`make test-compat`) across every registered service. Unsupported operations return a clean AWS error, never a false success.
- **Cross-service integration** — CloudFormation provisioning, DynamoDB Streams → Lambda, EventBridge targets, S3 → Lambda
- **Smithy-driven codegen** — Go types, routers and error catalogues generated from AWS models, with a weekly sync workflow that keeps them current
- **Single binary, zero-config** — one Docker image, one port (4747), no config file required; override with `DEVCLOUD_SERVICES`, `DEVCLOUD_DATA_DIR`, `DEVCLOUD_PORT`
Expand Down
7 changes: 7 additions & 0 deletions changes/unreleased/Added-20260913-140000.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: Added
body: 'Eight AWS services whose operations the generic CRUD engine cannot classify
— payment-cryptography-data, geo-routes, cloudsearch-domain, ec2-instance-connect,
kinesis-video-webrtc-storage, marketplace-commerce-analytics, eks-auth and
inspector-scan — now have hand-written providers, bringing coverage to 213
registered and 209 serving'
Issue: "161"
8 changes: 8 additions & 0 deletions cmd/devcloud/imports.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions cmd/devcloud/routing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: Apache-2.0

// cmd/devcloud/routing_test.go
package main

import (
"net/http/httptest"
"testing"

"github.com/skyoo2003/devcloud/internal/gateway"
"github.com/stretchr/testify/assert"
)

// TestContestedDataPlanesResolveToThemselves covers the three Phase 2 services
// that sign with a name an already-registered neighbour claims. Nothing else in
// the tree can catch this: the fidelity manifest is derived from dispatch
// literals, so it reports all three as fully hand-verified whether or not the
// gateway ever routes to them.
//
// It lives here rather than in internal/gateway because this package already
// blank-imports every service and the generated crudregistry, so the tables
// under test are the real ones.
func TestContestedDataPlanesResolveToThemselves(t *testing.T) {
cases := []struct{ name, signingName, method, uri, want string }{
{"payment_crypto_encrypt", "payment-cryptography", "POST", "/keys/k1/encrypt", "paymentcryptographydata"},
{"payment_crypto_verify_mac", "payment-cryptography", "POST", "/mac/verify", "paymentcryptographydata"},
{"cloudsearch_search", "cloudsearch", "GET", "/2013-01-01/search?format=sdk&pretty=true&q=x", "cloudsearchdomain"},
// botocore converts Search to a POST with a form body, so this — not the
// modelled GET above — is the request a real client sends. Asserting only
// the model's shape is how the gateway came to hand this to cloudsearch.
{"cloudsearch_search_as_boto3_sends_it", "cloudsearch", "POST", "/2013-01-01/search", "cloudsearchdomain"},
{"cloudsearch_upload", "cloudsearch", "POST", "/2013-01-01/documents/batch?format=sdk", "cloudsearchdomain"},
{"kvs_join", "kinesisvideo", "POST", "/joinStorageSession", "kinesisvideowebrtcstorage"},
{"kvs_join_as_viewer", "kinesisvideo", "POST", "/joinStorageSessionAsViewer", "kinesisvideowebrtcstorage"},
// The parents keep everything they model. A split that stole these would
// be a regression dressed as a fix.
{"kinesisvideo_keeps_its_own", "kinesisvideo", "POST", "/createStream", "kinesisvideo"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
req := httptest.NewRequest(c.method, c.uri, nil)
req.Header.Set("Authorization",
"AWS4-HMAC-SHA256 Credential=AKIA/20130524/us-east-1/"+c.signingName+"/aws4_request, Signature=abc")
proto, service := gateway.DetectProtocol(req)
assert.Equal(t, "rest-json", proto)
assert.Equal(t, c.want, service)
})
}
}
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

| Page | What it covers |
|---|---|
| [Coverage](coverage.md) | 205 registered / 201 serving — what the counts promise, and the target |
| [Coverage](coverage.md) | 213 registered / 209 serving — what the counts promise, and the target |
| [Compatibility Policy](compatibility-policy.md) | What v1.0 guarantees across 1.x, what it does not, and how deprecation works |
| [Fidelity Manifest](fidelity-manifest.md) | Per-operation tiers: how much to trust any given call |
| [CRUD Engine](crud-engine.md) | How engine-served operations behave, and where they stop |
Expand Down
2 changes: 1 addition & 1 deletion docs/compatibility-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ the operation. `CreateFunction` in `test_lambda.py` shows all three cases at onc
| `FunctionArn` | present | presence only — not that it stays ARN-shaped |
| `Runtime`, `Handler`, `MemorySize` | not asserted | nothing, though today's response includes them |

That narrowness is the point: it is the promise the repo can actually keep. The suite — 1,144
That narrowness is the point: it is the promise the repo can actually keep. The suite — 1,156
tests driving real boto3 clients — runs in CI on every push and again against the tagged commit
before a release publishes, so breaking an assertion fails the build rather than depending on
review discipline. Anything the suite does not assert rests on nothing but intent. Widening the
Expand Down
24 changes: 15 additions & 9 deletions docs/coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ alone.

| Number | What it means | Today |
|---|---|---|
| **Registered** | The gateway routes the service, so the call reaches DevCloud instead of real AWS. | **205** |
| **Serving ≥1 operation** | At least one operation returns a real, store-backed answer. | **201** |
| **Registered** | The gateway routes the service, so the call reaches DevCloud instead of real AWS. | **213** |
| **Serving ≥1 operation** | At least one operation returns a real, store-backed answer. | **209** |
| **Registered-only** | Routed, but every operation declines with a clean AWS error. | **4** |
| **Compatibility-tested** | A boto3 test exercises the service in CI and passes. | **203** |
| **Compatibility-tested** | A boto3 test exercises the service in CI and passes. | **211** |

Per operation, from the [fidelity manifest](fidelity-manifest.md):

| Tier | Operations |
|---|---|
| `hand-verified` | 4,497 |
| `hand-verified` | 4,528 |
| `auto-crud` | 5,193 |
| `unimplemented` | 2,717 |
| **total known** | **12,407** |
| **total known** | **12,438** |

> **The coverage target is 205 services, not 431.** It was 431, and the evidence
> did not support it — see [The target](#the-target).
Expand All @@ -37,8 +37,8 @@ the second still stops it.

| Protocol | Services | Operation name comes from |
|---|---|---|
| `rest-json` | 93 | HTTP method + path (`internal/shared/httproute`) |
| `json-1.1` | 64 | the `X-Amz-Target` header |
| `rest-json` | 99 | HTTP method + path (`internal/shared/httproute`) |
| `json-1.1` | 66 | the `X-Amz-Target` header |
| `json-1.0` | 17 | the `X-Amz-Target` header |
| `query` | 15 | the `Action` form field |
| `rest-xml` | 4 | HTTP method + path |
Expand All @@ -64,7 +64,7 @@ when a provider returns `plugin.ErrUnhandledOp`, so a hand-written provider that
refuses unknown operations itself (`apigatewayv2`, `xray`) never reaches it. The
manifest records this per service as `EngineWired`.

## Why compatibility-tested is 203, not 205
## Why compatibility-tested is 211, not 213

`tests/compatibility/test_service_smoke.py` parametrises over the generated
service list rather than a hand-written one, so a service cannot be registered
Expand All @@ -90,6 +90,12 @@ All four Lex clients sign as `lex` and none is named `lex`, so: `GET /bots` is
siblings — `DeleteBot` at `DELETE /bots/{id}` — and it is refused rather than
guessed: deleting the wrong bot is worse than an honest error.

Three data planes are separated the same way without holding a single
CRUD-classifiable operation: `payment-cryptography-data`, `cloudsearch-domain`
and `kinesis-video-webrtc-storage` declare their own route tables through
`crud.RegisterRoutes`, so route matching tells them apart from the neighbour
whose signing name they borrow. No override names a winner — the model does.

## What counts as a service

Upstream publishes 431 model files, and DevCloud counts **model files**, one
Expand Down Expand Up @@ -245,7 +251,7 @@ re-derive it with `python3 scripts/model_churn.py --upstream`.
make codegen # regenerate the manifest from the models
make stats # registered services and hand-written operations
go test ./cmd/devcloud/ # asserts every number on this page against the binary
make test-compat # the compatibility-tested number, over all 205 services
make test-compat # the compatibility-tested number, over all 213 services
```

Every figure comes from `internal/generated/fidelity/manifest_gen.go` and nothing
Expand Down
2 changes: 1 addition & 1 deletion docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ For services with a persistent backend (S3, DynamoDB, Lambda, IAM/STS), yes —
## Compatibility

**Will my existing boto3 code work?**
Most apps using the core services (S3, SQS, DynamoDB, Lambda, IAM, STS) and common integration services (SNS, CloudWatch, KMS, Secrets Manager, EventBridge, CloudFormation) work with only an `endpoint_url` change. The 1,144-test boto3 suite runs green in CI on every push; what that does and does not promise is spelled out in [compatibility-policy.md](compatibility-policy.md#wire-behaviour--scoped-to-the-compatibility-suite).
Most apps using the core services (S3, SQS, DynamoDB, Lambda, IAM, STS) and common integration services (SNS, CloudWatch, KMS, Secrets Manager, EventBridge, CloudFormation) work with only an `endpoint_url` change. The 1,156-test boto3 suite runs green in CI on every push; what that does and does not promise is spelled out in [compatibility-policy.md](compatibility-policy.md#wire-behaviour--scoped-to-the-compatibility-suite).

**What about Terraform / CDK?**
Point the AWS provider or CDK at `http://localhost:4747` with dummy credentials. Common resources (`aws_s3_bucket`, `aws_dynamodb_table`, `aws_lambda_function`) work out of the box. Complex IAM policies and deeply CSP-coupled resources are out of scope.
Expand Down
71 changes: 71 additions & 0 deletions internal/generated/compat/services.json
Original file line number Diff line number Diff line change
Expand Up @@ -1617,6 +1617,14 @@
"UpdateServiceAccessPolicies"
]
},
"cloudsearchdomain": {
"protocol": "rest-json",
"servedOps": [
"Search",
"Suggest",
"UploadDocuments"
]
},
"cloudtrail": {
"protocol": "json-1.1",
"servedOps": [
Expand Down Expand Up @@ -3509,6 +3517,13 @@
"TerminateInstances"
]
},
"ec2instanceconnect": {
"protocol": "json-1.1",
"servedOps": [
"SendSSHPublicKey",
"SendSerialConsoleSSHPublicKey"
]
},
"ecr": {
"protocol": "json-1.1",
"servedOps": [
Expand Down Expand Up @@ -3752,6 +3767,12 @@
"UpdatePodIdentityAssociation"
]
},
"eksauth": {
"protocol": "rest-json",
"servedOps": [
"AssumeRoleForPodIdentity"
]
},
"elasticache": {
"protocol": "query",
"servedOps": [
Expand Down Expand Up @@ -4485,6 +4506,16 @@
"UpdateVolume"
]
},
"georoutes": {
"protocol": "rest-json",
"servedOps": [
"CalculateIsolines",
"CalculateRouteMatrix",
"CalculateRoutes",
"OptimizeWaypoints",
"SnapToRoads"
]
},
"glacier": {
"protocol": "rest-json",
"servedOps": [
Expand Down Expand Up @@ -5144,6 +5175,12 @@
"UpdateOrganizationConfiguration"
]
},
"inspectorscan": {
"protocol": "rest-json",
"servedOps": [
"ScanSbom"
]
},
"iot": {
"protocol": "rest-json",
"servedOps": [
Expand Down Expand Up @@ -5585,6 +5622,13 @@
"UpdateStreamStorageConfiguration"
]
},
"kinesisvideowebrtcstorage": {
"protocol": "rest-json",
"servedOps": [
"JoinStorageSession",
"JoinStorageSessionAsViewer"
]
},
"kms": {
"protocol": "json-1.1",
"servedOps": [
Expand Down Expand Up @@ -6040,6 +6084,13 @@
"VoteOnProposal"
]
},
"marketplacecommerceanalytics": {
"protocol": "json-1.1",
"servedOps": [
"GenerateDataSet",
"StartSupportDataExport"
]
},
"mediaconnect": {
"protocol": "rest-json",
"servedOps": [
Expand Down Expand Up @@ -7045,6 +7096,26 @@
"UpdateAlias"
]
},
"paymentcryptographydata": {
"protocol": "rest-json",
"servedOps": [
"DecryptData",
"EncryptData",
"GenerateAs2805KekValidation",
"GenerateAuthRequestCryptogram",
"GenerateCardValidationData",
"GenerateMac",
"GenerateMacEmvPinChange",
"GeneratePinData",
"ReEncryptData",
"TranslateKeyMaterial",
"TranslatePinData",
"VerifyAuthRequestCryptogram",
"VerifyCardValidationData",
"VerifyMac",
"VerifyPinData"
]
},
"personalize": {
"protocol": "json-1.1",
"servedOps": [
Expand Down
Loading