From 7218685580f78b86b605669c0c6171674bbb2e9a Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Fri, 4 Oct 2024 15:42:08 +0200 Subject: [PATCH] introduce service.gpus Signed-off-by: Nicolas De Loof --- loader/loader_test.go | 26 +++ schema/compose-spec.json | 18 ++ transform/canonical.go | 1 - transform/defaults.go | 2 + transform/devices.go | 22 +-- types/derived.gen.go | 398 ++++++++++++++++++++------------------- types/types.go | 1 + validation/validation.go | 12 ++ 8 files changed, 276 insertions(+), 204 deletions(-) diff --git a/loader/loader_test.go b/loader/loader_test.go index 554ae9f6..ec686860 100644 --- a/loader/loader_test.go +++ b/loader/loader_test.go @@ -2224,6 +2224,32 @@ services: assert.ErrorContains(t, err, `capabilities is required`) } +func TestServiceGpus(t *testing.T) { + p, err := loadYAML(` +name: service-gpus +services: + test: + image: redis:alpine + gpus: + - driver: nvidia + - driver: 3dfx + device_ids: ["voodoo2"] + capabilities: ["directX"] +`) + assert.NilError(t, err) + assert.DeepEqual(t, p.Services["test"].Gpus, []types.DeviceRequest{ + { + Driver: "nvidia", + Count: -1, + }, + { + Capabilities: []string{"directX"}, + Driver: "3dfx", + IDs: []string{"voodoo2"}, + }, + }) +} + func TestServicePullPolicy(t *testing.T) { actual, err := loadYAML(` name: service-pull-policy diff --git a/schema/compose-spec.json b/schema/compose-spec.json index 95326f31..8fdda4ac 100644 --- a/schema/compose-spec.json +++ b/schema/compose-spec.json @@ -267,6 +267,7 @@ }, "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "extra_hosts": {"$ref": "#/definitions/extra_hosts"}, + "gpus": {"$ref": "#/definitions/gpus"}, "group_add": { "type": "array", "items": { @@ -651,6 +652,23 @@ } }, + "gpus": { + "id": "#/definitions/gpus", + "type": "array", + "items": { + "type": "object", + "properties": { + "capabilities": {"$ref": "#/definitions/list_of_strings"}, + "count": {"type": ["string", "integer"]}, + "device_ids": {"$ref": "#/definitions/list_of_strings"}, + "driver":{"type": "string"}, + "options":{"$ref": "#/definitions/list_or_dict"} + }, + "additionalProperties": false, + "patternProperties": {"^x-": {}} + } + }, + "include": { "id": "#/definitions/include", "oneOf": [ diff --git a/transform/canonical.go b/transform/canonical.go index fa844e76..a248b4be 100644 --- a/transform/canonical.go +++ b/transform/canonical.go @@ -29,7 +29,6 @@ func init() { transformers["services.*.build.secrets.*"] = transformFileMount transformers["services.*.build.additional_contexts"] = transformKeyValue transformers["services.*.depends_on"] = transformDependsOn - transformers["services.*.deploy.resources.reservations.devices.*"] = transformDeviceRequest transformers["services.*.env_file"] = transformEnvFile transformers["services.*.extends"] = transformExtends transformers["services.*.networks"] = transformServiceNetworks diff --git a/transform/defaults.go b/transform/defaults.go index 96693c65..041a6897 100644 --- a/transform/defaults.go +++ b/transform/defaults.go @@ -26,6 +26,8 @@ func init() { defaultValues["services.*.build"] = defaultBuildContext defaultValues["services.*.secrets.*"] = defaultSecretMount defaultValues["services.*.ports.*"] = portDefaults + defaultValues["services.*.deploy.resources.reservations.devices.*"] = deviceRequestDefaults + defaultValues["services.*.gpus.*"] = deviceRequestDefaults } // SetDefaultValues transforms a compose model to set default values to missing attributes diff --git a/transform/devices.go b/transform/devices.go index 558d47ed..3ce7fa00 100644 --- a/transform/devices.go +++ b/transform/devices.go @@ -22,19 +22,15 @@ import ( "github.com/compose-spec/compose-go/v2/tree" ) -func transformDeviceRequest(data any, p tree.Path, ignoreParseError bool) (any, error) { - switch v := data.(type) { - case map[string]any: - _, hasCount := v["count"] - _, hasIds := v["device_ids"] - if hasCount && hasIds { - return nil, fmt.Errorf(`%s: "count" and "device_ids" attributes are exclusive`, p) - } - if !hasCount && !hasIds { - v["count"] = "all" - } - return transformMapping(v, p, ignoreParseError) - default: +func deviceRequestDefaults(data any, p tree.Path, _ bool) (any, error) { + v, ok := data.(map[string]any) + if !ok { return data, fmt.Errorf("%s: invalid type %T for device request", p, v) } + _, hasCount := v["count"] + _, hasIds := v["device_ids"] + if !hasCount && !hasIds { + v["count"] = "all" + } + return v, nil } diff --git a/types/derived.gen.go b/types/derived.gen.go index bc5b5d9b..31323d08 100644 --- a/types/derived.gen.go +++ b/types/derived.gen.go @@ -445,12 +445,30 @@ func deriveDeepCopyService(dst, src *ServiceConfig) { } copy(dst.GroupAdd, src.GroupAdd) } + if src.Gpus == nil { + dst.Gpus = nil + } else { + if dst.Gpus != nil { + if len(src.Gpus) > len(dst.Gpus) { + if cap(dst.Gpus) >= len(src.Gpus) { + dst.Gpus = (dst.Gpus)[:len(src.Gpus)] + } else { + dst.Gpus = make([]DeviceRequest, len(src.Gpus)) + } + } else if len(src.Gpus) < len(dst.Gpus) { + dst.Gpus = (dst.Gpus)[:len(src.Gpus)] + } + } else { + dst.Gpus = make([]DeviceRequest, len(src.Gpus)) + } + deriveDeepCopy_15(dst.Gpus, src.Gpus) + } dst.Hostname = src.Hostname if src.HealthCheck == nil { dst.HealthCheck = nil } else { dst.HealthCheck = new(HealthCheckConfig) - deriveDeepCopy_15(dst.HealthCheck, src.HealthCheck) + deriveDeepCopy_16(dst.HealthCheck, src.HealthCheck) } dst.Image = src.Image if src.Init == nil { @@ -495,7 +513,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) { dst.Logging = nil } else { dst.Logging = new(LoggingConfig) - deriveDeepCopy_16(dst.Logging, src.Logging) + deriveDeepCopy_17(dst.Logging, src.Logging) } dst.LogDriver = src.LogDriver if src.LogOpt != nil { @@ -513,7 +531,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) { dst.NetworkMode = src.NetworkMode if src.Networks != nil { dst.Networks = make(map[string]*ServiceNetworkConfig, len(src.Networks)) - deriveDeepCopy_17(dst.Networks, src.Networks) + deriveDeepCopy_18(dst.Networks, src.Networks) } else { dst.Networks = nil } @@ -538,7 +556,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) { } else { dst.Ports = make([]ServicePortConfig, len(src.Ports)) } - deriveDeepCopy_18(dst.Ports, src.Ports) + deriveDeepCopy_19(dst.Ports, src.Ports) } dst.Privileged = src.Privileged dst.PullPolicy = src.PullPolicy @@ -567,7 +585,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) { } else { dst.Secrets = make([]ServiceSecretConfig, len(src.Secrets)) } - deriveDeepCopy_19(dst.Secrets, src.Secrets) + deriveDeepCopy_20(dst.Secrets, src.Secrets) } if src.SecurityOpt == nil { dst.SecurityOpt = nil @@ -629,7 +647,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) { dst.Tty = src.Tty if src.Ulimits != nil { dst.Ulimits = make(map[string]*UlimitsConfig, len(src.Ulimits)) - deriveDeepCopy_20(dst.Ulimits, src.Ulimits) + deriveDeepCopy_21(dst.Ulimits, src.Ulimits) } else { dst.Ulimits = nil } @@ -653,7 +671,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) { } else { dst.Volumes = make([]ServiceVolumeConfig, len(src.Volumes)) } - deriveDeepCopy_21(dst.Volumes, src.Volumes) + deriveDeepCopy_22(dst.Volumes, src.Volumes) } if src.VolumesFrom == nil { dst.VolumesFrom = nil @@ -690,7 +708,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) { } else { dst.PostStart = make([]ServiceHook, len(src.PostStart)) } - deriveDeepCopy_22(dst.PostStart, src.PostStart) + deriveDeepCopy_23(dst.PostStart, src.PostStart) } if src.PreStop == nil { dst.PreStop = nil @@ -708,7 +726,7 @@ func deriveDeepCopyService(dst, src *ServiceConfig) { } else { dst.PreStop = make([]ServiceHook, len(src.PreStop)) } - deriveDeepCopy_22(dst.PreStop, src.PreStop) + deriveDeepCopy_23(dst.PreStop, src.PreStop) } if src.Extensions != nil { dst.Extensions = make(map[string]any, len(src.Extensions)) @@ -734,7 +752,7 @@ func deriveDeepCopy_(dst, src map[string]NetworkConfig) { for src_key, src_value := range src { func() { field := new(NetworkConfig) - deriveDeepCopy_23(field, &src_value) + deriveDeepCopy_24(field, &src_value) dst[src_key] = *field }() } @@ -745,7 +763,7 @@ func deriveDeepCopy_1(dst, src map[string]VolumeConfig) { for src_key, src_value := range src { func() { field := new(VolumeConfig) - deriveDeepCopy_24(field, &src_value) + deriveDeepCopy_25(field, &src_value) dst[src_key] = *field }() } @@ -756,7 +774,7 @@ func deriveDeepCopy_2(dst, src map[string]SecretConfig) { for src_key, src_value := range src { func() { field := new(SecretConfig) - deriveDeepCopy_25(field, &src_value) + deriveDeepCopy_26(field, &src_value) dst[src_key] = *field }() } @@ -767,7 +785,7 @@ func deriveDeepCopy_3(dst, src map[string]ConfigObjConfig) { for src_key, src_value := range src { func() { field := new(ConfigObjConfig) - deriveDeepCopy_26(field, &src_value) + deriveDeepCopy_27(field, &src_value) dst[src_key] = *field }() } @@ -902,7 +920,7 @@ func deriveDeepCopy_5(dst, src *BuildConfig) { } else { dst.Secrets = make([]ServiceSecretConfig, len(src.Secrets)) } - deriveDeepCopy_19(dst.Secrets, src.Secrets) + deriveDeepCopy_20(dst.Secrets, src.Secrets) } dst.ShmSize = src.ShmSize if src.Tags == nil { @@ -925,7 +943,7 @@ func deriveDeepCopy_5(dst, src *BuildConfig) { } if src.Ulimits != nil { dst.Ulimits = make(map[string]*UlimitsConfig, len(src.Ulimits)) - deriveDeepCopy_20(dst.Ulimits, src.Ulimits) + deriveDeepCopy_21(dst.Ulimits, src.Ulimits) } else { dst.Ulimits = nil } @@ -974,7 +992,7 @@ func deriveDeepCopy_6(dst, src *DevelopConfig) { } else { dst.Watch = make([]Trigger, len(src.Watch)) } - deriveDeepCopy_27(dst.Watch, src.Watch) + deriveDeepCopy_28(dst.Watch, src.Watch) } if src.Extensions != nil { dst.Extensions = make(map[string]any, len(src.Extensions)) @@ -1003,7 +1021,7 @@ func deriveDeepCopy_7(dst, src *BlkioConfig) { } else { dst.WeightDevice = make([]WeightDevice, len(src.WeightDevice)) } - deriveDeepCopy_28(dst.WeightDevice, src.WeightDevice) + deriveDeepCopy_29(dst.WeightDevice, src.WeightDevice) } if src.DeviceReadBps == nil { dst.DeviceReadBps = nil @@ -1021,7 +1039,7 @@ func deriveDeepCopy_7(dst, src *BlkioConfig) { } else { dst.DeviceReadBps = make([]ThrottleDevice, len(src.DeviceReadBps)) } - deriveDeepCopy_29(dst.DeviceReadBps, src.DeviceReadBps) + deriveDeepCopy_30(dst.DeviceReadBps, src.DeviceReadBps) } if src.DeviceReadIOps == nil { dst.DeviceReadIOps = nil @@ -1039,7 +1057,7 @@ func deriveDeepCopy_7(dst, src *BlkioConfig) { } else { dst.DeviceReadIOps = make([]ThrottleDevice, len(src.DeviceReadIOps)) } - deriveDeepCopy_29(dst.DeviceReadIOps, src.DeviceReadIOps) + deriveDeepCopy_30(dst.DeviceReadIOps, src.DeviceReadIOps) } if src.DeviceWriteBps == nil { dst.DeviceWriteBps = nil @@ -1057,7 +1075,7 @@ func deriveDeepCopy_7(dst, src *BlkioConfig) { } else { dst.DeviceWriteBps = make([]ThrottleDevice, len(src.DeviceWriteBps)) } - deriveDeepCopy_29(dst.DeviceWriteBps, src.DeviceWriteBps) + deriveDeepCopy_30(dst.DeviceWriteBps, src.DeviceWriteBps) } if src.DeviceWriteIOps == nil { dst.DeviceWriteIOps = nil @@ -1075,7 +1093,7 @@ func deriveDeepCopy_7(dst, src *BlkioConfig) { } else { dst.DeviceWriteIOps = make([]ThrottleDevice, len(src.DeviceWriteIOps)) } - deriveDeepCopy_29(dst.DeviceWriteIOps, src.DeviceWriteIOps) + deriveDeepCopy_30(dst.DeviceWriteIOps, src.DeviceWriteIOps) } if src.Extensions != nil { dst.Extensions = make(map[string]any, len(src.Extensions)) @@ -1090,7 +1108,7 @@ func deriveDeepCopy_8(dst, src []ServiceConfigObjConfig) { for src_i, src_value := range src { func() { field := new(ServiceConfigObjConfig) - deriveDeepCopy_30(field, &src_value) + deriveDeepCopy_31(field, &src_value) dst[src_i] = *field }() } @@ -1114,7 +1132,7 @@ func deriveDeepCopy_10(dst, src map[string]ServiceDependency) { for src_key, src_value := range src { func() { field := new(ServiceDependency) - deriveDeepCopy_31(field, &src_value) + deriveDeepCopy_32(field, &src_value) dst[src_key] = *field }() } @@ -1139,28 +1157,28 @@ func deriveDeepCopy_11(dst, src *DeployConfig) { dst.UpdateConfig = nil } else { dst.UpdateConfig = new(UpdateConfig) - deriveDeepCopy_32(dst.UpdateConfig, src.UpdateConfig) + deriveDeepCopy_33(dst.UpdateConfig, src.UpdateConfig) } if src.RollbackConfig == nil { dst.RollbackConfig = nil } else { dst.RollbackConfig = new(UpdateConfig) - deriveDeepCopy_32(dst.RollbackConfig, src.RollbackConfig) + deriveDeepCopy_33(dst.RollbackConfig, src.RollbackConfig) } func() { field := new(Resources) - deriveDeepCopy_33(field, &src.Resources) + deriveDeepCopy_34(field, &src.Resources) dst.Resources = *field }() if src.RestartPolicy == nil { dst.RestartPolicy = nil } else { dst.RestartPolicy = new(RestartPolicy) - deriveDeepCopy_34(dst.RestartPolicy, src.RestartPolicy) + deriveDeepCopy_35(dst.RestartPolicy, src.RestartPolicy) } func() { field := new(Placement) - deriveDeepCopy_35(field, &src.Placement) + deriveDeepCopy_36(field, &src.Placement) dst.Placement = *field }() dst.EndpointMode = src.EndpointMode @@ -1177,7 +1195,7 @@ func deriveDeepCopy_12(dst, src []DeviceMapping) { for src_i, src_value := range src { func() { field := new(DeviceMapping) - deriveDeepCopy_36(field, &src_value) + deriveDeepCopy_37(field, &src_value) dst[src_i] = *field }() } @@ -1226,7 +1244,18 @@ func deriveDeepCopy_14(dst, src map[string][]string) { } // deriveDeepCopy_15 recursively copies the contents of src into dst. -func deriveDeepCopy_15(dst, src *HealthCheckConfig) { +func deriveDeepCopy_15(dst, src []DeviceRequest) { + for src_i, src_value := range src { + func() { + field := new(DeviceRequest) + deriveDeepCopy_38(field, &src_value) + dst[src_i] = *field + }() + } +} + +// deriveDeepCopy_16 recursively copies the contents of src into dst. +func deriveDeepCopy_16(dst, src *HealthCheckConfig) { if src.Test == nil { dst.Test = nil } else { @@ -1284,8 +1313,8 @@ func deriveDeepCopy_15(dst, src *HealthCheckConfig) { } } -// deriveDeepCopy_16 recursively copies the contents of src into dst. -func deriveDeepCopy_16(dst, src *LoggingConfig) { +// deriveDeepCopy_17 recursively copies the contents of src into dst. +func deriveDeepCopy_17(dst, src *LoggingConfig) { dst.Driver = src.Driver if src.Options != nil { dst.Options = make(map[string]string, len(src.Options)) @@ -1301,8 +1330,8 @@ func deriveDeepCopy_16(dst, src *LoggingConfig) { } } -// deriveDeepCopy_17 recursively copies the contents of src into dst. -func deriveDeepCopy_17(dst, src map[string]*ServiceNetworkConfig) { +// deriveDeepCopy_18 recursively copies the contents of src into dst. +func deriveDeepCopy_18(dst, src map[string]*ServiceNetworkConfig) { for src_key, src_value := range src { if src_value == nil { dst[src_key] = nil @@ -1311,35 +1340,35 @@ func deriveDeepCopy_17(dst, src map[string]*ServiceNetworkConfig) { dst[src_key] = nil } else { dst[src_key] = new(ServiceNetworkConfig) - deriveDeepCopy_37(dst[src_key], src_value) + deriveDeepCopy_39(dst[src_key], src_value) } } } -// deriveDeepCopy_18 recursively copies the contents of src into dst. -func deriveDeepCopy_18(dst, src []ServicePortConfig) { +// deriveDeepCopy_19 recursively copies the contents of src into dst. +func deriveDeepCopy_19(dst, src []ServicePortConfig) { for src_i, src_value := range src { func() { field := new(ServicePortConfig) - deriveDeepCopy_38(field, &src_value) + deriveDeepCopy_40(field, &src_value) dst[src_i] = *field }() } } -// deriveDeepCopy_19 recursively copies the contents of src into dst. -func deriveDeepCopy_19(dst, src []ServiceSecretConfig) { +// deriveDeepCopy_20 recursively copies the contents of src into dst. +func deriveDeepCopy_20(dst, src []ServiceSecretConfig) { for src_i, src_value := range src { func() { field := new(ServiceSecretConfig) - deriveDeepCopy_39(field, &src_value) + deriveDeepCopy_41(field, &src_value) dst[src_i] = *field }() } } -// deriveDeepCopy_20 recursively copies the contents of src into dst. -func deriveDeepCopy_20(dst, src map[string]*UlimitsConfig) { +// deriveDeepCopy_21 recursively copies the contents of src into dst. +func deriveDeepCopy_21(dst, src map[string]*UlimitsConfig) { for src_key, src_value := range src { if src_value == nil { dst[src_key] = nil @@ -1348,35 +1377,35 @@ func deriveDeepCopy_20(dst, src map[string]*UlimitsConfig) { dst[src_key] = nil } else { dst[src_key] = new(UlimitsConfig) - deriveDeepCopy_40(dst[src_key], src_value) + deriveDeepCopy_42(dst[src_key], src_value) } } } -// deriveDeepCopy_21 recursively copies the contents of src into dst. -func deriveDeepCopy_21(dst, src []ServiceVolumeConfig) { +// deriveDeepCopy_22 recursively copies the contents of src into dst. +func deriveDeepCopy_22(dst, src []ServiceVolumeConfig) { for src_i, src_value := range src { func() { field := new(ServiceVolumeConfig) - deriveDeepCopy_41(field, &src_value) + deriveDeepCopy_43(field, &src_value) dst[src_i] = *field }() } } -// deriveDeepCopy_22 recursively copies the contents of src into dst. -func deriveDeepCopy_22(dst, src []ServiceHook) { +// deriveDeepCopy_23 recursively copies the contents of src into dst. +func deriveDeepCopy_23(dst, src []ServiceHook) { for src_i, src_value := range src { func() { field := new(ServiceHook) - deriveDeepCopy_42(field, &src_value) + deriveDeepCopy_44(field, &src_value) dst[src_i] = *field }() } } -// deriveDeepCopy_23 recursively copies the contents of src into dst. -func deriveDeepCopy_23(dst, src *NetworkConfig) { +// deriveDeepCopy_24 recursively copies the contents of src into dst. +func deriveDeepCopy_24(dst, src *NetworkConfig) { dst.Name = src.Name dst.Driver = src.Driver if src.DriverOpts != nil { @@ -1387,7 +1416,7 @@ func deriveDeepCopy_23(dst, src *NetworkConfig) { } func() { field := new(IPAMConfig) - deriveDeepCopy_43(field, &src.Ipam) + deriveDeepCopy_45(field, &src.Ipam) dst.Ipam = *field }() dst.External = src.External @@ -1413,8 +1442,8 @@ func deriveDeepCopy_23(dst, src *NetworkConfig) { } } -// deriveDeepCopy_24 recursively copies the contents of src into dst. -func deriveDeepCopy_24(dst, src *VolumeConfig) { +// deriveDeepCopy_25 recursively copies the contents of src into dst. +func deriveDeepCopy_25(dst, src *VolumeConfig) { dst.Name = src.Name dst.Driver = src.Driver if src.DriverOpts != nil { @@ -1438,8 +1467,8 @@ func deriveDeepCopy_24(dst, src *VolumeConfig) { } } -// deriveDeepCopy_25 recursively copies the contents of src into dst. -func deriveDeepCopy_25(dst, src *SecretConfig) { +// deriveDeepCopy_26 recursively copies the contents of src into dst. +func deriveDeepCopy_26(dst, src *SecretConfig) { dst.Name = src.Name dst.File = src.File dst.Environment = src.Environment @@ -1467,8 +1496,8 @@ func deriveDeepCopy_25(dst, src *SecretConfig) { } } -// deriveDeepCopy_26 recursively copies the contents of src into dst. -func deriveDeepCopy_26(dst, src *ConfigObjConfig) { +// deriveDeepCopy_27 recursively copies the contents of src into dst. +func deriveDeepCopy_27(dst, src *ConfigObjConfig) { dst.Name = src.Name dst.File = src.File dst.Environment = src.Environment @@ -1496,41 +1525,41 @@ func deriveDeepCopy_26(dst, src *ConfigObjConfig) { } } -// deriveDeepCopy_27 recursively copies the contents of src into dst. -func deriveDeepCopy_27(dst, src []Trigger) { +// deriveDeepCopy_28 recursively copies the contents of src into dst. +func deriveDeepCopy_28(dst, src []Trigger) { for src_i, src_value := range src { func() { field := new(Trigger) - deriveDeepCopy_44(field, &src_value) + deriveDeepCopy_46(field, &src_value) dst[src_i] = *field }() } } -// deriveDeepCopy_28 recursively copies the contents of src into dst. -func deriveDeepCopy_28(dst, src []WeightDevice) { +// deriveDeepCopy_29 recursively copies the contents of src into dst. +func deriveDeepCopy_29(dst, src []WeightDevice) { for src_i, src_value := range src { func() { field := new(WeightDevice) - deriveDeepCopy_45(field, &src_value) + deriveDeepCopy_47(field, &src_value) dst[src_i] = *field }() } } -// deriveDeepCopy_29 recursively copies the contents of src into dst. -func deriveDeepCopy_29(dst, src []ThrottleDevice) { +// deriveDeepCopy_30 recursively copies the contents of src into dst. +func deriveDeepCopy_30(dst, src []ThrottleDevice) { for src_i, src_value := range src { func() { field := new(ThrottleDevice) - deriveDeepCopy_46(field, &src_value) + deriveDeepCopy_48(field, &src_value) dst[src_i] = *field }() } } -// deriveDeepCopy_30 recursively copies the contents of src into dst. -func deriveDeepCopy_30(dst, src *ServiceConfigObjConfig) { +// deriveDeepCopy_31 recursively copies the contents of src into dst. +func deriveDeepCopy_31(dst, src *ServiceConfigObjConfig) { dst.Source = src.Source dst.Target = src.Target dst.UID = src.UID @@ -1549,8 +1578,8 @@ func deriveDeepCopy_30(dst, src *ServiceConfigObjConfig) { } } -// deriveDeepCopy_31 recursively copies the contents of src into dst. -func deriveDeepCopy_31(dst, src *ServiceDependency) { +// deriveDeepCopy_32 recursively copies the contents of src into dst. +func deriveDeepCopy_32(dst, src *ServiceDependency) { dst.Condition = src.Condition dst.Restart = src.Restart if src.Extensions != nil { @@ -1562,8 +1591,8 @@ func deriveDeepCopy_31(dst, src *ServiceDependency) { dst.Required = src.Required } -// deriveDeepCopy_32 recursively copies the contents of src into dst. -func deriveDeepCopy_32(dst, src *UpdateConfig) { +// deriveDeepCopy_33 recursively copies the contents of src into dst. +func deriveDeepCopy_33(dst, src *UpdateConfig) { if src.Parallelism == nil { dst.Parallelism = nil } else { @@ -1583,19 +1612,19 @@ func deriveDeepCopy_32(dst, src *UpdateConfig) { } } -// deriveDeepCopy_33 recursively copies the contents of src into dst. -func deriveDeepCopy_33(dst, src *Resources) { +// deriveDeepCopy_34 recursively copies the contents of src into dst. +func deriveDeepCopy_34(dst, src *Resources) { if src.Limits == nil { dst.Limits = nil } else { dst.Limits = new(Resource) - deriveDeepCopy_47(dst.Limits, src.Limits) + deriveDeepCopy_49(dst.Limits, src.Limits) } if src.Reservations == nil { dst.Reservations = nil } else { dst.Reservations = new(Resource) - deriveDeepCopy_47(dst.Reservations, src.Reservations) + deriveDeepCopy_49(dst.Reservations, src.Reservations) } if src.Extensions != nil { dst.Extensions = make(map[string]any, len(src.Extensions)) @@ -1605,8 +1634,8 @@ func deriveDeepCopy_33(dst, src *Resources) { } } -// deriveDeepCopy_34 recursively copies the contents of src into dst. -func deriveDeepCopy_34(dst, src *RestartPolicy) { +// deriveDeepCopy_35 recursively copies the contents of src into dst. +func deriveDeepCopy_35(dst, src *RestartPolicy) { dst.Condition = src.Condition if src.Delay == nil { dst.Delay = nil @@ -1634,8 +1663,8 @@ func deriveDeepCopy_34(dst, src *RestartPolicy) { } } -// deriveDeepCopy_35 recursively copies the contents of src into dst. -func deriveDeepCopy_35(dst, src *Placement) { +// deriveDeepCopy_36 recursively copies the contents of src into dst. +func deriveDeepCopy_36(dst, src *Placement) { if src.Constraints == nil { dst.Constraints = nil } else { @@ -1670,7 +1699,7 @@ func deriveDeepCopy_35(dst, src *Placement) { } else { dst.Preferences = make([]PlacementPreferences, len(src.Preferences)) } - deriveDeepCopy_48(dst.Preferences, src.Preferences) + deriveDeepCopy_50(dst.Preferences, src.Preferences) } dst.MaxReplicas = src.MaxReplicas if src.Extensions != nil { @@ -1681,8 +1710,8 @@ func deriveDeepCopy_35(dst, src *Placement) { } } -// deriveDeepCopy_36 recursively copies the contents of src into dst. -func deriveDeepCopy_36(dst, src *DeviceMapping) { +// deriveDeepCopy_37 recursively copies the contents of src into dst. +func deriveDeepCopy_37(dst, src *DeviceMapping) { dst.Source = src.Source dst.Target = src.Target dst.Permissions = src.Permissions @@ -1694,8 +1723,56 @@ func deriveDeepCopy_36(dst, src *DeviceMapping) { } } -// deriveDeepCopy_37 recursively copies the contents of src into dst. -func deriveDeepCopy_37(dst, src *ServiceNetworkConfig) { +// deriveDeepCopy_38 recursively copies the contents of src into dst. +func deriveDeepCopy_38(dst, src *DeviceRequest) { + if src.Capabilities == nil { + dst.Capabilities = nil + } else { + if dst.Capabilities != nil { + if len(src.Capabilities) > len(dst.Capabilities) { + if cap(dst.Capabilities) >= len(src.Capabilities) { + dst.Capabilities = (dst.Capabilities)[:len(src.Capabilities)] + } else { + dst.Capabilities = make([]string, len(src.Capabilities)) + } + } else if len(src.Capabilities) < len(dst.Capabilities) { + dst.Capabilities = (dst.Capabilities)[:len(src.Capabilities)] + } + } else { + dst.Capabilities = make([]string, len(src.Capabilities)) + } + copy(dst.Capabilities, src.Capabilities) + } + dst.Driver = src.Driver + dst.Count = src.Count + if src.IDs == nil { + dst.IDs = nil + } else { + if dst.IDs != nil { + if len(src.IDs) > len(dst.IDs) { + if cap(dst.IDs) >= len(src.IDs) { + dst.IDs = (dst.IDs)[:len(src.IDs)] + } else { + dst.IDs = make([]string, len(src.IDs)) + } + } else if len(src.IDs) < len(dst.IDs) { + dst.IDs = (dst.IDs)[:len(src.IDs)] + } + } else { + dst.IDs = make([]string, len(src.IDs)) + } + copy(dst.IDs, src.IDs) + } + if src.Options != nil { + dst.Options = make(map[string]string, len(src.Options)) + deriveDeepCopy_4(dst.Options, src.Options) + } else { + dst.Options = nil + } +} + +// deriveDeepCopy_39 recursively copies the contents of src into dst. +func deriveDeepCopy_39(dst, src *ServiceNetworkConfig) { dst.Priority = src.Priority if src.Aliases == nil { dst.Aliases = nil @@ -1750,8 +1827,8 @@ func deriveDeepCopy_37(dst, src *ServiceNetworkConfig) { } } -// deriveDeepCopy_38 recursively copies the contents of src into dst. -func deriveDeepCopy_38(dst, src *ServicePortConfig) { +// deriveDeepCopy_40 recursively copies the contents of src into dst. +func deriveDeepCopy_40(dst, src *ServicePortConfig) { dst.Name = src.Name dst.Mode = src.Mode dst.HostIP = src.HostIP @@ -1767,8 +1844,8 @@ func deriveDeepCopy_38(dst, src *ServicePortConfig) { } } -// deriveDeepCopy_39 recursively copies the contents of src into dst. -func deriveDeepCopy_39(dst, src *ServiceSecretConfig) { +// deriveDeepCopy_41 recursively copies the contents of src into dst. +func deriveDeepCopy_41(dst, src *ServiceSecretConfig) { dst.Source = src.Source dst.Target = src.Target dst.UID = src.UID @@ -1787,8 +1864,8 @@ func deriveDeepCopy_39(dst, src *ServiceSecretConfig) { } } -// deriveDeepCopy_40 recursively copies the contents of src into dst. -func deriveDeepCopy_40(dst, src *UlimitsConfig) { +// deriveDeepCopy_42 recursively copies the contents of src into dst. +func deriveDeepCopy_42(dst, src *UlimitsConfig) { dst.Single = src.Single dst.Soft = src.Soft dst.Hard = src.Hard @@ -1800,8 +1877,8 @@ func deriveDeepCopy_40(dst, src *UlimitsConfig) { } } -// deriveDeepCopy_41 recursively copies the contents of src into dst. -func deriveDeepCopy_41(dst, src *ServiceVolumeConfig) { +// deriveDeepCopy_43 recursively copies the contents of src into dst. +func deriveDeepCopy_43(dst, src *ServiceVolumeConfig) { dst.Type = src.Type dst.Source = src.Source dst.Target = src.Target @@ -1811,19 +1888,19 @@ func deriveDeepCopy_41(dst, src *ServiceVolumeConfig) { dst.Bind = nil } else { dst.Bind = new(ServiceVolumeBind) - deriveDeepCopy_49(dst.Bind, src.Bind) + deriveDeepCopy_51(dst.Bind, src.Bind) } if src.Volume == nil { dst.Volume = nil } else { dst.Volume = new(ServiceVolumeVolume) - deriveDeepCopy_50(dst.Volume, src.Volume) + deriveDeepCopy_52(dst.Volume, src.Volume) } if src.Tmpfs == nil { dst.Tmpfs = nil } else { dst.Tmpfs = new(ServiceVolumeTmpfs) - deriveDeepCopy_51(dst.Tmpfs, src.Tmpfs) + deriveDeepCopy_53(dst.Tmpfs, src.Tmpfs) } if src.Extensions != nil { dst.Extensions = make(map[string]any, len(src.Extensions)) @@ -1833,8 +1910,8 @@ func deriveDeepCopy_41(dst, src *ServiceVolumeConfig) { } } -// deriveDeepCopy_42 recursively copies the contents of src into dst. -func deriveDeepCopy_42(dst, src *ServiceHook) { +// deriveDeepCopy_44 recursively copies the contents of src into dst. +func deriveDeepCopy_44(dst, src *ServiceHook) { if src.Command == nil { dst.Command = nil } else { @@ -1870,8 +1947,8 @@ func deriveDeepCopy_42(dst, src *ServiceHook) { } } -// deriveDeepCopy_43 recursively copies the contents of src into dst. -func deriveDeepCopy_43(dst, src *IPAMConfig) { +// deriveDeepCopy_45 recursively copies the contents of src into dst. +func deriveDeepCopy_45(dst, src *IPAMConfig) { dst.Driver = src.Driver if src.Config == nil { dst.Config = nil @@ -1889,7 +1966,7 @@ func deriveDeepCopy_43(dst, src *IPAMConfig) { } else { dst.Config = make([]*IPAMPool, len(src.Config)) } - deriveDeepCopy_52(dst.Config, src.Config) + deriveDeepCopy_54(dst.Config, src.Config) } if src.Extensions != nil { dst.Extensions = make(map[string]any, len(src.Extensions)) @@ -1899,8 +1976,8 @@ func deriveDeepCopy_43(dst, src *IPAMConfig) { } } -// deriveDeepCopy_44 recursively copies the contents of src into dst. -func deriveDeepCopy_44(dst, src *Trigger) { +// deriveDeepCopy_46 recursively copies the contents of src into dst. +func deriveDeepCopy_46(dst, src *Trigger) { dst.Path = src.Path dst.Action = src.Action dst.Target = src.Target @@ -1930,8 +2007,8 @@ func deriveDeepCopy_44(dst, src *Trigger) { } } -// deriveDeepCopy_45 recursively copies the contents of src into dst. -func deriveDeepCopy_45(dst, src *WeightDevice) { +// deriveDeepCopy_47 recursively copies the contents of src into dst. +func deriveDeepCopy_47(dst, src *WeightDevice) { dst.Path = src.Path dst.Weight = src.Weight if src.Extensions != nil { @@ -1942,8 +2019,8 @@ func deriveDeepCopy_45(dst, src *WeightDevice) { } } -// deriveDeepCopy_46 recursively copies the contents of src into dst. -func deriveDeepCopy_46(dst, src *ThrottleDevice) { +// deriveDeepCopy_48 recursively copies the contents of src into dst. +func deriveDeepCopy_48(dst, src *ThrottleDevice) { dst.Path = src.Path dst.Rate = src.Rate if src.Extensions != nil { @@ -1954,8 +2031,8 @@ func deriveDeepCopy_46(dst, src *ThrottleDevice) { } } -// deriveDeepCopy_47 recursively copies the contents of src into dst. -func deriveDeepCopy_47(dst, src *Resource) { +// deriveDeepCopy_49 recursively copies the contents of src into dst. +func deriveDeepCopy_49(dst, src *Resource) { dst.NanoCPUs = src.NanoCPUs dst.MemoryBytes = src.MemoryBytes dst.Pids = src.Pids @@ -1975,7 +2052,7 @@ func deriveDeepCopy_47(dst, src *Resource) { } else { dst.Devices = make([]DeviceRequest, len(src.Devices)) } - deriveDeepCopy_53(dst.Devices, src.Devices) + deriveDeepCopy_15(dst.Devices, src.Devices) } if src.GenericResources == nil { dst.GenericResources = nil @@ -1993,7 +2070,7 @@ func deriveDeepCopy_47(dst, src *Resource) { } else { dst.GenericResources = make([]GenericResource, len(src.GenericResources)) } - deriveDeepCopy_54(dst.GenericResources, src.GenericResources) + deriveDeepCopy_55(dst.GenericResources, src.GenericResources) } if src.Extensions != nil { dst.Extensions = make(map[string]any, len(src.Extensions)) @@ -2003,19 +2080,19 @@ func deriveDeepCopy_47(dst, src *Resource) { } } -// deriveDeepCopy_48 recursively copies the contents of src into dst. -func deriveDeepCopy_48(dst, src []PlacementPreferences) { +// deriveDeepCopy_50 recursively copies the contents of src into dst. +func deriveDeepCopy_50(dst, src []PlacementPreferences) { for src_i, src_value := range src { func() { field := new(PlacementPreferences) - deriveDeepCopy_55(field, &src_value) + deriveDeepCopy_56(field, &src_value) dst[src_i] = *field }() } } -// deriveDeepCopy_49 recursively copies the contents of src into dst. -func deriveDeepCopy_49(dst, src *ServiceVolumeBind) { +// deriveDeepCopy_51 recursively copies the contents of src into dst. +func deriveDeepCopy_51(dst, src *ServiceVolumeBind) { dst.SELinux = src.SELinux dst.Propagation = src.Propagation dst.CreateHostPath = src.CreateHostPath @@ -2027,8 +2104,8 @@ func deriveDeepCopy_49(dst, src *ServiceVolumeBind) { } } -// deriveDeepCopy_50 recursively copies the contents of src into dst. -func deriveDeepCopy_50(dst, src *ServiceVolumeVolume) { +// deriveDeepCopy_52 recursively copies the contents of src into dst. +func deriveDeepCopy_52(dst, src *ServiceVolumeVolume) { dst.NoCopy = src.NoCopy dst.Subpath = src.Subpath if src.Extensions != nil { @@ -2039,8 +2116,8 @@ func deriveDeepCopy_50(dst, src *ServiceVolumeVolume) { } } -// deriveDeepCopy_51 recursively copies the contents of src into dst. -func deriveDeepCopy_51(dst, src *ServiceVolumeTmpfs) { +// deriveDeepCopy_53 recursively copies the contents of src into dst. +func deriveDeepCopy_53(dst, src *ServiceVolumeTmpfs) { dst.Size = src.Size dst.Mode = src.Mode if src.Extensions != nil { @@ -2051,31 +2128,20 @@ func deriveDeepCopy_51(dst, src *ServiceVolumeTmpfs) { } } -// deriveDeepCopy_52 recursively copies the contents of src into dst. -func deriveDeepCopy_52(dst, src []*IPAMPool) { +// deriveDeepCopy_54 recursively copies the contents of src into dst. +func deriveDeepCopy_54(dst, src []*IPAMPool) { for src_i, src_value := range src { if src_value == nil { dst[src_i] = nil } else { dst[src_i] = new(IPAMPool) - deriveDeepCopy_56(dst[src_i], src_value) + deriveDeepCopy_57(dst[src_i], src_value) } } } -// deriveDeepCopy_53 recursively copies the contents of src into dst. -func deriveDeepCopy_53(dst, src []DeviceRequest) { - for src_i, src_value := range src { - func() { - field := new(DeviceRequest) - deriveDeepCopy_57(field, &src_value) - dst[src_i] = *field - }() - } -} - -// deriveDeepCopy_54 recursively copies the contents of src into dst. -func deriveDeepCopy_54(dst, src []GenericResource) { +// deriveDeepCopy_55 recursively copies the contents of src into dst. +func deriveDeepCopy_55(dst, src []GenericResource) { for src_i, src_value := range src { func() { field := new(GenericResource) @@ -2085,8 +2151,8 @@ func deriveDeepCopy_54(dst, src []GenericResource) { } } -// deriveDeepCopy_55 recursively copies the contents of src into dst. -func deriveDeepCopy_55(dst, src *PlacementPreferences) { +// deriveDeepCopy_56 recursively copies the contents of src into dst. +func deriveDeepCopy_56(dst, src *PlacementPreferences) { dst.Spread = src.Spread if src.Extensions != nil { dst.Extensions = make(map[string]any, len(src.Extensions)) @@ -2096,8 +2162,8 @@ func deriveDeepCopy_55(dst, src *PlacementPreferences) { } } -// deriveDeepCopy_56 recursively copies the contents of src into dst. -func deriveDeepCopy_56(dst, src *IPAMPool) { +// deriveDeepCopy_57 recursively copies the contents of src into dst. +func deriveDeepCopy_57(dst, src *IPAMPool) { dst.Subnet = src.Subnet dst.Gateway = src.Gateway dst.IPRange = src.IPRange @@ -2115,54 +2181,6 @@ func deriveDeepCopy_56(dst, src *IPAMPool) { } } -// deriveDeepCopy_57 recursively copies the contents of src into dst. -func deriveDeepCopy_57(dst, src *DeviceRequest) { - if src.Capabilities == nil { - dst.Capabilities = nil - } else { - if dst.Capabilities != nil { - if len(src.Capabilities) > len(dst.Capabilities) { - if cap(dst.Capabilities) >= len(src.Capabilities) { - dst.Capabilities = (dst.Capabilities)[:len(src.Capabilities)] - } else { - dst.Capabilities = make([]string, len(src.Capabilities)) - } - } else if len(src.Capabilities) < len(dst.Capabilities) { - dst.Capabilities = (dst.Capabilities)[:len(src.Capabilities)] - } - } else { - dst.Capabilities = make([]string, len(src.Capabilities)) - } - copy(dst.Capabilities, src.Capabilities) - } - dst.Driver = src.Driver - dst.Count = src.Count - if src.IDs == nil { - dst.IDs = nil - } else { - if dst.IDs != nil { - if len(src.IDs) > len(dst.IDs) { - if cap(dst.IDs) >= len(src.IDs) { - dst.IDs = (dst.IDs)[:len(src.IDs)] - } else { - dst.IDs = make([]string, len(src.IDs)) - } - } else if len(src.IDs) < len(dst.IDs) { - dst.IDs = (dst.IDs)[:len(src.IDs)] - } - } else { - dst.IDs = make([]string, len(src.IDs)) - } - copy(dst.IDs, src.IDs) - } - if src.Options != nil { - dst.Options = make(map[string]string, len(src.Options)) - deriveDeepCopy_4(dst.Options, src.Options) - } else { - dst.Options = nil - } -} - // deriveDeepCopy_58 recursively copies the contents of src into dst. func deriveDeepCopy_58(dst, src *GenericResource) { if src.DiscreteResourceSpec == nil { diff --git a/types/types.go b/types/types.go index dd9705f4..d96e206d 100644 --- a/types/types.go +++ b/types/types.go @@ -82,6 +82,7 @@ type ServiceConfig struct { ExternalLinks []string `yaml:"external_links,omitempty" json:"external_links,omitempty"` ExtraHosts HostsList `yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"` GroupAdd []string `yaml:"group_add,omitempty" json:"group_add,omitempty"` + Gpus []DeviceRequest `yaml:"gpus,omitempty" json:"gpus,omitempty"` Hostname string `yaml:"hostname,omitempty" json:"hostname,omitempty"` HealthCheck *HealthCheckConfig `yaml:"healthcheck,omitempty" json:"healthcheck,omitempty"` Image string `yaml:"image,omitempty" json:"image,omitempty"` diff --git a/validation/validation.go b/validation/validation.go index e7cd6754..707f247e 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -30,6 +30,8 @@ var checks = map[tree.Path]checkerFunc{ "configs.*": checkFileObject("file", "environment", "content"), "secrets.*": checkFileObject("file", "environment"), "services.*.develop.watch.*.path": checkPath, + "services.*.deploy.resources.reservations.devices.*": checkDeviceRequest, + "services.*.gpus.*": checkDeviceRequest, } func Validate(dict map[string]any) error { @@ -94,3 +96,13 @@ func checkPath(value any, p tree.Path) error { } return nil } + +func checkDeviceRequest(value any, p tree.Path) error { + v := value.(map[string]any) + _, hasCount := v["count"] + _, hasIds := v["device_ids"] + if hasCount && hasIds { + return fmt.Errorf(`%s: "count" and "device_ids" attributes are exclusive`, p) + } + return nil +}