5e59e2b0
wire: the spec/status vocabulary the loop always meant
a73x 2026-08-22 17:14
Commit message
RETRO.md
| Old | New | ||
|---|---|---|---|
| @@ -13,3 +13,7 @@ One line per push to `main`: what slowed the work down. Enforced by | |||
| 13 | means. Pushing main answered it: origin/main became HEAD, gremlins read a | 13 | means. Pushing main answered it: origin/main became HEAD, gremlins read a |
| 14 | diff with no entries as "everything changed", and the whole-tree run it | 14 | diff with no entries as "everything changed", and the whole-tree run it |
| 15 | started was killed by the one mutant that signals its own process group. | 15 | started was killed by the one mutant that signals its own process group. |
| 16 | - Wire rename: `make ci` failed twice on nothing wrong. A fresh worktree has | ||
| 17 | an empty web/dist, so the web package misses its coverage floor until | ||
| 18 | `make web` runs; and proto-check diffs generated code against the COMMIT, | ||
| 19 | so it always fails on an uncommitted .proto change. Neither gate says so. | ||
docs/architecture.md
| Old | New | ||
|---|---|---|---|
| @@ -16,8 +16,8 @@ hosts and share only a wire contract: | |||
| 16 | | **Data plane** (agent) | `internal/agent/*`, `cmd/eitri-agent` | Owns the entire VM lifecycle: disks, networking, VMM processes. | | 16 | | **Data plane** (agent) | `internal/agent/*`, `cmd/eitri-agent` | Owns the entire VM lifecycle: disks, networking, VMM processes. | |
| 17 | | **Wire contract** | `internal/pb`, `internal/transport` | The only code shared across the boundary: protobuf messages + QUIC framing/TLS. | | 17 | | **Wire contract** | `internal/pb`, `internal/transport` | The only code shared across the boundary: protobuf messages + QUIC framing/TLS. | |
| 18 | 18 | ||
| 19 | The server expresses intent as a `pb.DesiredStateSnapshot` (server → agent); the | 19 | The server expresses intent as a `pb.Snapshot` (server → agent); the |
| 20 | agent reports back a `pb.ActualStateReport` (agent → server). The server never | 20 | agent reports back a `pb.Report` (agent → server). The server never |
| 21 | touches a VM and never executes a process: besides writing desired state to its | 21 | touches a VM and never executes a process: besides writing desired state to its |
| 22 | store and poking the SSE hub, its only real-world side effects are control-plane | 22 | store and poking the SSE hub, its only real-world side effects are control-plane |
| 23 | services (the SSH jump gate and cert minting). All side effects on VMs and hosts | 23 | services (the SSH jump gate and cert minting). All side effects on VMs and hosts |
docs/assumptions.md
| Old | New | ||
|---|---|---|---|
| @@ -240,7 +240,7 @@ guests sit inside their own host's `bridge_cidr` and satisfy the old guard too. | |||
| 240 | 240 | ||
| 241 | **Inverted** 2026-08-04, which is what the entry above was asking for: the | 241 | **Inverted** 2026-08-04, which is what the entry above was asking for: the |
| 242 | subnet a host's guests are on is now reported BY the host, in every | 242 | subnet a host's guests are on is now reported BY the host, in every |
| 243 | `ActualStateReport`, and the fleet records what it is told. Proven on the same | 243 | `Report`, and the fleet records what it is told. Proven on the same |
| 244 | M1 — its row read 10.102.1.0/24, an allocation from a pool it never used, and | 244 | M1 — its row read 10.102.1.0/24, an allocation from a pool it never used, and |
| 245 | now reads 192.168.64.0/24, which is where its guests actually are. | 245 | now reads 192.168.64.0/24, which is where its guests actually are. |
| 246 | 246 | ||
internal/agent/exposeproxy/exposeproxy.go
| Old | New | ||
|---|---|---|---|
| @@ -166,7 +166,7 @@ func (e *exposure) close() { | |||
| 166 | // depends on. A change to any of it is a close and a rebind rather than an | 166 | // depends on. A change to any of it is a close and a rebind rather than an |
| 167 | // edit — the socket's own address, or the protocol it speaks, is part of what | 167 | // edit — the socket's own address, or the protocol it speaks, is part of what |
| 168 | // changed. | 168 | // changed. |
| 169 | func specKey(d *pb.ExposureDesired) string { | 169 | func specKey(d *pb.ExposureSpec) string { |
| 170 | return fmt.Sprintf("%s/%d/%d/%s", d.GetVmId(), d.GetGuestPort(), d.GetHostPort(), d.GetProtocol()) | 170 | return fmt.Sprintf("%s/%d/%d/%s", d.GetVmId(), d.GetGuestPort(), d.GetHostPort(), d.GetProtocol()) |
| 171 | } | 171 | } |
| 172 | 172 | ||
| @@ -182,11 +182,11 @@ func specKey(d *pb.ExposureDesired) string { | |||
| 182 | // about serving it is going wrong — a descriptor shortage the accept loop is | 182 | // about serving it is going wrong — a descriptor shortage the accept loop is |
| 183 | // riding out — because a port that is bound and struggling is not the same | 183 | // riding out — because a port that is bound and struggling is not the same |
| 184 | // thing as a port that is bound and fine. | 184 | // thing as a port that is bound and fine. |
| 185 | func (m *Manager) Converge(desired []*pb.ExposureDesired) []*pb.ExposureActual { | 185 | func (m *Manager) Converge(desired []*pb.ExposureSpec) []*pb.ExposureStatus { |
| 186 | m.mu.Lock() | 186 | m.mu.Lock() |
| 187 | defer m.mu.Unlock() | 187 | defer m.mu.Unlock() |
| 188 | 188 | ||
| 189 | want := make(map[string]*pb.ExposureDesired, len(desired)) | 189 | want := make(map[string]*pb.ExposureSpec, len(desired)) |
| 190 | for _, d := range desired { | 190 | for _, d := range desired { |
| 191 | want[d.GetId()] = d | 191 | want[d.GetId()] = d |
| 192 | } | 192 | } |
| @@ -198,7 +198,7 @@ func (m *Manager) Converge(desired []*pb.ExposureDesired) []*pb.ExposureActual { | |||
| 198 | delete(m.live, id) | 198 | delete(m.live, id) |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | out := make([]*pb.ExposureActual, 0, len(desired)) | 201 | out := make([]*pb.ExposureStatus, 0, len(desired)) |
| 202 | for _, d := range desired { | 202 | for _, d := range desired { |
| 203 | ex, ok := m.live[d.GetId()] | 203 | ex, ok := m.live[d.GetId()] |
| 204 | switch { | 204 | switch { |
| @@ -214,7 +214,7 @@ func (m *Manager) Converge(desired []*pb.ExposureDesired) []*pb.ExposureActual { | |||
| 214 | if !ex.bound() { | 214 | if !ex.bound() { |
| 215 | if err := m.bind(d, ex); err != nil { | 215 | if err := m.bind(d, ex); err != nil { |
| 216 | ex.reason = err.Error() | 216 | ex.reason = err.Error() |
| 217 | out = append(out, &pb.ExposureActual{Id: d.GetId(), State: "failed", Reason: ex.reason, Sessions: ex.counters()}) | 217 | out = append(out, &pb.ExposureStatus{Id: d.GetId(), State: "failed", Reason: ex.reason, Sessions: ex.counters()}) |
| 218 | continue | 218 | continue |
| 219 | } | 219 | } |
| 220 | } | 220 | } |
| @@ -227,7 +227,7 @@ func (m *Manager) Converge(desired []*pb.ExposureDesired) []*pb.ExposureActual { | |||
| 227 | if ex.pc != nil { | 227 | if ex.pc != nil { |
| 228 | ex.evictMovedSessions(m.addr(d.GetVmId())) | 228 | ex.evictMovedSessions(m.addr(d.GetVmId())) |
| 229 | } | 229 | } |
| 230 | out = append(out, &pb.ExposureActual{Id: d.GetId(), State: "active", Reason: ex.reason, Sessions: ex.counters()}) | 230 | out = append(out, &pb.ExposureStatus{Id: d.GetId(), State: "active", Reason: ex.reason, Sessions: ex.counters()}) |
| 231 | } | 231 | } |
| 232 | return out | 232 | return out |
| 233 | } | 233 | } |
| @@ -235,7 +235,7 @@ func (m *Manager) Converge(desired []*pb.ExposureDesired) []*pb.ExposureActual { | |||
| 235 | // bind opens the socket one desired exposure calls for and starts the loop that | 235 | // bind opens the socket one desired exposure calls for and starts the loop that |
| 236 | // serves it. UDP is a bound packet socket; everything else is a TCP listener — | 236 | // serves it. UDP is a bound packet socket; everything else is a TCP listener — |
| 237 | // a server that names no protocol at all means the one this proxy started with. | 237 | // a server that names no protocol at all means the one this proxy started with. |
| 238 | func (m *Manager) bind(d *pb.ExposureDesired, ex *exposure) error { | 238 | func (m *Manager) bind(d *pb.ExposureSpec, ex *exposure) error { |
| 239 | hostAddr := net.JoinHostPort("0.0.0.0", strconv.Itoa(int(d.GetHostPort()))) | 239 | hostAddr := net.JoinHostPort("0.0.0.0", strconv.Itoa(int(d.GetHostPort()))) |
| 240 | if d.GetProtocol() == "udp" { | 240 | if d.GetProtocol() == "udp" { |
| 241 | ua, err := net.ResolveUDPAddr("udp4", hostAddr) | 241 | ua, err := net.ResolveUDPAddr("udp4", hostAddr) |
internal/agent/exposeproxy/exposeproxy_test.go
| Old | New | ||
|---|---|---|---|
| @@ -79,12 +79,12 @@ func boundPort(t *testing.T, m *Manager, id string) string { | |||
| 79 | return ex.ln.Addr().String() | 79 | return ex.ln.Addr().String() |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | func desired(id, vmID string, guestPort, hostPort uint32) *pb.ExposureDesired { | 82 | func desired(id, vmID string, guestPort, hostPort uint32) *pb.ExposureSpec { |
| 83 | return &pb.ExposureDesired{Id: id, VmId: vmID, GuestPort: guestPort, HostPort: hostPort, Protocol: "tcp"} | 83 | return &pb.ExposureSpec{Id: id, VmId: vmID, GuestPort: guestPort, HostPort: hostPort, Protocol: "tcp"} |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | func desiredUDP(id, vmID string, guestPort, hostPort uint32) *pb.ExposureDesired { | 86 | func desiredUDP(id, vmID string, guestPort, hostPort uint32) *pb.ExposureSpec { |
| 87 | return &pb.ExposureDesired{Id: id, VmId: vmID, GuestPort: guestPort, HostPort: hostPort, Protocol: "udp"} | 87 | return &pb.ExposureSpec{Id: id, VmId: vmID, GuestPort: guestPort, HostPort: hostPort, Protocol: "udp"} |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | // speak dials addr, sends msg, and returns what came back. | 90 | // speak dials addr, sends msg, and returns what came back. |
| @@ -105,7 +105,7 @@ func TestConvergeBindsAndPipesToTheGuest(t *testing.T) { | |||
| 105 | g := newFakeGuest(t) | 105 | g := newFakeGuest(t) |
| 106 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 106 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 107 | 107 | ||
| 108 | got := m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", g.port, 0)}) | 108 | got := m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", g.port, 0)}) |
| 109 | require.Len(t, got, 1) | 109 | require.Len(t, got, 1) |
| 110 | assert.Equal(t, "e1", got[0].GetId()) | 110 | assert.Equal(t, "e1", got[0].GetId()) |
| 111 | assert.Equal(t, "active", got[0].GetState()) | 111 | assert.Equal(t, "active", got[0].GetState()) |
| @@ -117,9 +117,9 @@ func TestConvergeIsIdempotent(t *testing.T) { | |||
| 117 | g := newFakeGuest(t) | 117 | g := newFakeGuest(t) |
| 118 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 118 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 119 | 119 | ||
| 120 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", g.port, 0)}) | 120 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", g.port, 0)}) |
| 121 | addr := boundPort(t, m, "e1") | 121 | addr := boundPort(t, m, "e1") |
| 122 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", g.port, 0)}) | 122 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", g.port, 0)}) |
| 123 | 123 | ||
| 124 | assert.Equal(t, addr, boundPort(t, m, "e1"), "an unchanged spec keeps its listener") | 124 | assert.Equal(t, addr, boundPort(t, m, "e1"), "an unchanged spec keeps its listener") |
| 125 | assert.Equal(t, "echo:hi", speak(t, addr, "hi")) | 125 | assert.Equal(t, "echo:hi", speak(t, addr, "hi")) |
| @@ -128,7 +128,7 @@ func TestConvergeIsIdempotent(t *testing.T) { | |||
| 128 | func TestConvergeClosesAVanishedExposure(t *testing.T) { | 128 | func TestConvergeClosesAVanishedExposure(t *testing.T) { |
| 129 | g := newFakeGuest(t) | 129 | g := newFakeGuest(t) |
| 130 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 130 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 131 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", g.port, 0)}) | 131 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", g.port, 0)}) |
| 132 | addr := boundPort(t, m, "e1") | 132 | addr := boundPort(t, m, "e1") |
| 133 | 133 | ||
| 134 | got := m.Converge(nil) | 134 | got := m.Converge(nil) |
| @@ -143,10 +143,10 @@ func TestConvergeRebindsAChangedSpec(t *testing.T) { | |||
| 143 | second := newFakeGuest(t) | 143 | second := newFakeGuest(t) |
| 144 | m := newTestManager(t, map[string]string{"vm1": first.addr}) | 144 | m := newTestManager(t, map[string]string{"vm1": first.addr}) |
| 145 | 145 | ||
| 146 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", first.port, 0)}) | 146 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", first.port, 0)}) |
| 147 | oldAddr := boundPort(t, m, "e1") | 147 | oldAddr := boundPort(t, m, "e1") |
| 148 | 148 | ||
| 149 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", second.port, 0)}) | 149 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", second.port, 0)}) |
| 150 | newAddr := boundPort(t, m, "e1") | 150 | newAddr := boundPort(t, m, "e1") |
| 151 | assert.NotEqual(t, oldAddr, newAddr, "a changed spec is a close and a rebind") | 151 | assert.NotEqual(t, oldAddr, newAddr, "a changed spec is a close and a rebind") |
| 152 | 152 | ||
| @@ -163,7 +163,7 @@ func TestConvergeReportsABindFailureAndHealsWhenThePortFrees(t *testing.T) { | |||
| 163 | require.NoError(t, err) | 163 | require.NoError(t, err) |
| 164 | held := portOf(t, squatter.Addr()) | 164 | held := portOf(t, squatter.Addr()) |
| 165 | 165 | ||
| 166 | got := m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", g.port, held)}) | 166 | got := m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", g.port, held)}) |
| 167 | require.Len(t, got, 1) | 167 | require.Len(t, got, 1) |
| 168 | assert.Equal(t, "failed", got[0].GetState()) | 168 | assert.Equal(t, "failed", got[0].GetState()) |
| 169 | assert.NotEmpty(t, got[0].GetReason(), "the report carries what the OS said") | 169 | assert.NotEmpty(t, got[0].GetReason(), "the report carries what the OS said") |
| @@ -173,7 +173,7 @@ func TestConvergeReportsABindFailureAndHealsWhenThePortFrees(t *testing.T) { | |||
| 173 | require.NoError(t, squatter.Close()) | 173 | require.NoError(t, squatter.Close()) |
| 174 | var state string | 174 | var state string |
| 175 | for i := 0; i < 20; i++ { | 175 | for i := 0; i < 20; i++ { |
| 176 | got = m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", g.port, held)}) | 176 | got = m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", g.port, held)}) |
| 177 | state = got[0].GetState() | 177 | state = got[0].GetState() |
| 178 | if state == "active" { | 178 | if state == "active" { |
| 179 | break | 179 | break |
| @@ -187,7 +187,7 @@ func TestConvergeReportsABindFailureAndHealsWhenThePortFrees(t *testing.T) { | |||
| 187 | func TestConvergeRebindsAListenerWhoseAcceptLoopDied(t *testing.T) { | 187 | func TestConvergeRebindsAListenerWhoseAcceptLoopDied(t *testing.T) { |
| 188 | g := newFakeGuest(t) | 188 | g := newFakeGuest(t) |
| 189 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 189 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 190 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", g.port, 0)}) | 190 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", g.port, 0)}) |
| 191 | 191 | ||
| 192 | // Break the listener behind the manager's back, the way fd exhaustion would | 192 | // Break the listener behind the manager's back, the way fd exhaustion would |
| 193 | // end an accept loop: the manager never asked for this and still believes | 193 | // end an accept loop: the manager never asked for this and still believes |
| @@ -220,7 +220,7 @@ func TestConvergeRebindsAListenerWhoseAcceptLoopDied(t *testing.T) { | |||
| 220 | // 0, so that rebind cannot fail and no converge ever reports "failed" — the | 220 | // 0, so that rebind cannot fail and no converge ever reports "failed" — the |
| 221 | // interim report is only observable when the fresh bind is also refused, | 221 | // interim report is only observable when the fresh bind is also refused, |
| 222 | // which is the case TestConvergeReportsABindFailure... already covers. | 222 | // which is the case TestConvergeReportsABindFailure... already covers. |
| 223 | got := m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", g.port, 0)}) | 223 | got := m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", g.port, 0)}) |
| 224 | require.Len(t, got, 1) | 224 | require.Len(t, got, 1) |
| 225 | assert.Equal(t, "active", got[0].GetState()) | 225 | assert.Equal(t, "active", got[0].GetState()) |
| 226 | 226 | ||
| @@ -235,7 +235,7 @@ func TestConvergeRebindsAListenerWhoseAcceptLoopDied(t *testing.T) { | |||
| 235 | 235 | ||
| 236 | func TestConnectionClosesWhenTheGuestHasNoAddress(t *testing.T) { | 236 | func TestConnectionClosesWhenTheGuestHasNoAddress(t *testing.T) { |
| 237 | m := newTestManager(t, map[string]string{}) // the guest is still leasing | 237 | m := newTestManager(t, map[string]string{}) // the guest is still leasing |
| 238 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", 8080, 0)}) | 238 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", 8080, 0)}) |
| 239 | 239 | ||
| 240 | c, err := net.Dial("tcp", boundPort(t, m, "e1")) | 240 | c, err := net.Dial("tcp", boundPort(t, m, "e1")) |
| 241 | require.NoError(t, err) | 241 | require.NoError(t, err) |
| @@ -263,7 +263,7 @@ func TestSpliceIsHalfCloseAware(t *testing.T) { | |||
| 263 | }() | 263 | }() |
| 264 | 264 | ||
| 265 | m := newTestManager(t, map[string]string{"vm1": "127.0.0.1"}) | 265 | m := newTestManager(t, map[string]string{"vm1": "127.0.0.1"}) |
| 266 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", portOf(t, ln.Addr()), 0)}) | 266 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", portOf(t, ln.Addr()), 0)}) |
| 267 | 267 | ||
| 268 | c, err := net.Dial("tcp", boundPort(t, m, "e1")) | 268 | c, err := net.Dial("tcp", boundPort(t, m, "e1")) |
| 269 | require.NoError(t, err) | 269 | require.NoError(t, err) |
| @@ -298,7 +298,7 @@ func TestRevokeDrainsRatherThanCuts(t *testing.T) { | |||
| 298 | }() | 298 | }() |
| 299 | 299 | ||
| 300 | m := newTestManager(t, map[string]string{"vm1": "127.0.0.1"}) | 300 | m := newTestManager(t, map[string]string{"vm1": "127.0.0.1"}) |
| 301 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", portOf(t, ln.Addr()), 0)}) | 301 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", portOf(t, ln.Addr()), 0)}) |
| 302 | addr := boundPort(t, m, "e1") | 302 | addr := boundPort(t, m, "e1") |
| 303 | 303 | ||
| 304 | c, err := net.Dial("tcp", addr) | 304 | c, err := net.Dial("tcp", addr) |
| @@ -392,7 +392,7 @@ func TestExposureRefusesConnectionsPastItsCap(t *testing.T) { | |||
| 392 | g := newHoldingGuest(t) | 392 | g := newHoldingGuest(t) |
| 393 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 393 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 394 | m.maxConns = 2 | 394 | m.maxConns = 2 |
| 395 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", g.port, 0)}) | 395 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", g.port, 0)}) |
| 396 | addr := boundPort(t, m, "e1") | 396 | addr := boundPort(t, m, "e1") |
| 397 | 397 | ||
| 398 | first, second := hold(t, addr), hold(t, addr) | 398 | first, second := hold(t, addr), hold(t, addr) |
| @@ -415,7 +415,7 @@ func TestTheCapReleasesWhenAConnectionEnds(t *testing.T) { | |||
| 415 | g := newHoldingGuest(t) | 415 | g := newHoldingGuest(t) |
| 416 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 416 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 417 | m.maxConns = 1 | 417 | m.maxConns = 1 |
| 418 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", g.port, 0)}) | 418 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", g.port, 0)}) |
| 419 | addr := boundPort(t, m, "e1") | 419 | addr := boundPort(t, m, "e1") |
| 420 | 420 | ||
| 421 | first := hold(t, addr) | 421 | first := hold(t, addr) |
| @@ -431,7 +431,7 @@ func TestTheCapCountsConnectionsDrainingThroughARebind(t *testing.T) { | |||
| 431 | first, second := newHoldingGuest(t), newHoldingGuest(t) | 431 | first, second := newHoldingGuest(t), newHoldingGuest(t) |
| 432 | m := newTestManager(t, map[string]string{"vm1": first.addr}) | 432 | m := newTestManager(t, map[string]string{"vm1": first.addr}) |
| 433 | m.maxConns = 1 | 433 | m.maxConns = 1 |
| 434 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", first.port, 0)}) | 434 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", first.port, 0)}) |
| 435 | 435 | ||
| 436 | held := hold(t, boundPort(t, m, "e1")) | 436 | held := hold(t, boundPort(t, m, "e1")) |
| 437 | 437 | ||
| @@ -439,7 +439,7 @@ func TestTheCapCountsConnectionsDrainingThroughARebind(t *testing.T) { | |||
| 439 | // listener is still open and still spending its two descriptors, so it is | 439 | // listener is still open and still spending its two descriptors, so it is |
| 440 | // still what the cap is counting — an exposure that forgot it on the rebind | 440 | // still what the cap is counting — an exposure that forgot it on the rebind |
| 441 | // would let this port hold twice what it is allowed. | 441 | // would let this port hold twice what it is allowed. |
| 442 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", second.port, 0)}) | 442 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", second.port, 0)}) |
| 443 | waitConns(t, m, "e1", 1) | 443 | waitConns(t, m, "e1", 1) |
| 444 | 444 | ||
| 445 | over, err := net.Dial("tcp", boundPort(t, m, "e1")) | 445 | over, err := net.Dial("tcp", boundPort(t, m, "e1")) |
| @@ -497,7 +497,7 @@ func TestADescriptorShortageIsReportedWhileThePortStaysBound(t *testing.T) { | |||
| 497 | 497 | ||
| 498 | // The port is still bound, so it is still active — with the streak beside | 498 | // The port is still bound, so it is still active — with the streak beside |
| 499 | // it, which is the whole point: "active" alone would read as healthy. | 499 | // it, which is the whole point: "active" alone would read as healthy. |
| 500 | got := m.Converge([]*pb.ExposureDesired{d}) | 500 | got := m.Converge([]*pb.ExposureSpec{d}) |
| 501 | require.Len(t, got, 1) | 501 | require.Len(t, got, 1) |
| 502 | assert.Equal(t, "active", got[0].GetState()) | 502 | assert.Equal(t, "active", got[0].GetState()) |
| 503 | assert.Contains(t, got[0].GetReason(), "out of descriptors") | 503 | assert.Contains(t, got[0].GetReason(), "out of descriptors") |
| @@ -508,7 +508,7 @@ func TestADescriptorShortageIsReportedWhileThePortStaysBound(t *testing.T) { | |||
| 508 | defer client.Close() | 508 | defer client.Close() |
| 509 | ln.next <- acceptResult{conn: accepted} | 509 | ln.next <- acceptResult{conn: accepted} |
| 510 | waitReason(t, m, "e1", func(r string) bool { return r == "" }, "a recovered port must stop reporting a shortage") | 510 | waitReason(t, m, "e1", func(r string) bool { return r == "" }, "a recovered port must stop reporting a shortage") |
| 511 | got = m.Converge([]*pb.ExposureDesired{d}) | 511 | got = m.Converge([]*pb.ExposureSpec{d}) |
| 512 | assert.Equal(t, "active", got[0].GetState()) | 512 | assert.Equal(t, "active", got[0].GetState()) |
| 513 | assert.Empty(t, got[0].GetReason()) | 513 | assert.Empty(t, got[0].GetReason()) |
| 514 | } | 514 | } |
| @@ -580,7 +580,7 @@ func TestAcceptKeepsThePortThroughADescriptorShortage(t *testing.T) { | |||
| 580 | func TestStopAllClosesEveryListener(t *testing.T) { | 580 | func TestStopAllClosesEveryListener(t *testing.T) { |
| 581 | g := newFakeGuest(t) | 581 | g := newFakeGuest(t) |
| 582 | m := NewManager(func(string) string { return g.addr }) | 582 | m := NewManager(func(string) string { return g.addr }) |
| 583 | m.Converge([]*pb.ExposureDesired{ | 583 | m.Converge([]*pb.ExposureSpec{ |
| 584 | desired("e1", "vm1", g.port, 0), | 584 | desired("e1", "vm1", g.port, 0), |
| 585 | desired("e2", "vm1", g.port, 0), | 585 | desired("e2", "vm1", g.port, 0), |
| 586 | }) | 586 | }) |
| @@ -597,7 +597,7 @@ func TestStopAllClosesEveryListener(t *testing.T) { | |||
| 597 | // counters converges the same desired set again and returns what the named | 597 | // counters converges the same desired set again and returns what the named |
| 598 | // exposure reports about what it has carried. Converge is level-triggered, so | 598 | // exposure reports about what it has carried. Converge is level-triggered, so |
| 599 | // asking twice is how the report is taken in production too. | 599 | // asking twice is how the report is taken in production too. |
| 600 | func counters(t *testing.T, m *Manager, d []*pb.ExposureDesired, id string) *pb.ExposureSessions { | 600 | func counters(t *testing.T, m *Manager, d []*pb.ExposureSpec, id string) *pb.ExposureSessions { |
| 601 | t.Helper() | 601 | t.Helper() |
| 602 | for _, a := range m.Converge(d) { | 602 | for _, a := range m.Converge(d) { |
| 603 | if a.GetId() == id { | 603 | if a.GetId() == id { |
| @@ -616,7 +616,7 @@ func TestExposureCountsWhatItHoldsAndWhatItTurnsAway(t *testing.T) { | |||
| 616 | g := newHoldingGuest(t) | 616 | g := newHoldingGuest(t) |
| 617 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 617 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 618 | m.maxConns = 1 | 618 | m.maxConns = 1 |
| 619 | spec := []*pb.ExposureDesired{desired("e1", "vm1", g.port, 0)} | 619 | spec := []*pb.ExposureSpec{desired("e1", "vm1", g.port, 0)} |
| 620 | m.Converge(spec) | 620 | m.Converge(spec) |
| 621 | addr := boundPort(t, m, "e1") | 621 | addr := boundPort(t, m, "e1") |
| 622 | 622 | ||
| @@ -653,7 +653,7 @@ func TestExposureCountsWhatItHoldsAndWhatItTurnsAway(t *testing.T) { | |||
| 653 | // one reading "dropped" should look at the guest. | 653 | // one reading "dropped" should look at the guest. |
| 654 | func TestExposureCountsACallerItCannotCarry(t *testing.T) { | 654 | func TestExposureCountsACallerItCannotCarry(t *testing.T) { |
| 655 | m := newTestManager(t, map[string]string{}) // no address for vm1: still leasing | 655 | m := newTestManager(t, map[string]string{}) // no address for vm1: still leasing |
| 656 | spec := []*pb.ExposureDesired{desired("e1", "vm1", 8080, 0)} | 656 | spec := []*pb.ExposureSpec{desired("e1", "vm1", 8080, 0)} |
| 657 | m.Converge(spec) | 657 | m.Converge(spec) |
| 658 | addr := boundPort(t, m, "e1") | 658 | addr := boundPort(t, m, "e1") |
| 659 | 659 | ||
| @@ -680,7 +680,7 @@ func TestAFailedExposureStillReportsItsCounters(t *testing.T) { | |||
| 680 | 680 | ||
| 681 | m := newTestManager(t, map[string]string{"vm1": "127.0.0.1"}) | 681 | m := newTestManager(t, map[string]string{"vm1": "127.0.0.1"}) |
| 682 | port := portOf(t, held.Addr()) | 682 | port := portOf(t, held.Addr()) |
| 683 | out := m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", 8080, port)}) | 683 | out := m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", 8080, port)}) |
| 684 | require.Len(t, out, 1) | 684 | require.Len(t, out, 1) |
| 685 | require.Equal(t, "failed", out[0].GetState()) | 685 | require.Equal(t, "failed", out[0].GetState()) |
| 686 | require.NotNil(t, out[0].GetSessions()) | 686 | require.NotNil(t, out[0].GetSessions()) |
internal/agent/exposeproxy/udp_test.go
| Old | New | ||
|---|---|---|---|
| @@ -123,7 +123,7 @@ func TestUDPConvergeBindsAndForwardsBothWays(t *testing.T) { | |||
| 123 | g := newFakeUDPGuest(t) | 123 | g := newFakeUDPGuest(t) |
| 124 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 124 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 125 | 125 | ||
| 126 | got := m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", g.port, 0)}) | 126 | got := m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", g.port, 0)}) |
| 127 | require.Len(t, got, 1) | 127 | require.Len(t, got, 1) |
| 128 | assert.Equal(t, "active", got[0].GetState(), "active means the packet socket is bound") | 128 | assert.Equal(t, "active", got[0].GetState(), "active means the packet socket is bound") |
| 129 | 129 | ||
| @@ -135,7 +135,7 @@ func TestUDPConvergeBindsAndForwardsBothWays(t *testing.T) { | |||
| 135 | func TestUDPKeepsDatagramBoundaries(t *testing.T) { | 135 | func TestUDPKeepsDatagramBoundaries(t *testing.T) { |
| 136 | g := newFakeUDPGuest(t) | 136 | g := newFakeUDPGuest(t) |
| 137 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 137 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 138 | m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", g.port, 0)}) | 138 | m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", g.port, 0)}) |
| 139 | c := udpClient(t, boundUDPPort(t, m, "e1")) | 139 | c := udpClient(t, boundUDPPort(t, m, "e1")) |
| 140 | 140 | ||
| 141 | // Two datagrams in, two out, in order and whole — nothing here coalesces a | 141 | // Two datagrams in, two out, in order and whole — nothing here coalesces a |
| @@ -152,7 +152,7 @@ func TestUDPKeepsDatagramBoundaries(t *testing.T) { | |||
| 152 | func TestUDPSessionIsPerClientAddress(t *testing.T) { | 152 | func TestUDPSessionIsPerClientAddress(t *testing.T) { |
| 153 | g := newFakeUDPGuest(t) | 153 | g := newFakeUDPGuest(t) |
| 154 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 154 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 155 | m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", g.port, 0)}) | 155 | m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", g.port, 0)}) |
| 156 | addr := boundUDPPort(t, m, "e1") | 156 | addr := boundUDPPort(t, m, "e1") |
| 157 | 157 | ||
| 158 | first, second := udpClient(t, addr), udpClient(t, addr) | 158 | first, second := udpClient(t, addr), udpClient(t, addr) |
| @@ -174,7 +174,7 @@ func TestUDPSessionIsPromotedOnlyByAGuestReply(t *testing.T) { | |||
| 174 | quietPort := uint32(silent.LocalAddr().(*net.UDPAddr).Port) | 174 | quietPort := uint32(silent.LocalAddr().(*net.UDPAddr).Port) |
| 175 | 175 | ||
| 176 | m := newTestManager(t, map[string]string{"vm1": "127.0.0.1"}) | 176 | m := newTestManager(t, map[string]string{"vm1": "127.0.0.1"}) |
| 177 | m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", quietPort, 0)}) | 177 | m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", quietPort, 0)}) |
| 178 | c := udpClient(t, boundUDPPort(t, m, "e1")) | 178 | c := udpClient(t, boundUDPPort(t, m, "e1")) |
| 179 | _, err = c.Write([]byte("anyone there")) | 179 | _, err = c.Write([]byte("anyone there")) |
| 180 | require.NoError(t, err) | 180 | require.NoError(t, err) |
| @@ -186,7 +186,7 @@ func TestUDPSessionIsPromotedOnlyByAGuestReply(t *testing.T) { | |||
| 186 | 186 | ||
| 187 | // Now a guest that answers. The reply is the promotion. | 187 | // Now a guest that answers. The reply is the promotion. |
| 188 | g := newFakeUDPGuest(t) | 188 | g := newFakeUDPGuest(t) |
| 189 | m.Converge([]*pb.ExposureDesired{desiredUDP("e2", "vm1", g.port, 0)}) | 189 | m.Converge([]*pb.ExposureSpec{desiredUDP("e2", "vm1", g.port, 0)}) |
| 190 | talking := udpClient(t, boundUDPPort(t, m, "e2")) | 190 | talking := udpClient(t, boundUDPPort(t, m, "e2")) |
| 191 | require.Equal(t, "echo:hi", say(t, talking, "hi")) | 191 | require.Equal(t, "echo:hi", say(t, talking, "hi")) |
| 192 | 192 | ||
| @@ -206,7 +206,7 @@ func TestUDPSessionExpiresOnBothWindows(t *testing.T) { | |||
| 206 | silent, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}) | 206 | silent, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}) |
| 207 | require.NoError(t, err) | 207 | require.NoError(t, err) |
| 208 | t.Cleanup(func() { silent.Close() }) | 208 | t.Cleanup(func() { silent.Close() }) |
| 209 | m.Converge([]*pb.ExposureDesired{ | 209 | m.Converge([]*pb.ExposureSpec{ |
| 210 | desiredUDP("quiet", "vm1", uint32(silent.LocalAddr().(*net.UDPAddr).Port), 0), | 210 | desiredUDP("quiet", "vm1", uint32(silent.LocalAddr().(*net.UDPAddr).Port), 0), |
| 211 | desiredUDP("live", "vm1", g.port, 0), | 211 | desiredUDP("live", "vm1", g.port, 0), |
| 212 | }) | 212 | }) |
| @@ -231,7 +231,7 @@ func TestUDPRefusesNewSessionsAtItsCapWithoutEvicting(t *testing.T) { | |||
| 231 | g := newFakeUDPGuest(t) | 231 | g := newFakeUDPGuest(t) |
| 232 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 232 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 233 | m.maxSessions = 1 | 233 | m.maxSessions = 1 |
| 234 | m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", g.port, 0)}) | 234 | m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", g.port, 0)}) |
| 235 | addr := boundUDPPort(t, m, "e1") | 235 | addr := boundUDPPort(t, m, "e1") |
| 236 | 236 | ||
| 237 | held := udpClient(t, addr) | 237 | held := udpClient(t, addr) |
| @@ -262,13 +262,13 @@ func TestUDPConvergeEvictsASessionWhoseGuestMoved(t *testing.T) { | |||
| 262 | // Both fakes answer on 127.0.0.1, so the guest PORT is what tells them | 262 | // Both fakes answer on 127.0.0.1, so the guest PORT is what tells them |
| 263 | // apart; the pin under test is the address, so move the VM to an address | 263 | // apart; the pin under test is the address, so move the VM to an address |
| 264 | // nothing is at and prove the session goes. | 264 | // nothing is at and prove the session goes. |
| 265 | m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", first.port, 0)}) | 265 | m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", first.port, 0)}) |
| 266 | c := udpClient(t, boundUDPPort(t, m, "e1")) | 266 | c := udpClient(t, boundUDPPort(t, m, "e1")) |
| 267 | require.Equal(t, "echo:before", say(t, c, "before")) | 267 | require.Equal(t, "echo:before", say(t, c, "before")) |
| 268 | s := oneSession(t, m, "e1") | 268 | s := oneSession(t, m, "e1") |
| 269 | 269 | ||
| 270 | where["vm1"] = "127.0.0.2" | 270 | where["vm1"] = "127.0.0.2" |
| 271 | m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", first.port, 0)}) | 271 | m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", first.port, 0)}) |
| 272 | assert.Equal(t, 0, sessions(t, m, "e1"), "a pin that no longer matches is not a session to keep") | 272 | assert.Equal(t, 0, sessions(t, m, "e1"), "a pin that no longer matches is not a session to keep") |
| 273 | _, err := s.guest.Write([]byte("orphan")) | 273 | _, err := s.guest.Write([]byte("orphan")) |
| 274 | assert.Error(t, err, "the evicted session's socket is closed, not leaked") | 274 | assert.Error(t, err, "the evicted session's socket is closed, not leaked") |
| @@ -276,17 +276,17 @@ func TestUDPConvergeEvictsASessionWhoseGuestMoved(t *testing.T) { | |||
| 276 | // A guest the host has lost track of entirely is no different: there is | 276 | // A guest the host has lost track of entirely is no different: there is |
| 277 | // nowhere to send, so there is no session. | 277 | // nowhere to send, so there is no session. |
| 278 | where["vm1"] = second.addr | 278 | where["vm1"] = second.addr |
| 279 | m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", second.port, 0)}) | 279 | m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", second.port, 0)}) |
| 280 | require.Equal(t, "echo:after", say(t, udpClient(t, boundUDPPort(t, m, "e1")), "after")) | 280 | require.Equal(t, "echo:after", say(t, udpClient(t, boundUDPPort(t, m, "e1")), "after")) |
| 281 | where["vm1"] = "" | 281 | where["vm1"] = "" |
| 282 | m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", second.port, 0)}) | 282 | m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", second.port, 0)}) |
| 283 | assert.Equal(t, 0, sessions(t, m, "e1"), "no address is not an address to keep sending to") | 283 | assert.Equal(t, 0, sessions(t, m, "e1"), "no address is not an address to keep sending to") |
| 284 | } | 284 | } |
| 285 | 285 | ||
| 286 | func TestUDPSessionEndsWhenItsGuestSocketFails(t *testing.T) { | 286 | func TestUDPSessionEndsWhenItsGuestSocketFails(t *testing.T) { |
| 287 | g := newFakeUDPGuest(t) | 287 | g := newFakeUDPGuest(t) |
| 288 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 288 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 289 | m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", g.port, 0)}) | 289 | m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", g.port, 0)}) |
| 290 | c := udpClient(t, boundUDPPort(t, m, "e1")) | 290 | c := udpClient(t, boundUDPPort(t, m, "e1")) |
| 291 | require.Equal(t, "echo:up", say(t, c, "up")) | 291 | require.Equal(t, "echo:up", say(t, c, "up")) |
| 292 | 292 | ||
| @@ -304,7 +304,7 @@ func TestUDPSessionEndsWhenItsGuestSocketFails(t *testing.T) { | |||
| 304 | 304 | ||
| 305 | func TestUDPDropsADatagramForAGuestWithNoAddress(t *testing.T) { | 305 | func TestUDPDropsADatagramForAGuestWithNoAddress(t *testing.T) { |
| 306 | m := newTestManager(t, map[string]string{}) // the guest is still leasing | 306 | m := newTestManager(t, map[string]string{}) // the guest is still leasing |
| 307 | m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", 8080, 0)}) | 307 | m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", 8080, 0)}) |
| 308 | 308 | ||
| 309 | c := udpClient(t, boundUDPPort(t, m, "e1")) | 309 | c := udpClient(t, boundUDPPort(t, m, "e1")) |
| 310 | _, err := c.Write([]byte("anyone")) | 310 | _, err := c.Write([]byte("anyone")) |
| @@ -324,7 +324,7 @@ func TestUDPConvergeReportsABindFailure(t *testing.T) { | |||
| 324 | require.NoError(t, err) | 324 | require.NoError(t, err) |
| 325 | held := uint32(squatter.LocalAddr().(*net.UDPAddr).Port) | 325 | held := uint32(squatter.LocalAddr().(*net.UDPAddr).Port) |
| 326 | 326 | ||
| 327 | got := m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", g.port, held)}) | 327 | got := m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", g.port, held)}) |
| 328 | require.Len(t, got, 1) | 328 | require.Len(t, got, 1) |
| 329 | assert.Equal(t, "failed", got[0].GetState()) | 329 | assert.Equal(t, "failed", got[0].GetState()) |
| 330 | assert.NotEmpty(t, got[0].GetReason(), "the report carries what the OS said") | 330 | assert.NotEmpty(t, got[0].GetReason(), "the report carries what the OS said") |
| @@ -332,7 +332,7 @@ func TestUDPConvergeReportsABindFailure(t *testing.T) { | |||
| 332 | require.NoError(t, squatter.Close()) | 332 | require.NoError(t, squatter.Close()) |
| 333 | var state string | 333 | var state string |
| 334 | for i := 0; i < 20; i++ { | 334 | for i := 0; i < 20; i++ { |
| 335 | got = m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", g.port, held)}) | 335 | got = m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", g.port, held)}) |
| 336 | state = got[0].GetState() | 336 | state = got[0].GetState() |
| 337 | if state == "active" { | 337 | if state == "active" { |
| 338 | break | 338 | break |
| @@ -347,10 +347,10 @@ func TestChangingTheProtocolRebinds(t *testing.T) { | |||
| 347 | udpGuest := newFakeUDPGuest(t) | 347 | udpGuest := newFakeUDPGuest(t) |
| 348 | m := newTestManager(t, map[string]string{"vm1": tcpGuest.addr}) | 348 | m := newTestManager(t, map[string]string{"vm1": tcpGuest.addr}) |
| 349 | 349 | ||
| 350 | m.Converge([]*pb.ExposureDesired{desired("e1", "vm1", tcpGuest.port, 0)}) | 350 | m.Converge([]*pb.ExposureSpec{desired("e1", "vm1", tcpGuest.port, 0)}) |
| 351 | assert.Equal(t, "echo:tcp", speak(t, boundPort(t, m, "e1"), "tcp")) | 351 | assert.Equal(t, "echo:tcp", speak(t, boundPort(t, m, "e1"), "tcp")) |
| 352 | 352 | ||
| 353 | m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", udpGuest.port, 0)}) | 353 | m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", udpGuest.port, 0)}) |
| 354 | m.mu.Lock() | 354 | m.mu.Lock() |
| 355 | ex := m.live["e1"] | 355 | ex := m.live["e1"] |
| 356 | m.mu.Unlock() | 356 | m.mu.Unlock() |
| @@ -361,7 +361,7 @@ func TestChangingTheProtocolRebinds(t *testing.T) { | |||
| 361 | func TestStopAllClosesPacketSocketsToo(t *testing.T) { | 361 | func TestStopAllClosesPacketSocketsToo(t *testing.T) { |
| 362 | g := newFakeUDPGuest(t) | 362 | g := newFakeUDPGuest(t) |
| 363 | m := NewManager(func(string) string { return g.addr }) | 363 | m := NewManager(func(string) string { return g.addr }) |
| 364 | m.Converge([]*pb.ExposureDesired{desiredUDP("e1", "vm1", g.port, 0)}) | 364 | m.Converge([]*pb.ExposureSpec{desiredUDP("e1", "vm1", g.port, 0)}) |
| 365 | addr := boundUDPPort(t, m, "e1") | 365 | addr := boundUDPPort(t, m, "e1") |
| 366 | c := udpClient(t, addr) | 366 | c := udpClient(t, addr) |
| 367 | require.Equal(t, "echo:live", say(t, c, "live")) | 367 | require.Equal(t, "echo:live", say(t, c, "live")) |
| @@ -393,7 +393,7 @@ func TestUDPCountsItsSessionsAndRefusals(t *testing.T) { | |||
| 393 | g := newFakeUDPGuest(t) | 393 | g := newFakeUDPGuest(t) |
| 394 | m := newTestManager(t, map[string]string{"vm1": g.addr}) | 394 | m := newTestManager(t, map[string]string{"vm1": g.addr}) |
| 395 | m.maxSessions = 1 | 395 | m.maxSessions = 1 |
| 396 | spec := []*pb.ExposureDesired{desiredUDP("e1", "vm1", g.port, 0)} | 396 | spec := []*pb.ExposureSpec{desiredUDP("e1", "vm1", g.port, 0)} |
| 397 | m.Converge(spec) | 397 | m.Converge(spec) |
| 398 | addr := boundUDPPort(t, m, "e1") | 398 | addr := boundUDPPort(t, m, "e1") |
| 399 | 399 | ||
| @@ -421,7 +421,7 @@ func TestUDPCountsItsSessionsAndRefusals(t *testing.T) { | |||
| 421 | // port at its cap, and the two must not read the same in a report. | 421 | // port at its cap, and the two must not read the same in a report. |
| 422 | func TestUDPCountsADatagramForAGuestWithNoAddress(t *testing.T) { | 422 | func TestUDPCountsADatagramForAGuestWithNoAddress(t *testing.T) { |
| 423 | m := newTestManager(t, map[string]string{}) // vm1 has no address yet | 423 | m := newTestManager(t, map[string]string{}) // vm1 has no address yet |
| 424 | spec := []*pb.ExposureDesired{desiredUDP("e1", "vm1", 9999, 0)} | 424 | spec := []*pb.ExposureSpec{desiredUDP("e1", "vm1", 9999, 0)} |
| 425 | m.Converge(spec) | 425 | m.Converge(spec) |
| 426 | addr := boundUDPPort(t, m, "e1") | 426 | addr := boundUDPPort(t, m, "e1") |
| 427 | 427 | ||
internal/agent/reconcile/hostkey_test.go
| Old | New | ||
|---|---|---|---|
| @@ -14,11 +14,11 @@ import ( | |||
| 14 | ) | 14 | ) |
| 15 | 15 | ||
| 16 | // needsHostCert marks a desired VM the way a fleet with an SSH CA does. | 16 | // needsHostCert marks a desired VM the way a fleet with an SSH CA does. |
| 17 | func needsHostCert(v *pb.VMDesired) { v.HostCertRequired = true } | 17 | func needsHostCert(v *pb.VMSpec) { v.HostCertRequired = true } |
| 18 | 18 | ||
| 19 | // withHostCert supplies the certificate the control plane signed. | 19 | // withHostCert supplies the certificate the control plane signed. |
| 20 | func withHostCert(cert string) func(*pb.VMDesired) { | 20 | func withHostCert(cert string) func(*pb.VMSpec) { |
| 21 | return func(v *pb.VMDesired) { v.HostCertRequired = true; v.SshHostCert = cert } | 21 | return func(v *pb.VMSpec) { v.HostCertRequired = true; v.SshHostCert = cert } |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | // TestAwaitingHostCertDoesNotSpendRetryBudget is the regression that matters | 24 | // TestAwaitingHostCertDoesNotSpendRetryBudget is the regression that matters |
internal/agent/reconcile/quota_test.go
| Old | New | ||
|---|---|---|---|
| @@ -9,8 +9,8 @@ import ( | |||
| 9 | ) | 9 | ) |
| 10 | 10 | ||
| 11 | // withRes overrides a desired VM's resource request. | 11 | // withRes overrides a desired VM's resource request. |
| 12 | func withRes(vcpus, memMB, diskGB int64) func(*pb.VMDesired) { | 12 | func withRes(vcpus, memMB, diskGB int64) func(*pb.VMSpec) { |
| 13 | return func(v *pb.VMDesired) { v.Vcpus = vcpus; v.MemMb = memMB; v.DiskGb = diskGB } | 13 | return func(v *pb.VMSpec) { v.Vcpus = vcpus; v.MemMb = memMB; v.DiskGb = diskGB } |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | func TestQuotaUnderCapBoots(t *testing.T) { | 16 | func TestQuotaUnderCapBoots(t *testing.T) { |
| @@ -93,7 +93,7 @@ func TestQuotaFreedByQuarantineEventuallyBootsTheWaitingVM(t *testing.T) { | |||
| 93 | // Two ticks suffice deterministically: whatever the map order, tick 1 reaps | 93 | // Two ticks suffice deterministically: whatever the map order, tick 1 reaps |
| 94 | // vm1 and releases its compute, so tick 2 always admits vm2. The third is | 94 | // vm1 and releases its compute, so tick 2 always admits vm2. The third is |
| 95 | // slack, not a flake bound. | 95 | // slack, not a flake bound. |
| 96 | var rep *pb.ActualStateReport | 96 | var rep *pb.Report |
| 97 | for range 3 { | 97 | for range 3 { |
| 98 | rep = f.step(snap(2, | 98 | rep = f.step(snap(2, |
| 99 | tombstoned(vm("vm1", withRes(2, 512, 5))), | 99 | tombstoned(vm("vm1", withRes(2, 512, 5))), |
internal/agent/reconcile/reconcile.go
| Old | New | ||
|---|---|---|---|
| @@ -219,12 +219,12 @@ type Engine struct { | |||
| 219 | // explicit because the two carry different grace periods (VanishGrace vs the | 219 | // explicit because the two carry different grace periods (VanishGrace vs the |
| 220 | // shorter TombstoneGrace). | 220 | // shorter TombstoneGrace). |
| 221 | // | 221 | // |
| 222 | // LIFETIME: desired points into the caller's DesiredStateSnapshot, and a worker | 222 | // LIFETIME: desired points into the caller's Snapshot, and a worker |
| 223 | // holds that pointer well past the Step that delivered it — for as long as its | 223 | // holds that pointer well past the Step that delivered it — for as long as its |
| 224 | // pass runs, up to VMTimeout. A snapshot handed to Step must therefore be | 224 | // pass runs, up to VMTimeout. A snapshot handed to Step must therefore be |
| 225 | // treated as immutable while any pass may still be running. | 225 | // treated as immutable while any pass may still be running. |
| 226 | type assignment struct { | 226 | type assignment struct { |
| 227 | desired *pb.VMDesired | 227 | desired *pb.VMSpec |
| 228 | tombstoned bool | 228 | tombstoned bool |
| 229 | } | 229 | } |
| 230 | 230 | ||
| @@ -232,7 +232,7 @@ type assignment struct { | |||
| 232 | // desired state and local records: a desired-only id is a create, a record-only | 232 | // desired state and local records: a desired-only id is a create, a record-only |
| 233 | // id is a vanished VM to reap, and an id in both converges. The union is also | 233 | // id is a vanished VM to reap, and an id in both converges. The union is also |
| 234 | // exactly the set of VMs that need a reconcile pass this tick. | 234 | // exactly the set of VMs that need a reconcile pass this tick. |
| 235 | func assignments(snap *pb.DesiredStateSnapshot, recs map[string]state.Record) map[string]assignment { | 235 | func assignments(snap *pb.Snapshot, recs map[string]state.Record) map[string]assignment { |
| 236 | out := make(map[string]assignment, len(snap.Vms)+len(recs)) | 236 | out := make(map[string]assignment, len(snap.Vms)+len(recs)) |
| 237 | for _, d := range snap.Vms { | 237 | for _, d := range snap.Vms { |
| 238 | out[d.VmId] = assignment{desired: d, tombstoned: d.Tombstoned} | 238 | out[d.VmId] = assignment{desired: d, tombstoned: d.Tombstoned} |
| @@ -247,7 +247,7 @@ func assignments(snap *pb.DesiredStateSnapshot, recs map[string]state.Record) ma | |||
| 247 | 247 | ||
| 248 | // tombstonedSet returns the ids the control plane has flagged for deletion. | 248 | // tombstonedSet returns the ids the control plane has flagged for deletion. |
| 249 | // The destroy ack is level-triggered from it (see ackDestroyed). | 249 | // The destroy ack is level-triggered from it (see ackDestroyed). |
| 250 | func tombstonedSet(snap *pb.DesiredStateSnapshot) map[string]bool { | 250 | func tombstonedSet(snap *pb.Snapshot) map[string]bool { |
| 251 | out := make(map[string]bool, len(snap.Vms)) | 251 | out := make(map[string]bool, len(snap.Vms)) |
| 252 | for _, d := range snap.Vms { | 252 | for _, d := range snap.Vms { |
| 253 | if d.Tombstoned { | 253 | if d.Tombstoned { |
| @@ -258,7 +258,7 @@ func tombstonedSet(snap *pb.DesiredStateSnapshot) map[string]bool { | |||
| 258 | } | 258 | } |
| 259 | 259 | ||
| 260 | // Step routes one desired-state snapshot to the per-VM workers and returns the | 260 | // Step routes one desired-state snapshot to the per-VM workers and returns the |
| 261 | // host's ActualStateReport. It never waits for a worker: the report carries each | 261 | // host's Report. It never waits for a worker: the report carries each |
| 262 | // VM's LAST-PUBLISHED state, so a VM busy in a multi-second operation cannot | 262 | // VM's LAST-PUBLISHED state, so a VM busy in a multi-second operation cannot |
| 263 | // delay the heartbeat. A VM that has not published yet simply has no row. | 263 | // delay the heartbeat. A VM that has not published yet simply has no row. |
| 264 | // | 264 | // |
| @@ -267,7 +267,7 @@ func tombstonedSet(snap *pb.DesiredStateSnapshot) map[string]bool { | |||
| 267 | // | 267 | // |
| 268 | // ctx is accepted for signature stability and bounds nothing here — the work | 268 | // ctx is accepted for signature stability and bounds nothing here — the work |
| 269 | // happens in workers, where VMTimeout bounds each VM's pass. | 269 | // happens in workers, where VMTimeout bounds each VM's pass. |
| 270 | func (e *Engine) Step(ctx context.Context, snap *pb.DesiredStateSnapshot) *pb.ActualStateReport { | 270 | func (e *Engine) Step(ctx context.Context, snap *pb.Snapshot) *pb.Report { |
| 271 | // ── 1. Epoch fence ─────────────────────────────────────────────────────── | 271 | // ── 1. Epoch fence ─────────────────────────────────────────────────────── |
| 272 | // CRITICAL: the fence path must touch NOTHING: no SaveEpoch, no provisioner | 272 | // CRITICAL: the fence path must touch NOTHING: no SaveEpoch, no provisioner |
| 273 | // calls, no dispatch, no state mutations. It returns the current actual state | 273 | // calls, no dispatch, no state mutations. It returns the current actual state |
| @@ -304,7 +304,7 @@ func (e *Engine) Step(ctx context.Context, snap *pb.DesiredStateSnapshot) *pb.Ac | |||
| 304 | // reap frees compute may run after a sibling's admission, so the sibling is | 304 | // reap frees compute may run after a sibling's admission, so the sibling is |
| 305 | // quota-refused and boots on a later tick. The refusal is non-terminal and the | 305 | // quota-refused and boots on a later tick. The refusal is non-terminal and the |
| 306 | // loop is level-triggered, so the guarantee is eventual rather than same-tick. | 306 | // loop is level-triggered, so the guarantee is eventual rather than same-tick. |
| 307 | func (e *Engine) dispatch(snap *pb.DesiredStateSnapshot, recs map[string]state.Record) { | 307 | func (e *Engine) dispatch(snap *pb.Snapshot, recs map[string]state.Record) { |
| 308 | live := assignments(snap, recs) | 308 | live := assignments(snap, recs) |
| 309 | 309 | ||
| 310 | m := e.manager() | 310 | m := e.manager() |
| @@ -327,8 +327,8 @@ func (e *Engine) dispatch(snap *pb.DesiredStateSnapshot, recs map[string]state.R | |||
| 327 | // | 327 | // |
| 328 | // recs is the record view the destroy ack is computed against, with the caller | 328 | // recs is the record view the destroy ack is computed against, with the caller |
| 329 | // choosing how fresh it is (see ackDestroyed). | 329 | // choosing how fresh it is (see ackDestroyed). |
| 330 | func (e *Engine) aggregate(epoch uint64, recs map[string]state.Record) *pb.ActualStateReport { | 330 | func (e *Engine) aggregate(epoch uint64, recs map[string]state.Record) *pb.Report { |
| 331 | rep := &pb.ActualStateReport{LastSeenEpoch: epoch} | 331 | rep := &pb.Report{LastSeenEpoch: epoch} |
| 332 | m := e.manager() | 332 | m := e.manager() |
| 333 | m.collect(rep) | 333 | m.collect(rep) |
| 334 | e.ackDestroyed(rep, m.tombstones(), recs) | 334 | e.ackDestroyed(rep, m.tombstones(), recs) |
| @@ -338,8 +338,8 @@ func (e *Engine) aggregate(epoch uint64, recs map[string]state.Record) *pb.Actua | |||
| 338 | // fenceReport is the read-only report returned for a stale snapshot: current | 338 | // fenceReport is the read-only report returned for a stale snapshot: current |
| 339 | // actual state, derived entirely from persisted records, with no mutation and | 339 | // actual state, derived entirely from persisted records, with no mutation and |
| 340 | // no dispatch. | 340 | // no dispatch. |
| 341 | func (e *Engine) fenceReport(currentEpoch uint64) *pb.ActualStateReport { | 341 | func (e *Engine) fenceReport(currentEpoch uint64) *pb.Report { |
| 342 | rep := &pb.ActualStateReport{FenceViolation: true, LastSeenEpoch: currentEpoch} | 342 | rep := &pb.Report{FenceViolation: true, LastSeenEpoch: currentEpoch} |
| 343 | recs, err := e.St.LoadVMs() | 343 | recs, err := e.St.LoadVMs() |
| 344 | if err != nil { | 344 | if err != nil { |
| 345 | return rep | 345 | return rep |
| @@ -447,7 +447,7 @@ func (e *Engine) reconcileOne(ctx context.Context, id string, a assignment, pub | |||
| 447 | // Step passes the view it dispatched with, read before this tick's destroying | 447 | // Step passes the view it dispatched with, read before this tick's destroying |
| 448 | // passes ran, so the ack lands one or more ticks AFTER the pass that removed the | 448 | // passes ran, so the ack lands one or more ticks AFTER the pass that removed the |
| 449 | // record, not in the same tick. Being level-triggered is what makes that fine. | 449 | // record, not in the same tick. Being level-triggered is what makes that fine. |
| 450 | func (e *Engine) ackDestroyed(rep *pb.ActualStateReport, tombstoned map[string]bool, recs map[string]state.Record) { | 450 | func (e *Engine) ackDestroyed(rep *pb.Report, tombstoned map[string]bool, recs map[string]state.Record) { |
| 451 | for id := range tombstoned { | 451 | for id := range tombstoned { |
| 452 | if _, hasRecord := recs[id]; !hasRecord { | 452 | if _, hasRecord := recs[id]; !hasRecord { |
| 453 | rep.Destroyed = append(rep.Destroyed, id) | 453 | rep.Destroyed = append(rep.Destroyed, id) |
| @@ -522,7 +522,7 @@ func (e *Engine) reapVM(ctx context.Context, id string, rec state.Record, isTomb | |||
| 522 | // PrepareRootDisk but before boot leaves a disk on a still-empty BootID. Treating | 522 | // PrepareRootDisk but before boot leaves a disk on a still-empty BootID. Treating |
| 523 | // that disk as "exists" would divert the retry to converge(), which never | 523 | // that disk as "exists" would divert the retry to converge(), which never |
| 524 | // rebuilds the disk or seed, and the VM would never recover. | 524 | // rebuilds the disk or seed, and the VM would never recover. |
| 525 | func (e *Engine) reconcileVM(ctx context.Context, d *pb.VMDesired, rec state.Record, ok bool, res *vmResult, pub publisher) { | 525 | func (e *Engine) reconcileVM(ctx context.Context, d *pb.VMSpec, rec state.Record, ok bool, res *vmResult, pub publisher) { |
| 526 | if !ok || rec.BootID == "" { | 526 | if !ok || rec.BootID == "" { |
| 527 | e.create(ctx, d, rec, ok, res, pub) | 527 | e.create(ctx, d, rec, ok, res, pub) |
| 528 | return | 528 | return |
| @@ -673,7 +673,7 @@ func (e *Engine) quotaCheckLocked(vmID string, spec state.VMSpec) string { | |||
| 673 | // prior record (retry budget, last known address) and ok reports whether one | 673 | // prior record (retry budget, last known address) and ok reports whether one |
| 674 | // exists. Quota comes from the serialized admission ledger (see admit); the | 674 | // exists. Quota comes from the serialized admission ledger (see admit); the |
| 675 | // address comes from the backend, at boot. | 675 | // address comes from the backend, at boot. |
| 676 | func (e *Engine) create(ctx context.Context, d *pb.VMDesired, rec state.Record, ok bool, res *vmResult, pub publisher) { | 676 | func (e *Engine) create(ctx context.Context, d *pb.VMSpec, rec state.Record, ok bool, res *vmResult, pub publisher) { |
| 677 | // Defensive: never start an attempt (which would burn retry budget) on a | 677 | // Defensive: never start an attempt (which would burn retry budget) on a |
| 678 | // context that is already dead. UNREACHABLE today — a pass context is a | 678 | // context that is already dead. UNREACHABLE today — a pass context is a |
| 679 | // fresh context.Background plus VMTimeout (see worker.run), so it cannot | 679 | // fresh context.Background plus VMTimeout (see worker.run), so it cannot |
| @@ -685,7 +685,7 @@ func (e *Engine) create(ctx context.Context, d *pb.VMDesired, rec state.Record, | |||
| 685 | return | 685 | return |
| 686 | } | 686 | } |
| 687 | 687 | ||
| 688 | spec := specFromDesired(d) | 688 | spec := specFromWire(d) |
| 689 | 689 | ||
| 690 | // Fix 2: if the desired spec differs from the stored spec, the user edited the | 690 | // Fix 2: if the desired spec differs from the stored spec, the user edited the |
| 691 | // VM definition. Reset CreateAttempts so the new spec gets a fresh retry budget | 691 | // VM definition. Reset CreateAttempts so the new spec gets a fresh retry budget |
| @@ -934,7 +934,7 @@ func (e *Engine) failConverge(rec state.Record, err error, res *vmResult) { | |||
| 934 | 934 | ||
| 935 | // converge drives an existing VM toward its desired power state, | 935 | // converge drives an existing VM toward its desired power state, |
| 936 | // handling lost detection and restart logic. | 936 | // handling lost detection and restart logic. |
| 937 | func (e *Engine) converge(ctx context.Context, d *pb.VMDesired, rec state.Record, res *vmResult) { | 937 | func (e *Engine) converge(ctx context.Context, d *pb.VMSpec, rec state.Record, res *vmResult) { |
| 938 | // Keep the ledger reflecting this live VM (covers the un-delete case, where a | 938 | // Keep the ledger reflecting this live VM (covers the un-delete case, where a |
| 939 | // quarantined VM returns to desired after its compute was released). | 939 | // quarantined VM returns to desired after its compute was released). |
| 940 | e.note(d.VmId, rec.Spec) | 940 | e.note(d.VmId, rec.Spec) |
| @@ -1040,7 +1040,7 @@ func quarantinedEntry(rec state.Record, grace time.Duration) *pb.QuarantinedVM { | |||
| 1040 | // appending straight into the shared report, so a single VM's output stands on | 1040 | // appending straight into the shared report, so a single VM's output stands on |
| 1041 | // its own — which is what lets a per-VM worker publish it independently. | 1041 | // its own — which is what lets a per-VM worker publish it independently. |
| 1042 | type vmResult struct { | 1042 | type vmResult struct { |
| 1043 | vm *pb.ActualVM | 1043 | vm *pb.VMStatus |
| 1044 | quarantined *pb.QuarantinedVM | 1044 | quarantined *pb.QuarantinedVM |
| 1045 | // hostPubKey rides every row this VM contributes rather than being passed | 1045 | // hostPubKey rides every row this VM contributes rather than being passed |
| 1046 | // to report at each of its call sites. The public key is level-triggered — | 1046 | // to report at each of its call sites. The public key is level-triggered — |
| @@ -1066,7 +1066,7 @@ func recAddrs(rec state.Record) addrs { return addrs{ip: rec.IP, networkIP: rec. | |||
| 1066 | // report records this VM's actual row. A VM contributes at most one row, so a | 1066 | // report records this VM's actual row. A VM contributes at most one row, so a |
| 1067 | // later call in the same reconcile replaces an earlier one. | 1067 | // later call in the same reconcile replaces an earlier one. |
| 1068 | func (r *vmResult) report(vmID string, at addrs, power, phase, lastError string) { | 1068 | func (r *vmResult) report(vmID string, at addrs, power, phase, lastError string) { |
| 1069 | r.vm = newActualVM(vmID, at, power, phase, lastError) | 1069 | r.vm = newVMStatus(vmID, at, power, phase, lastError) |
| 1070 | } | 1070 | } |
| 1071 | 1071 | ||
| 1072 | // clone returns a deep copy, so the caller's report owns its rows outright. | 1072 | // clone returns a deep copy, so the caller's report owns its rows outright. |
| @@ -1076,7 +1076,7 @@ func (r *vmResult) report(vmID string, at addrs, power, phase, lastError string) | |||
| 1076 | func (r vmResult) clone() vmResult { | 1076 | func (r vmResult) clone() vmResult { |
| 1077 | out := r | 1077 | out := r |
| 1078 | if r.vm != nil { | 1078 | if r.vm != nil { |
| 1079 | out.vm = proto.Clone(r.vm).(*pb.ActualVM) | 1079 | out.vm = proto.Clone(r.vm).(*pb.VMStatus) |
| 1080 | } | 1080 | } |
| 1081 | if r.quarantined != nil { | 1081 | if r.quarantined != nil { |
| 1082 | out.quarantined = proto.Clone(r.quarantined).(*pb.QuarantinedVM) | 1082 | out.quarantined = proto.Clone(r.quarantined).(*pb.QuarantinedVM) |
| @@ -1085,7 +1085,7 @@ func (r vmResult) clone() vmResult { | |||
| 1085 | } | 1085 | } |
| 1086 | 1086 | ||
| 1087 | // merge folds this VM's result into the host report. | 1087 | // merge folds this VM's result into the host report. |
| 1088 | func (r *vmResult) merge(rep *pb.ActualStateReport) { | 1088 | func (r *vmResult) merge(rep *pb.Report) { |
| 1089 | if r.vm != nil { | 1089 | if r.vm != nil { |
| 1090 | r.vm.SshHostPubkey = r.hostPubKey | 1090 | r.vm.SshHostPubkey = r.hostPubKey |
| 1091 | rep.Vms = append(rep.Vms, r.vm) | 1091 | rep.Vms = append(rep.Vms, r.vm) |
| @@ -1120,17 +1120,17 @@ func byteScale(n int64) (float64, string) { | |||
| 1120 | return 1 << 20, "MiB" | 1120 | return 1 << 20, "MiB" |
| 1121 | } | 1121 | } |
| 1122 | 1122 | ||
| 1123 | // newActualVM builds one ActualVM row from what a reconcile pass observed. | 1123 | // newVMStatus builds one VMStatus row from what a reconcile pass observed. |
| 1124 | // The row's remaining field, ssh_host_pubkey, is stamped by merge — see | 1124 | // The row's remaining field, ssh_host_pubkey, is stamped by merge — see |
| 1125 | // vmResult.hostPubKey. Unset values are the proto zero-value "". | 1125 | // vmResult.hostPubKey. Unset values are the proto zero-value "". |
| 1126 | func newActualVM(vmID string, at addrs, power, phase, lastError string) *pb.ActualVM { | 1126 | func newVMStatus(vmID string, at addrs, power, phase, lastError string) *pb.VMStatus { |
| 1127 | return &pb.ActualVM{ | 1127 | return &pb.VMStatus{ |
| 1128 | VmId: vmID, | 1128 | VmId: vmID, |
| 1129 | Ip: at.ip, | 1129 | Ip: at.ip, |
| 1130 | NetworkIp: at.networkIP, | 1130 | NetworkIp: at.networkIP, |
| 1131 | Power: power, | 1131 | PowerState: power, |
| 1132 | Phase: phase, | 1132 | Phase: phase, |
| 1133 | LastError: lastError, | 1133 | LastError: lastError, |
| 1134 | } | 1134 | } |
| 1135 | } | 1135 | } |
| 1136 | 1136 | ||
| @@ -1155,8 +1155,8 @@ func netMACIfNetworked(spec state.VMSpec) string { | |||
| 1155 | return state.NetMAC(spec.VMID) | 1155 | return state.NetMAC(spec.VMID) |
| 1156 | } | 1156 | } |
| 1157 | 1157 | ||
| 1158 | // specFromDesired maps a pb.VMDesired to state.VMSpec. | 1158 | // specFromWire maps a pb.VMSpec to state.VMSpec. |
| 1159 | func specFromDesired(d *pb.VMDesired) state.VMSpec { | 1159 | func specFromWire(d *pb.VMSpec) state.VMSpec { |
| 1160 | return state.VMSpec{ | 1160 | return state.VMSpec{ |
| 1161 | VMID: d.VmId, | 1161 | VMID: d.VmId, |
| 1162 | Name: d.Name, | 1162 | Name: d.Name, |
internal/agent/reconcile/reconcile_test.go
| Old | New | ||
|---|---|---|---|
| @@ -247,7 +247,7 @@ func (f *fixture) restart(t *testing.T) { | |||
| 247 | // blocking on workers is the point — but a test has to observe a tick before it | 247 | // blocking on workers is the point — but a test has to observe a tick before it |
| 248 | // can assert on it. A fenced snapshot dispatches nothing, so its report is | 248 | // can assert on it. A fenced snapshot dispatches nothing, so its report is |
| 249 | // returned as-is. | 249 | // returned as-is. |
| 250 | func (f *fixture) step(s *pb.DesiredStateSnapshot) *pb.ActualStateReport { | 250 | func (f *fixture) step(s *pb.Snapshot) *pb.Report { |
| 251 | rep := f.eng.Step(context.Background(), s) | 251 | rep := f.eng.Step(context.Background(), s) |
| 252 | if rep.FenceViolation { | 252 | if rep.FenceViolation { |
| 253 | return rep | 253 | return rep |
| @@ -259,17 +259,17 @@ func (f *fixture) step(s *pb.DesiredStateSnapshot) *pb.ActualStateReport { | |||
| 259 | // aggregateNow re-reads records and builds the report for epoch, the way Step | 259 | // aggregateNow re-reads records and builds the report for epoch, the way Step |
| 260 | // does after its dispatch. Tests call it after waitIdle so the ack sees records | 260 | // does after its dispatch. Tests call it after waitIdle so the ack sees records |
| 261 | // the just-finished passes have already changed. | 261 | // the just-finished passes have already changed. |
| 262 | func (f *fixture) aggregateNow(epoch uint64) *pb.ActualStateReport { | 262 | func (f *fixture) aggregateNow(epoch uint64) *pb.Report { |
| 263 | recs, _ := f.st.LoadVMs() | 263 | recs, _ := f.st.LoadVMs() |
| 264 | return f.eng.aggregate(epoch, recs) | 264 | return f.eng.aggregate(epoch, recs) |
| 265 | } | 265 | } |
| 266 | 266 | ||
| 267 | func snap(epoch uint64, vms ...*pb.VMDesired) *pb.DesiredStateSnapshot { | 267 | func snap(epoch uint64, vms ...*pb.VMSpec) *pb.Snapshot { |
| 268 | return &pb.DesiredStateSnapshot{Epoch: epoch, Vms: vms} | 268 | return &pb.Snapshot{Epoch: epoch, Vms: vms} |
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | func vm(id string, opts ...func(*pb.VMDesired)) *pb.VMDesired { | 271 | func vm(id string, opts ...func(*pb.VMSpec)) *pb.VMSpec { |
| 272 | v := &pb.VMDesired{VmId: id, Name: "vm-" + id, ImageUrl: "http://x/i.img", | 272 | v := &pb.VMSpec{VmId: id, Name: "vm-" + id, ImageUrl: "http://x/i.img", |
| 273 | ImageSha256: "abc", Vcpus: 1, MemMb: 512, DiskGb: 5, PowerState: "running"} | 273 | ImageSha256: "abc", Vcpus: 1, MemMb: 512, DiskGb: 5, PowerState: "running"} |
| 274 | for _, o := range opts { | 274 | for _, o := range opts { |
| 275 | o(v) | 275 | o(v) |
| @@ -277,10 +277,10 @@ func vm(id string, opts ...func(*pb.VMDesired)) *pb.VMDesired { | |||
| 277 | return v | 277 | return v |
| 278 | } | 278 | } |
| 279 | 279 | ||
| 280 | func tombstoned(v *pb.VMDesired) *pb.VMDesired { v.Tombstoned = true; return v } | 280 | func tombstoned(v *pb.VMSpec) *pb.VMSpec { v.Tombstoned = true; return v } |
| 281 | func stopped(v *pb.VMDesired) { v.PowerState = "stopped" } | 281 | func stopped(v *pb.VMSpec) { v.PowerState = "stopped" } |
| 282 | 282 | ||
| 283 | func findVM(rep *pb.ActualStateReport, id string) *pb.ActualVM { | 283 | func findVM(rep *pb.Report, id string) *pb.VMStatus { |
| 284 | for _, v := range rep.Vms { | 284 | for _, v := range rep.Vms { |
| 285 | if v.VmId == id { | 285 | if v.VmId == id { |
| 286 | return v | 286 | return v |
| @@ -299,7 +299,7 @@ func TestCreateAllocatesIPPreparesAndBoots(t *testing.T) { | |||
| 299 | av := findVM(rep, "vm1") | 299 | av := findVM(rep, "vm1") |
| 300 | require.NotNil(t, av) | 300 | require.NotNil(t, av) |
| 301 | assert.Equal(t, "10.77.1.2", av.Ip, ".1 is the gateway") | 301 | assert.Equal(t, "10.77.1.2", av.Ip, ".1 is the gateway") |
| 302 | assert.Equal(t, "running", av.Power) | 302 | assert.Equal(t, "running", av.PowerState) |
| 303 | assert.Equal(t, "ready", av.Phase) | 303 | assert.Equal(t, "ready", av.Phase) |
| 304 | assert.Equal(t, uint64(1), rep.LastSeenEpoch) | 304 | assert.Equal(t, uint64(1), rep.LastSeenEpoch) |
| 305 | } | 305 | } |
| @@ -387,7 +387,7 @@ func TestSameTickSiblingCountsFailedCreateAgainstQuota(t *testing.T) { | |||
| 387 | f := setup(t) | 387 | f := setup(t) |
| 388 | f.eng.MaxVCPUs = 3 | 388 | f.eng.MaxVCPUs = 3 |
| 389 | f.prov.bootErr = assert.AnError | 389 | f.prov.bootErr = assert.AnError |
| 390 | twoVCPU := func(v *pb.VMDesired) { v.Vcpus = 2 } | 390 | twoVCPU := func(v *pb.VMSpec) { v.Vcpus = 2 } |
| 391 | 391 | ||
| 392 | rep := f.step(snap(1, vm("vm1", twoVCPU), vm("vm2", twoVCPU))) | 392 | rep := f.step(snap(1, vm("vm1", twoVCPU), vm("vm2", twoVCPU))) |
| 393 | 393 | ||
| @@ -433,7 +433,7 @@ func TestUserStopIsStoppedNotLost(t *testing.T) { | |||
| 433 | 433 | ||
| 434 | rep := f.step(snap(2, vm("vm1", stopped))) | 434 | rep := f.step(snap(2, vm("vm1", stopped))) |
| 435 | av := findVM(rep, "vm1") | 435 | av := findVM(rep, "vm1") |
| 436 | assert.Equal(t, "stopped", av.Power) | 436 | assert.Equal(t, "stopped", av.PowerState) |
| 437 | assert.NotEqual(t, "failed", av.Phase, "recorded stop request: stopped != lost") | 437 | assert.NotEqual(t, "failed", av.Phase, "recorded stop request: stopped != lost") |
| 438 | } | 438 | } |
| 439 | 439 | ||
| @@ -640,7 +640,7 @@ func TestVMTimeoutBoundsSlowOperations(t *testing.T) { | |||
| 640 | return "", ctx.Err() | 640 | return "", ctx.Err() |
| 641 | } | 641 | } |
| 642 | 642 | ||
| 643 | done := make(chan *pb.ActualStateReport, 1) | 643 | done := make(chan *pb.Report, 1) |
| 644 | go func() { done <- f.step(snap(1, vm("vm1"))) }() | 644 | go func() { done <- f.step(snap(1, vm("vm1"))) }() |
| 645 | 645 | ||
| 646 | select { | 646 | select { |
| @@ -830,7 +830,7 @@ func TestAddressDiscoveredAfterBootIsPersisted(t *testing.T) { | |||
| 830 | require.NotNil(t, av) | 830 | require.NotNil(t, av) |
| 831 | require.Equal(t, []string{"vm1"}, f.prov.booted) | 831 | require.Equal(t, []string{"vm1"}, f.prov.booted) |
| 832 | assert.Empty(t, av.Ip, "the guest has not asked for an address yet") | 832 | assert.Empty(t, av.Ip, "the guest has not asked for an address yet") |
| 833 | assert.Equal(t, "running", av.Power, "no address is not a failure") | 833 | assert.Equal(t, "running", av.PowerState, "no address is not a failure") |
| 834 | assert.Equal(t, "ready", av.Phase) | 834 | assert.Equal(t, "ready", av.Phase) |
| 835 | recs, _ := f.st.LoadVMs() | 835 | recs, _ := f.st.LoadVMs() |
| 836 | require.Contains(t, recs, "vm1") | 836 | require.Contains(t, recs, "vm1") |
| @@ -853,7 +853,7 @@ func TestAddressDiscoveredAfterBootIsPersisted(t *testing.T) { | |||
| 853 | // all; the LAN address arrives when the site's DHCP server says so. | 853 | // all; the LAN address arrives when the site's DHCP server says so. |
| 854 | func TestNetworkedVMReportsBothAddresses(t *testing.T) { | 854 | func TestNetworkedVMReportsBothAddresses(t *testing.T) { |
| 855 | f := setup(t) | 855 | f := setup(t) |
| 856 | lan := func(v *pb.VMDesired) { v.Network = "lan" } | 856 | lan := func(v *pb.VMSpec) { v.Network = "lan" } |
| 857 | 857 | ||
| 858 | rep := f.step(snap(1, vm("vm1", lan))) | 858 | rep := f.step(snap(1, vm("vm1", lan))) |
| 859 | av := findVM(rep, "vm1") | 859 | av := findVM(rep, "vm1") |
| @@ -911,7 +911,7 @@ func TestSeedCarriesBothNICsOnlyForANetworkedGuest(t *testing.T) { | |||
| 911 | var got seed.Params | 911 | var got seed.Params |
| 912 | f.eng.Seed = func(_ string, p seed.Params) error { got = p; return nil } | 912 | f.eng.Seed = func(_ string, p seed.Params) error { got = p; return nil } |
| 913 | 913 | ||
| 914 | f.step(snap(1, vm("vm1", func(v *pb.VMDesired) { v.Network = "lan" }))) | 914 | f.step(snap(1, vm("vm1", func(v *pb.VMSpec) { v.Network = "lan" }))) |
| 915 | assert.Equal(t, state.MAC("vm1"), got.MAC) | 915 | assert.Equal(t, state.MAC("vm1"), got.MAC) |
| 916 | assert.Equal(t, state.NetMAC("vm1"), got.NetworkMAC) | 916 | assert.Equal(t, state.NetMAC("vm1"), got.NetworkMAC) |
| 917 | 917 | ||
internal/agent/reconcile/worker.go
| Old | New | ||
|---|---|---|---|
| @@ -127,7 +127,7 @@ func (m *manager) tombstones() map[string]bool { | |||
| 127 | // it marshals — two reports marshalled concurrently would race on one message. | 127 | // it marshals — two reports marshalled concurrently would race on one message. |
| 128 | // Cloning makes the report self-contained instead of resting on an assumption | 128 | // Cloning makes the report self-contained instead of resting on an assumption |
| 129 | // about how many goroutines might serialize it. | 129 | // about how many goroutines might serialize it. |
| 130 | func (m *manager) collect(rep *pb.ActualStateReport) { | 130 | func (m *manager) collect(rep *pb.Report) { |
| 131 | for _, w := range m.snapshot() { | 131 | for _, w := range m.snapshot() { |
| 132 | w.mu.Lock() | 132 | w.mu.Lock() |
| 133 | res := w.result | 133 | res := w.result |
internal/agent/reconcile/worker_test.go
| Old | New | ||
|---|---|---|---|
| @@ -35,9 +35,9 @@ func wedge(arrived chan<- struct{}, release <-chan struct{}) func(context.Contex | |||
| 35 | // does not come back promptly. Every test below wedges a VM, so a Step that | 35 | // does not come back promptly. Every test below wedges a VM, so a Step that |
| 36 | // waited on its workers would hang the whole package instead of naming the | 36 | // waited on its workers would hang the whole package instead of naming the |
| 37 | // property that broke. | 37 | // property that broke. |
| 38 | func stepNoWait(t *testing.T, f *fixture, s *pb.DesiredStateSnapshot) *pb.ActualStateReport { | 38 | func stepNoWait(t *testing.T, f *fixture, s *pb.Snapshot) *pb.Report { |
| 39 | t.Helper() | 39 | t.Helper() |
| 40 | done := make(chan *pb.ActualStateReport, 1) | 40 | done := make(chan *pb.Report, 1) |
| 41 | go func() { done <- f.eng.Step(context.Background(), s) }() | 41 | go func() { done <- f.eng.Step(context.Background(), s) }() |
| 42 | select { | 42 | select { |
| 43 | case rep := <-done: | 43 | case rep := <-done: |
| @@ -104,11 +104,11 @@ func TestVMsReconcileConcurrently(t *testing.T) { | |||
| 104 | f.eng.manager().waitIdle() | 104 | f.eng.manager().waitIdle() |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | // TestLatestDesiredWinsForABusyVM pins coalescing: desired-state updates that | 107 | // TestLatestSpecWinsForABusyVM pins coalescing: desired-state updates that |
| 108 | // arrive while a VM is busy overwrite each other, so the worker reconciles | 108 | // arrive while a VM is busy overwrite each other, so the worker reconciles |
| 109 | // against the NEWEST desired when it comes free — a superseded intermediate | 109 | // against the NEWEST desired when it comes free — a superseded intermediate |
| 110 | // state is never acted on. | 110 | // state is never acted on. |
| 111 | func TestLatestDesiredWinsForABusyVM(t *testing.T) { | 111 | func TestLatestSpecWinsForABusyVM(t *testing.T) { |
| 112 | f := setup(t) | 112 | f := setup(t) |
| 113 | arrived := make(chan struct{}, 1) | 113 | arrived := make(chan struct{}, 1) |
| 114 | release := make(chan struct{}) | 114 | release := make(chan struct{}) |
| @@ -129,7 +129,7 @@ func TestLatestDesiredWinsForABusyVM(t *testing.T) { | |||
| 129 | f.eng.manager().waitIdle() | 129 | f.eng.manager().waitIdle() |
| 130 | 130 | ||
| 131 | rep := f.aggregateNow(3) // the newest snapshot the worker reconciled against | 131 | rep := f.aggregateNow(3) // the newest snapshot the worker reconciled against |
| 132 | assert.Equal(t, "running", findVM(rep, "vm1").GetPower()) | 132 | assert.Equal(t, "running", findVM(rep, "vm1").GetPowerState()) |
| 133 | assert.Empty(t, f.prov.shutdown, "the superseded 'stopped' desired must never be reconciled") | 133 | assert.Empty(t, f.prov.shutdown, "the superseded 'stopped' desired must never be reconciled") |
| 134 | } | 134 | } |
| 135 | 135 | ||
internal/agent/seed/seed.go
| Old | New | ||
|---|---|---|---|
| @@ -21,7 +21,7 @@ type Params struct { | |||
| 21 | InstanceID string // used as cloud-init instance-id; falls back to Hostname when empty | 21 | InstanceID string // used as cloud-init instance-id; falls back to Hostname when empty |
| 22 | // SSHUserCAAuthorizedKey, when non-empty, is the tenant's user-CA set in | 22 | // SSHUserCAAuthorizedKey, when non-empty, is the tenant's user-CA set in |
| 23 | // authorized_keys form — one canonical CA per line (from the agent joining the | 23 | // authorized_keys form — one canonical CA per line (from the agent joining the |
| 24 | // VMDesired.ssh_user_ca_authorized_keys set). Its presence | 24 | // VMSpec.ssh_user_ca_authorized_keys set). Its presence |
| 25 | // makes the seed inject an sshd drop-in (TrustedUserCAKeys) so the guest | 25 | // makes the seed inject an sshd drop-in (TrustedUserCAKeys) so the guest |
| 26 | // trusts CA-signed user certs minted by the jump gate. Empty = no injection. | 26 | // trusts CA-signed user certs minted by the jump gate. Empty = no injection. |
| 27 | // Exempt from the newline check: it is embedded as a YAML block scalar (like | 27 | // Exempt from the newline check: it is embedded as a YAML block scalar (like |
internal/agent/syncclient/client.go
| Old | New | ||
|---|---|---|---|
| @@ -47,7 +47,7 @@ type Console interface { | |||
| 47 | // *exposeproxy.Manager). A nil Exposures converges nothing and reports nothing | 47 | // *exposeproxy.Manager). A nil Exposures converges nothing and reports nothing |
| 48 | // — an agent with no proxy publishes no ports, and says so by saying nothing. | 48 | // — an agent with no proxy publishes no ports, and says so by saying nothing. |
| 49 | type Exposures interface { | 49 | type Exposures interface { |
| 50 | Converge(desired []*pb.ExposureDesired) []*pb.ExposureActual | 50 | Converge(desired []*pb.ExposureSpec) []*pb.ExposureStatus |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | // DefaultTickInterval is the fallback report/reconcile cadence when | 53 | // DefaultTickInterval is the fallback report/reconcile cadence when |
| @@ -220,7 +220,7 @@ func (c *Client) maybeUpgrade(ctx context.Context, up *pb.AgentUpgrade) { | |||
| 220 | // returns the rows the report carries. Level-triggered like everything else: | 220 | // returns the rows the report carries. Level-triggered like everything else: |
| 221 | // every snapshot re-converges, so a bind that lost its port to a squatting | 221 | // every snapshot re-converges, so a bind that lost its port to a squatting |
| 222 | // process is retried on the next tick. | 222 | // process is retried on the next tick. |
| 223 | func (c *Client) convergeExposures(snap *pb.DesiredStateSnapshot) []*pb.ExposureActual { | 223 | func (c *Client) convergeExposures(snap *pb.Snapshot) []*pb.ExposureStatus { |
| 224 | if c.Exposures == nil { | 224 | if c.Exposures == nil { |
| 225 | return nil | 225 | return nil |
| 226 | } | 226 | } |
| @@ -231,7 +231,7 @@ func (c *Client) convergeExposures(snap *pb.DesiredStateSnapshot) []*pb.Exposure | |||
| 231 | // fenced snapshot is one this host has already moved past, and driving | 231 | // fenced snapshot is one this host has already moved past, and driving |
| 232 | // listeners from it would re-open a port the fleet has since revoked — the | 232 | // listeners from it would re-open a port the fleet has since revoked — the |
| 233 | // same reason the fence path touches nothing else. | 233 | // same reason the fence path touches nothing else. |
| 234 | func (c *Client) reportExposures(snap *pb.DesiredStateSnapshot, rep *pb.ActualStateReport) []*pb.ExposureActual { | 234 | func (c *Client) reportExposures(snap *pb.Snapshot, rep *pb.Report) []*pb.ExposureStatus { |
| 235 | if rep.GetFenceViolation() { | 235 | if rep.GetFenceViolation() { |
| 236 | return nil | 236 | return nil |
| 237 | } | 237 | } |
| @@ -372,7 +372,7 @@ func (c *Client) session(ctx context.Context) error { | |||
| 372 | }() | 372 | }() |
| 373 | 373 | ||
| 374 | var mu sync.Mutex | 374 | var mu sync.Mutex |
| 375 | var latest *pb.DesiredStateSnapshot | 375 | var latest *pb.Snapshot |
| 376 | // connectedOnce is set true (under mu) once the recv goroutine reads its | 376 | // connectedOnce is set true (under mu) once the recv goroutine reads its |
| 377 | // first snapshot. session wraps its terminal error with errSessionConnected | 377 | // first snapshot. session wraps its terminal error with errSessionConnected |
| 378 | // when set, so Run() resets its consecutive-failure counter. | 378 | // when set, so Run() resets its consecutive-failure counter. |
internal/agent/syncclient/exposures_test.go
| Old | New | ||
|---|---|---|---|
| @@ -13,10 +13,10 @@ import ( | |||
| 13 | type fakeExposures struct { | 13 | type fakeExposures struct { |
| 14 | sawIDs []string | 14 | sawIDs []string |
| 15 | calls int | 15 | calls int |
| 16 | out []*pb.ExposureActual | 16 | out []*pb.ExposureStatus |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | func (f *fakeExposures) Converge(desired []*pb.ExposureDesired) []*pb.ExposureActual { | 19 | func (f *fakeExposures) Converge(desired []*pb.ExposureSpec) []*pb.ExposureStatus { |
| 20 | f.calls++ | 20 | f.calls++ |
| 21 | f.sawIDs = nil | 21 | f.sawIDs = nil |
| 22 | for _, d := range desired { | 22 | for _, d := range desired { |
| @@ -26,11 +26,11 @@ func (f *fakeExposures) Converge(desired []*pb.ExposureDesired) []*pb.ExposureAc | |||
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | func TestConvergeExposuresPassesTheSnapshotThrough(t *testing.T) { | 28 | func TestConvergeExposuresPassesTheSnapshotThrough(t *testing.T) { |
| 29 | fe := &fakeExposures{out: []*pb.ExposureActual{{Id: "e1", State: "active"}}} | 29 | fe := &fakeExposures{out: []*pb.ExposureStatus{{Id: "e1", State: "active"}}} |
| 30 | c := &Client{Exposures: fe} | 30 | c := &Client{Exposures: fe} |
| 31 | 31 | ||
| 32 | got := c.convergeExposures(&pb.DesiredStateSnapshot{ | 32 | got := c.convergeExposures(&pb.Snapshot{ |
| 33 | Exposures: []*pb.ExposureDesired{{Id: "e1", VmId: "vm1", GuestPort: 8080, HostPort: 30080}}, | 33 | Exposures: []*pb.ExposureSpec{{Id: "e1", VmId: "vm1", GuestPort: 8080, HostPort: 30080}}, |
| 34 | }) | 34 | }) |
| 35 | 35 | ||
| 36 | assert.Equal(t, []string{"e1"}, fe.sawIDs) | 36 | assert.Equal(t, []string{"e1"}, fe.sawIDs) |
| @@ -40,8 +40,8 @@ func TestConvergeExposuresPassesTheSnapshotThrough(t *testing.T) { | |||
| 40 | 40 | ||
| 41 | func TestConvergeExposuresWithoutAProxyConvergesNothing(t *testing.T) { | 41 | func TestConvergeExposuresWithoutAProxyConvergesNothing(t *testing.T) { |
| 42 | c := &Client{} | 42 | c := &Client{} |
| 43 | assert.Nil(t, c.convergeExposures(&pb.DesiredStateSnapshot{ | 43 | assert.Nil(t, c.convergeExposures(&pb.Snapshot{ |
| 44 | Exposures: []*pb.ExposureDesired{{Id: "e1"}}, | 44 | Exposures: []*pb.ExposureSpec{{Id: "e1"}}, |
| 45 | }), "an agent with no proxy publishes nothing, and says so by saying nothing") | 45 | }), "an agent with no proxy publishes nothing, and says so by saying nothing") |
| 46 | } | 46 | } |
| 47 | 47 | ||
| @@ -49,9 +49,9 @@ func TestConvergeExposuresRefusesAFencedSnapshot(t *testing.T) { | |||
| 49 | fe := &fakeExposures{} | 49 | fe := &fakeExposures{} |
| 50 | c := &Client{Exposures: fe} | 50 | c := &Client{Exposures: fe} |
| 51 | 51 | ||
| 52 | got := c.reportExposures(&pb.DesiredStateSnapshot{ | 52 | got := c.reportExposures(&pb.Snapshot{ |
| 53 | Exposures: []*pb.ExposureDesired{{Id: "e1"}}, | 53 | Exposures: []*pb.ExposureSpec{{Id: "e1"}}, |
| 54 | }, &pb.ActualStateReport{FenceViolation: true}) | 54 | }, &pb.Report{FenceViolation: true}) |
| 55 | 55 | ||
| 56 | assert.Zero(t, fe.calls, "a snapshot the engine refused must not drive the listeners") | 56 | assert.Zero(t, fe.calls, "a snapshot the engine refused must not drive the listeners") |
| 57 | assert.Nil(t, got) | 57 | assert.Nil(t, got) |
internal/agent/syncclient/leak_test.go
| Old | New | ||
|---|---|---|---|
| @@ -64,7 +64,7 @@ func newDropServer(t *testing.T) (addr, fp string, stop func()) { | |||
| 64 | // step; then drop so the client's recv goroutine errors and the | 64 | // step; then drop so the client's recv goroutine errors and the |
| 65 | // session ends the way a real server drop / idle timeout ends it. | 65 | // session ends the way a real server drop / idle timeout ends it. |
| 66 | _ = transport.WriteMsg(down, &pb.ServerMessage{ | 66 | _ = transport.WriteMsg(down, &pb.ServerMessage{ |
| 67 | Msg: &pb.ServerMessage_Snapshot{Snapshot: &pb.DesiredStateSnapshot{}}}) | 67 | Msg: &pb.ServerMessage_Snapshot{Snapshot: &pb.Snapshot{}}}) |
| 68 | time.Sleep(30 * time.Millisecond) | 68 | time.Sleep(30 * time.Millisecond) |
| 69 | _ = conn.CloseWithError(0, "drop") | 69 | _ = conn.CloseWithError(0, "drop") |
| 70 | }() | 70 | }() |
internal/pb/sync.pb.go
| Old | New | ||
|---|---|---|---|
| @@ -80,7 +80,7 @@ func (x *AgentMessage) GetHello() *Hello { | |||
| 80 | return nil | 80 | return nil |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | func (x *AgentMessage) GetReport() *ActualStateReport { | 83 | func (x *AgentMessage) GetReport() *Report { |
| 84 | if x != nil { | 84 | if x != nil { |
| 85 | if x, ok := x.Msg.(*AgentMessage_Report); ok { | 85 | if x, ok := x.Msg.(*AgentMessage_Report); ok { |
| 86 | return x.Report | 86 | return x.Report |
| @@ -116,7 +116,7 @@ type AgentMessage_Hello struct { | |||
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | type AgentMessage_Report struct { | 118 | type AgentMessage_Report struct { |
| 119 | Report *ActualStateReport `protobuf:"bytes,2,opt,name=report,proto3,oneof"` | 119 | Report *Report `protobuf:"bytes,2,opt,name=report,proto3,oneof"` |
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | type AgentMessage_ConsoleOpened struct { | 122 | type AgentMessage_ConsoleOpened struct { |
| @@ -184,7 +184,7 @@ func (x *ServerMessage) GetMsg() isServerMessage_Msg { | |||
| 184 | return nil | 184 | return nil |
| 185 | } | 185 | } |
| 186 | 186 | ||
| 187 | func (x *ServerMessage) GetSnapshot() *DesiredStateSnapshot { | 187 | func (x *ServerMessage) GetSnapshot() *Snapshot { |
| 188 | if x != nil { | 188 | if x != nil { |
| 189 | if x, ok := x.Msg.(*ServerMessage_Snapshot); ok { | 189 | if x, ok := x.Msg.(*ServerMessage_Snapshot); ok { |
| 190 | return x.Snapshot | 190 | return x.Snapshot |
| @@ -216,7 +216,7 @@ type isServerMessage_Msg interface { | |||
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | type ServerMessage_Snapshot struct { | 218 | type ServerMessage_Snapshot struct { |
| 219 | Snapshot *DesiredStateSnapshot `protobuf:"bytes,1,opt,name=snapshot,proto3,oneof"` | 219 | Snapshot *Snapshot `protobuf:"bytes,1,opt,name=snapshot,proto3,oneof"` |
| 220 | } | 220 | } |
| 221 | 221 | ||
| 222 | type ServerMessage_ConsoleOpen struct { | 222 | type ServerMessage_ConsoleOpen struct { |
| @@ -240,7 +240,7 @@ type Hello struct { | |||
| 240 | Os string `protobuf:"bytes,3,opt,name=os,proto3" json:"os,omitempty"` | 240 | Os string `protobuf:"bytes,3,opt,name=os,proto3" json:"os,omitempty"` |
| 241 | Arch string `protobuf:"bytes,4,opt,name=arch,proto3" json:"arch,omitempty"` | 241 | Arch string `protobuf:"bytes,4,opt,name=arch,proto3" json:"arch,omitempty"` |
| 242 | Provisioner string `protobuf:"bytes,5,opt,name=provisioner,proto3" json:"provisioner,omitempty"` // "cloudhv" | 242 | Provisioner string `protobuf:"bytes,5,opt,name=provisioner,proto3" json:"provisioner,omitempty"` // "cloudhv" |
| 243 | // ActualStateReport.guest_cidr. | 243 | // Report.guest_cidr. |
| 244 | LastSeenEpoch uint64 `protobuf:"varint,7,opt,name=last_seen_epoch,json=lastSeenEpoch,proto3" json:"last_seen_epoch,omitempty"` // for the restore runbook | 244 | LastSeenEpoch uint64 `protobuf:"varint,7,opt,name=last_seen_epoch,json=lastSeenEpoch,proto3" json:"last_seen_epoch,omitempty"` // for the restore runbook |
| 245 | Capacity *Capacity `protobuf:"bytes,8,opt,name=capacity,proto3" json:"capacity,omitempty"` | 245 | Capacity *Capacity `protobuf:"bytes,8,opt,name=capacity,proto3" json:"capacity,omitempty"` |
| 246 | Credential string `protobuf:"bytes,9,opt,name=credential,proto3" json:"credential,omitempty"` // Bearer host credential, verified in first frame | 246 | Credential string `protobuf:"bytes,9,opt,name=credential,proto3" json:"credential,omitempty"` // Bearer host credential, verified in first frame |
| @@ -610,13 +610,16 @@ func (x *HostMetrics) GetDiskFreeGb() int64 { | |||
| 610 | return 0 | 610 | return 0 |
| 611 | } | 611 | } |
| 612 | 612 | ||
| 613 | type ActualVM struct { | 613 | // VMStatus is the half of a VM its host owns: what it observed, never what it |
| 614 | state protoimpl.MessageState `protogen:"open.v1"` | 614 | // was told. One VM, two halves — VMSpec travels down in a Snapshot, VMStatus |
| 615 | VmId string `protobuf:"bytes,1,opt,name=vm_id,json=vmId,proto3" json:"vm_id,omitempty"` | 615 | // travels back in a Report, and the control plane holds both on one row. |
| 616 | Power string `protobuf:"bytes,2,opt,name=power,proto3" json:"power,omitempty"` // "running"|"stopped" | 616 | type VMStatus struct { |
| 617 | Phase string `protobuf:"bytes,3,opt,name=phase,proto3" json:"phase,omitempty"` // "creating"|"ready"|"failed"|"quarantined" | 617 | state protoimpl.MessageState `protogen:"open.v1"` |
| 618 | Ip string `protobuf:"bytes,4,opt,name=ip,proto3" json:"ip,omitempty"` // the guest's address on its host's NAT underlay — every guest has one, from boot | 618 | VmId string `protobuf:"bytes,1,opt,name=vm_id,json=vmId,proto3" json:"vm_id,omitempty"` |
| 619 | LastError string `protobuf:"bytes,5,opt,name=last_error,json=lastError,proto3" json:"last_error,omitempty"` | 619 | PowerState string `protobuf:"bytes,2,opt,name=power_state,json=powerState,proto3" json:"power_state,omitempty"` // "running"|"stopped" as observed; VMSpec.power_state is what was asked for |
| 620 | Phase string `protobuf:"bytes,3,opt,name=phase,proto3" json:"phase,omitempty"` // "creating"|"ready"|"failed"|"quarantined" | ||
| 621 | Ip string `protobuf:"bytes,4,opt,name=ip,proto3" json:"ip,omitempty"` // the guest's address on its host's NAT underlay — every guest has one, from boot | ||
| 622 | LastError string `protobuf:"bytes,5,opt,name=last_error,json=lastError,proto3" json:"last_error,omitempty"` | ||
| 620 | // The guest's ed25519 HOST public key. It is generated on the host, and the | 623 | // The guest's ed25519 HOST public key. It is generated on the host, and the |
| 621 | // private half never leaves it — this is the only half that travels. Sent on | 624 | // private half never leaves it — this is the only half that travels. Sent on |
| 622 | // every report for as long as the VM exists (level-triggered), so a lost | 625 | // every report for as long as the VM exists (level-triggered), so a lost |
| @@ -633,7 +636,7 @@ type ActualVM struct { | |||
| 633 | StatusDetail string `protobuf:"bytes,7,opt,name=status_detail,json=statusDetail,proto3" json:"status_detail,omitempty"` | 636 | StatusDetail string `protobuf:"bytes,7,opt,name=status_detail,json=statusDetail,proto3" json:"status_detail,omitempty"` |
| 634 | // The address the site's DHCP server granted this guest on its SECOND NIC, | 637 | // The address the site's DHCP server granted this guest on its SECOND NIC, |
| 635 | // the one attached to the named host network its spec asked for (see | 638 | // the one attached to the named host network its spec asked for (see |
| 636 | // VMDesired.network). Empty for the guests that have no such NIC — the | 639 | // VMSpec.network). Empty for the guests that have no such NIC — the |
| 637 | // majority — and for one DHCP round-trip after a networked guest boots, | 640 | // majority — and for one DHCP round-trip after a networked guest boots, |
| 638 | // because the host learns it by watching the exchange rather than granting | 641 | // because the host learns it by watching the exchange rather than granting |
| 639 | // it. Never a substitute for ip: that one is known before the guest is even | 642 | // it. Never a substitute for ip: that one is known before the guest is even |
| @@ -644,20 +647,20 @@ type ActualVM struct { | |||
| 644 | sizeCache protoimpl.SizeCache | 647 | sizeCache protoimpl.SizeCache |
| 645 | } | 648 | } |
| 646 | 649 | ||
| 647 | func (x *ActualVM) Reset() { | 650 | func (x *VMStatus) Reset() { |
| 648 | *x = ActualVM{} | 651 | *x = VMStatus{} |
| 649 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[6] | 652 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[6] |
| 650 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | 653 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
| 651 | ms.StoreMessageInfo(mi) | 654 | ms.StoreMessageInfo(mi) |
| 652 | } | 655 | } |
| 653 | 656 | ||
| 654 | func (x *ActualVM) String() string { | 657 | func (x *VMStatus) String() string { |
| 655 | return protoimpl.X.MessageStringOf(x) | 658 | return protoimpl.X.MessageStringOf(x) |
| 656 | } | 659 | } |
| 657 | 660 | ||
| 658 | func (*ActualVM) ProtoMessage() {} | 661 | func (*VMStatus) ProtoMessage() {} |
| 659 | 662 | ||
| 660 | func (x *ActualVM) ProtoReflect() protoreflect.Message { | 663 | func (x *VMStatus) ProtoReflect() protoreflect.Message { |
| 661 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[6] | 664 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[6] |
| 662 | if x != nil { | 665 | if x != nil { |
| 663 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | 666 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
| @@ -669,61 +672,61 @@ func (x *ActualVM) ProtoReflect() protoreflect.Message { | |||
| 669 | return mi.MessageOf(x) | 672 | return mi.MessageOf(x) |
| 670 | } | 673 | } |
| 671 | 674 | ||
| 672 | // Deprecated: Use ActualVM.ProtoReflect.Descriptor instead. | 675 | // Deprecated: Use VMStatus.ProtoReflect.Descriptor instead. |
| 673 | func (*ActualVM) Descriptor() ([]byte, []int) { | 676 | func (*VMStatus) Descriptor() ([]byte, []int) { |
| 674 | return file_proto_eitri_v1_sync_proto_rawDescGZIP(), []int{6} | 677 | return file_proto_eitri_v1_sync_proto_rawDescGZIP(), []int{6} |
| 675 | } | 678 | } |
| 676 | 679 | ||
| 677 | func (x *ActualVM) GetVmId() string { | 680 | func (x *VMStatus) GetVmId() string { |
| 678 | if x != nil { | 681 | if x != nil { |
| 679 | return x.VmId | 682 | return x.VmId |
| 680 | } | 683 | } |
| 681 | return "" | 684 | return "" |
| 682 | } | 685 | } |
| 683 | 686 | ||
| 684 | func (x *ActualVM) GetPower() string { | 687 | func (x *VMStatus) GetPowerState() string { |
| 685 | if x != nil { | 688 | if x != nil { |
| 686 | return x.Power | 689 | return x.PowerState |
| 687 | } | 690 | } |
| 688 | return "" | 691 | return "" |
| 689 | } | 692 | } |
| 690 | 693 | ||
| 691 | func (x *ActualVM) GetPhase() string { | 694 | func (x *VMStatus) GetPhase() string { |
| 692 | if x != nil { | 695 | if x != nil { |
| 693 | return x.Phase | 696 | return x.Phase |
| 694 | } | 697 | } |
| 695 | return "" | 698 | return "" |
| 696 | } | 699 | } |
| 697 | 700 | ||
| 698 | func (x *ActualVM) GetIp() string { | 701 | func (x *VMStatus) GetIp() string { |
| 699 | if x != nil { | 702 | if x != nil { |
| 700 | return x.Ip | 703 | return x.Ip |
| 701 | } | 704 | } |
| 702 | return "" | 705 | return "" |
| 703 | } | 706 | } |
| 704 | 707 | ||
| 705 | func (x *ActualVM) GetLastError() string { | 708 | func (x *VMStatus) GetLastError() string { |
| 706 | if x != nil { | 709 | if x != nil { |
| 707 | return x.LastError | 710 | return x.LastError |
| 708 | } | 711 | } |
| 709 | return "" | 712 | return "" |
| 710 | } | 713 | } |
| 711 | 714 | ||
| 712 | func (x *ActualVM) GetSshHostPubkey() string { | 715 | func (x *VMStatus) GetSshHostPubkey() string { |
| 713 | if x != nil { | 716 | if x != nil { |
| 714 | return x.SshHostPubkey | 717 | return x.SshHostPubkey |
| 715 | } | 718 | } |
| 716 | return "" | 719 | return "" |
| 717 | } | 720 | } |
| 718 | 721 | ||
| 719 | func (x *ActualVM) GetStatusDetail() string { | 722 | func (x *VMStatus) GetStatusDetail() string { |
| 720 | if x != nil { | 723 | if x != nil { |
| 721 | return x.StatusDetail | 724 | return x.StatusDetail |
| 722 | } | 725 | } |
| 723 | return "" | 726 | return "" |
| 724 | } | 727 | } |
| 725 | 728 | ||
| 726 | func (x *ActualVM) GetNetworkIp() string { | 729 | func (x *VMStatus) GetNetworkIp() string { |
| 727 | if x != nil { | 730 | if x != nil { |
| 728 | return x.NetworkIp | 731 | return x.NetworkIp |
| 729 | } | 732 | } |
| @@ -798,9 +801,11 @@ func (x *QuarantinedVM) GetDestroyAtUnix() int64 { | |||
| 798 | return 0 | 801 | return 0 |
| 799 | } | 802 | } |
| 800 | 803 | ||
| 801 | type ActualStateReport struct { | 804 | // Report is what one host observes, level-triggered: the status of every VM |
| 805 | // and exposure it holds, and the host's own numbers. | ||
| 806 | type Report struct { | ||
| 802 | state protoimpl.MessageState `protogen:"open.v1"` | 807 | state protoimpl.MessageState `protogen:"open.v1"` |
| 803 | Vms []*ActualVM `protobuf:"bytes,1,rep,name=vms,proto3" json:"vms,omitempty"` | 808 | Vms []*VMStatus `protobuf:"bytes,1,rep,name=vms,proto3" json:"vms,omitempty"` |
| 804 | // LEVEL-TRIGGERED destroy ack: ALL tombstoned vm_ids with no local | 809 | // LEVEL-TRIGGERED destroy ack: ALL tombstoned vm_ids with no local |
| 805 | // record/disk/process, repeated every report until hard-deleted server-side. | 810 | // record/disk/process, repeated every report until hard-deleted server-side. |
| 806 | Destroyed []string `protobuf:"bytes,2,rep,name=destroyed,proto3" json:"destroyed,omitempty"` | 811 | Destroyed []string `protobuf:"bytes,2,rep,name=destroyed,proto3" json:"destroyed,omitempty"` |
| @@ -818,7 +823,7 @@ type ActualStateReport struct { | |||
| 818 | // knowable at connect time at all. Capacity is in both messages for the same | 823 | // knowable at connect time at all. Capacity is in both messages for the same |
| 819 | // reason: an opening value, then the ongoing truth. | 824 | // reason: an opening value, then the ongoing truth. |
| 820 | GuestCidr string `protobuf:"bytes,8,opt,name=guest_cidr,json=guestCidr,proto3" json:"guest_cidr,omitempty"` | 825 | GuestCidr string `protobuf:"bytes,8,opt,name=guest_cidr,json=guestCidr,proto3" json:"guest_cidr,omitempty"` |
| 821 | Exposures []*ExposureActual `protobuf:"bytes,9,rep,name=exposures,proto3" json:"exposures,omitempty"` | 826 | Exposures []*ExposureStatus `protobuf:"bytes,9,rep,name=exposures,proto3" json:"exposures,omitempty"` |
| 822 | // The address this host presents on the network it reaches the control | 827 | // The address this host presents on the network it reaches the control |
| 823 | // plane over — the address an operator dials to reach a published guest | 828 | // plane over — the address an operator dials to reach a published guest |
| 824 | // port. Empty means "not yet known", never "no address": a host that cannot | 829 | // port. Empty means "not yet known", never "no address": a host that cannot |
| @@ -830,20 +835,20 @@ type ActualStateReport struct { | |||
| 830 | sizeCache protoimpl.SizeCache | 835 | sizeCache protoimpl.SizeCache |
| 831 | } | 836 | } |
| 832 | 837 | ||
| 833 | func (x *ActualStateReport) Reset() { | 838 | func (x *Report) Reset() { |
| 834 | *x = ActualStateReport{} | 839 | *x = Report{} |
| 835 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[8] | 840 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[8] |
| 836 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | 841 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
| 837 | ms.StoreMessageInfo(mi) | 842 | ms.StoreMessageInfo(mi) |
| 838 | } | 843 | } |
| 839 | 844 | ||
| 840 | func (x *ActualStateReport) String() string { | 845 | func (x *Report) String() string { |
| 841 | return protoimpl.X.MessageStringOf(x) | 846 | return protoimpl.X.MessageStringOf(x) |
| 842 | } | 847 | } |
| 843 | 848 | ||
| 844 | func (*ActualStateReport) ProtoMessage() {} | 849 | func (*Report) ProtoMessage() {} |
| 845 | 850 | ||
| 846 | func (x *ActualStateReport) ProtoReflect() protoreflect.Message { | 851 | func (x *Report) ProtoReflect() protoreflect.Message { |
| 847 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[8] | 852 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[8] |
| 848 | if x != nil { | 853 | if x != nil { |
| 849 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | 854 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
| @@ -855,82 +860,83 @@ func (x *ActualStateReport) ProtoReflect() protoreflect.Message { | |||
| 855 | return mi.MessageOf(x) | 860 | return mi.MessageOf(x) |
| 856 | } | 861 | } |
| 857 | 862 | ||
| 858 | // Deprecated: Use ActualStateReport.ProtoReflect.Descriptor instead. | 863 | // Deprecated: Use Report.ProtoReflect.Descriptor instead. |
| 859 | func (*ActualStateReport) Descriptor() ([]byte, []int) { | 864 | func (*Report) Descriptor() ([]byte, []int) { |
| 860 | return file_proto_eitri_v1_sync_proto_rawDescGZIP(), []int{8} | 865 | return file_proto_eitri_v1_sync_proto_rawDescGZIP(), []int{8} |
| 861 | } | 866 | } |
| 862 | 867 | ||
| 863 | func (x *ActualStateReport) GetVms() []*ActualVM { | 868 | func (x *Report) GetVms() []*VMStatus { |
| 864 | if x != nil { | 869 | if x != nil { |
| 865 | return x.Vms | 870 | return x.Vms |
| 866 | } | 871 | } |
| 867 | return nil | 872 | return nil |
| 868 | } | 873 | } |
| 869 | 874 | ||
| 870 | func (x *ActualStateReport) GetDestroyed() []string { | 875 | func (x *Report) GetDestroyed() []string { |
| 871 | if x != nil { | 876 | if x != nil { |
| 872 | return x.Destroyed | 877 | return x.Destroyed |
| 873 | } | 878 | } |
| 874 | return nil | 879 | return nil |
| 875 | } | 880 | } |
| 876 | 881 | ||
| 877 | func (x *ActualStateReport) GetQuarantined() []*QuarantinedVM { | 882 | func (x *Report) GetQuarantined() []*QuarantinedVM { |
| 878 | if x != nil { | 883 | if x != nil { |
| 879 | return x.Quarantined | 884 | return x.Quarantined |
| 880 | } | 885 | } |
| 881 | return nil | 886 | return nil |
| 882 | } | 887 | } |
| 883 | 888 | ||
| 884 | func (x *ActualStateReport) GetCapacity() *Capacity { | 889 | func (x *Report) GetCapacity() *Capacity { |
| 885 | if x != nil { | 890 | if x != nil { |
| 886 | return x.Capacity | 891 | return x.Capacity |
| 887 | } | 892 | } |
| 888 | return nil | 893 | return nil |
| 889 | } | 894 | } |
| 890 | 895 | ||
| 891 | func (x *ActualStateReport) GetFenceViolation() bool { | 896 | func (x *Report) GetFenceViolation() bool { |
| 892 | if x != nil { | 897 | if x != nil { |
| 893 | return x.FenceViolation | 898 | return x.FenceViolation |
| 894 | } | 899 | } |
| 895 | return false | 900 | return false |
| 896 | } | 901 | } |
| 897 | 902 | ||
| 898 | func (x *ActualStateReport) GetLastSeenEpoch() uint64 { | 903 | func (x *Report) GetLastSeenEpoch() uint64 { |
| 899 | if x != nil { | 904 | if x != nil { |
| 900 | return x.LastSeenEpoch | 905 | return x.LastSeenEpoch |
| 901 | } | 906 | } |
| 902 | return 0 | 907 | return 0 |
| 903 | } | 908 | } |
| 904 | 909 | ||
| 905 | func (x *ActualStateReport) GetMetrics() *HostMetrics { | 910 | func (x *Report) GetMetrics() *HostMetrics { |
| 906 | if x != nil { | 911 | if x != nil { |
| 907 | return x.Metrics | 912 | return x.Metrics |
| 908 | } | 913 | } |
| 909 | return nil | 914 | return nil |
| 910 | } | 915 | } |
| 911 | 916 | ||
| 912 | func (x *ActualStateReport) GetGuestCidr() string { | 917 | func (x *Report) GetGuestCidr() string { |
| 913 | if x != nil { | 918 | if x != nil { |
| 914 | return x.GuestCidr | 919 | return x.GuestCidr |
| 915 | } | 920 | } |
| 916 | return "" | 921 | return "" |
| 917 | } | 922 | } |
| 918 | 923 | ||
| 919 | func (x *ActualStateReport) GetExposures() []*ExposureActual { | 924 | func (x *Report) GetExposures() []*ExposureStatus { |
| 920 | if x != nil { | 925 | if x != nil { |
| 921 | return x.Exposures | 926 | return x.Exposures |
| 922 | } | 927 | } |
| 923 | return nil | 928 | return nil |
| 924 | } | 929 | } |
| 925 | 930 | ||
| 926 | func (x *ActualStateReport) GetHostUplinkAddr() string { | 931 | func (x *Report) GetHostUplinkAddr() string { |
| 927 | if x != nil { | 932 | if x != nil { |
| 928 | return x.HostUplinkAddr | 933 | return x.HostUplinkAddr |
| 929 | } | 934 | } |
| 930 | return "" | 935 | return "" |
| 931 | } | 936 | } |
| 932 | 937 | ||
| 933 | type VMDesired struct { | 938 | // VMSpec is the half of a VM the control plane owns. See VMStatus. |
| 939 | type VMSpec struct { | ||
| 934 | state protoimpl.MessageState `protogen:"open.v1"` | 940 | state protoimpl.MessageState `protogen:"open.v1"` |
| 935 | VmId string `protobuf:"bytes,1,opt,name=vm_id,json=vmId,proto3" json:"vm_id,omitempty"` | 941 | VmId string `protobuf:"bytes,1,opt,name=vm_id,json=vmId,proto3" json:"vm_id,omitempty"` |
| 936 | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` | 942 | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` |
| @@ -953,7 +959,7 @@ type VMDesired struct { | |||
| 953 | Tombstoned bool `protobuf:"varint,11,opt,name=tombstoned,proto3" json:"tombstoned,omitempty"` // present-but-tombstoned (drives quarantine + destroyed[]) | 959 | Tombstoned bool `protobuf:"varint,11,opt,name=tombstoned,proto3" json:"tombstoned,omitempty"` // present-but-tombstoned (drives quarantine + destroyed[]) |
| 954 | SshAuthorizedKey string `protobuf:"bytes,12,opt,name=ssh_authorized_key,json=sshAuthorizedKey,proto3" json:"ssh_authorized_key,omitempty"` | 960 | SshAuthorizedKey string `protobuf:"bytes,12,opt,name=ssh_authorized_key,json=sshAuthorizedKey,proto3" json:"ssh_authorized_key,omitempty"` |
| 955 | // The certificate the control plane signed for the public key the host | 961 | // The certificate the control plane signed for the public key the host |
| 956 | // reported in ActualVM.ssh_host_pubkey (authorized_keys form); seed installs | 962 | // reported in VMStatus.ssh_host_pubkey (authorized_keys form); seed installs |
| 957 | // it as /etc/ssh/ssh_host_ed25519_key-cert.pub. Empty until the round trip | 963 | // it as /etc/ssh/ssh_host_ed25519_key-cert.pub. Empty until the round trip |
| 958 | // completes, and forever when the jump gate is off. | 964 | // completes, and forever when the jump gate is off. |
| 959 | SshHostCert string `protobuf:"bytes,17,opt,name=ssh_host_cert,json=sshHostCert,proto3" json:"ssh_host_cert,omitempty"` | 965 | SshHostCert string `protobuf:"bytes,17,opt,name=ssh_host_cert,json=sshHostCert,proto3" json:"ssh_host_cert,omitempty"` |
| @@ -974,20 +980,20 @@ type VMDesired struct { | |||
| 974 | sizeCache protoimpl.SizeCache | 980 | sizeCache protoimpl.SizeCache |
| 975 | } | 981 | } |
| 976 | 982 | ||
| 977 | func (x *VMDesired) Reset() { | 983 | func (x *VMSpec) Reset() { |
| 978 | *x = VMDesired{} | 984 | *x = VMSpec{} |
| 979 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[9] | 985 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[9] |
| 980 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | 986 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
| 981 | ms.StoreMessageInfo(mi) | 987 | ms.StoreMessageInfo(mi) |
| 982 | } | 988 | } |
| 983 | 989 | ||
| 984 | func (x *VMDesired) String() string { | 990 | func (x *VMSpec) String() string { |
| 985 | return protoimpl.X.MessageStringOf(x) | 991 | return protoimpl.X.MessageStringOf(x) |
| 986 | } | 992 | } |
| 987 | 993 | ||
| 988 | func (*VMDesired) ProtoMessage() {} | 994 | func (*VMSpec) ProtoMessage() {} |
| 989 | 995 | ||
| 990 | func (x *VMDesired) ProtoReflect() protoreflect.Message { | 996 | func (x *VMSpec) ProtoReflect() protoreflect.Message { |
| 991 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[9] | 997 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[9] |
| 992 | if x != nil { | 998 | if x != nil { |
| 993 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | 999 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
| @@ -999,147 +1005,148 @@ func (x *VMDesired) ProtoReflect() protoreflect.Message { | |||
| 999 | return mi.MessageOf(x) | 1005 | return mi.MessageOf(x) |
| 1000 | } | 1006 | } |
| 1001 | 1007 | ||
| 1002 | // Deprecated: Use VMDesired.ProtoReflect.Descriptor instead. | 1008 | // Deprecated: Use VMSpec.ProtoReflect.Descriptor instead. |
| 1003 | func (*VMDesired) Descriptor() ([]byte, []int) { | 1009 | func (*VMSpec) Descriptor() ([]byte, []int) { |
| 1004 | return file_proto_eitri_v1_sync_proto_rawDescGZIP(), []int{9} | 1010 | return file_proto_eitri_v1_sync_proto_rawDescGZIP(), []int{9} |
| 1005 | } | 1011 | } |
| 1006 | 1012 | ||
| 1007 | func (x *VMDesired) GetVmId() string { | 1013 | func (x *VMSpec) GetVmId() string { |
| 1008 | if x != nil { | 1014 | if x != nil { |
| 1009 | return x.VmId | 1015 | return x.VmId |
| 1010 | } | 1016 | } |
| 1011 | return "" | 1017 | return "" |
| 1012 | } | 1018 | } |
| 1013 | 1019 | ||
| 1014 | func (x *VMDesired) GetName() string { | 1020 | func (x *VMSpec) GetName() string { |
| 1015 | if x != nil { | 1021 | if x != nil { |
| 1016 | return x.Name | 1022 | return x.Name |
| 1017 | } | 1023 | } |
| 1018 | return "" | 1024 | return "" |
| 1019 | } | 1025 | } |
| 1020 | 1026 | ||
| 1021 | func (x *VMDesired) GetImageUrl() string { | 1027 | func (x *VMSpec) GetImageUrl() string { |
| 1022 | if x != nil { | 1028 | if x != nil { |
| 1023 | return x.ImageUrl | 1029 | return x.ImageUrl |
| 1024 | } | 1030 | } |
| 1025 | return "" | 1031 | return "" |
| 1026 | } | 1032 | } |
| 1027 | 1033 | ||
| 1028 | func (x *VMDesired) GetImageSha256() string { | 1034 | func (x *VMSpec) GetImageSha256() string { |
| 1029 | if x != nil { | 1035 | if x != nil { |
| 1030 | return x.ImageSha256 | 1036 | return x.ImageSha256 |
| 1031 | } | 1037 | } |
| 1032 | return "" | 1038 | return "" |
| 1033 | } | 1039 | } |
| 1034 | 1040 | ||
| 1035 | func (x *VMDesired) GetCloudInit() string { | 1041 | func (x *VMSpec) GetCloudInit() string { |
| 1036 | if x != nil { | 1042 | if x != nil { |
| 1037 | return x.CloudInit | 1043 | return x.CloudInit |
| 1038 | } | 1044 | } |
| 1039 | return "" | 1045 | return "" |
| 1040 | } | 1046 | } |
| 1041 | 1047 | ||
| 1042 | func (x *VMDesired) GetVcpus() int64 { | 1048 | func (x *VMSpec) GetVcpus() int64 { |
| 1043 | if x != nil { | 1049 | if x != nil { |
| 1044 | return x.Vcpus | 1050 | return x.Vcpus |
| 1045 | } | 1051 | } |
| 1046 | return 0 | 1052 | return 0 |
| 1047 | } | 1053 | } |
| 1048 | 1054 | ||
| 1049 | func (x *VMDesired) GetMemMb() int64 { | 1055 | func (x *VMSpec) GetMemMb() int64 { |
| 1050 | if x != nil { | 1056 | if x != nil { |
| 1051 | return x.MemMb | 1057 | return x.MemMb |
| 1052 | } | 1058 | } |
| 1053 | return 0 | 1059 | return 0 |
| 1054 | } | 1060 | } |
| 1055 | 1061 | ||
| 1056 | func (x *VMDesired) GetDiskGb() int64 { | 1062 | func (x *VMSpec) GetDiskGb() int64 { |
| 1057 | if x != nil { | 1063 | if x != nil { |
| 1058 | return x.DiskGb | 1064 | return x.DiskGb |
| 1059 | } | 1065 | } |
| 1060 | return 0 | 1066 | return 0 |
| 1061 | } | 1067 | } |
| 1062 | 1068 | ||
| 1063 | func (x *VMDesired) GetPersistent() bool { | 1069 | func (x *VMSpec) GetPersistent() bool { |
| 1064 | if x != nil { | 1070 | if x != nil { |
| 1065 | return x.Persistent | 1071 | return x.Persistent |
| 1066 | } | 1072 | } |
| 1067 | return false | 1073 | return false |
| 1068 | } | 1074 | } |
| 1069 | 1075 | ||
| 1070 | func (x *VMDesired) GetPowerState() string { | 1076 | func (x *VMSpec) GetPowerState() string { |
| 1071 | if x != nil { | 1077 | if x != nil { |
| 1072 | return x.PowerState | 1078 | return x.PowerState |
| 1073 | } | 1079 | } |
| 1074 | return "" | 1080 | return "" |
| 1075 | } | 1081 | } |
| 1076 | 1082 | ||
| 1077 | func (x *VMDesired) GetTombstoned() bool { | 1083 | func (x *VMSpec) GetTombstoned() bool { |
| 1078 | if x != nil { | 1084 | if x != nil { |
| 1079 | return x.Tombstoned | 1085 | return x.Tombstoned |
| 1080 | } | 1086 | } |
| 1081 | return false | 1087 | return false |
| 1082 | } | 1088 | } |
| 1083 | 1089 | ||
| 1084 | func (x *VMDesired) GetSshAuthorizedKey() string { | 1090 | func (x *VMSpec) GetSshAuthorizedKey() string { |
| 1085 | if x != nil { | 1091 | if x != nil { |
| 1086 | return x.SshAuthorizedKey | 1092 | return x.SshAuthorizedKey |
| 1087 | } | 1093 | } |
| 1088 | return "" | 1094 | return "" |
| 1089 | } | 1095 | } |
| 1090 | 1096 | ||
| 1091 | func (x *VMDesired) GetSshHostCert() string { | 1097 | func (x *VMSpec) GetSshHostCert() string { |
| 1092 | if x != nil { | 1098 | if x != nil { |
| 1093 | return x.SshHostCert | 1099 | return x.SshHostCert |
| 1094 | } | 1100 | } |
| 1095 | return "" | 1101 | return "" |
| 1096 | } | 1102 | } |
| 1097 | 1103 | ||
| 1098 | func (x *VMDesired) GetSshUserCaAuthorizedKeys() []string { | 1104 | func (x *VMSpec) GetSshUserCaAuthorizedKeys() []string { |
| 1099 | if x != nil { | 1105 | if x != nil { |
| 1100 | return x.SshUserCaAuthorizedKeys | 1106 | return x.SshUserCaAuthorizedKeys |
| 1101 | } | 1107 | } |
| 1102 | return nil | 1108 | return nil |
| 1103 | } | 1109 | } |
| 1104 | 1110 | ||
| 1105 | func (x *VMDesired) GetHostCertRequired() bool { | 1111 | func (x *VMSpec) GetHostCertRequired() bool { |
| 1106 | if x != nil { | 1112 | if x != nil { |
| 1107 | return x.HostCertRequired | 1113 | return x.HostCertRequired |
| 1108 | } | 1114 | } |
| 1109 | return false | 1115 | return false |
| 1110 | } | 1116 | } |
| 1111 | 1117 | ||
| 1112 | func (x *VMDesired) GetNetwork() string { | 1118 | func (x *VMSpec) GetNetwork() string { |
| 1113 | if x != nil { | 1119 | if x != nil { |
| 1114 | return x.Network | 1120 | return x.Network |
| 1115 | } | 1121 | } |
| 1116 | return "" | 1122 | return "" |
| 1117 | } | 1123 | } |
| 1118 | 1124 | ||
| 1119 | type DesiredStateSnapshot struct { | 1125 | // Snapshot is the FULL spec for one host; the agent converges toward it. |
| 1126 | type Snapshot struct { | ||
| 1120 | state protoimpl.MessageState `protogen:"open.v1"` | 1127 | state protoimpl.MessageState `protogen:"open.v1"` |
| 1121 | Epoch uint64 `protobuf:"varint,1,opt,name=epoch,proto3" json:"epoch,omitempty"` // agents refuse epoch < highest seen | 1128 | Epoch uint64 `protobuf:"varint,1,opt,name=epoch,proto3" json:"epoch,omitempty"` // agents refuse epoch < highest seen |
| 1122 | Vms []*VMDesired `protobuf:"bytes,2,rep,name=vms,proto3" json:"vms,omitempty"` // FULL set for this host, including tombstoned | 1129 | Vms []*VMSpec `protobuf:"bytes,2,rep,name=vms,proto3" json:"vms,omitempty"` // FULL set for this host, including tombstoned |
| 1123 | AgentUpgrade *AgentUpgrade `protobuf:"bytes,3,opt,name=agent_upgrade,json=agentUpgrade,proto3" json:"agent_upgrade,omitempty"` // optional operator-initiated agent self-upgrade | 1130 | AgentUpgrade *AgentUpgrade `protobuf:"bytes,3,opt,name=agent_upgrade,json=agentUpgrade,proto3" json:"agent_upgrade,omitempty"` // optional operator-initiated agent self-upgrade |
| 1124 | Exposures []*ExposureDesired `protobuf:"bytes,4,rep,name=exposures,proto3" json:"exposures,omitempty"` // FULL set for this host | 1131 | Exposures []*ExposureSpec `protobuf:"bytes,4,rep,name=exposures,proto3" json:"exposures,omitempty"` // FULL set for this host |
| 1125 | unknownFields protoimpl.UnknownFields | 1132 | unknownFields protoimpl.UnknownFields |
| 1126 | sizeCache protoimpl.SizeCache | 1133 | sizeCache protoimpl.SizeCache |
| 1127 | } | 1134 | } |
| 1128 | 1135 | ||
| 1129 | func (x *DesiredStateSnapshot) Reset() { | 1136 | func (x *Snapshot) Reset() { |
| 1130 | *x = DesiredStateSnapshot{} | 1137 | *x = Snapshot{} |
| 1131 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[10] | 1138 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[10] |
| 1132 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | 1139 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
| 1133 | ms.StoreMessageInfo(mi) | 1140 | ms.StoreMessageInfo(mi) |
| 1134 | } | 1141 | } |
| 1135 | 1142 | ||
| 1136 | func (x *DesiredStateSnapshot) String() string { | 1143 | func (x *Snapshot) String() string { |
| 1137 | return protoimpl.X.MessageStringOf(x) | 1144 | return protoimpl.X.MessageStringOf(x) |
| 1138 | } | 1145 | } |
| 1139 | 1146 | ||
| 1140 | func (*DesiredStateSnapshot) ProtoMessage() {} | 1147 | func (*Snapshot) ProtoMessage() {} |
| 1141 | 1148 | ||
| 1142 | func (x *DesiredStateSnapshot) ProtoReflect() protoreflect.Message { | 1149 | func (x *Snapshot) ProtoReflect() protoreflect.Message { |
| 1143 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[10] | 1150 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[10] |
| 1144 | if x != nil { | 1151 | if x != nil { |
| 1145 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | 1152 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
| @@ -1151,33 +1158,33 @@ func (x *DesiredStateSnapshot) ProtoReflect() protoreflect.Message { | |||
| 1151 | return mi.MessageOf(x) | 1158 | return mi.MessageOf(x) |
| 1152 | } | 1159 | } |
| 1153 | 1160 | ||
| 1154 | // Deprecated: Use DesiredStateSnapshot.ProtoReflect.Descriptor instead. | 1161 | // Deprecated: Use Snapshot.ProtoReflect.Descriptor instead. |
| 1155 | func (*DesiredStateSnapshot) Descriptor() ([]byte, []int) { | 1162 | func (*Snapshot) Descriptor() ([]byte, []int) { |
| 1156 | return file_proto_eitri_v1_sync_proto_rawDescGZIP(), []int{10} | 1163 | return file_proto_eitri_v1_sync_proto_rawDescGZIP(), []int{10} |
| 1157 | } | 1164 | } |
| 1158 | 1165 | ||
| 1159 | func (x *DesiredStateSnapshot) GetEpoch() uint64 { | 1166 | func (x *Snapshot) GetEpoch() uint64 { |
| 1160 | if x != nil { | 1167 | if x != nil { |
| 1161 | return x.Epoch | 1168 | return x.Epoch |
| 1162 | } | 1169 | } |
| 1163 | return 0 | 1170 | return 0 |
| 1164 | } | 1171 | } |
| 1165 | 1172 | ||
| 1166 | func (x *DesiredStateSnapshot) GetVms() []*VMDesired { | 1173 | func (x *Snapshot) GetVms() []*VMSpec { |
| 1167 | if x != nil { | 1174 | if x != nil { |
| 1168 | return x.Vms | 1175 | return x.Vms |
| 1169 | } | 1176 | } |
| 1170 | return nil | 1177 | return nil |
| 1171 | } | 1178 | } |
| 1172 | 1179 | ||
| 1173 | func (x *DesiredStateSnapshot) GetAgentUpgrade() *AgentUpgrade { | 1180 | func (x *Snapshot) GetAgentUpgrade() *AgentUpgrade { |
| 1174 | if x != nil { | 1181 | if x != nil { |
| 1175 | return x.AgentUpgrade | 1182 | return x.AgentUpgrade |
| 1176 | } | 1183 | } |
| 1177 | return nil | 1184 | return nil |
| 1178 | } | 1185 | } |
| 1179 | 1186 | ||
| 1180 | func (x *DesiredStateSnapshot) GetExposures() []*ExposureDesired { | 1187 | func (x *Snapshot) GetExposures() []*ExposureSpec { |
| 1181 | if x != nil { | 1188 | if x != nil { |
| 1182 | return x.Exposures | 1189 | return x.Exposures |
| 1183 | } | 1190 | } |
| @@ -1460,13 +1467,13 @@ func (x *TCPOpened) GetError() string { | |||
| 1460 | return "" | 1467 | return "" |
| 1461 | } | 1468 | } |
| 1462 | 1469 | ||
| 1463 | // ExposureDesired is one published guest port a host should be serving: bind | 1470 | // ExposureSpec is one published guest port a host should be serving: bind |
| 1464 | // host_port on the host, pipe every accepted connection (or every datagram) | 1471 | // host_port on the host, pipe every accepted connection (or every datagram) |
| 1465 | // to guest_port inside the guest. It rides the snapshot at TOP LEVEL rather | 1472 | // to guest_port inside the guest. It rides the snapshot at TOP LEVEL rather |
| 1466 | // than nested in VMDesired, because exposures are their own objects converging | 1473 | // than nested in VMSpec, because exposures are their own objects converging |
| 1467 | // on their own cadence — an exposure can be created while its VM is still | 1474 | // on their own cadence — an exposure can be created while its VM is still |
| 1468 | // imaging, and it binds immediately. | 1475 | // imaging, and it binds immediately. |
| 1469 | type ExposureDesired struct { | 1476 | type ExposureSpec struct { |
| 1470 | state protoimpl.MessageState `protogen:"open.v1"` | 1477 | state protoimpl.MessageState `protogen:"open.v1"` |
| 1471 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` | 1478 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` |
| 1472 | VmId string `protobuf:"bytes,2,opt,name=vm_id,json=vmId,proto3" json:"vm_id,omitempty"` | 1479 | VmId string `protobuf:"bytes,2,opt,name=vm_id,json=vmId,proto3" json:"vm_id,omitempty"` |
| @@ -1477,20 +1484,20 @@ type ExposureDesired struct { | |||
| 1477 | sizeCache protoimpl.SizeCache | 1484 | sizeCache protoimpl.SizeCache |
| 1478 | } | 1485 | } |
| 1479 | 1486 | ||
| 1480 | func (x *ExposureDesired) Reset() { | 1487 | func (x *ExposureSpec) Reset() { |
| 1481 | *x = ExposureDesired{} | 1488 | *x = ExposureSpec{} |
| 1482 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[16] | 1489 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[16] |
| 1483 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | 1490 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
| 1484 | ms.StoreMessageInfo(mi) | 1491 | ms.StoreMessageInfo(mi) |
| 1485 | } | 1492 | } |
| 1486 | 1493 | ||
| 1487 | func (x *ExposureDesired) String() string { | 1494 | func (x *ExposureSpec) String() string { |
| 1488 | return protoimpl.X.MessageStringOf(x) | 1495 | return protoimpl.X.MessageStringOf(x) |
| 1489 | } | 1496 | } |
| 1490 | 1497 | ||
| 1491 | func (*ExposureDesired) ProtoMessage() {} | 1498 | func (*ExposureSpec) ProtoMessage() {} |
| 1492 | 1499 | ||
| 1493 | func (x *ExposureDesired) ProtoReflect() protoreflect.Message { | 1500 | func (x *ExposureSpec) ProtoReflect() protoreflect.Message { |
| 1494 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[16] | 1501 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[16] |
| 1495 | if x != nil { | 1502 | if x != nil { |
| 1496 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | 1503 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
| @@ -1502,51 +1509,51 @@ func (x *ExposureDesired) ProtoReflect() protoreflect.Message { | |||
| 1502 | return mi.MessageOf(x) | 1509 | return mi.MessageOf(x) |
| 1503 | } | 1510 | } |
| 1504 | 1511 | ||
| 1505 | // Deprecated: Use ExposureDesired.ProtoReflect.Descriptor instead. | 1512 | // Deprecated: Use ExposureSpec.ProtoReflect.Descriptor instead. |
| 1506 | func (*ExposureDesired) Descriptor() ([]byte, []int) { | 1513 | func (*ExposureSpec) Descriptor() ([]byte, []int) { |
| 1507 | return file_proto_eitri_v1_sync_proto_rawDescGZIP(), []int{16} | 1514 | return file_proto_eitri_v1_sync_proto_rawDescGZIP(), []int{16} |
| 1508 | } | 1515 | } |
| 1509 | 1516 | ||
| 1510 | func (x *ExposureDesired) GetId() string { | 1517 | func (x *ExposureSpec) GetId() string { |
| 1511 | if x != nil { | 1518 | if x != nil { |
| 1512 | return x.Id | 1519 | return x.Id |
| 1513 | } | 1520 | } |
| 1514 | return "" | 1521 | return "" |
| 1515 | } | 1522 | } |
| 1516 | 1523 | ||
| 1517 | func (x *ExposureDesired) GetVmId() string { | 1524 | func (x *ExposureSpec) GetVmId() string { |
| 1518 | if x != nil { | 1525 | if x != nil { |
| 1519 | return x.VmId | 1526 | return x.VmId |
| 1520 | } | 1527 | } |
| 1521 | return "" | 1528 | return "" |
| 1522 | } | 1529 | } |
| 1523 | 1530 | ||
| 1524 | func (x *ExposureDesired) GetGuestPort() uint32 { | 1531 | func (x *ExposureSpec) GetGuestPort() uint32 { |
| 1525 | if x != nil { | 1532 | if x != nil { |
| 1526 | return x.GuestPort | 1533 | return x.GuestPort |
| 1527 | } | 1534 | } |
| 1528 | return 0 | 1535 | return 0 |
| 1529 | } | 1536 | } |
| 1530 | 1537 | ||
| 1531 | func (x *ExposureDesired) GetHostPort() uint32 { | 1538 | func (x *ExposureSpec) GetHostPort() uint32 { |
| 1532 | if x != nil { | 1539 | if x != nil { |
| 1533 | return x.HostPort | 1540 | return x.HostPort |
| 1534 | } | 1541 | } |
| 1535 | return 0 | 1542 | return 0 |
| 1536 | } | 1543 | } |
| 1537 | 1544 | ||
| 1538 | func (x *ExposureDesired) GetProtocol() string { | 1545 | func (x *ExposureSpec) GetProtocol() string { |
| 1539 | if x != nil { | 1546 | if x != nil { |
| 1540 | return x.Protocol | 1547 | return x.Protocol |
| 1541 | } | 1548 | } |
| 1542 | return "" | 1549 | return "" |
| 1543 | } | 1550 | } |
| 1544 | 1551 | ||
| 1545 | // ExposureActual is one exposure's state as its host observes it: "active" | 1552 | // ExposureStatus is one exposure's state as its host observes it: "active" |
| 1546 | // once the host socket is bound, "failed" with the OS error otherwise. | 1553 | // once the host socket is bound, "failed" with the OS error otherwise. |
| 1547 | // "active" means the HOST half of the pipe exists — whether anything answers | 1554 | // "active" means the HOST half of the pipe exists — whether anything answers |
| 1548 | // inside the guest is the guest's half, and this does not pretend otherwise. | 1555 | // inside the guest is the guest's half, and this does not pretend otherwise. |
| 1549 | type ExposureActual struct { | 1556 | type ExposureStatus struct { |
| 1550 | state protoimpl.MessageState `protogen:"open.v1"` | 1557 | state protoimpl.MessageState `protogen:"open.v1"` |
| 1551 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` | 1558 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` |
| 1552 | State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` // "active"|"failed" | 1559 | State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` // "active"|"failed" |
| @@ -1556,20 +1563,20 @@ type ExposureActual struct { | |||
| 1556 | sizeCache protoimpl.SizeCache | 1563 | sizeCache protoimpl.SizeCache |
| 1557 | } | 1564 | } |
| 1558 | 1565 | ||
| 1559 | func (x *ExposureActual) Reset() { | 1566 | func (x *ExposureStatus) Reset() { |
| 1560 | *x = ExposureActual{} | 1567 | *x = ExposureStatus{} |
| 1561 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[17] | 1568 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[17] |
| 1562 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | 1569 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
| 1563 | ms.StoreMessageInfo(mi) | 1570 | ms.StoreMessageInfo(mi) |
| 1564 | } | 1571 | } |
| 1565 | 1572 | ||
| 1566 | func (x *ExposureActual) String() string { | 1573 | func (x *ExposureStatus) String() string { |
| 1567 | return protoimpl.X.MessageStringOf(x) | 1574 | return protoimpl.X.MessageStringOf(x) |
| 1568 | } | 1575 | } |
| 1569 | 1576 | ||
| 1570 | func (*ExposureActual) ProtoMessage() {} | 1577 | func (*ExposureStatus) ProtoMessage() {} |
| 1571 | 1578 | ||
| 1572 | func (x *ExposureActual) ProtoReflect() protoreflect.Message { | 1579 | func (x *ExposureStatus) ProtoReflect() protoreflect.Message { |
| 1573 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[17] | 1580 | mi := &file_proto_eitri_v1_sync_proto_msgTypes[17] |
| 1574 | if x != nil { | 1581 | if x != nil { |
| 1575 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | 1582 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
| @@ -1581,33 +1588,33 @@ func (x *ExposureActual) ProtoReflect() protoreflect.Message { | |||
| 1581 | return mi.MessageOf(x) | 1588 | return mi.MessageOf(x) |
| 1582 | } | 1589 | } |
| 1583 | 1590 | ||
| 1584 | // Deprecated: Use ExposureActual.ProtoReflect.Descriptor instead. | 1591 | // Deprecated: Use ExposureStatus.ProtoReflect.Descriptor instead. |
| 1585 | func (*ExposureActual) Descriptor() ([]byte, []int) { | 1592 | func (*ExposureStatus) Descriptor() ([]byte, []int) { |
| 1586 | return file_proto_eitri_v1_sync_proto_rawDescGZIP(), []int{17} | 1593 | return file_proto_eitri_v1_sync_proto_rawDescGZIP(), []int{17} |
| 1587 | } | 1594 | } |
| 1588 | 1595 | ||
| 1589 | func (x *ExposureActual) GetId() string { | 1596 | func (x *ExposureStatus) GetId() string { |
| 1590 | if x != nil { | 1597 | if x != nil { |
| 1591 | return x.Id | 1598 | return x.Id |
| 1592 | } | 1599 | } |
| 1593 | return "" | 1600 | return "" |
| 1594 | } | 1601 | } |
| 1595 | 1602 | ||
| 1596 | func (x *ExposureActual) GetState() string { | 1603 | func (x *ExposureStatus) GetState() string { |
| 1597 | if x != nil { | 1604 | if x != nil { |
| 1598 | return x.State | 1605 | return x.State |
| 1599 | } | 1606 | } |
| 1600 | return "" | 1607 | return "" |
| 1601 | } | 1608 | } |
| 1602 | 1609 | ||
| 1603 | func (x *ExposureActual) GetReason() string { | 1610 | func (x *ExposureStatus) GetReason() string { |
| 1604 | if x != nil { | 1611 | if x != nil { |
| 1605 | return x.Reason | 1612 | return x.Reason |
| 1606 | } | 1613 | } |
| 1607 | return "" | 1614 | return "" |
| 1608 | } | 1615 | } |
| 1609 | 1616 | ||
| 1610 | func (x *ExposureActual) GetSessions() *ExposureSessions { | 1617 | func (x *ExposureStatus) GetSessions() *ExposureSessions { |
| 1611 | if x != nil { | 1618 | if x != nil { |
| 1612 | return x.Sessions | 1619 | return x.Sessions |
| 1613 | } | 1620 | } |
| @@ -1700,16 +1707,16 @@ var File_proto_eitri_v1_sync_proto protoreflect.FileDescriptor | |||
| 1700 | 1707 | ||
| 1701 | const file_proto_eitri_v1_sync_proto_rawDesc = "" + | 1708 | const file_proto_eitri_v1_sync_proto_rawDesc = "" + |
| 1702 | "\n" + | 1709 | "\n" + |
| 1703 | "\x19proto/eitri/v1/sync.proto\x12\beitri.v1\"\xed\x01\n" + | 1710 | "\x19proto/eitri/v1/sync.proto\x12\beitri.v1\"\xe2\x01\n" + |
| 1704 | "\fAgentMessage\x12'\n" + | 1711 | "\fAgentMessage\x12'\n" + |
| 1705 | "\x05hello\x18\x01 \x01(\v2\x0f.eitri.v1.HelloH\x00R\x05hello\x125\n" + | 1712 | "\x05hello\x18\x01 \x01(\v2\x0f.eitri.v1.HelloH\x00R\x05hello\x12*\n" + |
| 1706 | "\x06report\x18\x02 \x01(\v2\x1b.eitri.v1.ActualStateReportH\x00R\x06report\x12@\n" + | 1713 | "\x06report\x18\x02 \x01(\v2\x10.eitri.v1.ReportH\x00R\x06report\x12@\n" + |
| 1707 | "\x0econsole_opened\x18\x03 \x01(\v2\x17.eitri.v1.ConsoleOpenedH\x00R\rconsoleOpened\x124\n" + | 1714 | "\x0econsole_opened\x18\x03 \x01(\v2\x17.eitri.v1.ConsoleOpenedH\x00R\rconsoleOpened\x124\n" + |
| 1708 | "\n" + | 1715 | "\n" + |
| 1709 | "tcp_opened\x18\x04 \x01(\v2\x13.eitri.v1.TCPOpenedH\x00R\ttcpOpenedB\x05\n" + | 1716 | "tcp_opened\x18\x04 \x01(\v2\x13.eitri.v1.TCPOpenedH\x00R\ttcpOpenedB\x05\n" + |
| 1710 | "\x03msg\"\xc0\x01\n" + | 1717 | "\x03msg\"\xb4\x01\n" + |
| 1711 | "\rServerMessage\x12<\n" + | 1718 | "\rServerMessage\x120\n" + |
| 1712 | "\bsnapshot\x18\x01 \x01(\v2\x1e.eitri.v1.DesiredStateSnapshotH\x00R\bsnapshot\x12:\n" + | 1719 | "\bsnapshot\x18\x01 \x01(\v2\x12.eitri.v1.SnapshotH\x00R\bsnapshot\x12:\n" + |
| 1713 | "\fconsole_open\x18\x02 \x01(\v2\x15.eitri.v1.ConsoleOpenH\x00R\vconsoleOpen\x12.\n" + | 1720 | "\fconsole_open\x18\x02 \x01(\v2\x15.eitri.v1.ConsoleOpenH\x00R\vconsoleOpen\x12.\n" + |
| 1714 | "\btcp_open\x18\x03 \x01(\v2\x11.eitri.v1.TCPOpenH\x00R\atcpOpenB\x05\n" + | 1721 | "\btcp_open\x18\x03 \x01(\v2\x11.eitri.v1.TCPOpenH\x00R\atcpOpenB\x05\n" + |
| 1715 | "\x03msg\"\xdd\x02\n" + | 1722 | "\x03msg\"\xdd\x02\n" + |
| @@ -1750,10 +1757,11 @@ const file_proto_eitri_v1_sync_proto_rawDesc = "" + | |||
| 1750 | "\fdisk_used_gb\x18\a \x01(\x03R\n" + | 1757 | "\fdisk_used_gb\x18\a \x01(\x03R\n" + |
| 1751 | "diskUsedGb\x12 \n" + | 1758 | "diskUsedGb\x12 \n" + |
| 1752 | "\fdisk_free_gb\x18\b \x01(\x03R\n" + | 1759 | "\fdisk_free_gb\x18\b \x01(\x03R\n" + |
| 1753 | "diskFreeGb\"\xe6\x01\n" + | 1760 | "diskFreeGb\"\xf1\x01\n" + |
| 1754 | "\bActualVM\x12\x13\n" + | 1761 | "\bVMStatus\x12\x13\n" + |
| 1755 | "\x05vm_id\x18\x01 \x01(\tR\x04vmId\x12\x14\n" + | 1762 | "\x05vm_id\x18\x01 \x01(\tR\x04vmId\x12\x1f\n" + |
| 1756 | "\x05power\x18\x02 \x01(\tR\x05power\x12\x14\n" + | 1763 | "\vpower_state\x18\x02 \x01(\tR\n" + |
| 1764 | "powerState\x12\x14\n" + | ||
| 1757 | "\x05phase\x18\x03 \x01(\tR\x05phase\x12\x0e\n" + | 1765 | "\x05phase\x18\x03 \x01(\tR\x05phase\x12\x0e\n" + |
| 1758 | "\x02ip\x18\x04 \x01(\tR\x02ip\x12\x1d\n" + | 1766 | "\x02ip\x18\x04 \x01(\tR\x02ip\x12\x1d\n" + |
| 1759 | "\n" + | 1767 | "\n" + |
| @@ -1767,9 +1775,9 @@ const file_proto_eitri_v1_sync_proto_rawDesc = "" + | |||
| 1767 | "\x04name\x18\x02 \x01(\tR\x04name\x12\x1f\n" + | 1775 | "\x04name\x18\x02 \x01(\tR\x04name\x12\x1f\n" + |
| 1768 | "\vvmspec_json\x18\x03 \x01(\fR\n" + | 1776 | "\vvmspec_json\x18\x03 \x01(\fR\n" + |
| 1769 | "vmspecJson\x12&\n" + | 1777 | "vmspecJson\x12&\n" + |
| 1770 | "\x0fdestroy_at_unix\x18\x04 \x01(\x03R\rdestroyAtUnix\"\xc5\x03\n" + | 1778 | "\x0fdestroy_at_unix\x18\x04 \x01(\x03R\rdestroyAtUnix\"\xba\x03\n" + |
| 1771 | "\x11ActualStateReport\x12$\n" + | 1779 | "\x06Report\x12$\n" + |
| 1772 | "\x03vms\x18\x01 \x03(\v2\x12.eitri.v1.ActualVMR\x03vms\x12\x1c\n" + | 1780 | "\x03vms\x18\x01 \x03(\v2\x12.eitri.v1.VMStatusR\x03vms\x12\x1c\n" + |
| 1773 | "\tdestroyed\x18\x02 \x03(\tR\tdestroyed\x129\n" + | 1781 | "\tdestroyed\x18\x02 \x03(\tR\tdestroyed\x129\n" + |
| 1774 | "\vquarantined\x18\x03 \x03(\v2\x17.eitri.v1.QuarantinedVMR\vquarantined\x12.\n" + | 1782 | "\vquarantined\x18\x03 \x03(\v2\x17.eitri.v1.QuarantinedVMR\vquarantined\x12.\n" + |
| 1775 | "\bcapacity\x18\x04 \x01(\v2\x12.eitri.v1.CapacityR\bcapacity\x12'\n" + | 1783 | "\bcapacity\x18\x04 \x01(\v2\x12.eitri.v1.CapacityR\bcapacity\x12'\n" + |
| @@ -1778,10 +1786,10 @@ const file_proto_eitri_v1_sync_proto_rawDesc = "" + | |||
| 1778 | "\ametrics\x18\a \x01(\v2\x15.eitri.v1.HostMetricsR\ametrics\x12\x1d\n" + | 1786 | "\ametrics\x18\a \x01(\v2\x15.eitri.v1.HostMetricsR\ametrics\x12\x1d\n" + |
| 1779 | "\n" + | 1787 | "\n" + |
| 1780 | "guest_cidr\x18\b \x01(\tR\tguestCidr\x126\n" + | 1788 | "guest_cidr\x18\b \x01(\tR\tguestCidr\x126\n" + |
| 1781 | "\texposures\x18\t \x03(\v2\x18.eitri.v1.ExposureActualR\texposures\x12(\n" + | 1789 | "\texposures\x18\t \x03(\v2\x18.eitri.v1.ExposureStatusR\texposures\x12(\n" + |
| 1782 | "\x10host_uplink_addr\x18\n" + | 1790 | "\x10host_uplink_addr\x18\n" + |
| 1783 | " \x01(\tR\x0ehostUplinkAddr\"\xbc\x04\n" + | 1791 | " \x01(\tR\x0ehostUplinkAddr\"\xb9\x04\n" + |
| 1784 | "\tVMDesired\x12\x13\n" + | 1792 | "\x06VMSpec\x12\x13\n" + |
| 1785 | "\x05vm_id\x18\x01 \x01(\tR\x04vmId\x12\x12\n" + | 1793 | "\x05vm_id\x18\x01 \x01(\tR\x04vmId\x12\x12\n" + |
| 1786 | "\x04name\x18\x02 \x01(\tR\x04name\x12\x1b\n" + | 1794 | "\x04name\x18\x02 \x01(\tR\x04name\x12\x1b\n" + |
| 1787 | "\timage_url\x18\x03 \x01(\tR\bimageUrl\x12!\n" + | 1795 | "\timage_url\x18\x03 \x01(\tR\bimageUrl\x12!\n" + |
| @@ -1804,12 +1812,12 @@ const file_proto_eitri_v1_sync_proto_rawDesc = "" + | |||
| 1804 | "\rssh_host_cert\x18\x11 \x01(\tR\vsshHostCert\x12<\n" + | 1812 | "\rssh_host_cert\x18\x11 \x01(\tR\vsshHostCert\x12<\n" + |
| 1805 | "\x1bssh_user_ca_authorized_keys\x18\x12 \x03(\tR\x17sshUserCaAuthorizedKeys\x12,\n" + | 1813 | "\x1bssh_user_ca_authorized_keys\x18\x12 \x03(\tR\x17sshUserCaAuthorizedKeys\x12,\n" + |
| 1806 | "\x12host_cert_required\x18\x13 \x01(\bR\x10hostCertRequired\x12\x18\n" + | 1814 | "\x12host_cert_required\x18\x13 \x01(\bR\x10hostCertRequired\x12\x18\n" + |
| 1807 | "\anetwork\x18\x14 \x01(\tR\anetworkJ\x04\b\r\x10\x0eJ\x04\b\x0e\x10\x0fJ\x04\b\x0f\x10\x10J\x04\b\x10\x10\x11R\x10ssh_host_key_pem\"\xc9\x01\n" + | 1815 | "\anetwork\x18\x14 \x01(\tR\anetworkJ\x04\b\r\x10\x0eJ\x04\b\x0e\x10\x0fJ\x04\b\x0f\x10\x10J\x04\b\x10\x10\x11R\x10ssh_host_key_pem\"\xb7\x01\n" + |
| 1808 | "\x14DesiredStateSnapshot\x12\x14\n" + | 1816 | "\bSnapshot\x12\x14\n" + |
| 1809 | "\x05epoch\x18\x01 \x01(\x04R\x05epoch\x12%\n" + | 1817 | "\x05epoch\x18\x01 \x01(\x04R\x05epoch\x12\"\n" + |
| 1810 | "\x03vms\x18\x02 \x03(\v2\x13.eitri.v1.VMDesiredR\x03vms\x12;\n" + | 1818 | "\x03vms\x18\x02 \x03(\v2\x10.eitri.v1.VMSpecR\x03vms\x12;\n" + |
| 1811 | "\ragent_upgrade\x18\x03 \x01(\v2\x16.eitri.v1.AgentUpgradeR\fagentUpgrade\x127\n" + | 1819 | "\ragent_upgrade\x18\x03 \x01(\v2\x16.eitri.v1.AgentUpgradeR\fagentUpgrade\x124\n" + |
| 1812 | "\texposures\x18\x04 \x03(\v2\x19.eitri.v1.ExposureDesiredR\texposures\"R\n" + | 1820 | "\texposures\x18\x04 \x03(\v2\x16.eitri.v1.ExposureSpecR\texposures\"R\n" + |
| 1813 | "\fAgentUpgrade\x12\x18\n" + | 1821 | "\fAgentUpgrade\x12\x18\n" + |
| 1814 | "\aversion\x18\x01 \x01(\tR\aversion\x12\x10\n" + | 1822 | "\aversion\x18\x01 \x01(\tR\aversion\x12\x10\n" + |
| 1815 | "\x03url\x18\x02 \x01(\tR\x03url\x12\x16\n" + | 1823 | "\x03url\x18\x02 \x01(\tR\x03url\x12\x16\n" + |
| @@ -1824,15 +1832,15 @@ const file_proto_eitri_v1_sync_proto_rawDesc = "" + | |||
| 1824 | "\x04port\x18\x02 \x01(\rR\x04port\"1\n" + | 1832 | "\x04port\x18\x02 \x01(\rR\x04port\"1\n" + |
| 1825 | "\tTCPOpened\x12\x0e\n" + | 1833 | "\tTCPOpened\x12\x0e\n" + |
| 1826 | "\x02ok\x18\x01 \x01(\bR\x02ok\x12\x14\n" + | 1834 | "\x02ok\x18\x01 \x01(\bR\x02ok\x12\x14\n" + |
| 1827 | "\x05error\x18\x02 \x01(\tR\x05error\"\x8e\x01\n" + | 1835 | "\x05error\x18\x02 \x01(\tR\x05error\"\x8b\x01\n" + |
| 1828 | "\x0fExposureDesired\x12\x0e\n" + | 1836 | "\fExposureSpec\x12\x0e\n" + |
| 1829 | "\x02id\x18\x01 \x01(\tR\x02id\x12\x13\n" + | 1837 | "\x02id\x18\x01 \x01(\tR\x02id\x12\x13\n" + |
| 1830 | "\x05vm_id\x18\x02 \x01(\tR\x04vmId\x12\x1d\n" + | 1838 | "\x05vm_id\x18\x02 \x01(\tR\x04vmId\x12\x1d\n" + |
| 1831 | "\n" + | 1839 | "\n" + |
| 1832 | "guest_port\x18\x03 \x01(\rR\tguestPort\x12\x1b\n" + | 1840 | "guest_port\x18\x03 \x01(\rR\tguestPort\x12\x1b\n" + |
| 1833 | "\thost_port\x18\x04 \x01(\rR\bhostPort\x12\x1a\n" + | 1841 | "\thost_port\x18\x04 \x01(\rR\bhostPort\x12\x1a\n" + |
| 1834 | "\bprotocol\x18\x05 \x01(\tR\bprotocol\"\x86\x01\n" + | 1842 | "\bprotocol\x18\x05 \x01(\tR\bprotocol\"\x86\x01\n" + |
| 1835 | "\x0eExposureActual\x12\x0e\n" + | 1843 | "\x0eExposureStatus\x12\x0e\n" + |
| 1836 | "\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" + | 1844 | "\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" + |
| 1837 | "\x05state\x18\x02 \x01(\tR\x05state\x12\x16\n" + | 1845 | "\x05state\x18\x02 \x01(\tR\x05state\x12\x16\n" + |
| 1838 | "\x06reason\x18\x03 \x01(\tR\x06reason\x126\n" + | 1846 | "\x06reason\x18\x03 \x01(\tR\x06reason\x126\n" + |
| @@ -1856,45 +1864,45 @@ func file_proto_eitri_v1_sync_proto_rawDescGZIP() []byte { | |||
| 1856 | 1864 | ||
| 1857 | var file_proto_eitri_v1_sync_proto_msgTypes = make([]protoimpl.MessageInfo, 19) | 1865 | var file_proto_eitri_v1_sync_proto_msgTypes = make([]protoimpl.MessageInfo, 19) |
| 1858 | var file_proto_eitri_v1_sync_proto_goTypes = []any{ | 1866 | var file_proto_eitri_v1_sync_proto_goTypes = []any{ |
| 1859 | (*AgentMessage)(nil), // 0: eitri.v1.AgentMessage | 1867 | (*AgentMessage)(nil), // 0: eitri.v1.AgentMessage |
| 1860 | (*ServerMessage)(nil), // 1: eitri.v1.ServerMessage | 1868 | (*ServerMessage)(nil), // 1: eitri.v1.ServerMessage |
| 1861 | (*Hello)(nil), // 2: eitri.v1.Hello | 1869 | (*Hello)(nil), // 2: eitri.v1.Hello |
| 1862 | (*Capacity)(nil), // 3: eitri.v1.Capacity | 1870 | (*Capacity)(nil), // 3: eitri.v1.Capacity |
| 1863 | (*HostFacts)(nil), // 4: eitri.v1.HostFacts | 1871 | (*HostFacts)(nil), // 4: eitri.v1.HostFacts |
| 1864 | (*HostMetrics)(nil), // 5: eitri.v1.HostMetrics | 1872 | (*HostMetrics)(nil), // 5: eitri.v1.HostMetrics |
| 1865 | (*ActualVM)(nil), // 6: eitri.v1.ActualVM | 1873 | (*VMStatus)(nil), // 6: eitri.v1.VMStatus |
| 1866 | (*QuarantinedVM)(nil), // 7: eitri.v1.QuarantinedVM | 1874 | (*QuarantinedVM)(nil), // 7: eitri.v1.QuarantinedVM |
| 1867 | (*ActualStateReport)(nil), // 8: eitri.v1.ActualStateReport | 1875 | (*Report)(nil), // 8: eitri.v1.Report |
| 1868 | (*VMDesired)(nil), // 9: eitri.v1.VMDesired | 1876 | (*VMSpec)(nil), // 9: eitri.v1.VMSpec |
| 1869 | (*DesiredStateSnapshot)(nil), // 10: eitri.v1.DesiredStateSnapshot | 1877 | (*Snapshot)(nil), // 10: eitri.v1.Snapshot |
| 1870 | (*AgentUpgrade)(nil), // 11: eitri.v1.AgentUpgrade | 1878 | (*AgentUpgrade)(nil), // 11: eitri.v1.AgentUpgrade |
| 1871 | (*ConsoleOpen)(nil), // 12: eitri.v1.ConsoleOpen | 1879 | (*ConsoleOpen)(nil), // 12: eitri.v1.ConsoleOpen |
| 1872 | (*ConsoleOpened)(nil), // 13: eitri.v1.ConsoleOpened | 1880 | (*ConsoleOpened)(nil), // 13: eitri.v1.ConsoleOpened |
| 1873 | (*TCPOpen)(nil), // 14: eitri.v1.TCPOpen | 1881 | (*TCPOpen)(nil), // 14: eitri.v1.TCPOpen |
| 1874 | (*TCPOpened)(nil), // 15: eitri.v1.TCPOpened | 1882 | (*TCPOpened)(nil), // 15: eitri.v1.TCPOpened |
| 1875 | (*ExposureDesired)(nil), // 16: eitri.v1.ExposureDesired | 1883 | (*ExposureSpec)(nil), // 16: eitri.v1.ExposureSpec |
| 1876 | (*ExposureActual)(nil), // 17: eitri.v1.ExposureActual | 1884 | (*ExposureStatus)(nil), // 17: eitri.v1.ExposureStatus |
| 1877 | (*ExposureSessions)(nil), // 18: eitri.v1.ExposureSessions | 1885 | (*ExposureSessions)(nil), // 18: eitri.v1.ExposureSessions |
| 1878 | } | 1886 | } |
| 1879 | var file_proto_eitri_v1_sync_proto_depIdxs = []int32{ | 1887 | var file_proto_eitri_v1_sync_proto_depIdxs = []int32{ |
| 1880 | 2, // 0: eitri.v1.AgentMessage.hello:type_name -> eitri.v1.Hello | 1888 | 2, // 0: eitri.v1.AgentMessage.hello:type_name -> eitri.v1.Hello |
| 1881 | 8, // 1: eitri.v1.AgentMessage.report:type_name -> eitri.v1.ActualStateReport | 1889 | 8, // 1: eitri.v1.AgentMessage.report:type_name -> eitri.v1.Report |
| 1882 | 13, // 2: eitri.v1.AgentMessage.console_opened:type_name -> eitri.v1.ConsoleOpened | 1890 | 13, // 2: eitri.v1.AgentMessage.console_opened:type_name -> eitri.v1.ConsoleOpened |
| 1883 | 15, // 3: eitri.v1.AgentMessage.tcp_opened:type_name -> eitri.v1.TCPOpened | 1891 | 15, // 3: eitri.v1.AgentMessage.tcp_opened:type_name -> eitri.v1.TCPOpened |
| 1884 | 10, // 4: eitri.v1.ServerMessage.snapshot:type_name -> eitri.v1.DesiredStateSnapshot | 1892 | 10, // 4: eitri.v1.ServerMessage.snapshot:type_name -> eitri.v1.Snapshot |
| 1885 | 12, // 5: eitri.v1.ServerMessage.console_open:type_name -> eitri.v1.ConsoleOpen | 1893 | 12, // 5: eitri.v1.ServerMessage.console_open:type_name -> eitri.v1.ConsoleOpen |
| 1886 | 14, // 6: eitri.v1.ServerMessage.tcp_open:type_name -> eitri.v1.TCPOpen | 1894 | 14, // 6: eitri.v1.ServerMessage.tcp_open:type_name -> eitri.v1.TCPOpen |
| 1887 | 3, // 7: eitri.v1.Hello.capacity:type_name -> eitri.v1.Capacity | 1895 | 3, // 7: eitri.v1.Hello.capacity:type_name -> eitri.v1.Capacity |
| 1888 | 4, // 8: eitri.v1.Hello.facts:type_name -> eitri.v1.HostFacts | 1896 | 4, // 8: eitri.v1.Hello.facts:type_name -> eitri.v1.HostFacts |
| 1889 | 6, // 9: eitri.v1.ActualStateReport.vms:type_name -> eitri.v1.ActualVM | 1897 | 6, // 9: eitri.v1.Report.vms:type_name -> eitri.v1.VMStatus |
| 1890 | 7, // 10: eitri.v1.ActualStateReport.quarantined:type_name -> eitri.v1.QuarantinedVM | 1898 | 7, // 10: eitri.v1.Report.quarantined:type_name -> eitri.v1.QuarantinedVM |
| 1891 | 3, // 11: eitri.v1.ActualStateReport.capacity:type_name -> eitri.v1.Capacity | 1899 | 3, // 11: eitri.v1.Report.capacity:type_name -> eitri.v1.Capacity |
| 1892 | 5, // 12: eitri.v1.ActualStateReport.metrics:type_name -> eitri.v1.HostMetrics | 1900 | 5, // 12: eitri.v1.Report.metrics:type_name -> eitri.v1.HostMetrics |
| 1893 | 17, // 13: eitri.v1.ActualStateReport.exposures:type_name -> eitri.v1.ExposureActual | 1901 | 17, // 13: eitri.v1.Report.exposures:type_name -> eitri.v1.ExposureStatus |
| 1894 | 9, // 14: eitri.v1.DesiredStateSnapshot.vms:type_name -> eitri.v1.VMDesired | 1902 | 9, // 14: eitri.v1.Snapshot.vms:type_name -> eitri.v1.VMSpec |
| 1895 | 11, // 15: eitri.v1.DesiredStateSnapshot.agent_upgrade:type_name -> eitri.v1.AgentUpgrade | 1903 | 11, // 15: eitri.v1.Snapshot.agent_upgrade:type_name -> eitri.v1.AgentUpgrade |
| 1896 | 16, // 16: eitri.v1.DesiredStateSnapshot.exposures:type_name -> eitri.v1.ExposureDesired | 1904 | 16, // 16: eitri.v1.Snapshot.exposures:type_name -> eitri.v1.ExposureSpec |
| 1897 | 18, // 17: eitri.v1.ExposureActual.sessions:type_name -> eitri.v1.ExposureSessions | 1905 | 18, // 17: eitri.v1.ExposureStatus.sessions:type_name -> eitri.v1.ExposureSessions |
| 1898 | 18, // [18:18] is the sub-list for method output_type | 1906 | 18, // [18:18] is the sub-list for method output_type |
| 1899 | 18, // [18:18] is the sub-list for method input_type | 1907 | 18, // [18:18] is the sub-list for method input_type |
| 1900 | 18, // [18:18] is the sub-list for extension type_name | 1908 | 18, // [18:18] is the sub-list for extension type_name |
internal/server/api/api.go
| Old | New | ||
|---|---|---|---|
| @@ -657,7 +657,7 @@ func (a *API) buildVMResponses(vms []store.VM, states map[string]regState) []typ | |||
| 657 | if rs := states[vm.HostID]; rs.ok { | 657 | if rs := states[vm.HostID]; rs.ok { |
| 658 | for _, av := range rs.st.Report.VMs { | 658 | for _, av := range rs.st.Report.VMs { |
| 659 | if av.VMID == vm.ID { | 659 | if av.VMID == vm.ID { |
| 660 | actualPower = av.Power | 660 | actualPower = av.PowerState |
| 661 | phase = av.Phase | 661 | phase = av.Phase |
| 662 | statusDetail = av.StatusDetail | 662 | statusDetail = av.StatusDetail |
| 663 | break | 663 | break |
internal/server/api/narration_test.go
| Old | New | ||
|---|---|---|---|
| @@ -34,8 +34,8 @@ func TestWhatAHostIsDoingReachesTheWire(t *testing.T) { | |||
| 34 | vmID := createTestVM(t, ts, host["host_id"], "web-1") | 34 | vmID := createTestVM(t, ts, host["host_id"], "web-1") |
| 35 | 35 | ||
| 36 | reg.UpdateReport(host["host_id"], registry.Report{ | 36 | reg.UpdateReport(host["host_id"], registry.Report{ |
| 37 | VMs: []registry.ActualVM{{ | 37 | VMs: []registry.VMStatus{{ |
| 38 | VMID: vmID, Power: "stopped", Phase: "creating", | 38 | VMID: vmID, PowerState: "stopped", Phase: "creating", |
| 39 | StatusDetail: "downloading image 1.2/3.7 GiB", | 39 | StatusDetail: "downloading image 1.2/3.7 GiB", |
| 40 | }}, | 40 | }}, |
| 41 | }) | 41 | }) |
| @@ -54,7 +54,7 @@ func TestAVMOnASilentAgentReadsAsItAlwaysDid(t *testing.T) { | |||
| 54 | vmID := createTestVM(t, ts, host["host_id"], "web-1") | 54 | vmID := createTestVM(t, ts, host["host_id"], "web-1") |
| 55 | 55 | ||
| 56 | reg.UpdateReport(host["host_id"], registry.Report{ | 56 | reg.UpdateReport(host["host_id"], registry.Report{ |
| 57 | VMs: []registry.ActualVM{{VMID: vmID, Power: "stopped", Phase: "creating"}}, | 57 | VMs: []registry.VMStatus{{VMID: vmID, PowerState: "stopped", Phase: "creating"}}, |
| 58 | }) | 58 | }) |
| 59 | 59 | ||
| 60 | vm := vmByID(t, ts.URL, vmID) | 60 | vm := vmByID(t, ts.URL, vmID) |
internal/server/registry/registry.go
| Old | New | ||
|---|---|---|---|
| @@ -36,8 +36,8 @@ type Metrics struct { | |||
| 36 | DiskUsedGB, DiskFreeGB int64 | 36 | DiskUsedGB, DiskFreeGB int64 |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | type ActualVM struct { | 39 | type VMStatus struct { |
| 40 | VMID, Power, Phase, IP, LastError string | 40 | VMID, PowerState, Phase, IP, LastError string |
| 41 | // StatusDetail is what the host is doing about this VM right now, in the | 41 | // StatusDetail is what the host is doing about this VM right now, in the |
| 42 | // host's own words. Empty is the normal state of a settled VM, and is also | 42 | // host's own words. Empty is the normal state of a settled VM, and is also |
| 43 | // what an agent too old to say anything leaves behind. | 43 | // what an agent too old to say anything leaves behind. |
| @@ -70,7 +70,7 @@ type ExposureStatus struct { | |||
| 70 | type ExposureSessions struct{ Active, Refused, Dropped int64 } | 70 | type ExposureSessions struct{ Active, Refused, Dropped int64 } |
| 71 | 71 | ||
| 72 | type Report struct { | 72 | type Report struct { |
| 73 | VMs []ActualVM | 73 | VMs []VMStatus |
| 74 | Quarantined []QuarantinedVM | 74 | Quarantined []QuarantinedVM |
| 75 | Exposures []ExposureStatus | 75 | Exposures []ExposureStatus |
| 76 | Capacity Capacity | 76 | Capacity Capacity |
internal/server/registry/registry_test.go
| Old | New | ||
|---|---|---|---|
| @@ -11,7 +11,7 @@ import ( | |||
| 11 | func TestGetReturnsDefensiveCopy(t *testing.T) { | 11 | func TestGetReturnsDefensiveCopy(t *testing.T) { |
| 12 | r := New(time.Now) | 12 | r := New(time.Now) |
| 13 | r.UpdateReport("h1", Report{ | 13 | r.UpdateReport("h1", Report{ |
| 14 | VMs: []ActualVM{{VMID: "vm1", Phase: "ready"}}, | 14 | VMs: []VMStatus{{VMID: "vm1", Phase: "ready"}}, |
| 15 | Quarantined: []QuarantinedVM{{VMID: "q1", VMSpecJSON: []byte("{}")}}, | 15 | Quarantined: []QuarantinedVM{{VMID: "q1", VMSpecJSON: []byte("{}")}}, |
| 16 | }) | 16 | }) |
| 17 | st, _ := r.Get("h1") | 17 | st, _ := r.Get("h1") |
| @@ -26,7 +26,7 @@ func TestReportRoundTripsAndOnlineWindow(t *testing.T) { | |||
| 26 | now := time.Now() | 26 | now := time.Now() |
| 27 | r := New(func() time.Time { return now }) | 27 | r := New(func() time.Time { return now }) |
| 28 | r.UpdateReport("h1", Report{ | 28 | r.UpdateReport("h1", Report{ |
| 29 | VMs: []ActualVM{{VMID: "vm1", Power: "running", Phase: "ready", IP: "10.77.1.2"}}, | 29 | VMs: []VMStatus{{VMID: "vm1", PowerState: "running", Phase: "ready", IP: "10.77.1.2"}}, |
| 30 | Capacity: Capacity{VCPUs: 8, MemMB: 16384, DiskGB: 200}, | 30 | Capacity: Capacity{VCPUs: 8, MemMB: 16384, DiskGB: 200}, |
| 31 | LastSeenEpoch: 4, | 31 | LastSeenEpoch: 4, |
| 32 | }) | 32 | }) |
internal/server/store/store.go
| Old | New | ||
|---|---|---|---|
| @@ -477,7 +477,7 @@ func (s *Store) Close() error { return s.db.Close() } | |||
| 477 | func (s *Store) Ping(ctx context.Context) error { return s.db.PingContext(ctx) } | 477 | func (s *Store) Ping(ctx context.Context) error { return s.db.PingContext(ctx) } |
| 478 | 478 | ||
| 479 | // Epoch reads the current epoch value directly. Production callers get the | 479 | // Epoch reads the current epoch value directly. Production callers get the |
| 480 | // epoch via DesiredForHost (paired with a matching desired-VM read in the | 480 | // epoch via SnapshotForHost (paired with a matching desired-VM read in the |
| 481 | // same tx); this exists as a test/observability hook for asserting exactly | 481 | // same tx); this exists as a test/observability hook for asserting exactly |
| 482 | // which mutations bump the epoch. | 482 | // which mutations bump the epoch. |
| 483 | func (s *Store) Epoch() (uint64, error) { | 483 | func (s *Store) Epoch() (uint64, error) { |
| @@ -1545,7 +1545,7 @@ func scanVM(rows *sql.Rows) (VM, error) { | |||
| 1545 | // queryVMs runs a vmColumns-projected SELECT against vms, with where appended | 1545 | // queryVMs runs a vmColumns-projected SELECT against vms, with where appended |
| 1546 | // verbatim after `FROM vms` (e.g. " WHERE id=?", or "" for none) and args | 1546 | // verbatim after `FROM vms` (e.g. " WHERE id=?", or "" for none) and args |
| 1547 | // bound in order, scanning every matching row. listVMs, VMByTenantName, GetVM, | 1547 | // bound in order, scanning every matching row. listVMs, VMByTenantName, GetVM, |
| 1548 | // and DesiredForHost all share this — they differ only in WHERE clause, | 1548 | // and SnapshotForHost all share this — they differ only in WHERE clause, |
| 1549 | // row-count expectations, and whether q is *sql.DB or an in-flight *sql.Tx. | 1549 | // row-count expectations, and whether q is *sql.DB or an in-flight *sql.Tx. |
| 1550 | func queryVMs(q querier, where string, args ...any) ([]VM, error) { | 1550 | func queryVMs(q querier, where string, args ...any) ([]VM, error) { |
| 1551 | rows, err := q.Query(`SELECT `+vmColumns+` FROM vms`+where, args...) | 1551 | rows, err := q.Query(`SELECT `+vmColumns+` FROM vms`+where, args...) |
| @@ -1624,7 +1624,7 @@ func (s *Store) Snapshot() ([]Host, map[string]Alloc, []VM, error) { | |||
| 1624 | return hosts, alloc, vms, tx.Commit() | 1624 | return hosts, alloc, vms, tx.Commit() |
| 1625 | } | 1625 | } |
| 1626 | 1626 | ||
| 1627 | func (s *Store) DesiredForHost(hostID string) (uint64, []VM, error) { | 1627 | func (s *Store) SnapshotForHost(hostID string) (uint64, []VM, error) { |
| 1628 | tx, err := s.db.Begin() | 1628 | tx, err := s.db.Begin() |
| 1629 | if err != nil { | 1629 | if err != nil { |
| 1630 | return 0, nil, err | 1630 | return 0, nil, err |
internal/server/store/store_test.go
| Old | New | ||
|---|---|---|---|
| @@ -251,8 +251,8 @@ func TestRecordVMHostKeyCertifiesOnlyItsOwnHostsVM(t *testing.T) { | |||
| 251 | require.NoError(t, s.RecordVMHostKey("vm1", h.ID, pubkey, cert)) | 251 | require.NoError(t, s.RecordVMHostKey("vm1", h.ID, pubkey, cert)) |
| 252 | 252 | ||
| 253 | // Both halves must round-trip through the read paths the snapshot and the | 253 | // Both halves must round-trip through the read paths the snapshot and the |
| 254 | // gate rely on: DesiredForHost (agent-facing) and VMByTenantName. | 254 | // gate rely on: SnapshotForHost (agent-facing) and VMByTenantName. |
| 255 | _, vms, err := s.DesiredForHost(h.ID) | 255 | _, vms, err := s.SnapshotForHost(h.ID) |
| 256 | require.NoError(t, err) | 256 | require.NoError(t, err) |
| 257 | require.Len(t, vms, 1) | 257 | require.Len(t, vms, 1) |
| 258 | assert.Equal(t, pubkey, vms[0].SSHHostPubKey) | 258 | assert.Equal(t, pubkey, vms[0].SSHHostPubKey) |
| @@ -610,7 +610,7 @@ func TestEnrollmentAssignsSequentialCIDRsAndIsOneTimeUse(t *testing.T) { | |||
| 610 | assert.Equal(t, "10.77.2.0/24", h2.BridgeCIDR) | 610 | assert.Equal(t, "10.77.2.0/24", h2.BridgeCIDR) |
| 611 | } | 611 | } |
| 612 | 612 | ||
| 613 | func TestDesiredStateMutationsBumpEpochButStatusWritesDoNot(t *testing.T) { | 613 | func TestSpecMutationsBumpEpochButStatusWritesDoNot(t *testing.T) { |
| 614 | s := newStore(t) | 614 | s := newStore(t) |
| 615 | h := enrollHost(t, s) | 615 | h := enrollHost(t, s) |
| 616 | e0, _ := s.Epoch() | 616 | e0, _ := s.Epoch() |
| @@ -652,13 +652,13 @@ func TestNameUniqueAmongLiveRowsOnly(t *testing.T) { | |||
| 652 | assert.NoError(t, s.CreateVM(mk("vm3")), "tombstoned row must not block the name") | 652 | assert.NoError(t, s.CreateVM(mk("vm3")), "tombstoned row must not block the name") |
| 653 | } | 653 | } |
| 654 | 654 | ||
| 655 | func TestDesiredForHostIncludesTombstonedAndEpochConsistently(t *testing.T) { | 655 | func TestSnapshotForHostIncludesTombstonedAndEpochConsistently(t *testing.T) { |
| 656 | s := newStore(t) | 656 | s := newStore(t) |
| 657 | h := enrollHost(t, s) | 657 | h := enrollHost(t, s) |
| 658 | require.NoError(t, s.CreateVM(VM{ID: "vm1", HostID: h.ID, Name: "a", ImageURL: "u", | 658 | require.NoError(t, s.CreateVM(VM{ID: "vm1", HostID: h.ID, Name: "a", ImageURL: "u", |
| 659 | ImageSHA256: "s", VCPUs: 1, MemMB: 512, DiskGB: 5, PowerState: "running"})) | 659 | ImageSHA256: "s", VCPUs: 1, MemMB: 512, DiskGB: 5, PowerState: "running"})) |
| 660 | require.NoError(t, s.TombstoneVM("vm1")) | 660 | require.NoError(t, s.TombstoneVM("vm1")) |
| 661 | epoch, vms, err := s.DesiredForHost(h.ID) | 661 | epoch, vms, err := s.SnapshotForHost(h.ID) |
| 662 | require.NoError(t, err) | 662 | require.NoError(t, err) |
| 663 | e, _ := s.Epoch() | 663 | e, _ := s.Epoch() |
| 664 | assert.Equal(t, e, epoch) | 664 | assert.Equal(t, e, epoch) |
internal/server/syncsvc/exposures_test.go
| Old | New | ||
|---|---|---|---|
| @@ -59,8 +59,8 @@ func TestSnapshotDropsExposuresOfATombstonedVM(t *testing.T) { | |||
| 59 | func TestApplyReportFoldsExposureStateIntoTheRegistry(t *testing.T) { | 59 | func TestApplyReportFoldsExposureStateIntoTheRegistry(t *testing.T) { |
| 60 | f := setup(t) | 60 | f := setup(t) |
| 61 | 61 | ||
| 62 | f.svc.applyReport(f.host.ID, &pb.ActualStateReport{ | 62 | f.svc.applyReport(f.host.ID, &pb.Report{ |
| 63 | Exposures: []*pb.ExposureActual{ | 63 | Exposures: []*pb.ExposureStatus{ |
| 64 | {Id: "e1", State: "active"}, | 64 | {Id: "e1", State: "active"}, |
| 65 | {Id: "e2", State: "failed", Reason: "listen tcp 0.0.0.0:30080: bind: address already in use"}, | 65 | {Id: "e2", State: "failed", Reason: "listen tcp 0.0.0.0:30080: bind: address already in use"}, |
| 66 | }, | 66 | }, |
| @@ -79,12 +79,12 @@ func TestApplyReportFoldsExposureStateIntoTheRegistry(t *testing.T) { | |||
| 79 | func TestApplyReportRecordsTheHostUplinkAddress(t *testing.T) { | 79 | func TestApplyReportRecordsTheHostUplinkAddress(t *testing.T) { |
| 80 | f := setup(t) | 80 | f := setup(t) |
| 81 | 81 | ||
| 82 | f.svc.applyReport(f.host.ID, &pb.ActualStateReport{HostUplinkAddr: "192.168.0.190"}) | 82 | f.svc.applyReport(f.host.ID, &pb.Report{HostUplinkAddr: "192.168.0.190"}) |
| 83 | h, err := f.st.GetHost(f.host.ID) | 83 | h, err := f.st.GetHost(f.host.ID) |
| 84 | require.NoError(t, err) | 84 | require.NoError(t, err) |
| 85 | assert.Equal(t, "192.168.0.190", h.UplinkAddr) | 85 | assert.Equal(t, "192.168.0.190", h.UplinkAddr) |
| 86 | 86 | ||
| 87 | f.svc.applyReport(f.host.ID, &pb.ActualStateReport{}) | 87 | f.svc.applyReport(f.host.ID, &pb.Report{}) |
| 88 | h, err = f.st.GetHost(f.host.ID) | 88 | h, err = f.st.GetHost(f.host.ID) |
| 89 | require.NoError(t, err) | 89 | require.NoError(t, err) |
| 90 | assert.Equal(t, "192.168.0.190", h.UplinkAddr, "silence is not a statement") | 90 | assert.Equal(t, "192.168.0.190", h.UplinkAddr, "silence is not a statement") |
internal/server/syncsvc/hostcert.go
| Old | New | ||
|---|---|---|---|
| @@ -64,7 +64,7 @@ func (s *Service) signAndRecordHostCert(hostID, vmID, pubLine string) error { | |||
| 64 | // | 64 | // |
| 65 | // A failure is logged and dropped, like every other per-VM failure in a report: | 65 | // A failure is logged and dropped, like every other per-VM failure in a report: |
| 66 | // the key rides every later report too, so the next tick tries again. | 66 | // the key rides every later report too, so the next tick tries again. |
| 67 | func (s *Service) certifyReportedHostKeys(hostID string, vms []*pb.ActualVM) { | 67 | func (s *Service) certifyReportedHostKeys(hostID string, vms []*pb.VMStatus) { |
| 68 | if s.certs == nil { | 68 | if s.certs == nil { |
| 69 | return | 69 | return |
| 70 | } | 70 | } |
internal/server/syncsvc/hostcert_test.go
| Old | New | ||
|---|---|---|---|
| @@ -45,9 +45,9 @@ func testPubKey(t *testing.T) string { | |||
| 45 | 45 | ||
| 46 | // reportKey builds the report a host sends for a VM that has generated its host | 46 | // reportKey builds the report a host sends for a VM that has generated its host |
| 47 | // key and is waiting for the certificate. | 47 | // key and is waiting for the certificate. |
| 48 | func reportKey(vmID, pubkey string) *pb.ActualStateReport { | 48 | func reportKey(vmID, pubkey string) *pb.Report { |
| 49 | return &pb.ActualStateReport{Vms: []*pb.ActualVM{ | 49 | return &pb.Report{Vms: []*pb.VMStatus{ |
| 50 | {VmId: vmID, Phase: "creating", Power: "stopped", SshHostPubkey: pubkey}, | 50 | {VmId: vmID, Phase: "creating", PowerState: "stopped", SshHostPubkey: pubkey}, |
| 51 | }} | 51 | }} |
| 52 | } | 52 | } |
| 53 | 53 | ||
internal/server/syncsvc/narration_test.go
| Old | New | ||
|---|---|---|---|
| @@ -15,9 +15,9 @@ import ( | |||
| 15 | func TestApplyReportCarriesWhatAHostIsDoing(t *testing.T) { | 15 | func TestApplyReportCarriesWhatAHostIsDoing(t *testing.T) { |
| 16 | f := setup(t) | 16 | f := setup(t) |
| 17 | 17 | ||
| 18 | f.svc.applyReport(f.host.ID, &pb.ActualStateReport{ | 18 | f.svc.applyReport(f.host.ID, &pb.Report{ |
| 19 | Vms: []*pb.ActualVM{{ | 19 | Vms: []*pb.VMStatus{{ |
| 20 | VmId: "vm1", Power: "stopped", Phase: "creating", | 20 | VmId: "vm1", PowerState: "stopped", Phase: "creating", |
| 21 | StatusDetail: "downloading image 1.2/3.7 GiB", | 21 | StatusDetail: "downloading image 1.2/3.7 GiB", |
| 22 | }}, | 22 | }}, |
| 23 | }) | 23 | }) |
| @@ -35,8 +35,8 @@ func TestApplyReportCarriesWhatAHostIsDoing(t *testing.T) { | |||
| 35 | func TestApplyReportFromAnAgentThatSaysNothing(t *testing.T) { | 35 | func TestApplyReportFromAnAgentThatSaysNothing(t *testing.T) { |
| 36 | f := setup(t) | 36 | f := setup(t) |
| 37 | 37 | ||
| 38 | f.svc.applyReport(f.host.ID, &pb.ActualStateReport{ | 38 | f.svc.applyReport(f.host.ID, &pb.Report{ |
| 39 | Vms: []*pb.ActualVM{{VmId: "vm1", Power: "stopped", Phase: "creating"}}, | 39 | Vms: []*pb.VMStatus{{VmId: "vm1", PowerState: "stopped", Phase: "creating"}}, |
| 40 | }) | 40 | }) |
| 41 | 41 | ||
| 42 | got, ok := f.reg.Get(f.host.ID) | 42 | got, ok := f.reg.Get(f.host.ID) |
| @@ -51,8 +51,8 @@ func TestApplyReportFromAnAgentThatSaysNothing(t *testing.T) { | |||
| 51 | func TestApplyReportCountsExposureSessions(t *testing.T) { | 51 | func TestApplyReportCountsExposureSessions(t *testing.T) { |
| 52 | f := setup(t) | 52 | f := setup(t) |
| 53 | 53 | ||
| 54 | f.svc.applyReport(f.host.ID, &pb.ActualStateReport{ | 54 | f.svc.applyReport(f.host.ID, &pb.Report{ |
| 55 | Exposures: []*pb.ExposureActual{{ | 55 | Exposures: []*pb.ExposureStatus{{ |
| 56 | Id: "e1", State: "active", | 56 | Id: "e1", State: "active", |
| 57 | Sessions: &pb.ExposureSessions{Active: 7, Refused: 12, Dropped: 3}, | 57 | Sessions: &pb.ExposureSessions{Active: 7, Refused: 12, Dropped: 3}, |
| 58 | }}, | 58 | }}, |
| @@ -73,8 +73,8 @@ func TestApplyReportCountsExposureSessions(t *testing.T) { | |||
| 73 | func TestApplyReportFromAnAgentThatCountsNothing(t *testing.T) { | 73 | func TestApplyReportFromAnAgentThatCountsNothing(t *testing.T) { |
| 74 | f := setup(t) | 74 | f := setup(t) |
| 75 | 75 | ||
| 76 | f.svc.applyReport(f.host.ID, &pb.ActualStateReport{ | 76 | f.svc.applyReport(f.host.ID, &pb.Report{ |
| 77 | Exposures: []*pb.ExposureActual{{Id: "e1", State: "active"}}, | 77 | Exposures: []*pb.ExposureStatus{{Id: "e1", State: "active"}}, |
| 78 | }) | 78 | }) |
| 79 | 79 | ||
| 80 | got, ok := f.reg.Get(f.host.ID) | 80 | got, ok := f.reg.Get(f.host.ID) |
internal/server/syncsvc/network_test.go
| Old | New | ||
|---|---|---|---|
| @@ -70,9 +70,9 @@ func TestApplyReportRecordsBothAddresses(t *testing.T) { | |||
| 70 | f := setup(t) | 70 | f := setup(t) |
| 71 | networkedVM(t, f, "vm1") | 71 | networkedVM(t, f, "vm1") |
| 72 | 72 | ||
| 73 | f.svc.applyReport(f.host.ID, &pb.ActualStateReport{ | 73 | f.svc.applyReport(f.host.ID, &pb.Report{ |
| 74 | Vms: []*pb.ActualVM{{ | 74 | Vms: []*pb.VMStatus{{ |
| 75 | VmId: "vm1", Power: "running", Phase: "ready", | 75 | VmId: "vm1", PowerState: "running", Phase: "ready", |
| 76 | Ip: "10.77.1.2", NetworkIp: "192.168.0.42", | 76 | Ip: "10.77.1.2", NetworkIp: "192.168.0.42", |
| 77 | }}, | 77 | }}, |
| 78 | }) | 78 | }) |
| @@ -91,8 +91,8 @@ func TestApplyReportRecordsTheLeaseBeforeTheGuestIsReady(t *testing.T) { | |||
| 91 | f := setup(t) | 91 | f := setup(t) |
| 92 | networkedVM(t, f, "vm1") | 92 | networkedVM(t, f, "vm1") |
| 93 | 93 | ||
| 94 | f.svc.applyReport(f.host.ID, &pb.ActualStateReport{ | 94 | f.svc.applyReport(f.host.ID, &pb.Report{ |
| 95 | Vms: []*pb.ActualVM{{VmId: "vm1", Power: "running", Phase: "creating", NetworkIp: "192.168.0.42"}}, | 95 | Vms: []*pb.VMStatus{{VmId: "vm1", PowerState: "running", Phase: "creating", NetworkIp: "192.168.0.42"}}, |
| 96 | }) | 96 | }) |
| 97 | 97 | ||
| 98 | vm, err := f.st.GetVM("vm1") | 98 | vm, err := f.st.GetVM("vm1") |
| @@ -107,9 +107,9 @@ func TestApplyReportRecordsTheLeaseBeforeTheGuestIsReady(t *testing.T) { | |||
| 107 | func TestApplyReportKeepsTheLastKnownLease(t *testing.T) { | 107 | func TestApplyReportKeepsTheLastKnownLease(t *testing.T) { |
| 108 | f := setup(t) | 108 | f := setup(t) |
| 109 | networkedVM(t, f, "vm1") | 109 | networkedVM(t, f, "vm1") |
| 110 | ready := func(netIP string) *pb.ActualStateReport { | 110 | ready := func(netIP string) *pb.Report { |
| 111 | return &pb.ActualStateReport{Vms: []*pb.ActualVM{{ | 111 | return &pb.Report{Vms: []*pb.VMStatus{{ |
| 112 | VmId: "vm1", Power: "running", Phase: "ready", Ip: "10.77.1.2", NetworkIp: netIP, | 112 | VmId: "vm1", PowerState: "running", Phase: "ready", Ip: "10.77.1.2", NetworkIp: netIP, |
| 113 | }}} | 113 | }}} |
| 114 | } | 114 | } |
| 115 | 115 | ||
| @@ -147,8 +147,8 @@ func TestApplyReportKeepsTheLastKnownLease(t *testing.T) { | |||
| 147 | func TestApplyReportSkipsUnchangedLeases(t *testing.T) { | 147 | func TestApplyReportSkipsUnchangedLeases(t *testing.T) { |
| 148 | f := setup(t) | 148 | f := setup(t) |
| 149 | networkedVM(t, f, "vm1") | 149 | networkedVM(t, f, "vm1") |
| 150 | rep := &pb.ActualStateReport{Vms: []*pb.ActualVM{{ | 150 | rep := &pb.Report{Vms: []*pb.VMStatus{{ |
| 151 | VmId: "vm1", Power: "running", Phase: "ready", Ip: "10.77.1.2", NetworkIp: "192.168.0.42", | 151 | VmId: "vm1", PowerState: "running", Phase: "ready", Ip: "10.77.1.2", NetworkIp: "192.168.0.42", |
| 152 | }}} | 152 | }}} |
| 153 | 153 | ||
| 154 | f.svc.applyReport(f.host.ID, rep) | 154 | f.svc.applyReport(f.host.ID, rep) |
| @@ -167,11 +167,11 @@ func TestApplyReportForgetsAReapedVMsLease(t *testing.T) { | |||
| 167 | f := setup(t) | 167 | f := setup(t) |
| 168 | networkedVM(t, f, "vm1") | 168 | networkedVM(t, f, "vm1") |
| 169 | 169 | ||
| 170 | f.svc.applyReport(f.host.ID, &pb.ActualStateReport{ | 170 | f.svc.applyReport(f.host.ID, &pb.Report{ |
| 171 | Vms: []*pb.ActualVM{{VmId: "vm1", Power: "running", Phase: "ready", NetworkIp: "192.168.0.42"}}, | 171 | Vms: []*pb.VMStatus{{VmId: "vm1", PowerState: "running", Phase: "ready", NetworkIp: "192.168.0.42"}}, |
| 172 | }) | 172 | }) |
| 173 | require.NoError(t, f.st.TombstoneVM("vm1")) | 173 | require.NoError(t, f.st.TombstoneVM("vm1")) |
| 174 | f.svc.applyReport(f.host.ID, &pb.ActualStateReport{Destroyed: []string{"vm1"}}) | 174 | f.svc.applyReport(f.host.ID, &pb.Report{Destroyed: []string{"vm1"}}) |
| 175 | 175 | ||
| 176 | f.svc.netIPTrack.mu.Lock() | 176 | f.svc.netIPTrack.mu.Lock() |
| 177 | defer f.svc.netIPTrack.mu.Unlock() | 177 | defer f.svc.netIPTrack.mu.Unlock() |
internal/server/syncsvc/persistent_test.go
| Old | New | ||
|---|---|---|---|
| @@ -13,7 +13,7 @@ import ( | |||
| 13 | // agent, but it has not stopped being on the wire, and this is the test that | 13 | // agent, but it has not stopped being on the wire, and this is the test that |
| 14 | // fails if someone finishes the job too early. | 14 | // fails if someone finishes the job too early. |
| 15 | // | 15 | // |
| 16 | // A v0.0.5 agent — which exists, in production — reads VMDesired.persistent as | 16 | // A v0.0.5 agent — which exists, in production — reads VMSpec.persistent as |
| 17 | // a restart policy, and proto3 gives an absent bool the value false. False | 17 | // a restart policy, and proto3 gives an absent bool the value false. False |
| 18 | // means ephemeral, and an ephemeral guest that goes lost is marked failed | 18 | // means ephemeral, and an ephemeral guest that goes lost is marked failed |
| 19 | // forever and never booted again. Every guest goes lost when its host reboots. | 19 | // forever and never booted again. Every guest goes lost when its host reboots. |
internal/server/syncsvc/syncsvc.go
| Old | New | ||
|---|---|---|---|
| @@ -325,12 +325,12 @@ func (s *Service) handleConn(ctx context.Context, conn quic.Connection) { | |||
| 325 | 325 | ||
| 326 | // buildSnapshot reads hostID's desired state — its VMs and the exposures it | 326 | // buildSnapshot reads hostID's desired state — its VMs and the exposures it |
| 327 | // should be serving — and renders it as one snapshot. | 327 | // should be serving — and renders it as one snapshot. |
| 328 | func (s *Service) buildSnapshot(hostID string) (*pb.DesiredStateSnapshot, error) { | 328 | func (s *Service) buildSnapshot(hostID string) (*pb.Snapshot, error) { |
| 329 | epoch, vms, err := s.st.DesiredForHost(hostID) | 329 | epoch, vms, err := s.st.SnapshotForHost(hostID) |
| 330 | if err != nil { | 330 | if err != nil { |
| 331 | return nil, fmt.Errorf("desired for host: %w", err) | 331 | return nil, fmt.Errorf("desired for host: %w", err) |
| 332 | } | 332 | } |
| 333 | snap := &pb.DesiredStateSnapshot{Epoch: epoch, Vms: make([]*pb.VMDesired, 0, len(vms))} | 333 | snap := &pb.Snapshot{Epoch: epoch, Vms: make([]*pb.VMSpec, 0, len(vms))} |
| 334 | snap.AgentUpgrade = s.offerFor(hostID) | 334 | snap.AgentUpgrade = s.offerFor(hostID) |
| 335 | caCache := map[string][]string{} // tenant -> canonical CA lines, legacy rows only | 335 | caCache := map[string][]string{} // tenant -> canonical CA lines, legacy rows only |
| 336 | for _, v := range vms { | 336 | for _, v := range vms { |
| @@ -374,7 +374,7 @@ func (s *Service) buildSnapshot(hostID string) (*pb.DesiredStateSnapshot, error) | |||
| 374 | } | 374 | } |
| 375 | cas = legacy | 375 | cas = legacy |
| 376 | } | 376 | } |
| 377 | snap.Vms = append(snap.Vms, &pb.VMDesired{ | 377 | snap.Vms = append(snap.Vms, &pb.VMSpec{ |
| 378 | VmId: v.ID, Name: v.Name, ImageUrl: v.ImageURL, ImageSha256: v.ImageSHA256, | 378 | VmId: v.ID, Name: v.Name, ImageUrl: v.ImageURL, ImageSha256: v.ImageSHA256, |
| 379 | CloudInit: v.CloudInit, Vcpus: v.VCPUs, MemMb: v.MemMB, DiskGb: v.DiskGB, | 379 | CloudInit: v.CloudInit, Vcpus: v.VCPUs, MemMb: v.MemMB, DiskGb: v.DiskGB, |
| 380 | // Always true, and no longer read from anywhere: every VM is | 380 | // Always true, and no longer read from anywhere: every VM is |
| @@ -406,7 +406,7 @@ func (s *Service) buildSnapshot(hostID string) (*pb.DesiredStateSnapshot, error) | |||
| 406 | // In-range by construction: the API validates ports and closes the | 406 | // In-range by construction: the API validates ports and closes the |
| 407 | // protocol at the two the agent can bind (validateExposure), and the | 407 | // protocol at the two the agent can bind (validateExposure), and the |
| 408 | // store accepts rows only from the API. | 408 | // store accepts rows only from the API. |
| 409 | snap.Exposures = append(snap.Exposures, &pb.ExposureDesired{ | 409 | snap.Exposures = append(snap.Exposures, &pb.ExposureSpec{ |
| 410 | Id: e.ID, VmId: e.VMID, | 410 | Id: e.ID, VmId: e.VMID, |
| 411 | GuestPort: uint32(e.GuestPort), HostPort: uint32(e.HostPort), | 411 | GuestPort: uint32(e.GuestPort), HostPort: uint32(e.HostPort), |
| 412 | Protocol: e.Protocol, | 412 | Protocol: e.Protocol, |
| @@ -506,7 +506,7 @@ func (s *Service) failWrite(conn quic.Connection, hostID string, err error) { | |||
| 506 | 506 | ||
| 507 | // applyReport updates the registry and durably records VM status changes. | 507 | // applyReport updates the registry and durably records VM status changes. |
| 508 | // Errors within the report are logged and skipped — they must never kill the stream. | 508 | // Errors within the report are logged and skipped — they must never kill the stream. |
| 509 | func (s *Service) applyReport(hostID string, rep *pb.ActualStateReport) { | 509 | func (s *Service) applyReport(hostID string, rep *pb.Report) { |
| 510 | // Build registry report. | 510 | // Build registry report. |
| 511 | r := registry.Report{ | 511 | r := registry.Report{ |
| 512 | LastSeenEpoch: rep.GetLastSeenEpoch(), | 512 | LastSeenEpoch: rep.GetLastSeenEpoch(), |
| @@ -647,17 +647,17 @@ func (s *Service) applyReport(hostID string, rep *pb.ActualStateReport) { | |||
| 647 | } | 647 | } |
| 648 | } | 648 | } |
| 649 | 649 | ||
| 650 | // toRegistryVMs maps reported ActualVMs to registry rows. Returns nil (not an | 650 | // toRegistryVMs maps reported VMStatus rows to registry rows. Returns nil (not an |
| 651 | // empty slice) for empty input, matching the original append-into-nil behavior. | 651 | // empty slice) for empty input, matching the original append-into-nil behavior. |
| 652 | func toRegistryVMs(in []*pb.ActualVM) []registry.ActualVM { | 652 | func toRegistryVMs(in []*pb.VMStatus) []registry.VMStatus { |
| 653 | if len(in) == 0 { | 653 | if len(in) == 0 { |
| 654 | return nil | 654 | return nil |
| 655 | } | 655 | } |
| 656 | out := make([]registry.ActualVM, 0, len(in)) | 656 | out := make([]registry.VMStatus, 0, len(in)) |
| 657 | for _, v := range in { | 657 | for _, v := range in { |
| 658 | out = append(out, registry.ActualVM{ | 658 | out = append(out, registry.VMStatus{ |
| 659 | VMID: v.GetVmId(), | 659 | VMID: v.GetVmId(), |
| 660 | Power: v.GetPower(), | 660 | PowerState: v.GetPowerState(), |
| 661 | Phase: v.GetPhase(), | 661 | Phase: v.GetPhase(), |
| 662 | IP: v.GetIp(), | 662 | IP: v.GetIp(), |
| 663 | LastError: v.GetLastError(), | 663 | LastError: v.GetLastError(), |
| @@ -687,7 +687,7 @@ func toRegistryQuarantined(in []*pb.QuarantinedVM) []registry.QuarantinedVM { | |||
| 687 | 687 | ||
| 688 | // toRegistryExposures maps reported exposure state to registry rows. Returns | 688 | // toRegistryExposures maps reported exposure state to registry rows. Returns |
| 689 | // nil (not an empty slice) for empty input, matching append-into-nil behavior. | 689 | // nil (not an empty slice) for empty input, matching append-into-nil behavior. |
| 690 | func toRegistryExposures(in []*pb.ExposureActual) []registry.ExposureStatus { | 690 | func toRegistryExposures(in []*pb.ExposureStatus) []registry.ExposureStatus { |
| 691 | if len(in) == 0 { | 691 | if len(in) == 0 { |
| 692 | return nil | 692 | return nil |
| 693 | } | 693 | } |
internal/server/syncsvc/syncsvc_test.go
| Old | New | ||
|---|---|---|---|
| @@ -200,7 +200,7 @@ func TestReportWritesThroughAndHardDeletesAckedTombstones(t *testing.T) { | |||
| 200 | c.recv(t) // initial snapshot | 200 | c.recv(t) // initial snapshot |
| 201 | 201 | ||
| 202 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ | 202 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ |
| 203 | Report: &pb.ActualStateReport{ | 203 | Report: &pb.Report{ |
| 204 | Destroyed: []string{"vm1"}, // level-triggered ack | 204 | Destroyed: []string{"vm1"}, // level-triggered ack |
| 205 | Capacity: &pb.Capacity{Vcpus: 8}, | 205 | Capacity: &pb.Capacity{Vcpus: 8}, |
| 206 | LastSeenEpoch: 2, | 206 | LastSeenEpoch: 2, |
| @@ -256,8 +256,8 @@ func TestApplyReportSkipsUnchangedWrites(t *testing.T) { | |||
| 256 | 256 | ||
| 257 | ready := func(ip string) *pb.AgentMessage { | 257 | ready := func(ip string) *pb.AgentMessage { |
| 258 | return &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ | 258 | return &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ |
| 259 | Report: &pb.ActualStateReport{Vms: []*pb.ActualVM{ | 259 | Report: &pb.Report{Vms: []*pb.VMStatus{ |
| 260 | {VmId: "vm1", Power: "running", Phase: "ready", Ip: ip}, | 260 | {VmId: "vm1", PowerState: "running", Phase: "ready", Ip: ip}, |
| 261 | }}}} | 261 | }}}} |
| 262 | } | 262 | } |
| 263 | 263 | ||
| @@ -295,8 +295,8 @@ func TestReportWithValidIPUpdatesRegistryAndStore(t *testing.T) { | |||
| 295 | c.recv(t) | 295 | c.recv(t) |
| 296 | 296 | ||
| 297 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ | 297 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ |
| 298 | Report: &pb.ActualStateReport{Vms: []*pb.ActualVM{ | 298 | Report: &pb.Report{Vms: []*pb.VMStatus{ |
| 299 | {VmId: "vm1", Power: "running", Phase: "ready", Ip: "10.77.1.2"}, | 299 | {VmId: "vm1", PowerState: "running", Phase: "ready", Ip: "10.77.1.2"}, |
| 300 | }}}}) | 300 | }}}}) |
| 301 | 301 | ||
| 302 | require.Eventually(t, func() bool { | 302 | require.Eventually(t, func() bool { |
| @@ -320,8 +320,8 @@ func TestReportRecoversFromAnUnusableAddress(t *testing.T) { | |||
| 320 | 320 | ||
| 321 | ready := func(ip string) *pb.AgentMessage { | 321 | ready := func(ip string) *pb.AgentMessage { |
| 322 | return &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ | 322 | return &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ |
| 323 | Report: &pb.ActualStateReport{Vms: []*pb.ActualVM{ | 323 | Report: &pb.Report{Vms: []*pb.VMStatus{ |
| 324 | {VmId: "vm1", Power: "running", Phase: "ready", Ip: ip}, | 324 | {VmId: "vm1", PowerState: "running", Phase: "ready", Ip: ip}, |
| 325 | }}}} | 325 | }}}} |
| 326 | } | 326 | } |
| 327 | 327 | ||
| @@ -356,7 +356,7 @@ func TestHardDeleteTriggersRepush(t *testing.T) { | |||
| 356 | 356 | ||
| 357 | // Send a report acking the destroyed VM. | 357 | // Send a report acking the destroyed VM. |
| 358 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ | 358 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ |
| 359 | Report: &pb.ActualStateReport{ | 359 | Report: &pb.Report{ |
| 360 | Destroyed: []string{"vm1"}, | 360 | Destroyed: []string{"vm1"}, |
| 361 | LastSeenEpoch: snap.Epoch, | 361 | LastSeenEpoch: snap.Epoch, |
| 362 | }}}) | 362 | }}}) |
| @@ -623,7 +623,7 @@ func TestMaxAgeEnforcedMidSession(t *testing.T) { | |||
| 623 | 623 | ||
| 624 | time.Sleep(1800 * time.Millisecond) // credential ages past maxCredAge | 624 | time.Sleep(1800 * time.Millisecond) // credential ages past maxCredAge |
| 625 | 625 | ||
| 626 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{Report: &pb.ActualStateReport{}}}) | 626 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{Report: &pb.Report{}}}) |
| 627 | // The server must close the connection with CodeAuthRejected; the next | 627 | // The server must close the connection with CodeAuthRejected; the next |
| 628 | // read on the down-stream surfaces it. | 628 | // read on the down-stream surfaces it. |
| 629 | var msg pb.ServerMessage | 629 | var msg pb.ServerMessage |
| @@ -815,7 +815,7 @@ func TestReportMetricsLandInRegistry(t *testing.T) { | |||
| 815 | c := mustDial(t, f) | 815 | c := mustDial(t, f) |
| 816 | c.recv(t) // initial snapshot | 816 | c.recv(t) // initial snapshot |
| 817 | 817 | ||
| 818 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{Report: &pb.ActualStateReport{ | 818 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{Report: &pb.Report{ |
| 819 | Capacity: &pb.Capacity{Vcpus: 8}, | 819 | Capacity: &pb.Capacity{Vcpus: 8}, |
| 820 | Metrics: &pb.HostMetrics{UptimeS: 3600, Load1: 1.5, MemUsedMb: 2048, MemAvailableMb: 6144, DiskUsedGb: 20, DiskFreeGb: 80}, | 820 | Metrics: &pb.HostMetrics{UptimeS: 3600, Load1: 1.5, MemUsedMb: 2048, MemAvailableMb: 6144, DiskUsedGb: 20, DiskFreeGb: 80}, |
| 821 | LastSeenEpoch: 2, | 821 | LastSeenEpoch: 2, |
| @@ -934,7 +934,7 @@ func TestReportRecordsTheHostsGuestSubnet(t *testing.T) { | |||
| 934 | c.recv(t) | 934 | c.recv(t) |
| 935 | 935 | ||
| 936 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ | 936 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ |
| 937 | Report: &pb.ActualStateReport{GuestCidr: "192.168.64.0/24"}}}) | 937 | Report: &pb.Report{GuestCidr: "192.168.64.0/24"}}}) |
| 938 | require.Eventually(t, func() bool { | 938 | require.Eventually(t, func() bool { |
| 939 | h, err := f.st.GetHost(f.host.ID) | 939 | h, err := f.st.GetHost(f.host.ID) |
| 940 | return err == nil && h.BridgeCIDR == "192.168.64.0/24" | 940 | return err == nil && h.BridgeCIDR == "192.168.64.0/24" |
| @@ -943,7 +943,7 @@ func TestReportRecordsTheHostsGuestSubnet(t *testing.T) { | |||
| 943 | // Empty is "no answer", never "no network": a host that cannot see its own | 943 | // Empty is "no answer", never "no network": a host that cannot see its own |
| 944 | // network yet must not erase what it told us when it could. | 944 | // network yet must not erase what it told us when it could. |
| 945 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ | 945 | c.send(t, &pb.AgentMessage{Msg: &pb.AgentMessage_Report{ |
| 946 | Report: &pb.ActualStateReport{GuestCidr: ""}}}) | 946 | Report: &pb.Report{GuestCidr: ""}}}) |
| 947 | require.Never(t, func() bool { | 947 | require.Never(t, func() bool { |
| 948 | h, err := f.st.GetHost(f.host.ID) | 948 | h, err := f.st.GetHost(f.host.ID) |
| 949 | return err == nil && h.BridgeCIDR != "192.168.64.0/24" | 949 | return err == nil && h.BridgeCIDR != "192.168.64.0/24" |
internal/transport/contract_test.go
| Old | New | ||
|---|---|---|---|
| @@ -18,13 +18,13 @@ import ( | |||
| 18 | // the framing is altered incompatibly, proto.Equal fails here. | 18 | // the framing is altered incompatibly, proto.Equal fails here. |
| 19 | 19 | ||
| 20 | func TestAgentMessageReportRoundTrip(t *testing.T) { | 20 | func TestAgentMessageReportRoundTrip(t *testing.T) { |
| 21 | in := &pb.AgentMessage{Msg: &pb.AgentMessage_Report{Report: &pb.ActualStateReport{ | 21 | in := &pb.AgentMessage{Msg: &pb.AgentMessage_Report{Report: &pb.Report{ |
| 22 | Vms: []*pb.ActualVM{ | 22 | Vms: []*pb.VMStatus{ |
| 23 | {VmId: "vm-1", Power: "on", Phase: "running", Ip: "10.0.0.5", LastError: ""}, | 23 | {VmId: "vm-1", PowerState: "on", Phase: "running", Ip: "10.0.0.5", LastError: ""}, |
| 24 | {VmId: "vm-2", Power: "off", Phase: "stopped"}, | 24 | {VmId: "vm-2", PowerState: "off", Phase: "stopped"}, |
| 25 | {VmId: "vm-3", Power: "off", Phase: "creating", StatusDetail: "downloading image 1.2/3.7 GiB"}, | 25 | {VmId: "vm-3", PowerState: "off", Phase: "creating", StatusDetail: "downloading image 1.2/3.7 GiB"}, |
| 26 | }, | 26 | }, |
| 27 | Exposures: []*pb.ExposureActual{{ | 27 | Exposures: []*pb.ExposureStatus{{ |
| 28 | Id: "e-1", State: "active", | 28 | Id: "e-1", State: "active", |
| 29 | Sessions: &pb.ExposureSessions{Active: 7, Refused: 12, Dropped: 3}, | 29 | Sessions: &pb.ExposureSessions{Active: 7, Refused: 12, Dropped: 3}, |
| 30 | }}, | 30 | }}, |
| @@ -41,9 +41,9 @@ func TestAgentMessageReportRoundTrip(t *testing.T) { | |||
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | func TestServerMessageSnapshotRoundTrip(t *testing.T) { | 43 | func TestServerMessageSnapshotRoundTrip(t *testing.T) { |
| 44 | in := &pb.ServerMessage{Msg: &pb.ServerMessage_Snapshot{Snapshot: &pb.DesiredStateSnapshot{ | 44 | in := &pb.ServerMessage{Msg: &pb.ServerMessage_Snapshot{Snapshot: &pb.Snapshot{ |
| 45 | Epoch: 7, | 45 | Epoch: 7, |
| 46 | Vms: []*pb.VMDesired{{ | 46 | Vms: []*pb.VMSpec{{ |
| 47 | VmId: "vm-1", Name: "web", ImageUrl: "https://img/x.qcow2", ImageSha256: "abc", | 47 | VmId: "vm-1", Name: "web", ImageUrl: "https://img/x.qcow2", ImageSha256: "abc", |
| 48 | CloudInit: "#cloud-config", Vcpus: 4, MemMb: 8192, DiskGb: 40, | 48 | CloudInit: "#cloud-config", Vcpus: 4, MemMb: 8192, DiskGb: 40, |
| 49 | Persistent: true, PowerState: "on", Tombstoned: false, SshAuthorizedKey: "ssh-ed25519 AAAA", | 49 | Persistent: true, PowerState: "on", Tombstoned: false, SshAuthorizedKey: "ssh-ed25519 AAAA", |
| @@ -63,15 +63,15 @@ func TestServerMessageSnapshotRoundTrip(t *testing.T) { | |||
| 63 | // know. Field 99 is nothing in this schema and stands in for whatever a later | 63 | // know. Field 99 is nothing in this schema and stands in for whatever a later |
| 64 | // release adds beside status_detail. | 64 | // release adds beside status_detail. |
| 65 | func TestAReportFromANewerAgentDecodesOnAnOlderServer(t *testing.T) { | 65 | func TestAReportFromANewerAgentDecodesOnAnOlderServer(t *testing.T) { |
| 66 | raw, err := proto.Marshal(&pb.ActualVM{ | 66 | raw, err := proto.Marshal(&pb.VMStatus{ |
| 67 | VmId: "vm-1", Power: "off", Phase: "creating", | 67 | VmId: "vm-1", PowerState: "off", Phase: "creating", |
| 68 | StatusDetail: "downloading image 1.2/3.7 GiB", | 68 | StatusDetail: "downloading image 1.2/3.7 GiB", |
| 69 | }) | 69 | }) |
| 70 | require.NoError(t, err) | 70 | require.NoError(t, err) |
| 71 | raw = protowire.AppendTag(raw, 99, protowire.BytesType) | 71 | raw = protowire.AppendTag(raw, 99, protowire.BytesType) |
| 72 | raw = protowire.AppendString(raw, "a field from a release this peer predates") | 72 | raw = protowire.AppendString(raw, "a field from a release this peer predates") |
| 73 | 73 | ||
| 74 | var got pb.ActualVM | 74 | var got pb.VMStatus |
| 75 | require.NoError(t, proto.Unmarshal(raw, &got), "an unknown field must not fail the frame") | 75 | require.NoError(t, proto.Unmarshal(raw, &got), "an unknown field must not fail the frame") |
| 76 | assert.Equal(t, "vm-1", got.GetVmId()) | 76 | assert.Equal(t, "vm-1", got.GetVmId()) |
| 77 | assert.Equal(t, "creating", got.GetPhase()) | 77 | assert.Equal(t, "creating", got.GetPhase()) |
| @@ -84,13 +84,13 @@ func TestAReportFromANewerAgentDecodesOnAnOlderServer(t *testing.T) { | |||
| 84 | // status_detail is empty (nothing to add) and absent counters are NIL — not a | 84 | // status_detail is empty (nothing to add) and absent counters are NIL — not a |
| 85 | // zeroed struct claiming this port has turned nobody away. | 85 | // zeroed struct claiming this port has turned nobody away. |
| 86 | func TestAReportFromAnOlderAgentReadsAsSilence(t *testing.T) { | 86 | func TestAReportFromAnOlderAgentReadsAsSilence(t *testing.T) { |
| 87 | raw, err := proto.Marshal(&pb.ActualStateReport{ | 87 | raw, err := proto.Marshal(&pb.Report{ |
| 88 | Vms: []*pb.ActualVM{{VmId: "vm-1", Power: "off", Phase: "creating"}}, | 88 | Vms: []*pb.VMStatus{{VmId: "vm-1", PowerState: "off", Phase: "creating"}}, |
| 89 | Exposures: []*pb.ExposureActual{{Id: "e-1", State: "active"}}, | 89 | Exposures: []*pb.ExposureStatus{{Id: "e-1", State: "active"}}, |
| 90 | }) | 90 | }) |
| 91 | require.NoError(t, err) | 91 | require.NoError(t, err) |
| 92 | 92 | ||
| 93 | var got pb.ActualStateReport | 93 | var got pb.Report |
| 94 | require.NoError(t, proto.Unmarshal(raw, &got)) | 94 | require.NoError(t, proto.Unmarshal(raw, &got)) |
| 95 | assert.Empty(t, got.GetVms()[0].GetStatusDetail()) | 95 | assert.Empty(t, got.GetVms()[0].GetStatusDetail()) |
| 96 | assert.Nil(t, got.GetExposures()[0].GetSessions(), "an uncounted port must not decode as zeros") | 96 | assert.Nil(t, got.GetExposures()[0].GetSessions(), "an uncounted port must not decode as zeros") |
internal/transport/fieldnumbers_test.go
| Old | New | ||
|---|---|---|---|
| @@ -81,9 +81,9 @@ var wireSchema = map[string]map[string]protoreflect.FieldNumber{ | |||
| 81 | "disk_used_gb": 7, | 81 | "disk_used_gb": 7, |
| 82 | "disk_free_gb": 8, | 82 | "disk_free_gb": 8, |
| 83 | }, | 83 | }, |
| 84 | "ActualVM": { | 84 | "VMStatus": { |
| 85 | "vm_id": 1, | 85 | "vm_id": 1, |
| 86 | "power": 2, | 86 | "power_state": 2, |
| 87 | "phase": 3, | 87 | "phase": 3, |
| 88 | "ip": 4, | 88 | "ip": 4, |
| 89 | "last_error": 5, | 89 | "last_error": 5, |
| @@ -97,7 +97,7 @@ var wireSchema = map[string]map[string]protoreflect.FieldNumber{ | |||
| 97 | "vmspec_json": 3, | 97 | "vmspec_json": 3, |
| 98 | "destroy_at_unix": 4, | 98 | "destroy_at_unix": 4, |
| 99 | }, | 99 | }, |
| 100 | "ActualStateReport": { | 100 | "Report": { |
| 101 | "vms": 1, | 101 | "vms": 1, |
| 102 | "destroyed": 2, | 102 | "destroyed": 2, |
| 103 | "quarantined": 3, | 103 | "quarantined": 3, |
| @@ -109,7 +109,7 @@ var wireSchema = map[string]map[string]protoreflect.FieldNumber{ | |||
| 109 | "exposures": 9, | 109 | "exposures": 9, |
| 110 | "host_uplink_addr": 10, | 110 | "host_uplink_addr": 10, |
| 111 | }, | 111 | }, |
| 112 | "VMDesired": { | 112 | "VMSpec": { |
| 113 | "vm_id": 1, | 113 | "vm_id": 1, |
| 114 | "name": 2, | 114 | "name": 2, |
| 115 | "image_url": 3, | 115 | "image_url": 3, |
| @@ -127,7 +127,7 @@ var wireSchema = map[string]map[string]protoreflect.FieldNumber{ | |||
| 127 | "host_cert_required": 19, | 127 | "host_cert_required": 19, |
| 128 | "network": 20, | 128 | "network": 20, |
| 129 | }, | 129 | }, |
| 130 | "DesiredStateSnapshot": { | 130 | "Snapshot": { |
| 131 | "epoch": 1, | 131 | "epoch": 1, |
| 132 | "vms": 2, | 132 | "vms": 2, |
| 133 | "agent_upgrade": 3, | 133 | "agent_upgrade": 3, |
| @@ -153,14 +153,14 @@ var wireSchema = map[string]map[string]protoreflect.FieldNumber{ | |||
| 153 | "ok": 1, | 153 | "ok": 1, |
| 154 | "error": 2, | 154 | "error": 2, |
| 155 | }, | 155 | }, |
| 156 | "ExposureDesired": { | 156 | "ExposureSpec": { |
| 157 | "id": 1, | 157 | "id": 1, |
| 158 | "vm_id": 2, | 158 | "vm_id": 2, |
| 159 | "guest_port": 3, | 159 | "guest_port": 3, |
| 160 | "host_port": 4, | 160 | "host_port": 4, |
| 161 | "protocol": 5, | 161 | "protocol": 5, |
| 162 | }, | 162 | }, |
| 163 | "ExposureActual": { | 163 | "ExposureStatus": { |
| 164 | "id": 1, | 164 | "id": 1, |
| 165 | "state": 2, | 165 | "state": 2, |
| 166 | "reason": 3, | 166 | "reason": 3, |
proto/eitri/v1/sync.proto
| Old | New | ||
|---|---|---|---|
| @@ -5,7 +5,7 @@ option go_package = "github.com/a73x/eitri/internal/pb;pb"; | |||
| 5 | message AgentMessage { | 5 | message AgentMessage { |
| 6 | oneof msg { | 6 | oneof msg { |
| 7 | Hello hello = 1; | 7 | Hello hello = 1; |
| 8 | ActualStateReport report = 2; | 8 | Report report = 2; |
| 9 | ConsoleOpened console_opened = 3; | 9 | ConsoleOpened console_opened = 3; |
| 10 | TCPOpened tcp_opened = 4; | 10 | TCPOpened tcp_opened = 4; |
| 11 | } | 11 | } |
| @@ -13,7 +13,7 @@ message AgentMessage { | |||
| 13 | 13 | ||
| 14 | message ServerMessage { | 14 | message ServerMessage { |
| 15 | oneof msg { | 15 | oneof msg { |
| 16 | DesiredStateSnapshot snapshot = 1; | 16 | Snapshot snapshot = 1; |
| 17 | ConsoleOpen console_open = 2; | 17 | ConsoleOpen console_open = 2; |
| 18 | TCPOpen tcp_open = 3; | 18 | TCPOpen tcp_open = 3; |
| 19 | } | 19 | } |
| @@ -27,7 +27,7 @@ message Hello { | |||
| 27 | string provisioner = 5; // "cloudhv" | 27 | string provisioner = 5; // "cloudhv" |
| 28 | reserved 6; // was bridge_cidr: the fleet told the host its | 28 | reserved 6; // was bridge_cidr: the fleet told the host its |
| 29 | reserved "bridge_cidr"; // subnet. The host reports it now — see | 29 | reserved "bridge_cidr"; // subnet. The host reports it now — see |
| 30 | // ActualStateReport.guest_cidr. | 30 | // Report.guest_cidr. |
| 31 | uint64 last_seen_epoch = 7; // for the restore runbook | 31 | uint64 last_seen_epoch = 7; // for the restore runbook |
| 32 | Capacity capacity = 8; | 32 | Capacity capacity = 8; |
| 33 | string credential = 9; // Bearer host credential, verified in first frame | 33 | string credential = 9; // Bearer host credential, verified in first frame |
| @@ -70,9 +70,12 @@ message HostMetrics { | |||
| 70 | int64 disk_free_gb = 8; | 70 | int64 disk_free_gb = 8; |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | message ActualVM { | 73 | // VMStatus is the half of a VM its host owns: what it observed, never what it |
| 74 | // was told. One VM, two halves — VMSpec travels down in a Snapshot, VMStatus | ||
| 75 | // travels back in a Report, and the control plane holds both on one row. | ||
| 76 | message VMStatus { | ||
| 74 | string vm_id = 1; | 77 | string vm_id = 1; |
| 75 | string power = 2; // "running"|"stopped" | 78 | string power_state = 2; // "running"|"stopped" as observed; VMSpec.power_state is what was asked for |
| 76 | string phase = 3; // "creating"|"ready"|"failed"|"quarantined" | 79 | string phase = 3; // "creating"|"ready"|"failed"|"quarantined" |
| 77 | string ip = 4; // the guest's address on its host's NAT underlay — every guest has one, from boot | 80 | string ip = 4; // the guest's address on its host's NAT underlay — every guest has one, from boot |
| 78 | string last_error = 5; | 81 | string last_error = 5; |
| @@ -92,7 +95,7 @@ message ActualVM { | |||
| 92 | string status_detail = 7; | 95 | string status_detail = 7; |
| 93 | // The address the site's DHCP server granted this guest on its SECOND NIC, | 96 | // The address the site's DHCP server granted this guest on its SECOND NIC, |
| 94 | // the one attached to the named host network its spec asked for (see | 97 | // the one attached to the named host network its spec asked for (see |
| 95 | // VMDesired.network). Empty for the guests that have no such NIC — the | 98 | // VMSpec.network). Empty for the guests that have no such NIC — the |
| 96 | // majority — and for one DHCP round-trip after a networked guest boots, | 99 | // majority — and for one DHCP round-trip after a networked guest boots, |
| 97 | // because the host learns it by watching the exchange rather than granting | 100 | // because the host learns it by watching the exchange rather than granting |
| 98 | // it. Never a substitute for ip: that one is known before the guest is even | 101 | // it. Never a substitute for ip: that one is known before the guest is even |
| @@ -108,8 +111,10 @@ message QuarantinedVM { | |||
| 108 | int64 destroy_at_unix = 4; | 111 | int64 destroy_at_unix = 4; |
| 109 | } | 112 | } |
| 110 | 113 | ||
| 111 | message ActualStateReport { | 114 | // Report is what one host observes, level-triggered: the status of every VM |
| 112 | repeated ActualVM vms = 1; | 115 | // and exposure it holds, and the host's own numbers. |
| 116 | message Report { | ||
| 117 | repeated VMStatus vms = 1; | ||
| 113 | // LEVEL-TRIGGERED destroy ack: ALL tombstoned vm_ids with no local | 118 | // LEVEL-TRIGGERED destroy ack: ALL tombstoned vm_ids with no local |
| 114 | // record/disk/process, repeated every report until hard-deleted server-side. | 119 | // record/disk/process, repeated every report until hard-deleted server-side. |
| 115 | repeated string destroyed = 2; | 120 | repeated string destroyed = 2; |
| @@ -127,7 +132,7 @@ message ActualStateReport { | |||
| 127 | // knowable at connect time at all. Capacity is in both messages for the same | 132 | // knowable at connect time at all. Capacity is in both messages for the same |
| 128 | // reason: an opening value, then the ongoing truth. | 133 | // reason: an opening value, then the ongoing truth. |
| 129 | string guest_cidr = 8; | 134 | string guest_cidr = 8; |
| 130 | repeated ExposureActual exposures = 9; | 135 | repeated ExposureStatus exposures = 9; |
| 131 | // The address this host presents on the network it reaches the control | 136 | // The address this host presents on the network it reaches the control |
| 132 | // plane over — the address an operator dials to reach a published guest | 137 | // plane over — the address an operator dials to reach a published guest |
| 133 | // port. Empty means "not yet known", never "no address": a host that cannot | 138 | // port. Empty means "not yet known", never "no address": a host that cannot |
| @@ -137,7 +142,8 @@ message ActualStateReport { | |||
| 137 | string host_uplink_addr = 10; | 142 | string host_uplink_addr = 10; |
| 138 | } | 143 | } |
| 139 | 144 | ||
| 140 | message VMDesired { | 145 | // VMSpec is the half of a VM the control plane owns. See VMStatus. |
| 146 | message VMSpec { | ||
| 141 | string vm_id = 1; | 147 | string vm_id = 1; |
| 142 | string name = 2; | 148 | string name = 2; |
| 143 | string image_url = 3; | 149 | string image_url = 3; |
| @@ -167,7 +173,7 @@ message VMDesired { | |||
| 167 | reserved 16; | 173 | reserved 16; |
| 168 | reserved "ssh_host_key_pem"; | 174 | reserved "ssh_host_key_pem"; |
| 169 | // The certificate the control plane signed for the public key the host | 175 | // The certificate the control plane signed for the public key the host |
| 170 | // reported in ActualVM.ssh_host_pubkey (authorized_keys form); seed installs | 176 | // reported in VMStatus.ssh_host_pubkey (authorized_keys form); seed installs |
| 171 | // it as /etc/ssh/ssh_host_ed25519_key-cert.pub. Empty until the round trip | 177 | // it as /etc/ssh/ssh_host_ed25519_key-cert.pub. Empty until the round trip |
| 172 | // completes, and forever when the jump gate is off. | 178 | // completes, and forever when the jump gate is off. |
| 173 | string ssh_host_cert = 17; | 179 | string ssh_host_cert = 17; |
| @@ -186,11 +192,12 @@ message VMDesired { | |||
| 186 | string network = 20; | 192 | string network = 20; |
| 187 | } | 193 | } |
| 188 | 194 | ||
| 189 | message DesiredStateSnapshot { | 195 | // Snapshot is the FULL spec for one host; the agent converges toward it. |
| 196 | message Snapshot { | ||
| 190 | uint64 epoch = 1; // agents refuse epoch < highest seen | 197 | uint64 epoch = 1; // agents refuse epoch < highest seen |
| 191 | repeated VMDesired vms = 2; // FULL set for this host, including tombstoned | 198 | repeated VMSpec vms = 2; // FULL set for this host, including tombstoned |
| 192 | AgentUpgrade agent_upgrade = 3; // optional operator-initiated agent self-upgrade | 199 | AgentUpgrade agent_upgrade = 3; // optional operator-initiated agent self-upgrade |
| 193 | repeated ExposureDesired exposures = 4; // FULL set for this host | 200 | repeated ExposureSpec exposures = 4; // FULL set for this host |
| 194 | } | 201 | } |
| 195 | 202 | ||
| 196 | // AgentUpgrade asks the agent to replace its own binary: download url, verify | 203 | // AgentUpgrade asks the agent to replace its own binary: download url, verify |
| @@ -234,13 +241,13 @@ message TCPOpened { | |||
| 234 | string error = 2; | 241 | string error = 2; |
| 235 | } | 242 | } |
| 236 | 243 | ||
| 237 | // ExposureDesired is one published guest port a host should be serving: bind | 244 | // ExposureSpec is one published guest port a host should be serving: bind |
| 238 | // host_port on the host, pipe every accepted connection (or every datagram) | 245 | // host_port on the host, pipe every accepted connection (or every datagram) |
| 239 | // to guest_port inside the guest. It rides the snapshot at TOP LEVEL rather | 246 | // to guest_port inside the guest. It rides the snapshot at TOP LEVEL rather |
| 240 | // than nested in VMDesired, because exposures are their own objects converging | 247 | // than nested in VMSpec, because exposures are their own objects converging |
| 241 | // on their own cadence — an exposure can be created while its VM is still | 248 | // on their own cadence — an exposure can be created while its VM is still |
| 242 | // imaging, and it binds immediately. | 249 | // imaging, and it binds immediately. |
| 243 | message ExposureDesired { | 250 | message ExposureSpec { |
| 244 | string id = 1; | 251 | string id = 1; |
| 245 | string vm_id = 2; | 252 | string vm_id = 2; |
| 246 | uint32 guest_port = 3; | 253 | uint32 guest_port = 3; |
| @@ -248,11 +255,11 @@ message ExposureDesired { | |||
| 248 | string protocol = 5; // "tcp"|"udp" | 255 | string protocol = 5; // "tcp"|"udp" |
| 249 | } | 256 | } |
| 250 | 257 | ||
| 251 | // ExposureActual is one exposure's state as its host observes it: "active" | 258 | // ExposureStatus is one exposure's state as its host observes it: "active" |
| 252 | // once the host socket is bound, "failed" with the OS error otherwise. | 259 | // once the host socket is bound, "failed" with the OS error otherwise. |
| 253 | // "active" means the HOST half of the pipe exists — whether anything answers | 260 | // "active" means the HOST half of the pipe exists — whether anything answers |
| 254 | // inside the guest is the guest's half, and this does not pretend otherwise. | 261 | // inside the guest is the guest's half, and this does not pretend otherwise. |
| 255 | message ExposureActual { | 262 | message ExposureStatus { |
| 256 | string id = 1; | 263 | string id = 1; |
| 257 | string state = 2; // "active"|"failed" | 264 | string state = 2; // "active"|"failed" |
| 258 | string reason = 3; // the OS error, when failed; a bound port's ongoing trouble otherwise | 265 | string reason = 3; // the OS error, when failed; a bound port's ongoing trouble otherwise |