region HA 2a: revert the edges loop edits; the provider is read per pass and the seal stands
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 files changed,
+12
−62
internal/host/awsaddr.go+4 −13internal/host/boxaddress_test.go+0 −28internal/host/edgesclaims.go+2 −4internal/host/edgesfile.go+3 −10internal/provision/plan14_root_reads_test.go+1 −3internal/provision/testdata/plan14-root-reads-region-ha-2a.tsv+2 −4
diff --git a/internal/host/awsaddr.go b/internal/host/awsaddr.go| index 4a042fe6a..f63823b2b 100644 |
| --- a/internal/host/awsaddr.go |
| +++ b/internal/host/awsaddr.go |
| @@ -69,18 +69,9 @@ func (s imdsAddresses) BoxAddresses() (BoxAddresses, bool) { |
| } |
| // boxAddressSource is what the edges file publisher reads with region HA |
| -// on: host.env's settings, then IMDS on EC2. Whether this is an EC2 box is |
| -// decided once, here, when the loop arms (as readPlacementRegion is): a box |
| -// where IMDS does not answer a token request now reads host.env only, and |
| -// makes no metadata request on any later pass. An EC2 box whose IMDS fails |
| -// after this reports unknown on that pass instead. |
| +// on: host.env's settings, then IMDS on EC2. IMDS is asked on every |
| +// publication that host.env does not fully answer; a failed request makes |
| +// that publication unknown, and the pass writes nothing and retries. |
| func boxAddressSource(getenv func(string) string) AddressSource { |
| - return boxAddressSourceFrom(getenv, newIMDSAddresses()) |
| -} |
| - |
| -func boxAddressSourceFrom(getenv func(string) string, imds imdsAddresses) AddressSource { |
| - if imds.token() == "" { |
| - return envAddresses{getenv: getenv} |
| - } |
| - return envAddresses{getenv: getenv, provider: imds} |
| + return envAddresses{getenv: getenv, provider: newIMDSAddresses()} |
| } |
diff --git a/internal/host/boxaddress_test.go b/internal/host/boxaddress_test.go| index 8328e3e0e..ec234441f 100644 |
| --- a/internal/host/boxaddress_test.go |
| +++ b/internal/host/boxaddress_test.go |
| @@ -259,34 +259,6 @@ func TestEnvAddressesCIDRWithoutIP(t *testing.T) { |
| } |
| } |
| -// IMDS is decided once, when the loop arms. A box where it does not answer |
| -// reads host.env only and makes no metadata request on any later pass. |
| -func TestBoxAddressSourceWithNoIMDSAtArmNeverQueriesIt(t *testing.T) { |
| - tokens, leaves := 0, 0 |
| - imds := imdsAddresses{ |
| - token: func() string { tokens++; return "" }, |
| - leaf: func(token, path string) (string, int) { leaves++; return "10.0.1.5", http.StatusOK }, |
| - } |
| - src := boxAddressSourceFrom(envOf(map[string]string{"CARLOS_PUBLIC_IP": "198.51.100.7"}), imds) |
| - for range 3 { |
| - if got := addrs(src); got != (BoxAddresses{Public: "198.51.100.7"}) { |
| - t.Fatalf("got %+v", got) |
| - } |
| - } |
| - if tokens != 1 || leaves != 0 { |
| - t.Fatalf("token requests %d (want the one at arm), leaf requests %d (want 0)", tokens, leaves) |
| - } |
| - // And a box where it does answer asks on every pass. |
| - tokens = 0 |
| - imds.token = func() string { tokens++; return "tok" } |
| - src = boxAddressSourceFrom(envOf(nil), imds) |
| - addrs(src) |
| - addrs(src) |
| - if tokens != 3 { |
| - t.Fatalf("token requests %d, want 1 at arm and 1 per pass", tokens) |
| - } |
| -} |
| - |
| // An unknown read (a metadata hiccup) writes nothing: the previous file |
| // stands, the pass reports an error so the loop retries, and a later good |
| // read publishes again. |
diff --git a/internal/host/edgesclaims.go b/internal/host/edgesclaims.go| index 9fac525e4..96749ffdc 100644 |
| --- a/internal/host/edgesclaims.go |
| +++ b/internal/host/edgesclaims.go |
| @@ -74,15 +74,13 @@ type edgesClaims struct { |
| failingRun int // consecutive passes with a failed claim read |
| // addrs is the box's own addresses, read before every publication |
| // (region HA spec 2 §6). It rides here because this cache exists |
| - // exactly when region HA is on. It starts as host.env alone, so |
| - // building the cache makes no metadata request; the loop sets |
| - // boxAddressSource when it arms. |
| + // exactly when region HA is on. |
| addrs AddressSource |
| } |
| func newEdgesClaims(self string, read func(context.Context, string) (hostclaim.Observed, error)) *edgesClaims { |
| return &edgesClaims{self: self, read: read, now: time.Now, entries: map[string]edgesClaimEntry{}, |
| - passDeadline: edgesClaimPassDeadline, logf: log.Printf, addrs: envAddresses{getenv: os.Getenv}} |
| + passDeadline: edgesClaimPassDeadline, logf: log.Printf, addrs: boxAddressSource(os.Getenv)} |
| } |
| // relocatableRow is spec 1 §2's relocatable host as a registry row shows |
diff --git a/internal/host/edgesfile.go b/internal/host/edgesfile.go| index 384ccbe95..dbcdc63da 100644 |
| --- a/internal/host/edgesfile.go |
| +++ b/internal/host/edgesfile.go |
| @@ -835,15 +835,8 @@ func edgesFileLoop(reg *registry.Registry) { |
| fpErrs, passErrs := 0, 0 |
| // The EIP cannot change under a running box, so it is read once here and |
| // re-read only on the timer's passes, never on every change pass: off |
| - // EC2 each read can block for the probe's whole timeout. With claims |
| - // (region HA on) the address source supplies it afresh on every |
| - // publication and this reading is never used, so it is not taken. |
| - eip := "" |
| - if claims == nil { |
| - eip = readPublicIP() |
| - } else { |
| - claims.addrs = boxAddressSource(os.Getenv) |
| - } |
| + // EC2 each read can block for the probe's whole timeout. |
| + eip := readPublicIP() |
| for now := range tick.C { |
| fp, err := reg.Fingerprint() |
| if err != nil { |
| @@ -859,7 +852,7 @@ func edgesFileLoop(reg *registry.Registry) { |
| if !state.due(now, fp, err == nil) { |
| continue |
| } |
| - if claims == nil && state.timed() { |
| + if state.timed() { |
| eip = readPublicIP() |
| } |
| ident.Headroom = headroomFor(ident.Capabilities, dataDir, reg) |
diff --git a/internal/provision/plan14_root_reads_test.go b/internal/provision/plan14_root_reads_test.go| index 24e963023..4c5bc4c89 100644 |
| --- a/internal/provision/plan14_root_reads_test.go |
| +++ b/internal/provision/plan14_root_reads_test.go |
| @@ -797,9 +797,7 @@ func TestPlan14RootReadCensus(t *testing.T) { |
| if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-region-ha-2a.tsv"), &inv, true); err != nil { |
| t.Fatal(err) |
| } |
| - // Task 7's fix round re-digests edgesFileLoop at its accepted disposition |
| - // (class, task, basis and read sites unchanged), so the seal moves. |
| - seal = "ede89d37d408863fd01a395afe791d587cbafac40e9b5db03d5b7089e6c4787a" |
| + seal = "5d1b9b6addf6c9964ed18b417ee5b3d0d49fd43e1acabb775e054bba4b6e314e" |
| errs := append(compareRootReads(got, inv), checkRootReadLegacy(inv, seal)...) |
| errs = append(errs, checkRootReadDeclarations(got, inv)...) |
| if len(errs) > 0 { |
diff --git a/internal/provision/testdata/plan14-root-reads-region-ha-2a.tsv b/internal/provision/testdata/plan14-root-reads-region-ha-2a.tsv| index d6e4160ad..792db1fc7 100644 |
| --- a/internal/provision/testdata/plan14-root-reads-region-ha-2a.tsv |
| +++ b/internal/provision/testdata/plan14-root-reads-region-ha-2a.tsv |
| @@ -4,13 +4,11 @@ |
| # Task 4: the placement policy (internal/boxchoice policy.go; headroom totals and the edges file's generation carried through Eligible and edgesfile.Snapshot.BoxChoice). Envelope-only: no root-read unit changed or was added, and the legacy seal stands. |
| # Task 5: the edges file's headroom carries the box's total memory and data volume size (headroomFor and capacityMiB, internal/host/headroom.go; edgeHeadroom's two additive fields). headroomFor calls the existing readMemBytes and diskBytes and adds no read of its own. Envelope-only: readHeadroom and readMemBytes are unchanged, no root-read unit changed or was added, and the legacy seal stands. |
| # Task 6: hostops.ExecutorChooser applies spec 2's policy in Choose and Permits (creates and relocation targets) and names the placeable set in PlaceableBoxes and Capacity. ExecutorChooser.eligible, the accepted-legacy unit (merge ruling 2), is unchanged: the policy runs over its result. Envelope-only, and the legacy seal stands. |
| -# Task 7: the box's own addresses (internal/host boxaddress.go, the provider-neutral source over CARLOS_PRIVATE_IP with CARLOS_PRIVATE_CIDR, and CARLOS_PUBLIC_IP, all from root-owned host.env; awsaddr.go, the IMDS adapter, which reads the VPC's CIDR blocks too) and the edges file's private_ip and private_cidr, validated before they are written by edgesfile.PrivateAddress (census ruling region-ha-2a, condition 1). edgesFileWriteClaims (authority, F,J1) is re-digested with its class, task, basis and sites unchanged: with region HA on it reads the address source before every publication and no longer holds a blank EIP. It adds no registry, /data, release or claim read, and nothing runs as root that did not before. Fix round 1: edgesFileLoop (accepted-legacy, unchanged class, task, basis and kinds; digest and legacy fingerprint re-pinned, so the legacy seal moves) arms the address source once and no longer reads the public address per pass when region HA is on; the source reports unknown separately from an authoritative blank, and an unknown read fails the pass without writing. |
| +# Task 7: the box's own addresses (internal/host boxaddress.go, the provider-neutral source over CARLOS_PRIVATE_IP with CARLOS_PRIVATE_CIDR, and CARLOS_PUBLIC_IP, all from root-owned host.env; awsaddr.go, the IMDS adapter, which reads the VPC's CIDR blocks too) and the edges file's private_ip and private_cidr, validated before they are written by edgesfile.PrivateAddress (census ruling region-ha-2a, condition 1). edgesFileWriteClaims (authority, F,J1) is re-digested with its class, task, basis and sites unchanged: with region HA on it reads the address source before every publication and no longer holds a blank EIP. It adds no registry, /data, release or claim read, and nothing runs as root that did not before; the legacy seal stands. |
| @coverage internal/lbhealth ad1211a01a4eb3d02cd8ed95cbff0f8b17b3976ce9c0279ebd2cae2f20c07124 |
| @coverage internal/hostactors 6736698380a247764ada687f2476f89382fc32a1183a50c06afe75967d716eab |
| @coverage internal/boxchoice 1bfd561bcb48b51e60e63f99e086fd44d3b1be2160e28b3ed0253a14bbb9ff66 |
| @coverage internal/edgesfile f62c3973bb48fa36f561d26f496c0ba9a14fe882fe5843867e9f7a9eaf0abbdf |
| -@coverage internal/host 3fedcb518fb123d97a8d90775f1303048768c5f32756fab62575a325b8d19602 |
| +@coverage internal/host 75e0a977c0f286966f2fb1ebbca10aa40f3795117958bf82e80cf545d6a776fa |
| @coverage internal/hostops 3dc23aaff7bdae0341a8c9a6fbac076a2f57ca1e4676d83543433ff9ad7d1c79 |
| internal/host/edgesfile.go edgesFileWriteClaims authority F,J1 root-route;kept=F;scope=protected-decision-only registry:Account@10835,registry:Alias@5297,registry:Alias@5332,registry:Alias@5754,registry:Alias@5911,registry:Alias@5981,registry:Alias@6013,registry:Alias@6482,registry:Alias@8772,registry:App@10847,registry:DNSStatus@5429,registry:DNSStatus@5920,registry:Host@10480,registry:Host@2878,registry:Host@5389,registry:Host@6234,registry:Host@6567,registry:Host@8819,registry:Host@8866,registry:Host@8916,registry:Host@8951,registry:Host@9000,registry:Platform@2921,registry:Provisioned@2935,registry:Replica@2959,registry:Status@877,whole:Route@3212,whole:Route@323,whole:Route@3505,whole:Route@351,whole:RouteAlias@5282,whole:RouteAlias@8725,whole:RoutePath@3520,whole:WildcardDomain@8905 8c7cdffba66886b92f9a2ac663a86dbdd5f80699afd2e6afb4571da916c727ad |
| -@legacy 8cd94c1313fbc3c08923f74a4e9f23528e953ddeb81b77699c195fd0751b2db0 internal/host/edgesfile.go edgesFileLoop legacy accepted-legacy accepted-legacy;rev3=census-ruling-seeded-instances;basis=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body with the box's seeds fields added to the edges file it publishes (withSeedsFields); a report, not a decision file:Read@2587 02247880844913b821a4cad9d024ee164bd705e3197b8a2fa6b76b02fb6253d4 |
| -internal/host/edgesfile.go edgesFileLoop legacy accepted-legacy accepted-legacy;rev3=census-ruling-seeded-instances;basis=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body with the box's seeds fields added to the edges file it publishes (withSeedsFields); a report, not a decision file:Read@2587 02247880844913b821a4cad9d024ee164bd705e3197b8a2fa6b76b02fb6253d4 |