From 41e1e2a1bfbaec36eb666c82a5eaf7d797180fc3 Mon Sep 17 00:00:00 2001 From: breken-ai <312387581+breken-ai@users.noreply.github.com> Date: Fri, 25 Sep 2026 18:26:40 -0700 Subject: [PATCH] manager/controlapi: reject named generic resource reservations validateResources called genericresource.ValidateTask but returned nil when it failed, so a service reserving a named generic resource (for example NamedResourceSpec gpu=UUID1) was accepted. Tasks can only claim discrete generic resources, so the scheduler's ResourceFilter rejects every node for such a task and it stays pending forever with "insufficient resources", even on nodes that advertise that resource. Return the validation error as InvalidArgument. Assisted-By: Claude Signed-off-by: breken-ai <312387581+breken-ai@users.noreply.github.com> --- manager/controlapi/service.go | 2 +- manager/controlapi/service_test.go | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/manager/controlapi/service.go b/manager/controlapi/service.go index ef79d776cf..cc9b249bf4 100644 --- a/manager/controlapi/service.go +++ b/manager/controlapi/service.go @@ -45,7 +45,7 @@ func validateResources(r *api.Resources) error { return status.Errorf(codes.InvalidArgument, "invalid memory value %d: Must be at least 4MiB", r.MemoryBytes) } if err := genericresource.ValidateTask(r); err != nil { - return nil + return status.Error(codes.InvalidArgument, err.Error()) } return nil } diff --git a/manager/controlapi/service_test.go b/manager/controlapi/service_test.go index 737dc7f3b7..a71eec0621 100644 --- a/manager/controlapi/service_test.go +++ b/manager/controlapi/service_test.go @@ -10,6 +10,7 @@ import ( gogotypes "github.com/gogo/protobuf/types" "github.com/moby/swarmkit/v2/api" + "github.com/moby/swarmkit/v2/api/genericresource" "github.com/moby/swarmkit/v2/identity" "github.com/moby/swarmkit/v2/manager/state/store" "github.com/moby/swarmkit/v2/testutils" @@ -174,11 +175,14 @@ func TestValidateResources(t *testing.T) { bad := []*api.Resources{ {MemoryBytes: 1}, {NanoCPUs: 42}, + // tasks can only request discrete generic resources + {Generic: genericresource.NewSet("gpu", "UUID1")}, } good := []*api.Resources{ {MemoryBytes: 4096 * 1024 * 1024}, {NanoCPUs: 1e9}, + {Generic: []*api.GenericResource{genericresource.NewDiscrete("gpu", 2)}}, } for _, b := range bad { @@ -196,6 +200,7 @@ func TestValidateResourceRequirements(t *testing.T) { bad := []*api.ResourceRequirements{ {Limits: &api.Resources{MemoryBytes: 1}}, {Reservations: &api.Resources{MemoryBytes: 1}}, + {Reservations: &api.Resources{Generic: genericresource.NewSet("gpu", "UUID1")}}, } good := []*api.ResourceRequirements{ {Limits: &api.Resources{NanoCPUs: 1e9}},