From 6e93ce5565a019f71d83b5b715f47fe2365f5169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20ROBERT?= Date: Mon, 14 Sep 2026 16:42:29 +0200 Subject: [PATCH] chore(scaleway): the Terraform provider pin moves to 2.82.0, and the move measures what it exposed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The three exact pins in this repository go from 2.81.0 to 2.82.0: the conformance fixture, examples/stacks/scaleway and examples/quickstart/scaleway. The pin is exact so that moving it is an act, and this is that act. WHAT THE RELEASE CHANGES THAT COULD REACH THIS EMULATOR Read from the release notes rather than met one failure at a time. 2.82.0 adds an `srn` attribute to six products, three of which this emulator serves — iam, vpcgw, ipam/flexibleip — plus `fix(instance): infer project_id from server if not explicit` on a pack this repository does serve. Everything else is a new product (autoscaling, datawarehouse) or a test chore. WHAT THAT TURNED OUT TO MEAN, MEASURED `srn` is computed client-side, and the SDK 2.82.0 bumps to asks the gateway for the domain to build it with: apiMetadata, err := s.client.GetAPIMetadata() // GET /metadata if err == nil { resp.setSRN(apiMetadata.Domain) } This emulator mounts no `/metadata`. One apply plus destroy of the conformance fixture, recorded through `feint proxy --record`: **539 exchanges over 100 paths, of which 148 are `GET /metadata`, every one answered 404** — 148 of the run's 169 refusals. Nothing fails, because the error is discarded; that is luck rather than a decision, and it is the shape of #257 exactly. So the answer is known rather than guessed. A read-only shot at a real fr-par account, no resource created, 2026-09-14: GET https://api.scaleway.com/metadata -> 200 {"platform": "external", "partition": "scw", "domain": "scw.eu"} Not mounted here, because `Route.Operation` must name an operation the drift scan finds and that scan walks `api//` only, where `GetAPIMetadata` is not. A route declaring it becomes an orphan and all three baselines carry zero. The instrument gets decided before the handler: #776, with docs/limits.md carrying the measurement in the meantime. The same shot recorded a second divergence, left to its own issue: the gateway answers an unknown path with a Scaleway error document, where this emulator answers `net/http`'s plain-text `404 page not found`. PROVEN - conformance:leg -- terraform: green, and the log says which client answered — `Installed scaleway/scaleway v2.82.0 (signed, key ID F5BF26CADF6F9614)`; - conformance:leg -- scw-cli, conformance:quickstart (scaleway up, empty second plan, down), conformance:stacks: green; - mise run check and docs:check green; the generated client tables regenerated. - conformance:functional under incus-ovn, Scaleway half: seven machines, each 'running' and each Running on the host, public paths, firewall rules, cross-VPC isolation, a restart through the API, and the stack taken down. A false alarm worth recording, because a single probe produced it: the provider registry answered 504 on v2.82.0's SHA256SUMS twice running, which reads exactly like a broken release. Fifteen requests against 2.82.0 and fifteen against 2.81.0 as a witness came back 200/200 on both, same size. One attempt at a remote service settles nothing. The same measurement found a real outage, and it is not this release. The functional leg's Outscale half died on `Failed to install provider outscale/outscale v1.8.0 [...] 504 Gateway Timeout returned from github.com`, and 15 requests for that asset came back **5 in 200 and 10 in 504** while both Scaleway assets were 15/15. So the Outscale half of that leg is not measured here, for a reason upstream of this repository and unrelated to the pin this commit moves. NOT IN THIS CHANGE `mise run limits:check` is red on 9 sections whose cited issues closed after anyone last looked. It was red on main before this branch — verified by stashing — and is a separate piece of work. The section added here carries its own acknowledgement and is not among them. Assisted-by: Claude Code (claude-opus-5) --- README.fr.md | 2 +- README.md | 6 ++-- docs/clients.md | 6 ++-- docs/limits-acks.json | 1 + docs/limits.md | 37 ++++++++++++++++++++ examples/quickstart/scaleway/main.tf | 2 +- examples/stacks/scaleway/main.tf | 8 +++-- internal/cli/docs_proved_test.go | 6 +++- tools/conformance/scaleway/terraform/main.tf | 9 ++++- 9 files changed, 65 insertions(+), 12 deletions(-) diff --git a/README.fr.md b/README.fr.md index d11ba23d..ba0ca96f 100644 --- a/README.fr.md +++ b/README.fr.md @@ -31,7 +31,7 @@ > > **Prouvé** : 376 des 397 opérations montées sont pilotées par un vrai client, à chaque pull request. `scw`, `octl`, `exo`, Terraform et OpenTofu tournent contre l'émulateur en CI, et les machines démarrent réellement : connexion ssh sur le compte par défaut de chaque provider, subnets isolés, pare-feu qui filtre. La chaîne complète est décrite dans [docs/conformance.md](docs/conformance.md). > -> **Pas prouvé** : quotas, prix, capacité réelle, validation des identifiants, authentification, cohérence à terme. Les 56 sections de [docs/limits.md](docs/limits.md) disent chacune ce qu'elle coûte. Un émulateur avec un seul compte implicite et aucune grille tarifaire devrait inventer ces chiffres, et quelqu'un agirait dessus. +> **Pas prouvé** : quotas, prix, capacité réelle, validation des identifiants, authentification, cohérence à terme. Les 57 sections de [docs/limits.md](docs/limits.md) disent chacune ce qu'elle coûte. Un émulateur avec un seul compte implicite et aucune grille tarifaire devrait inventer ces chiffres, et quelqu'un agirait dessus. > > **Inconnu** : 21 opérations sont montées et n'ont jamais été pilotées par un client. Chacune dit pourquoi aucun client officiel ne l'atteint, à la route et dans [docs/routes.md](docs/routes.md). Elles sont comptées plutôt qu'escamotées, une par une, dans [coverage/evidence.json](coverage/evidence.json). > diff --git a/README.md b/README.md index 0d20782b..9f861983 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ > > **Proven**: 376 of the 397 mounted operations are driven by a real client, on every pull request: each pack's own official CLI, and an infrastructure engine wherever a pack admits one, all of them running against the emulator in CI, and machines really boot: an ssh login on each provider's own default account, isolated subnets, a firewall that filters. The whole chain is described in [docs/conformance.md](docs/conformance.md). > -> **Not proven**: quotas, prices, real capacity, identifier validation, authentication, eventual consistency. The 56 sections of [docs/limits.md](docs/limits.md) each say what one costs. An emulator with a single implicit account and no price list would have to invent those figures, and somebody would act on them. +> **Not proven**: quotas, prices, real capacity, identifier validation, authentication, eventual consistency. The 57 sections of [docs/limits.md](docs/limits.md) each say what one costs. An emulator with a single implicit account and no price list would have to invent those figures, and somebody would act on them. > > **Unknown**: 21 operations are mounted and have never been driven by a client. Every one of them states why no official client reaches it, at the route and in [docs/routes.md](docs/routes.md). They are counted rather than glossed, one by one, in [coverage/evidence.json](coverage/evidence.json). > @@ -831,8 +831,8 @@ argument is that the upstream moves: | Client | Version proven in CI | Emulated provider | |---|---|---| | `scw` | 2.56.3 | Scaleway | -| Terraform | 1.13.3 with providers `outscale/outscale ~> 1.7`, `scaleway/scaleway 2.81.0` | Exoscale, Outscale, Scaleway | -| OpenTofu | 1.12.5 with providers `outscale/outscale ~> 1.7`, `scaleway/scaleway 2.81.0` | Exoscale, Outscale, Scaleway | +| Terraform | 1.13.3 with providers `outscale/outscale ~> 1.7`, `scaleway/scaleway 2.82.0` | Exoscale, Outscale, Scaleway | +| OpenTofu | 1.12.5 with providers `outscale/outscale ~> 1.7`, `scaleway/scaleway 2.82.0` | Exoscale, Outscale, Scaleway | | `octl` | 0.0.32 | Outscale | | `exo` | 1.95.6 | Exoscale | diff --git a/docs/clients.md b/docs/clients.md index 957e1b76..064860d7 100644 --- a/docs/clients.md +++ b/docs/clients.md @@ -76,13 +76,13 @@ Each row is one `required_providers` entry, read where it is written. | `tools/conformance/faults` | `outscale/outscale` | `~> 1.7` | constraint: resolved fresh on each run, so the version that answered is not knowable here | yes | | `tools/conformance/outscale/terraform` | `outscale/outscale` | `~> 1.7` | constraint: resolved fresh on each run, so the version that answered is not knowable here | yes | | `tools/conformance/outscale/terraform-doorway` | `outscale/outscale` | `~> 1.7` | constraint: resolved fresh on each run, so the version that answered is not knowable here | yes | -| `tools/conformance/scaleway/terraform` | `scaleway/scaleway` | `2.81.0` | exact: the version that answered | yes | +| `tools/conformance/scaleway/terraform` | `scaleway/scaleway` | `2.82.0` | exact: the version that answered | yes | | `examples/stacks/exoscale` | `exoscale/exoscale` | `>= 0.71.0` | constraint: resolved fresh on each run, so the version that answered is not knowable here | yes | | `examples/stacks/outscale` | `outscale/outscale` | `~> 1.7` | constraint: resolved fresh on each run, so the version that answered is not knowable here | yes | | `examples/stacks/outscale/modules/net` | `outscale/outscale` | `~> 1.7` | constraint: resolved fresh on each run, so the version that answered is not knowable here | yes | -| `examples/stacks/scaleway` | `scaleway/scaleway` | `2.81.0` | exact: the version that answered | yes | +| `examples/stacks/scaleway` | `scaleway/scaleway` | `2.82.0` | exact: the version that answered | yes | | `examples/quickstart/outscale` | `outscale/outscale` | `~> 1.7` | constraint: resolved fresh on each run, so the version that answered is not knowable here | yes | -| `examples/quickstart/scaleway` | `scaleway/scaleway` | `2.81.0` | exact: the version that answered | yes | +| `examples/quickstart/scaleway` | `scaleway/scaleway` | `2.82.0` | exact: the version that answered | yes | Read the third column narrowly, because it is the one a consumer pins against. An **exact** constraint names the version that answered. A **constraint** is diff --git a/docs/limits-acks.json b/docs/limits-acks.json index a7d40727..c5ebdddb 100644 --- a/docs/limits-acks.json +++ b/docs/limits-acks.json @@ -30,6 +30,7 @@ "The cost of DNS/TLS interception, measured (#76)": "2026-08-27", "The firewall enforces, within stated bounds": "2026-08-28", "The guest's DHCP client is not how a published address reaches a machine (#587)": "2026-08-29", + "The Scaleway gateway serves `GET /metadata` and this emulator does not (#776)": "2026-09-14", "The per-parameter half: 18 Scaleway list operations, 72 parameters, each served or refused (#277)": "2026-08-29", "The station reaches an OVN private address only via the network's router, and the posted uplink routes do not go there (#496)": "2026-08-27", "The three packs hand their security groups to the runtime, within two measured bounds": "2026-08-28", diff --git a/docs/limits.md b/docs/limits.md index a2b5dda7..f873773f 100644 --- a/docs/limits.md +++ b/docs/limits.md @@ -3252,6 +3252,43 @@ choice rather than an accident: publishes none, and listing the private groups under a public label would be the same lie #271 names, pointed the other way. +## The Scaleway gateway serves `GET /metadata` and this emulator does not (#776) + +The Scaleway SDK that provider **2.82.0** embeds asks the gateway for its +metadata, and uses the domain it answers to compute a `srn://…` client-side for +every product that gained one. This emulator mounts no such route. + +Measured 2026-09-14, one `apply` plus `destroy` of the conformance fixture +through `feint proxy --record`: **539 exchanges over 100 paths, of which 148 are +`GET /metadata` and every one is answered 404** — 148 of the run's 169 refusals. + +Nothing fails, and that is luck rather than a decision. `scw/client.go` reads the +metadata and its callers discard the error: + +```go +apiMetadata, err := s.client.GetAPIMetadata() +if err == nil { + resp.setSRN(apiMetadata.Domain) +} +``` + +So the SRN stays empty and the apply completes. A caller that stops ignoring it +turns this into a failure with no change on this side, which is the shape of +#257 exactly. + +What the real cloud answers is known rather than guessed — a read-only shot +against a real `fr-par` account the same day, no resource created: + +``` +GET https://api.scaleway.com/metadata -> 200 +{"platform": "external", "partition": "scw", "domain": "scw.eu"} +``` + +It is not mounted yet because `Route.Operation` must name an operation the drift +scan finds, and that scan walks `api//` only, where +`GetAPIMetadata` is not. A route declaring it becomes an orphan, and all three +baselines carry zero. The instrument gets decided before the handler: #776. + ## The per-parameter half: 18 Scaleway list operations, 72 parameters, each served or refused (#277) #271's gate catches a handler that never reads its query at all. Its comment diff --git a/examples/quickstart/scaleway/main.tf b/examples/quickstart/scaleway/main.tf index 23ca4ca4..b0640da5 100644 --- a/examples/quickstart/scaleway/main.tf +++ b/examples/quickstart/scaleway/main.tf @@ -21,7 +21,7 @@ terraform { # apply proves the emulator answered whatever was newest that morning and # nothing anybody can replay. `feint docs --check` refuses an applied # example that pins nothing. - version = "2.81.0" + version = "2.82.0" } } } diff --git a/examples/stacks/scaleway/main.tf b/examples/stacks/scaleway/main.tf index cb0b4d61..30fbad54 100644 --- a/examples/stacks/scaleway/main.tf +++ b/examples/stacks/scaleway/main.tf @@ -35,11 +35,15 @@ terraform { source = "scaleway/scaleway" # Exact for the same reason the conformance fixture is exact: a floating # constraint turned CI red the hour 2.81.0 was published, with no change - # on this side (#257). 2.81.0 is the pin rather than the 2.80.0 that made + # on this side (#257). 2.81.0 was the pin rather than the 2.80.0 that made # it green again — #260 serves the /instance/v2alpha1 routes that release # reads private NICs through, and a stack pinned below them would stop # exercising what the emulator now claims to serve. - version = "2.81.0" + # + # Moved to 2.82.0 on 2026-09-14, with the conformance fixture, and the + # move is measured rather than assumed: that release adds an `srn` + # attribute to six products, three of them served here. + version = "2.82.0" } } } diff --git a/internal/cli/docs_proved_test.go b/internal/cli/docs_proved_test.go index feeab8b1..a8ebee62 100644 --- a/internal/cli/docs_proved_test.go +++ b/internal/cli/docs_proved_test.go @@ -133,8 +133,12 @@ func rowContaining(t *testing.T, rendered, key string) string { func TestTheProvedPageSeparatesAnExactPinFromAConstraintAndFromNothing(t *testing.T) { rendered := provedPage(t) + // The number is written here rather than read from the fixture, on purpose: + // read from it, this would compare the page against its own source and pass + // whatever the pin became. Written here, moving the pin costs one line and + // somebody has to look at this test while doing it. exact := rowContaining(t, rendered, "tools/conformance/scaleway/terraform`") - if !strings.Contains(exact, "2.81.0") || !strings.Contains(exact, "exact") { + if !strings.Contains(exact, "2.82.0") || !strings.Contains(exact, "exact") { t.Errorf("the Scaleway fixture pins one version and the page does not say so:\n %s", exact) } diff --git a/tools/conformance/scaleway/terraform/main.tf b/tools/conformance/scaleway/terraform/main.tf index 8ab4c2d6..a9375358 100644 --- a/tools/conformance/scaleway/terraform/main.tf +++ b/tools/conformance/scaleway/terraform/main.tf @@ -28,7 +28,14 @@ terraform { # the machine that regenerated the artefact and false on the runner. An # artefact claiming "a real client proved this" has to mean the client CI # actually runs. - version = "2.81.0" + # + # Moved to 2.82.0 on 2026-09-14, and the move is the proof rather than the + # claim: the release adds an `srn` attribute to six products, three of + # which this emulator serves (iam, vpcgw, ipam/flexibleip), plus + # `fix(instance): infer project_id from server if not explicit`. Every one + # of those is a reason a newer provider could stop driving this fixture, + # which is exactly why the pin is exact and why it moves deliberately. + version = "2.82.0" } } }