diff --git a/cli/compose/convert/compose.go b/cli/compose/convert/compose.go index e683a7c7da0f..123b1aa1df14 100644 --- a/cli/compose/convert/compose.go +++ b/cli/compose/convert/compose.go @@ -5,7 +5,6 @@ import ( "strings" composetypes "github.com/compose-spec/compose-go/v2/types" - "github.com/docker/docker/api/types" networktypes "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/swarm" ) @@ -52,13 +51,13 @@ func AddStackLabel(namespace Namespace, labels map[string]string) map[string]str type networkMap map[string]composetypes.NetworkConfig // Networks from the compose-file type to the engine API type -func Networks(namespace Namespace, networks composetypes.Networks, servicesNetworks map[string]struct{}) (map[string]types.NetworkCreate, []string) { +func Networks(namespace Namespace, networks composetypes.Networks, servicesNetworks map[string]struct{}) (map[string]networktypes.CreateOptions, []string) { if networks == nil { networks = make(map[string]composetypes.NetworkConfig) } externalNetworks := []string{} - result := make(map[string]network.CreateOptions) + result := make(map[string]networktypes.CreateOptions) for internalName := range servicesNetworks { network := networks[internalName] if network.External { @@ -66,31 +65,31 @@ func Networks(namespace Namespace, networks composetypes.Networks, servicesNetwo continue } - createOpts := network.CreateOptions{ - Labels: AddStackLabel(namespace, nw.Labels), - Driver: nw.Driver, - Options: nw.DriverOpts, - Internal: nw.Internal, - Attachable: nw.Attachable, + createOpts := networktypes.CreateOptions{ + Labels: AddStackLabel(namespace, network.Labels), + Driver: network.Driver, + Options: network.DriverOpts, + Internal: network.Internal, + Attachable: network.Attachable, } - if nw.Ipam.Driver != "" || len(nw.Ipam.Config) > 0 { - createOpts.IPAM = &network.IPAM{} + if network.Ipam.Driver != "" || len(network.Ipam.Config) > 0 { + createOpts.IPAM = &networktypes.IPAM{} } - if nw.Ipam.Driver != "" { - createOpts.IPAM.Driver = nw.Ipam.Driver + if network.Ipam.Driver != "" { + createOpts.IPAM.Driver = network.Ipam.Driver } - for _, ipamConfig := range nw.Ipam.Config { - config := network.IPAMConfig{ + for _, ipamConfig := range network.Ipam.Config { + config := networktypes.IPAMConfig{ Subnet: ipamConfig.Subnet, } createOpts.IPAM.Config = append(createOpts.IPAM.Config, config) } networkName := namespace.Scope(internalName) - if nw.Name != "" { - networkName = nw.Name + if network.Name != "" { + networkName = network.Name } result[networkName] = createOpts } diff --git a/cli/compose/convert/service.go b/cli/compose/convert/service.go index bd856779ac85..b4c1b2eebe9a 100644 --- a/cli/compose/convert/service.go +++ b/cli/compose/convert/service.go @@ -85,10 +85,7 @@ func Service( return swarm.ServiceSpec{}, err } - resources, err := convertResources(service.Deploy.Resources) - if err != nil { - return swarm.ServiceSpec{}, err - } + resources := convertResources(service.Deploy.Resources) restartPolicy, err := convertRestartPolicy( service.Restart, service.Deploy.RestartPolicy) @@ -535,17 +532,10 @@ func convertUpdateConfig(source *composetypes.UpdateConfig) *swarm.UpdateConfig } } -func convertResources(source composetypes.Resources) (*swarm.ResourceRequirements, error) { +func convertResources(source composetypes.Resources) *swarm.ResourceRequirements { resources := &swarm.ResourceRequirements{} - var err error if source.Limits != nil { - var cpus int64 - if source.Limits.NanoCPUs != "" { - cpus, err = opts.ParseCPUs(source.Limits.NanoCPUs) - if err != nil { - return nil, err - } - } + cpus := int64(source.Limits.NanoCPUs * 1e9) resources.Limits = &swarm.Limit{ NanoCPUs: cpus, MemoryBytes: int64(source.Limits.MemoryBytes), @@ -553,13 +543,7 @@ func convertResources(source composetypes.Resources) (*swarm.ResourceRequirement } } if source.Reservations != nil { - var cpus int64 - if source.Reservations.NanoCPUs != "" { - cpus, err = opts.ParseCPUs(source.Reservations.NanoCPUs) - if err != nil { - return nil, err - } - } + cpus := int64(source.Reservations.NanoCPUs * 1e9) var generic []swarm.GenericResource for _, res := range source.Reservations.GenericResources { @@ -581,7 +565,7 @@ func convertResources(source composetypes.Resources) (*swarm.ResourceRequirement GenericResources: generic, } } - return resources, nil + return resources } func convertEndpointSpec(endpointMode string, source []composetypes.ServicePortConfig) (*swarm.EndpointSpec, error) { diff --git a/cli/compose/convert/service_test.go b/cli/compose/convert/service_test.go index d13c3ea57772..5a99a2f278b4 100644 --- a/cli/compose/convert/service_test.go +++ b/cli/compose/convert/service_test.go @@ -83,16 +83,15 @@ func TestConvertExtraHosts(t *testing.T) { func TestConvertResourcesFull(t *testing.T) { source := composetypes.Resources{ Limits: &composetypes.Resource{ - NanoCPUs: "0.003", + NanoCPUs: 0.003, MemoryBytes: composetypes.UnitBytes(300000000), }, Reservations: &composetypes.Resource{ - NanoCPUs: "0.002", + NanoCPUs: 0.002, MemoryBytes: composetypes.UnitBytes(200000000), }, } - resources, err := convertResources(source) - assert.NilError(t, err) + resources := convertResources(source) expected := &swarm.ResourceRequirements{ Limits: &swarm.Limit{ @@ -116,8 +115,7 @@ func TestConvertResourcesOnlyMemory(t *testing.T) { MemoryBytes: composetypes.UnitBytes(200000000), }, } - resources, err := convertResources(source) - assert.NilError(t, err) + resources := convertResources(source) expected := &swarm.ResourceRequirements{ Limits: &swarm.Limit{ diff --git a/cli/compose/convert/volume_test.go b/cli/compose/convert/volume_test.go index 97ae2fac1157..fe6a867c45c5 100644 --- a/cli/compose/convert/volume_test.go +++ b/cli/compose/convert/volume_test.go @@ -154,49 +154,6 @@ func TestConvertVolumeToMountConflictingOptionsTmpfsInBind(t *testing.T) { assert.Error(t, err, "tmpfs options are incompatible with type bind") } -func TestConvertVolumeToMountConflictingOptionsClusterInVolume(t *testing.T) { - namespace := NewNamespace("foo") - - config := composetypes.ServiceVolumeConfig{ - Type: "volume", - Target: "/target", - Cluster: &composetypes.ServiceVolumeCluster{}, - } - _, err := convertVolumeToMount(config, volumes{}, namespace) - assert.Error(t, err, "cluster options are incompatible with type volume") -} - -func TestConvertVolumeToMountConflictingOptionsClusterInBind(t *testing.T) { - namespace := NewNamespace("foo") - - config := composetypes.ServiceVolumeConfig{ - Type: "bind", - Source: "/foo", - Target: "/target", - Bind: &composetypes.ServiceVolumeBind{ - Propagation: "slave", - }, - Cluster: &composetypes.ServiceVolumeCluster{}, - } - _, err := convertVolumeToMount(config, volumes{}, namespace) - assert.Error(t, err, "cluster options are incompatible with type bind") -} - -func TestConvertVolumeToMountConflictingOptionsClusterInTmpfs(t *testing.T) { - namespace := NewNamespace("foo") - - config := composetypes.ServiceVolumeConfig{ - Type: "tmpfs", - Target: "/target", - Tmpfs: &composetypes.ServiceVolumeTmpfs{ - Size: 1000, - }, - Cluster: &composetypes.ServiceVolumeCluster{}, - } - _, err := convertVolumeToMount(config, volumes{}, namespace) - assert.Error(t, err, "cluster options are incompatible with type tmpfs") -} - func TestConvertVolumeToMountConflictingOptionsBindInTmpfs(t *testing.T) { namespace := NewNamespace("foo") diff --git a/cli/compose/loader/full-struct_test.go b/cli/compose/loader/full-struct_test.go index c72fc01646d8..e3fff93ea5b9 100644 --- a/cli/compose/loader/full-struct_test.go +++ b/cli/compose/loader/full-struct_test.go @@ -90,12 +90,12 @@ func services(workingDir, homeDir string) types.Services { }, Resources: types.Resources{ Limits: &types.Resource{ - NanoCPUs: "0.001", + NanoCPUs: 0.001, MemoryBytes: 50 * 1024 * 1024, Pids: 100, }, Reservations: &types.Resource{ - NanoCPUs: "0.0001", + NanoCPUs: 0.0001, MemoryBytes: 20 * 1024 * 1024, GenericResources: []types.GenericResource{ { @@ -130,7 +130,9 @@ func services(workingDir, homeDir string) types.Services { }, EndpointMode: "dnsrr", }, - Devices: []string{"/dev/ttyUSB0:/dev/ttyUSB0"}, + Devices: []types.DeviceMapping{{ + Source: "/dev/ttyUSB0", Target: "/dev/ttyUSB0", + }}, DNS: []string{"8.8.8.8", "9.9.9.9"}, DNSSearch: []string{"dc1.example.com", "dc2.example.com"}, DomainName: "foo.com", diff --git a/cli/compose/loader/testdata/full-example.json.golden b/cli/compose/loader/testdata/full-example.json.golden index 945eec4f64e1..cff218f45b2c 100644 --- a/cli/compose/loader/testdata/full-example.json.golden +++ b/cli/compose/loader/testdata/full-example.json.golden @@ -1,81 +1,5 @@ { - "configs": { - "config1": { - "file": "/foo/config_data", - "labels": { - "foo": "bar" - } - }, - "config2": { - "name": "my_config", - "external": true - }, - "config3": { - "name": "config3", - "external": true - }, - "config4": { - "name": "foo", - "file": "/foo" - } - }, "name": "full-example", - "networks": { - "external-network": { - "name": "external-network", - "ipam": {}, - "external": true - }, - "other-external-network": { - "name": "my-cool-network", - "ipam": {}, - "external": true - }, - "other-network": { - "driver": "overlay", - "driver_opts": { - "baz": "1", - "foo": "bar" - }, - "ipam": { - "driver": "overlay", - "config": [ - { - "subnet": "172.16.238.0/24" - }, - { - "subnet": "2001:3984:3989::/64" - } - ] - }, - "labels": { - "foo": "bar" - } - }, - "some-network": { - "ipam": {} - } - }, - "secrets": { - "secret1": { - "file": "/foo/secret_data", - "labels": { - "foo": "bar" - } - }, - "secret2": { - "name": "my_secret", - "external": true - }, - "secret3": { - "name": "secret3", - "external": true - }, - "secret4": { - "name": "bar", - "file": "/foo" - } - }, "services": { "foo": { "build": { @@ -160,12 +84,12 @@ }, "resources": { "limits": { - "cpus": "0.001", + "cpus": 0.001, "memory": "52428800", "pids": 100 }, "reservations": { - "cpus": "0.0001", + "cpus": 0.0001, "memory": "20971520", "generic_resources": [ { @@ -203,7 +127,10 @@ "endpoint_mode": "dnsrr" }, "devices": [ - "/dev/ttyUSB0:/dev/ttyUSB0" + { + "source": "/dev/ttyUSB0", + "target": "/dev/ttyUSB0" + } ], "dns": [ "8.8.8.8", @@ -516,6 +443,42 @@ "working_dir": "/code" } }, + "networks": { + "external-network": { + "name": "external-network", + "ipam": {}, + "external": true + }, + "other-external-network": { + "name": "my-cool-network", + "ipam": {}, + "external": true + }, + "other-network": { + "driver": "overlay", + "driver_opts": { + "baz": "1", + "foo": "bar" + }, + "ipam": { + "driver": "overlay", + "config": [ + { + "subnet": "172.16.238.0/24" + }, + { + "subnet": "2001:3984:3989::/64" + } + ] + }, + "labels": { + "foo": "bar" + } + }, + "some-network": { + "ipam": {} + } + }, "volumes": { "another-volume": { "name": "user_specified_name", @@ -552,10 +515,44 @@ }, "some-volume": {} }, - "x-bar": "baz", - "x-foo": "bar", - "x-nested": { - "bar": "baz", - "foo": "bar" + "secrets": { + "secret1": { + "file": "/foo/secret_data", + "labels": { + "foo": "bar" + } + }, + "secret2": { + "name": "my_secret", + "external": true + }, + "secret3": { + "name": "secret3", + "external": true + }, + "secret4": { + "name": "bar", + "file": "/foo" + } + }, + "configs": { + "config1": { + "file": "/foo/config_data", + "labels": { + "foo": "bar" + } + }, + "config2": { + "name": "my_config", + "external": true + }, + "config3": { + "name": "config3", + "external": true + }, + "config4": { + "name": "foo", + "file": "/foo" + } } -} +} \ No newline at end of file diff --git a/cli/compose/loader/testdata/full-example.yaml.golden b/cli/compose/loader/testdata/full-example.yaml.golden index 0d4e875be0c9..6089662a3ee8 100644 --- a/cli/compose/loader/testdata/full-example.yaml.golden +++ b/cli/compose/loader/testdata/full-example.yaml.golden @@ -64,11 +64,11 @@ services: order: start-first resources: limits: - cpus: "0.001" + cpus: 0.001 memory: "52428800" pids: 100 reservations: - cpus: "0.0001" + cpus: 0.0001 memory: "20971520" generic_resources: - discrete_resource_spec: @@ -90,7 +90,8 @@ services: max_replicas_per_node: 5 endpoint_mode: dnsrr devices: - - /dev/ttyUSB0:/dev/ttyUSB0 + - source: /dev/ttyUSB0 + target: /dev/ttyUSB0 dns: - 8.8.8.8 - 9.9.9.9