diff --git a/.changelog/23894.txt b/.changelog/23894.txt new file mode 100644 index 000000000..47ac8af7c --- /dev/null +++ b/.changelog/23894.txt @@ -0,0 +1,3 @@ +```release-note:improvement +quotas (Enterprise): Added the possibility to set device count limits +``` diff --git a/command/quota_apply.go b/command/quota_apply.go index bdc70e982..84d23d931 100644 --- a/command/quota_apply.go +++ b/command/quota_apply.go @@ -300,7 +300,15 @@ func parseQuotaResource(result *api.Resources, list *ast.ObjectList) error { } func parseDeviceResource(result *[]*api.RequestedDevice, list *ast.ObjectList) error { - for _, o := range list.Elem().Items { + for idx, o := range list.Items { + if l := len(o.Keys); l == 0 { + return multierror.Prefix(fmt.Errorf("missing device name"), fmt.Sprintf("resources, device[%d]->", idx)) + } else if l > 1 { + return multierror.Prefix(fmt.Errorf("only one name may be specified"), fmt.Sprintf("resources, device[%d]->", idx)) + } + + name := o.Keys[0].Token.Value().(string) + // Check for invalid keys valid := []string{ "name", @@ -310,12 +318,15 @@ func parseDeviceResource(result *[]*api.RequestedDevice, list *ast.ObjectList) e return err } + // Set the name + var device api.RequestedDevice + device.Name = name + var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } - var device api.RequestedDevice if err := mapstructure.WeakDecode(m, &device); err != nil { return err } diff --git a/command/quota_init.go b/command/quota_init.go index 94df329c3..2a7c46ebd 100644 --- a/command/quota_init.go +++ b/command/quota_init.go @@ -124,7 +124,7 @@ limit { memory = 1000 memory_max = 1000 device "nvidia/gpu/1080ti" { - count = 1, + count = 1 } } variables_limit = 1000 diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 9c2abf6cd..2c521be19 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -2713,6 +2713,21 @@ func (r *Resources) Add(delta *Resources) { r.Networks[idx].Add(n) } } + + if r.Devices == nil && delta.Devices != nil { + r.Devices = make(ResourceDevices, 0) + } + for _, dd := range delta.Devices { + idx := slices.IndexFunc(r.Devices, func(d *RequestedDevice) bool { return d.Name == dd.Name }) + + // means it's not found + if idx < 0 { + r.Devices = append(r.Devices, dd) + continue + } + + r.Devices[idx].Count += dd.Count + } } // GoString returns the string representation of the Resources struct. diff --git a/nomad/structs/structs_test.go b/nomad/structs/structs_test.go index 8406aa03b..79da3505e 100644 --- a/nomad/structs/structs_test.go +++ b/nomad/structs/structs_test.go @@ -3821,6 +3821,12 @@ func TestResource_Add(t *testing.T) { ReservedPorts: []Port{{"web", 80, 0, ""}}, }, }, + Devices: []*RequestedDevice{ + { + Name: "nvidia/gpu/Tesla M60", + Count: 1, + }, + }, } r1.Add(r2) @@ -3838,6 +3844,12 @@ func TestResource_Add(t *testing.T) { ReservedPorts: []Port{{"ssh", 22, 0, ""}, {"web", 80, 0, ""}}, }, }, + Devices: []*RequestedDevice{ + { + Name: "nvidia/gpu/Tesla M60", + Count: 1, + }, + }, } must.Eq(t, expect, r1)