Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions cli/command/service/opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,15 +235,17 @@ type resourceOptions struct {
resCPU opts.NanoCPUs
resMemBytes opts.MemBytes
resGenericResources []string
swapBytes opts.MemBytes
memSwappiness int64
}

func (r *resourceOptions) ToResourceRequirements() (*swarm.ResourceRequirements, error) {
func (r *resourceOptions) ToResourceRequirements(flags *pflag.FlagSet) (*swarm.ResourceRequirements, error) {
generic, err := ParseGenericResources(r.resGenericResources)
if err != nil {
return nil, err
}

return &swarm.ResourceRequirements{
resreq := &swarm.ResourceRequirements{
Limits: &swarm.Limit{
NanoCPUs: r.limitCPU.Value(),
MemoryBytes: r.limitMemBytes.Value(),
Expand All @@ -254,7 +256,20 @@ func (r *resourceOptions) ToResourceRequirements() (*swarm.ResourceRequirements,
MemoryBytes: r.resMemBytes.Value(),
GenericResources: generic,
},
}, nil
}

// SwapBytes and MemorySwappiness are *int64 (pointers), so we need to have
// a variable we can take a pointer to. Additionally, we need to ensure
// that these values are only set if they are set as options.
if flags.Changed(flagSwapBytes) {
swapBytes := r.swapBytes.Value()
resreq.SwapBytes = &swapBytes
}
if flags.Changed(flagMemSwappiness) {
resreq.MemorySwappiness = &r.memSwappiness
}

return resreq, nil
}

type restartPolicyOptions struct {
Expand Down Expand Up @@ -734,7 +749,7 @@ func (options *serviceOptions) ToService(ctx context.Context, apiClient client.N
return networks[i].Target < networks[j].Target
})

resources, err := options.resources.ToResourceRequirements()
resources, err := options.resources.ToResourceRequirements(flags)
if err != nil {
return service, err
}
Expand Down Expand Up @@ -889,6 +904,10 @@ func addServiceFlags(flags *pflag.FlagSet, options *serviceOptions, defaultFlagV
flags.Var(&options.resources.resMemBytes, flagReserveMemory, "Reserve Memory")
flags.Int64Var(&options.resources.limitPids, flagLimitPids, 0, "Limit maximum number of processes (default 0 = unlimited)")
flags.SetAnnotation(flagLimitPids, "version", []string{"1.41"})
flags.Var(&options.resources.swapBytes, flagSwapBytes, "Swap Bytes (-1 for unlimited)")
flags.SetAnnotation(flagLimitPids, "version", []string{"1.52"})
flags.Int64Var(&options.resources.memSwappiness, flagMemSwappiness, -1, "Tune memory swappiness (0-100), -1 to reset to default")
flags.SetAnnotation(flagLimitPids, "version", []string{"1.52"})

flags.Var(&options.stopGrace, flagStopGracePeriod, flagDesc(flagStopGracePeriod, "Time to wait before force killing a container (ns|us|ms|s|m|h)"))
flags.Var(&options.replicas, flagReplicas, "Number of tasks")
Expand Down Expand Up @@ -1073,6 +1092,8 @@ const (
flagUlimitAdd = "ulimit-add"
flagUlimitRemove = "ulimit-rm"
flagOomScoreAdj = "oom-score-adj"
flagSwapBytes = "memory-swap"
flagMemSwappiness = "memory-swappiness"
)

func toNetipAddrSlice(ips []string) []netip.Addr {
Expand Down
35 changes: 33 additions & 2 deletions cli/command/service/opts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,10 @@ func TestResourceOptionsToResourceRequirements(t *testing.T) {
},
}

flags := newCreateCommand(nil).Flags()

for _, opt := range incorrectOptions {
_, err := opt.ToResourceRequirements()
_, err := opt.ToResourceRequirements(flags)
assert.Check(t, is.ErrorContains(err, ""))
}

Expand All @@ -178,12 +180,41 @@ func TestResourceOptionsToResourceRequirements(t *testing.T) {
}

for _, opt := range correctOptions {
r, err := opt.ToResourceRequirements()
r, err := opt.ToResourceRequirements(flags)
assert.NilError(t, err)
assert.Check(t, is.Len(r.Reservations.GenericResources, len(opt.resGenericResources)))
}
}

func TestResourceOptionsToResourceRequirementsSwap(t *testing.T) {
// first, check that no flag set means no field set in the return
flags := newCreateCommand(nil).Flags()

// These should be the default values of the field.
swapOptions := resourceOptions{
swapBytes: 0,
memSwappiness: -1,
}

r, err := swapOptions.ToResourceRequirements(flags)
assert.NilError(t, err)
assert.Check(t, is.Nil(r.SwapBytes))
assert.Check(t, is.Nil(r.MemorySwappiness))

// now set the flags and some values
flags.Set(flagSwapBytes, "86000")
flags.Set(flagMemSwappiness, "23")
swapOptions.swapBytes = 86000
swapOptions.memSwappiness = 23

r, err = swapOptions.ToResourceRequirements(flags)
assert.NilError(t, err)
assert.Check(t, r.SwapBytes != nil)
assert.Check(t, is.Equal(*(r.SwapBytes), int64(86000)))
assert.Check(t, r.MemorySwappiness != nil)
assert.Check(t, is.Equal(*(r.MemorySwappiness), int64(23)))
}

func TestToServiceNetwork(t *testing.T) {
nws := []network.Inspect{
{
Expand Down
4 changes: 4 additions & 0 deletions cli/compose/convert/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,10 @@ func convertResources(source composetypes.Resources) (*swarm.ResourceRequirement
GenericResources: generic,
}
}
// These fields are themselves pointers -- we can simply assign, no need to
// nil-check them. Nil is nil.
resources.SwapBytes = source.MemswapLimit
resources.MemorySwappiness = source.MemSwappiness
return resources, nil
}

Expand Down
7 changes: 7 additions & 0 deletions cli/compose/convert/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ func TestConvertExtraHosts(t *testing.T) {
}

func TestConvertResourcesFull(t *testing.T) {
// create some variables so we can get pointers
memswap := int64(72090)
swappiness := int64(27)
source := composetypes.Resources{
Limits: &composetypes.ResourceLimit{
NanoCPUs: "0.003",
Expand All @@ -90,6 +93,8 @@ func TestConvertResourcesFull(t *testing.T) {
NanoCPUs: "0.002",
MemoryBytes: composetypes.UnitBytes(200000000),
},
MemswapLimit: &memswap,
MemSwappiness: &swappiness,
}
resources, err := convertResources(source)
assert.NilError(t, err)
Expand All @@ -103,6 +108,8 @@ func TestConvertResourcesFull(t *testing.T) {
NanoCPUs: 2000000,
MemoryBytes: 200000000,
},
SwapBytes: &memswap,
MemorySwappiness: &swappiness,
}
assert.Check(t, is.DeepEqual(expected, resources))
}
Expand Down
4 changes: 3 additions & 1 deletion cli/compose/loader/full-example.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "3.13"
version: "3.14"

services:
foo:
Expand Down Expand Up @@ -79,6 +79,8 @@ services:
- discrete_resource_spec:
kind: 'ssd'
value: 1
memswap_limit: 86000
mem_swappiness: 27
restart_policy:
condition: on-failure
delay: 5s
Expand Down
4 changes: 3 additions & 1 deletion cli/compose/loader/full-struct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

func fullExampleConfig(workingDir, homeDir string) *types.Config {
return &types.Config{
Version: "3.13",
Version: "3.14",
Services: services(workingDir, homeDir),
Networks: networks(),
Volumes: volumes(),
Expand Down Expand Up @@ -108,6 +108,8 @@ func services(workingDir, homeDir string) []types.ServiceConfig {
},
},
},
MemswapLimit: int64Ptr(86000),
MemSwappiness: int64Ptr(27),
},
RestartPolicy: &types.RestartPolicy{
Condition: "on-failure",
Expand Down
6 changes: 5 additions & 1 deletion cli/compose/loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func strPtr(val string) *string {
}

var sampleConfig = types.Config{
Version: "3.13",
Version: "3.14",
Services: []types.ServiceConfig{
{
Name: "foo",
Expand Down Expand Up @@ -970,6 +970,10 @@ func uint32Ptr(value uint32) *uint32 {
return &value
}

func int64Ptr(value int64) *int64 {
return &value
}

func TestFullExample(t *testing.T) {
skip.If(t, runtime.GOOS == "windows", "FIXME: substitutes platform-specific HOME-dirs and requires platform-specific golden files; see https://github.com/docker/cli/pull/4610")

Expand Down
6 changes: 4 additions & 2 deletions cli/compose/loader/testdata/full-example.json.golden
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,9 @@
}
}
]
}
},
"memswap_limit": 86000,
"mem_swappiness": 27
},
"restart_policy": {
"condition": "on-failure",
Expand Down Expand Up @@ -513,7 +515,7 @@
"working_dir": "/code"
}
},
"version": "3.13",
"version": "3.14",
"volumes": {
"another-volume": {
"name": "user_specified_name",
Expand Down
4 changes: 3 additions & 1 deletion cli/compose/loader/testdata/full-example.yaml.golden
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "3.13"
version: "3.14"
services:
foo:
build:
Expand Down Expand Up @@ -73,6 +73,8 @@ services:
- discrete_resource_spec:
kind: ssd
value: 1
memswap_limit: 86000
mem_swappiness: 27
restart_policy:
condition: on-failure
delay: 5s
Expand Down
Loading
Loading