internal/agent/reconcile/minversion_test.go
Ref: Size: 3.0 KiB History
package reconcile
import (
"context"
"testing"
"github.com/stretchr/testify/require"
)
// An agent below the snapshot's floor must not materialise anything from it:
// every live VM reports failed with a reason naming both versions, nothing is
// dispatched, and the epoch is still accepted so the report is not fenced.
func TestStepRefusesSnapshotAboveAgentVersion(t *testing.T) {
f := setup(t)
f.eng.AgentVersion = "v0.0.6"
s := snap(1, vm("vm1"))
s.MinAgentVersion = "v0.0.7"
rep := f.eng.Step(context.Background(), s)
// Settle any worker the guard failed to prevent, so the assertion below
// reads a finished tick rather than racing one.
f.eng.manager().waitIdle()
require.False(t, rep.FenceViolation)
require.EqualValues(t, 1, rep.LastSeenEpoch)
require.Len(t, rep.Vms, 1)
require.Equal(t, "failed", rep.Vms[0].Phase)
require.Contains(t, rep.Vms[0].LastError, "v0.0.7")
require.Contains(t, rep.Vms[0].LastError, "v0.0.6")
require.Empty(t, f.prov.prepared, "nothing is materialised from a snapshot the agent cannot read")
}
// The refusal asserts nothing it did not look at. It runs before any record is
// read, so it cannot know whether a guest is running — and the control plane
// PERSISTS what a report claims. An empty power state is the wire's "no
// observation"; "stopped" would be a fleet-wide lie about power, showing an
// operator healthy VMs as dark because their host is one release behind.
func TestRefusedSnapshotClaimsNoPowerState(t *testing.T) {
f := setup(t)
f.eng.AgentVersion = "v0.0.6"
s := snap(1, vm("vm1"))
s.MinAgentVersion = "v0.0.7"
rep := f.eng.Step(context.Background(), s)
f.eng.manager().waitIdle()
require.Len(t, rep.Vms, 1)
require.Empty(t, rep.Vms[0].PowerState, "a refusal that inspected nothing must claim nothing about power")
}
// A tombstoned VM is already deleted. Hanging an upgrade-the-agent error on it
// would leave a spurious failure on a VM on its way out, which no operator can
// clear — so the refusal passes over it entirely.
func TestRefusedSnapshotSkipsTombstonedVMs(t *testing.T) {
f := setup(t)
f.eng.AgentVersion = "v0.0.6"
s := snap(1, vm("vm1"), tombstoned(vm("vm2")))
s.MinAgentVersion = "v0.0.7"
rep := f.eng.Step(context.Background(), s)
f.eng.manager().waitIdle()
require.Len(t, rep.Vms, 1, "the tombstoned VM earns no row")
require.Equal(t, "vm1", rep.Vms[0].VmId)
}
// An unstamped agent ("dev") is never floored: it is the build every developer
// runs, and version.Less leaves unparsable versions unordered. The VM must be
// materialised exactly as it would be with no floor at all — asserting on the
// work done, not merely on an empty report, which an unreached snapshot would
// also produce.
func TestStepDoesNotFloorUnstampedAgent(t *testing.T) {
f := setup(t)
f.eng.AgentVersion = "dev"
s := snap(1, vm("vm1"))
s.MinAgentVersion = "v0.0.7"
rep := f.eng.Step(context.Background(), s)
f.eng.manager().waitIdle()
require.False(t, rep.FenceViolation)
require.Equal(t, []string{"vm1"}, f.prov.prepared, "an unstamped agent converges the snapshot as if it named no floor")
}