carlos / platform Public

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>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev e19a292ea853e48ff7e88e3a3d3d5fa7522269ae parent 2d38c8b
6 files changed, +12 −62
  • internal/host/awsaddr.go +4 −13
  • internal/host/boxaddress_test.go +0 −28
  • internal/host/edgesclaims.go +2 −4
  • internal/host/edgesfile.go +3 −10
  • internal/provision/plan14_root_reads_test.go +1 −3
  • internal/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