mirror of https://github.com/docker/cli.git
Add ulimits option to docker service create/update/inspect
This is related to moby/moby 40639. Signed-off-by: Albin Kerouanton <albin@akerouanton.name> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
866e4b10a1
commit
a9158bdc50
|
@ -64,6 +64,8 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
flags.SetAnnotation(flagInit, "version", []string{"1.37"})
|
flags.SetAnnotation(flagInit, "version", []string{"1.37"})
|
||||||
flags.Var(&opts.sysctls, flagSysCtl, "Sysctl options")
|
flags.Var(&opts.sysctls, flagSysCtl, "Sysctl options")
|
||||||
flags.SetAnnotation(flagSysCtl, "version", []string{"1.40"})
|
flags.SetAnnotation(flagSysCtl, "version", []string{"1.40"})
|
||||||
|
flags.Var(&opts.ulimits, flagUlimit, "Ulimit options")
|
||||||
|
flags.SetAnnotation(flagUlimit, "version", []string{"1.41"})
|
||||||
|
|
||||||
flags.Var(cliopts.NewListOptsRef(&opts.resources.resGenericResources, ValidateSingleGenericResource), "generic-resource", "User defined resources")
|
flags.Var(cliopts.NewListOptsRef(&opts.resources.resGenericResources, ValidateSingleGenericResource), "generic-resource", "User defined resources")
|
||||||
flags.SetAnnotation(flagHostAdd, "version", []string{"1.32"})
|
flags.SetAnnotation(flagHostAdd, "version", []string{"1.32"})
|
||||||
|
|
|
@ -111,6 +111,11 @@ SysCtls:
|
||||||
{{- range $k, $v := .ContainerSysCtls }}
|
{{- range $k, $v := .ContainerSysCtls }}
|
||||||
{{ $k }}{{if $v }}: {{ $v }}{{ end }}
|
{{ $k }}{{if $v }}: {{ $v }}{{ end }}
|
||||||
{{- end }}{{ end }}
|
{{- end }}{{ end }}
|
||||||
|
{{- if .ContainerUlimits }}
|
||||||
|
Ulimits:
|
||||||
|
{{- range $k, $v := .ContainerUlimits }}
|
||||||
|
{{ $k }}: {{ $v }}
|
||||||
|
{{- end }}{{ end }}
|
||||||
{{- if .ContainerMounts }}
|
{{- if .ContainerMounts }}
|
||||||
Mounts:
|
Mounts:
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
@ -467,6 +472,20 @@ func (ctx *serviceInspectContext) HasContainerSysCtls() bool {
|
||||||
return len(ctx.Service.Spec.TaskTemplate.ContainerSpec.Sysctls) > 0
|
return len(ctx.Service.Spec.TaskTemplate.ContainerSpec.Sysctls) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) ContainerUlimits() map[string]string {
|
||||||
|
ulimits := map[string]string{}
|
||||||
|
|
||||||
|
for _, u := range ctx.Service.Spec.TaskTemplate.ContainerSpec.Ulimits {
|
||||||
|
ulimits[u.Name] = fmt.Sprintf("%d:%d", u.Soft, u.Hard)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ulimits
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) HasContainerUlimits() bool {
|
||||||
|
return len(ctx.Service.Spec.TaskTemplate.ContainerSpec.Ulimits) > 0
|
||||||
|
}
|
||||||
|
|
||||||
func (ctx *serviceInspectContext) HasResources() bool {
|
func (ctx *serviceInspectContext) HasResources() bool {
|
||||||
return ctx.Service.Spec.TaskTemplate.Resources != nil
|
return ctx.Service.Spec.TaskTemplate.Resources != nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -508,6 +508,7 @@ type serviceOptions struct {
|
||||||
sysctls opts.ListOpts
|
sysctls opts.ListOpts
|
||||||
capAdd opts.ListOpts
|
capAdd opts.ListOpts
|
||||||
capDrop opts.ListOpts
|
capDrop opts.ListOpts
|
||||||
|
ulimits opts.UlimitOpt
|
||||||
|
|
||||||
resources resourceOptions
|
resources resourceOptions
|
||||||
stopGrace opts.DurationOpt
|
stopGrace opts.DurationOpt
|
||||||
|
@ -553,6 +554,7 @@ func newServiceOptions() *serviceOptions {
|
||||||
sysctls: opts.NewListOpts(nil),
|
sysctls: opts.NewListOpts(nil),
|
||||||
capAdd: opts.NewListOpts(nil),
|
capAdd: opts.NewListOpts(nil),
|
||||||
capDrop: opts.NewListOpts(nil),
|
capDrop: opts.NewListOpts(nil),
|
||||||
|
ulimits: *opts.NewUlimitOpt(nil),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,6 +726,7 @@ func (options *serviceOptions) ToService(ctx context.Context, apiClient client.N
|
||||||
Sysctls: opts.ConvertKVStringsToMap(options.sysctls.GetAll()),
|
Sysctls: opts.ConvertKVStringsToMap(options.sysctls.GetAll()),
|
||||||
CapabilityAdd: capAdd,
|
CapabilityAdd: capAdd,
|
||||||
CapabilityDrop: capDrop,
|
CapabilityDrop: capDrop,
|
||||||
|
Ulimits: options.ulimits.GetList(),
|
||||||
},
|
},
|
||||||
Networks: networks,
|
Networks: networks,
|
||||||
Resources: resources,
|
Resources: resources,
|
||||||
|
@ -1015,6 +1018,9 @@ const (
|
||||||
flagIsolation = "isolation"
|
flagIsolation = "isolation"
|
||||||
flagCapAdd = "cap-add"
|
flagCapAdd = "cap-add"
|
||||||
flagCapDrop = "cap-drop"
|
flagCapDrop = "cap-drop"
|
||||||
|
flagUlimit = "ulimit"
|
||||||
|
flagUlimitAdd = "ulimit-add"
|
||||||
|
flagUlimitRemove = "ulimit-rm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func validateAPIVersion(c swarm.ServiceSpec, serverAPIVersion string) error {
|
func validateAPIVersion(c swarm.ServiceSpec, serverAPIVersion string) error {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
|
units "github.com/docker/go-units"
|
||||||
"github.com/docker/swarmkit/api/defaults"
|
"github.com/docker/swarmkit/api/defaults"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -100,6 +101,10 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
flags.SetAnnotation(flagSysCtlAdd, "version", []string{"1.40"})
|
flags.SetAnnotation(flagSysCtlAdd, "version", []string{"1.40"})
|
||||||
flags.Var(newListOptsVar(), flagSysCtlRemove, "Remove a Sysctl option")
|
flags.Var(newListOptsVar(), flagSysCtlRemove, "Remove a Sysctl option")
|
||||||
flags.SetAnnotation(flagSysCtlRemove, "version", []string{"1.40"})
|
flags.SetAnnotation(flagSysCtlRemove, "version", []string{"1.40"})
|
||||||
|
flags.Var(&options.ulimits, flagUlimitAdd, "Add or update a ulimit option")
|
||||||
|
flags.SetAnnotation(flagUlimitAdd, "version", []string{"1.41"})
|
||||||
|
flags.Var(newListOptsVar(), flagUlimitRemove, "Remove a ulimit option")
|
||||||
|
flags.SetAnnotation(flagUlimitRemove, "version", []string{"1.41"})
|
||||||
|
|
||||||
// Add needs parsing, Remove only needs the key
|
// Add needs parsing, Remove only needs the key
|
||||||
flags.Var(newListOptsVar(), flagGenericResourcesRemove, "Remove a Generic resource")
|
flags.Var(newListOptsVar(), flagGenericResourcesRemove, "Remove a Generic resource")
|
||||||
|
@ -344,6 +349,7 @@ func updateService(ctx context.Context, apiClient client.NetworkAPIClient, flags
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSysCtls(flags, &task.ContainerSpec.Sysctls)
|
updateSysCtls(flags, &task.ContainerSpec.Sysctls)
|
||||||
|
task.ContainerSpec.Ulimits = updateUlimits(flags, task.ContainerSpec.Ulimits)
|
||||||
|
|
||||||
if anyChanged(flags, flagLimitCPU, flagLimitMemory, flagLimitPids) {
|
if anyChanged(flags, flagLimitCPU, flagLimitMemory, flagLimitPids) {
|
||||||
taskResources().Limits = spec.TaskTemplate.Resources.Limits
|
taskResources().Limits = spec.TaskTemplate.Resources.Limits
|
||||||
|
@ -700,6 +706,35 @@ func updateSysCtls(flags *pflag.FlagSet, field *map[string]string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateUlimits(flags *pflag.FlagSet, ulimits []*units.Ulimit) []*units.Ulimit {
|
||||||
|
newUlimits := make(map[string]*units.Ulimit)
|
||||||
|
|
||||||
|
for _, ulimit := range ulimits {
|
||||||
|
newUlimits[ulimit.Name] = ulimit
|
||||||
|
}
|
||||||
|
if flags.Changed(flagUlimitRemove) {
|
||||||
|
values := flags.Lookup(flagUlimitRemove).Value.(*opts.ListOpts).GetAll()
|
||||||
|
for key := range opts.ConvertKVStringsToMap(values) {
|
||||||
|
delete(newUlimits, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if flags.Changed(flagUlimitAdd) {
|
||||||
|
for _, ulimit := range flags.Lookup(flagUlimitAdd).Value.(*opts.UlimitOpt).GetList() {
|
||||||
|
newUlimits[ulimit.Name] = ulimit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var limits []*units.Ulimit
|
||||||
|
for _, ulimit := range newUlimits {
|
||||||
|
limits = append(limits, ulimit)
|
||||||
|
}
|
||||||
|
sort.SliceStable(limits, func(i, j int) bool {
|
||||||
|
return limits[i].Name < limits[j].Name
|
||||||
|
})
|
||||||
|
return limits
|
||||||
|
}
|
||||||
|
|
||||||
func updateEnvironment(flags *pflag.FlagSet, field *[]string) {
|
func updateEnvironment(flags *pflag.FlagSet, field *[]string) {
|
||||||
toRemove := buildToRemoveSet(flags, flagEnvRemove)
|
toRemove := buildToRemoveSet(flags, flagEnvRemove)
|
||||||
*field = removeItems(*field, toRemove, envKey)
|
*field = removeItems(*field, toRemove, envKey)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
mounttypes "github.com/docker/docker/api/types/mount"
|
mounttypes "github.com/docker/docker/api/types/mount"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/docker/go-units"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
is "gotest.tools/v3/assert/cmp"
|
is "gotest.tools/v3/assert/cmp"
|
||||||
)
|
)
|
||||||
|
@ -1541,3 +1542,120 @@ func TestUpdateCaps(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateUlimits(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
spec []*units.Ulimit
|
||||||
|
rm []string
|
||||||
|
add []string
|
||||||
|
expected []*units.Ulimit
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "from scratch",
|
||||||
|
add: []string{"nofile=512:1024", "core=1024:1024"},
|
||||||
|
expected: []*units.Ulimit{
|
||||||
|
{Name: "core", Hard: 1024, Soft: 1024},
|
||||||
|
{Name: "nofile", Hard: 1024, Soft: 512},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "append new",
|
||||||
|
spec: []*units.Ulimit{
|
||||||
|
{Name: "nofile", Hard: 1024, Soft: 512},
|
||||||
|
},
|
||||||
|
add: []string{"core=1024:1024"},
|
||||||
|
expected: []*units.Ulimit{
|
||||||
|
{Name: "core", Hard: 1024, Soft: 1024},
|
||||||
|
{Name: "nofile", Hard: 1024, Soft: 512},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove and append new should append",
|
||||||
|
spec: []*units.Ulimit{
|
||||||
|
{Name: "core", Hard: 1024, Soft: 1024},
|
||||||
|
{Name: "nofile", Hard: 1024, Soft: 512},
|
||||||
|
},
|
||||||
|
rm: []string{"nofile=512:1024"},
|
||||||
|
add: []string{"nofile=512:1024"},
|
||||||
|
expected: []*units.Ulimit{
|
||||||
|
{Name: "core", Hard: 1024, Soft: 1024},
|
||||||
|
{Name: "nofile", Hard: 1024, Soft: 512},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "update existing",
|
||||||
|
spec: []*units.Ulimit{
|
||||||
|
{Name: "nofile", Hard: 2048, Soft: 1024},
|
||||||
|
},
|
||||||
|
add: []string{"nofile=512:1024"},
|
||||||
|
expected: []*units.Ulimit{
|
||||||
|
{Name: "nofile", Hard: 1024, Soft: 512},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "update existing twice",
|
||||||
|
spec: []*units.Ulimit{
|
||||||
|
{Name: "nofile", Hard: 2048, Soft: 1024},
|
||||||
|
},
|
||||||
|
add: []string{"nofile=256:512", "nofile=512:1024"},
|
||||||
|
expected: []*units.Ulimit{
|
||||||
|
{Name: "nofile", Hard: 1024, Soft: 512},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove all",
|
||||||
|
spec: []*units.Ulimit{
|
||||||
|
{Name: "core", Hard: 1024, Soft: 1024},
|
||||||
|
{Name: "nofile", Hard: 1024, Soft: 512},
|
||||||
|
},
|
||||||
|
rm: []string{"nofile=512:1024", "core=1024:1024"},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove by key",
|
||||||
|
spec: []*units.Ulimit{
|
||||||
|
{Name: "core", Hard: 1024, Soft: 1024},
|
||||||
|
{Name: "nofile", Hard: 1024, Soft: 512},
|
||||||
|
},
|
||||||
|
rm: []string{"core"},
|
||||||
|
expected: []*units.Ulimit{
|
||||||
|
{Name: "nofile", Hard: 1024, Soft: 512},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove by key and different value",
|
||||||
|
spec: []*units.Ulimit{
|
||||||
|
{Name: "core", Hard: 1024, Soft: 1024},
|
||||||
|
{Name: "nofile", Hard: 1024, Soft: 512},
|
||||||
|
},
|
||||||
|
rm: []string{"core=1234:5678"},
|
||||||
|
expected: []*units.Ulimit{
|
||||||
|
{Name: "nofile", Hard: 1024, Soft: 512},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
svc := swarm.ServiceSpec{
|
||||||
|
TaskTemplate: swarm.TaskSpec{
|
||||||
|
ContainerSpec: &swarm.ContainerSpec{Ulimits: tc.spec},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
flags := newUpdateCommand(nil).Flags()
|
||||||
|
for _, v := range tc.add {
|
||||||
|
assert.NilError(t, flags.Set(flagUlimitAdd, v))
|
||||||
|
}
|
||||||
|
for _, v := range tc.rm {
|
||||||
|
assert.NilError(t, flags.Set(flagUlimitRemove, v))
|
||||||
|
}
|
||||||
|
err := updateService(ctx, &fakeClient{}, flags, &svc)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, svc.TaskTemplate.ContainerSpec.Ulimits, tc.expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3754,6 +3754,7 @@ _docker_service_update_and_create() {
|
||||||
--publish -p
|
--publish -p
|
||||||
--secret
|
--secret
|
||||||
--sysctl
|
--sysctl
|
||||||
|
--ulimit
|
||||||
"
|
"
|
||||||
|
|
||||||
case "$prev" in
|
case "$prev" in
|
||||||
|
@ -3806,6 +3807,8 @@ _docker_service_update_and_create() {
|
||||||
--secret-rm
|
--secret-rm
|
||||||
--sysctl-add
|
--sysctl-add
|
||||||
--sysctl-rm
|
--sysctl-rm
|
||||||
|
--ulimit-add
|
||||||
|
--ulimit-rm
|
||||||
"
|
"
|
||||||
|
|
||||||
boolean_options="$boolean_options
|
boolean_options="$boolean_options
|
||||||
|
|
|
@ -74,6 +74,7 @@ Options:
|
||||||
--stop-signal string Signal to stop the container
|
--stop-signal string Signal to stop the container
|
||||||
--sysctl list Sysctl options
|
--sysctl list Sysctl options
|
||||||
-t, --tty Allocate a pseudo-TTY
|
-t, --tty Allocate a pseudo-TTY
|
||||||
|
--ulimit ulimit Ulimit options (default [])
|
||||||
--update-delay duration Delay between updates (ns|us|ms|s|m|h) (default 0s)
|
--update-delay duration Delay between updates (ns|us|ms|s|m|h) (default 0s)
|
||||||
--update-failure-action string Action on update failure ("pause"|"continue"|"rollback") (default "pause")
|
--update-failure-action string Action on update failure ("pause"|"continue"|"rollback") (default "pause")
|
||||||
--update-max-failure-ratio float Failure rate to tolerate during an update (default 0)
|
--update-max-failure-ratio float Failure rate to tolerate during an update (default 0)
|
||||||
|
|
|
@ -91,6 +91,8 @@ Options:
|
||||||
--sysctl-add list Add or update a Sysctl option
|
--sysctl-add list Add or update a Sysctl option
|
||||||
--sysctl-rm list Remove a Sysctl option
|
--sysctl-rm list Remove a Sysctl option
|
||||||
-t, --tty Allocate a pseudo-TTY
|
-t, --tty Allocate a pseudo-TTY
|
||||||
|
--ulimit-add ulimit Add or update a ulimit option (default [])
|
||||||
|
--ulimit-rm list Remove a ulimit option
|
||||||
--update-delay duration Delay between updates (ns|us|ms|s|m|h)
|
--update-delay duration Delay between updates (ns|us|ms|s|m|h)
|
||||||
--update-failure-action string Action on update failure ("pause"|"continue"|"rollback")
|
--update-failure-action string Action on update failure ("pause"|"continue"|"rollback")
|
||||||
--update-max-failure-ratio float Failure rate to tolerate during an update
|
--update-max-failure-ratio float Failure rate to tolerate during an update
|
||||||
|
|
Loading…
Reference in New Issue