Skip to content

Commit 6d9c367

Browse files
kvapsclaude
andcommitted
test(rest): pin autoplace EVICTED-skip behaviour
Locks in the new candidatePools filter: an EVICTED node is excluded from autoplace, even when it has a matching pool. Without this test the regression is silent — placement still works, it just re-uses the node the operator wanted drained. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent 1670522 commit 6d9c367

1 file changed

Lines changed: 62 additions & 0 deletions

File tree

pkg/rest/autoplace_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,68 @@ func TestAutoplacePrefersFreestPool(t *testing.T) {
215215
}
216216
}
217217

218+
// TestAutoplaceSkipsEvictedNodes: a node flagged EVICTED is excluded
219+
// from the candidate pool so autoplace does not undo an eviction
220+
// the operator just initiated.
221+
func TestAutoplaceSkipsEvictedNodes(t *testing.T) {
222+
st := store.NewInMemory()
223+
ctx := t.Context()
224+
225+
if err := st.ResourceDefinitions().Create(ctx, &apiv1.ResourceDefinition{Name: "pvc-1"}); err != nil {
226+
t.Fatalf("seed RD: %v", err)
227+
}
228+
229+
for _, n := range []string{"n1", "n2", "n3"} {
230+
if err := st.Nodes().Create(ctx, &apiv1.Node{Name: n, Type: apiv1.NodeTypeSatellite}); err != nil {
231+
t.Fatalf("seed node %s: %v", n, err)
232+
}
233+
if err := st.StoragePools().Create(ctx, &apiv1.StoragePool{
234+
StoragePoolName: "pool",
235+
NodeName: n,
236+
ProviderKind: apiv1.StoragePoolKindLVMThin,
237+
}); err != nil {
238+
t.Fatalf("seed pool %s: %v", n, err)
239+
}
240+
}
241+
242+
if err := st.Nodes().Update(ctx, &apiv1.Node{
243+
Name: "n2",
244+
Type: apiv1.NodeTypeSatellite,
245+
Flags: []string{apiv1.NodeFlagEvicted},
246+
}); err != nil {
247+
t.Fatalf("evict n2: %v", err)
248+
}
249+
250+
base, stop := startServerWithStore(t, st)
251+
defer stop()
252+
253+
body, _ := json.Marshal(apiv1.AutoPlaceRequest{
254+
SelectFilter: apiv1.AutoSelectFilter{PlaceCount: 2, StoragePool: "pool"},
255+
})
256+
257+
resp := httpPost(t, base+"/v1/resource-definitions/pvc-1/autoplace", body)
258+
_ = resp.Body.Close()
259+
260+
if resp.StatusCode != http.StatusOK {
261+
t.Fatalf("status: got %d, want 200", resp.StatusCode)
262+
}
263+
264+
got, err := st.Resources().ListByDefinition(ctx, "pvc-1")
265+
if err != nil {
266+
t.Fatalf("list: %v", err)
267+
}
268+
269+
for _, r := range got {
270+
if r.NodeName == "n2" {
271+
t.Errorf("autoplace landed on EVICTED node n2; replicas=%v", got)
272+
}
273+
}
274+
275+
if len(got) != 2 {
276+
t.Errorf("placed: got %d, want 2", len(got))
277+
}
278+
}
279+
218280
// TestResourceCreateAndDelete: explicit single-replica placement via REST.
219281
func TestResourceCreateAndDelete(t *testing.T) {
220282
st := store.NewInMemory()

0 commit comments

Comments
 (0)