Explorar o código

Implemet docker update command

It's used for updating properties of one or more containers, we only
support resource configs for now. It can be extended in the future.

Signed-off-by: Qiang Huang <h.huangqiang@huawei.com>
Qiang Huang %!s(int64=9) %!d(string=hai) anos
pai
achega
8799c4fc0f

+ 1 - 0
api/client/client.go

@@ -43,6 +43,7 @@ type apiClient interface {
 	ContainerStop(containerID string, timeout int) error
 	ContainerTop(containerID string, arguments []string) (types.ContainerProcessList, error)
 	ContainerUnpause(containerID string) error
+	ContainerUpdate(containerID string, hostConfig container.HostConfig) error
 	ContainerWait(containerID string) (int, error)
 	CopyFromContainer(containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
 	CopyToContainer(options types.CopyToContainerOptions) error

+ 12 - 0
api/client/lib/container_update.go

@@ -0,0 +1,12 @@
+package lib
+
+import (
+	"github.com/docker/docker/api/types/container"
+)
+
+// ContainerUpdate updates resources of a container
+func (cli *Client) ContainerUpdate(containerID string, hostConfig container.HostConfig) error {
+	resp, err := cli.post("/containers/"+containerID+"/update", nil, hostConfig, nil)
+	ensureReaderClosed(resp)
+	return err
+}

+ 104 - 0
api/client/update.go

@@ -0,0 +1,104 @@
+package client
+
+import (
+	"fmt"
+
+	"github.com/docker/docker/api/types/container"
+	Cli "github.com/docker/docker/cli"
+	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/docker/go-units"
+)
+
+// CmdUpdate updates resources of one or more containers.
+//
+// Usage: docker update [OPTIONS] CONTAINER [CONTAINER...]
+func (cli *DockerCli) CmdUpdate(args ...string) error {
+	cmd := Cli.Subcmd("update", []string{"CONTAINER [CONTAINER...]"}, Cli.DockerCommands["update"].Description, true)
+	flBlkioWeight := cmd.Uint16([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000")
+	flCPUPeriod := cmd.Int64([]string{"-cpu-period"}, 0, "Limit CPU CFS (Completely Fair Scheduler) period")
+	flCPUQuota := cmd.Int64([]string{"-cpu-quota"}, 0, "Limit CPU CFS (Completely Fair Scheduler) quota")
+	flCpusetCpus := cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)")
+	flCpusetMems := cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)")
+	flCPUShares := cmd.Int64([]string{"#c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
+	flMemoryString := cmd.String([]string{"m", "-memory"}, "", "Memory limit")
+	flMemoryReservation := cmd.String([]string{"-memory-reservation"}, "", "Memory soft limit")
+	flMemorySwap := cmd.String([]string{"-memory-swap"}, "", "Total memory (memory + swap), '-1' to disable swap")
+	flKernelMemory := cmd.String([]string{"-kernel-memory"}, "", "Kernel memory limit")
+
+	cmd.Require(flag.Min, 1)
+	cmd.ParseFlags(args, true)
+	if cmd.NFlag() == 0 {
+		return fmt.Errorf("You must provide one or more flags when using this command.")
+	}
+
+	var err error
+	var flMemory int64
+	if *flMemoryString != "" {
+		flMemory, err = units.RAMInBytes(*flMemoryString)
+		if err != nil {
+			return err
+		}
+	}
+
+	var memoryReservation int64
+	if *flMemoryReservation != "" {
+		memoryReservation, err = units.RAMInBytes(*flMemoryReservation)
+		if err != nil {
+			return err
+		}
+	}
+
+	var memorySwap int64
+	if *flMemorySwap != "" {
+		if *flMemorySwap == "-1" {
+			memorySwap = -1
+		} else {
+			memorySwap, err = units.RAMInBytes(*flMemorySwap)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	var kernelMemory int64
+	if *flKernelMemory != "" {
+		kernelMemory, err = units.RAMInBytes(*flKernelMemory)
+		if err != nil {
+			return err
+		}
+	}
+
+	resources := container.Resources{
+		BlkioWeight:       *flBlkioWeight,
+		CpusetCpus:        *flCpusetCpus,
+		CpusetMems:        *flCpusetMems,
+		CPUShares:         *flCPUShares,
+		Memory:            flMemory,
+		MemoryReservation: memoryReservation,
+		MemorySwap:        memorySwap,
+		KernelMemory:      kernelMemory,
+		CPUPeriod:         *flCPUPeriod,
+		CPUQuota:          *flCPUQuota,
+	}
+
+	hostConfig := container.HostConfig{
+		Resources: resources,
+	}
+
+	names := cmd.Args()
+	var errNames []string
+	for _, name := range names {
+		if err := cli.client.ContainerUpdate(name, hostConfig); err != nil {
+			fmt.Fprintf(cli.err, "%s\n", err)
+			errNames = append(errNames, name)
+		} else {
+			fmt.Fprintf(cli.out, "%s\n", name)
+		}
+	}
+
+	if len(errNames) > 0 {
+		return fmt.Errorf("Error: failed to update resources of containers: %v", errNames)
+	}
+
+	return nil
+}

+ 1 - 0
api/server/router/container/backend.go

@@ -42,6 +42,7 @@ type stateBackend interface {
 	ContainerStart(name string, hostConfig *container.HostConfig) error
 	ContainerStop(name string, seconds int) error
 	ContainerUnpause(name string) error
+	ContainerUpdate(name string, hostConfig *container.HostConfig) ([]string, error)
 	ContainerWait(name string, timeout time.Duration) (int, error)
 	Exists(id string) bool
 	IsPaused(id string) bool

+ 1 - 0
api/server/router/container/container.go

@@ -57,6 +57,7 @@ func (r *containerRouter) initRoutes() {
 		local.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
 		local.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
 		local.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
+		local.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate),
 		// PUT
 		local.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
 		// DELETE

+ 24 - 0
api/server/router/container/container_routes.go

@@ -323,6 +323,30 @@ func (s *containerRouter) postContainerRename(ctx context.Context, w http.Respon
 	return nil
 }
 
+func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	if err := httputils.ParseForm(r); err != nil {
+		return err
+	}
+	if err := httputils.CheckForJSON(r); err != nil {
+		return err
+	}
+
+	_, hostConfig, err := runconfig.DecodeContainerConfig(r.Body)
+	if err != nil {
+		return err
+	}
+
+	name := vars["name"]
+	warnings, err := s.backend.ContainerUpdate(name, hostConfig)
+	if err != nil {
+		return err
+	}
+
+	return httputils.WriteJSON(w, http.StatusOK, &types.ContainerUpdateResponse{
+		Warnings: warnings,
+	})
+}
+
 func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := httputils.ParseForm(r); err != nil {
 		return err

+ 7 - 0
api/types/types.go

@@ -28,6 +28,13 @@ type ContainerExecCreateResponse struct {
 	ID string `json:"Id"`
 }
 
+// ContainerUpdateResponse contains response of Remote API:
+// POST /containers/{name:.*}/update
+type ContainerUpdateResponse struct {
+	// Warnings are any warnings encountered during the updating of the container.
+	Warnings []string `json:"Warnings"`
+}
+
 // AuthResponse contains response of Remote API:
 // POST "/auth"
 type AuthResponse struct {

+ 1 - 0
cli/common.go

@@ -64,6 +64,7 @@ var dockerCommands = []Command{
 	{"tag", "Tag an image into a repository"},
 	{"top", "Display the running processes of a container"},
 	{"unpause", "Unpause all processes within a container"},
+	{"update", "Update resources of one or more containers"},
 	{"version", "Show the Docker version information"},
 	{"volume", "Manage Docker volumes"},
 	{"wait", "Block until a container stops, then print its exit code"},

+ 70 - 0
container/container_unix.go

@@ -13,6 +13,7 @@ import (
 	"syscall"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/daemon/execdriver"
 	derr "github.com/docker/docker/errors"
@@ -543,6 +544,75 @@ func (container *Container) IpcMounts() []execdriver.Mount {
 	return mounts
 }
 
+func updateCommand(c *execdriver.Command, resources container.Resources) {
+	c.Resources.BlkioWeight = resources.BlkioWeight
+	c.Resources.CPUShares = resources.CPUShares
+	c.Resources.CPUPeriod = resources.CPUPeriod
+	c.Resources.CPUQuota = resources.CPUQuota
+	c.Resources.CpusetCpus = resources.CpusetCpus
+	c.Resources.CpusetMems = resources.CpusetMems
+	c.Resources.Memory = resources.Memory
+	c.Resources.MemorySwap = resources.MemorySwap
+	c.Resources.MemoryReservation = resources.MemoryReservation
+	c.Resources.KernelMemory = resources.KernelMemory
+}
+
+// UpdateContainer updates resources of a container.
+func (container *Container) UpdateContainer(hostConfig *container.HostConfig) error {
+	container.Lock()
+
+	resources := hostConfig.Resources
+	cResources := &container.HostConfig.Resources
+	if resources.BlkioWeight != 0 {
+		cResources.BlkioWeight = resources.BlkioWeight
+	}
+	if resources.CPUShares != 0 {
+		cResources.CPUShares = resources.CPUShares
+	}
+	if resources.CPUPeriod != 0 {
+		cResources.CPUPeriod = resources.CPUPeriod
+	}
+	if resources.CPUQuota != 0 {
+		cResources.CPUQuota = resources.CPUQuota
+	}
+	if resources.CpusetCpus != "" {
+		cResources.CpusetCpus = resources.CpusetCpus
+	}
+	if resources.CpusetMems != "" {
+		cResources.CpusetMems = resources.CpusetMems
+	}
+	if resources.Memory != 0 {
+		cResources.Memory = resources.Memory
+	}
+	if resources.MemorySwap != 0 {
+		cResources.MemorySwap = resources.MemorySwap
+	}
+	if resources.MemoryReservation != 0 {
+		cResources.MemoryReservation = resources.MemoryReservation
+	}
+	if resources.KernelMemory != 0 {
+		cResources.KernelMemory = resources.KernelMemory
+	}
+	container.Unlock()
+
+	// If container is not running, update hostConfig struct is enough,
+	// resources will be updated when the container is started again.
+	// If container is running (including paused), we need to update
+	// the command so we can update configs to the real world.
+	if container.IsRunning() {
+		container.Lock()
+		updateCommand(container.Command, resources)
+		container.Unlock()
+	}
+
+	if err := container.ToDiskLocking(); err != nil {
+		logrus.Errorf("Error saving updated container: %v", err)
+		return err
+	}
+
+	return nil
+}
+
 func detachMounted(path string) error {
 	return syscall.Unmount(path, syscall.MNT_DETACH)
 }

+ 6 - 0
container/container_windows.go

@@ -3,6 +3,7 @@
 package container
 
 import (
+	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/daemon/execdriver"
 	"github.com/docker/docker/volume"
 )
@@ -47,6 +48,11 @@ func (container *Container) TmpfsMounts() []execdriver.Mount {
 	return nil
 }
 
+// UpdateContainer updates resources of a container
+func (container *Container) UpdateContainer(hostConfig *container.HostConfig) error {
+	return nil
+}
+
 // appendNetworkMounts appends any network mounts to the array of mount points passed in.
 // Windows does not support network mounts (not to be confused with SMB network mounts), so
 // this is a no-op.

+ 3 - 0
daemon/execdriver/driver.go

@@ -91,6 +91,9 @@ type Driver interface {
 	// Stats returns resource stats for a running container
 	Stats(id string) (*ResourceStats, error)
 
+	// Update updates resource configs for a container
+	Update(c *Command) error
+
 	// SupportsHooks refers to the driver capability to exploit pre/post hook functionality
 	SupportsHooks() bool
 }

+ 20 - 0
daemon/execdriver/native/driver.go

@@ -398,6 +398,26 @@ func (d *Driver) Stats(id string) (*execdriver.ResourceStats, error) {
 	}, nil
 }
 
+// Update updates configs for a container
+func (d *Driver) Update(c *execdriver.Command) error {
+	d.Lock()
+	cont := d.activeContainers[c.ID]
+	d.Unlock()
+	if cont == nil {
+		return execdriver.ErrNotRunning
+	}
+	config := cont.Config()
+	if err := execdriver.SetupCgroups(&config, c); err != nil {
+		return err
+	}
+
+	if err := cont.Set(config); err != nil {
+		return err
+	}
+
+	return nil
+}
+
 // TtyConsole implements the exec driver Terminal interface.
 type TtyConsole struct {
 	console libcontainer.Console

+ 14 - 0
daemon/execdriver/windows/update.go

@@ -0,0 +1,14 @@
+// +build windows
+
+package windows
+
+import (
+	"fmt"
+
+	"github.com/docker/docker/daemon/execdriver"
+)
+
+// Update updates resource configs for a container.
+func (d *Driver) Update(c *execdriver.Command) error {
+	return fmt.Errorf("Windows: Update not implemented")
+}

+ 58 - 0
daemon/update.go

@@ -0,0 +1,58 @@
+package daemon
+
+import (
+	"fmt"
+
+	"github.com/docker/docker/api/types/container"
+)
+
+// ContainerUpdate updates resources of the container
+func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig) ([]string, error) {
+	var warnings []string
+
+	warnings, err := daemon.verifyContainerSettings(hostConfig, nil)
+	if err != nil {
+		return warnings, err
+	}
+
+	if err := daemon.update(name, hostConfig); err != nil {
+		return warnings, err
+	}
+
+	return warnings, nil
+}
+
+func (daemon *Daemon) update(name string, hostConfig *container.HostConfig) error {
+	if hostConfig == nil {
+		return nil
+	}
+
+	container, err := daemon.GetContainer(name)
+	if err != nil {
+		return err
+	}
+
+	if container.RemovalInProgress || container.Dead {
+		return fmt.Errorf("Container is marked for removal and cannot be \"update\".")
+	}
+
+	if container.IsRunning() && hostConfig.KernelMemory != 0 {
+		return fmt.Errorf("Can not update kernel memory to a running container, please stop it first.")
+	}
+
+	if err := container.UpdateContainer(hostConfig); err != nil {
+		return err
+	}
+
+	// If container is not running, update hostConfig struct is enough,
+	// resources will be updated when the container is started again.
+	// If container is running (including paused), we need to update configs
+	// to the real world.
+	if container.IsRunning() {
+		if err := daemon.execDriver.Update(container.Command); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

+ 44 - 0
docs/reference/api/docker_remote_api_v1.22.md

@@ -1003,6 +1003,50 @@ Status Codes:
 -   **404** – no such container
 -   **500** – server error
 
+### Update a container
+
+`POST /containers/(id)/update`
+
+Update resource configs of one or more containers.
+
+**Example request**:
+
+       POST /containers/(id)/update HTTP/1.1
+       Content-Type: application/json
+
+       {
+           "HostConfig": {
+               "Resources": {
+                   "BlkioWeight": 300,
+                   "CpuShares": 512,
+                   "CpuPeriod": 100000,
+                   "CpuQuota": 50000,
+                   "CpusetCpus": "0,1",
+                   "CpusetMems": "0",
+                   "Memory": 314572800,
+                   "MemorySwap": 514288000,
+                   "MemoryReservation": 209715200,
+                   "KernelMemory": 52428800,
+               }
+           }
+       }
+
+**Example response**:
+
+       HTTP/1.1 200 OK
+       Content-Type: application/json
+
+       {
+           "Warnings": []
+       }
+
+Status Codes:
+
+-   **200** – no error
+-   **400** – bad parameter
+-   **404** – no such container
+-   **500** – server error
+
 ### Rename a container
 
 `POST /containers/(id)/rename`

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
docs/reference/api/images/event_state.gliffy


+ 1 - 0
docs/reference/commandline/index.md

@@ -59,6 +59,7 @@ You start the Docker daemon with the command line. How you start the daemon affe
 * [stop](stop.md)
 * [top](top.md)
 * [unpause](unpause.md)
+* [update](update.md)
 * [wait](wait.md)
 
 ### Hub and registry commands

+ 61 - 0
docs/reference/commandline/update.md

@@ -0,0 +1,61 @@
+<!--[metadata]>
++++
+title = "update"
+description = "The update command description and usage"
+keywords = ["resources, update, dynamically"]
+[menu.main]
+parent = "smn_cli"
++++
+<![end-metadata]-->
+
+## update
+
+    Usage: docker update [OPTIONS] CONTAINER [CONTAINER...]
+
+    Updates container resource limits
+
+      --help=false               Print usage
+      --blkio-weight=0           Block IO (relative weight), between 10 and 1000
+      --cpu-shares=0             CPU shares (relative weight)
+      --cpu-period=0             Limit the CPU CFS (Completely Fair Scheduler) period
+      --cpu-quota=0              Limit the CPU CFS (Completely Fair Scheduler) quota
+      --cpuset-cpus=""           CPUs in which to allow execution (0-3, 0,1)
+      --cpuset-mems=""           Memory nodes (MEMs) in which to allow execution (0-3, 0,1)
+      -m, --memory=""            Memory limit
+      --memory-reservation=""    Memory soft limit
+      --memory-swap=""           Total memory (memory + swap), '-1' to disable swap
+      --kernel-memory=""         Kernel memory limit: container must be stopped
+
+The `docker update` command dynamically updates container resources.  Use this
+command to prevent containers from consuming too many resources from their
+Docker host.  With a single command, you can place limits on a single
+container or on many. To specify more than one container, provide
+space-separated list of container names or IDs.
+
+With the exception of the `--kernel-memory` value, you can specify these
+options on a running or a stopped container. You can only update
+`--kernel-memory` on a stopped container. When you run `docker update` on
+stopped container, the next time you restart it, the container uses those
+values.
+
+## EXAMPLES
+
+The following sections illustrate ways to use this command.
+
+### Update a container with cpu-shares=512
+
+To limit a container's cpu-shares to 512, first identify the container
+name or ID. You can use **docker ps** to find these values. You can also
+use the ID returned from the **docker run** command.  Then, do the following:
+
+```bash
+$ docker update --cpu-shares 512 abebf7571666
+```
+
+### Update a container with cpu-shares and memory
+
+To update multiple resource configurations for multiple containers:
+
+```bash
+$ docker update --cpu-shares 512 -m 300M abebf7571666 hopeful_morse
+```

+ 43 - 0
integration-cli/docker_api_update_unix_test.go

@@ -0,0 +1,43 @@
+// +build !windows
+
+package main
+
+import (
+	"strings"
+
+	"github.com/docker/docker/pkg/integration/checker"
+	"github.com/go-check/check"
+)
+
+func (s *DockerSuite) TestApiUpdateContainer(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	testRequires(c, memoryLimitSupport)
+	testRequires(c, swapMemorySupport)
+
+	name := "apiUpdateContainer"
+	hostConfig := map[string]interface{}{
+		"Memory":     314572800,
+		"MemorySwap": 524288000,
+	}
+	dockerCmd(c, "run", "-d", "--name", name, "-m", "200M", "busybox", "top")
+	_, _, err := sockRequest("POST", "/containers/"+name+"/update", hostConfig)
+	c.Assert(err, check.IsNil)
+
+	memory, err := inspectField(name, "HostConfig.Memory")
+	c.Assert(err, check.IsNil)
+	if memory != "314572800" {
+		c.Fatalf("Got the wrong memory value, we got %d, expected 314572800(300M).", memory)
+	}
+	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
+	out, _ := dockerCmd(c, "exec", name, "cat", file)
+	c.Assert(strings.TrimSpace(out), checker.Equals, "314572800")
+
+	memorySwap, err := inspectField(name, "HostConfig.MemorySwap")
+	c.Assert(err, check.IsNil)
+	if memorySwap != "524288000" {
+		c.Fatalf("Got the wrong memorySwap value, we got %d, expected 524288000(500M).", memorySwap)
+	}
+	file = "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"
+	out, _ = dockerCmd(c, "exec", name, "cat", file)
+	c.Assert(strings.TrimSpace(out), checker.Equals, "524288000")
+}

+ 1 - 1
integration-cli/docker_cli_help_test.go

@@ -226,7 +226,7 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
 		}
 
 		// Number of commands for standard release and experimental release
-		standard := 40
+		standard := 41
 		experimental := 1
 		expected := standard + experimental
 		if isLocalDaemon {

+ 162 - 0
integration-cli/docker_cli_update_unix_test.go

@@ -0,0 +1,162 @@
+// +build !windows
+
+package main
+
+import (
+	"strings"
+
+	"github.com/docker/docker/pkg/integration/checker"
+	"github.com/go-check/check"
+)
+
+func (s *DockerSuite) TestUpdateRunningContainer(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	testRequires(c, memoryLimitSupport)
+
+	name := "test-update-container"
+	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
+	dockerCmd(c, "update", "-m", "500M", name)
+
+	memory, err := inspectField(name, "HostConfig.Memory")
+	c.Assert(err, check.IsNil)
+	if memory != "524288000" {
+		c.Fatalf("Got the wrong memory value, we got %d, expected 524288000(500M).", memory)
+	}
+
+	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
+	out, _ := dockerCmd(c, "exec", name, "cat", file)
+	c.Assert(strings.TrimSpace(out), checker.Equals, "524288000")
+}
+
+func (s *DockerSuite) TestUpdateRunningContainerWithRestart(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	testRequires(c, memoryLimitSupport)
+
+	name := "test-update-container"
+	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
+	dockerCmd(c, "update", "-m", "500M", name)
+	dockerCmd(c, "restart", name)
+
+	memory, err := inspectField(name, "HostConfig.Memory")
+	c.Assert(err, check.IsNil)
+	if memory != "524288000" {
+		c.Fatalf("Got the wrong memory value, we got %d, expected 524288000(500M).", memory)
+	}
+
+	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
+	out, _ := dockerCmd(c, "exec", name, "cat", file)
+	c.Assert(strings.TrimSpace(out), checker.Equals, "524288000")
+}
+
+func (s *DockerSuite) TestUpdateStoppedContainer(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	testRequires(c, memoryLimitSupport)
+
+	name := "test-update-container"
+	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
+	dockerCmd(c, "run", "--name", name, "-m", "300M", "busybox", "cat", file)
+	dockerCmd(c, "update", "-m", "500M", name)
+
+	memory, err := inspectField(name, "HostConfig.Memory")
+	c.Assert(err, check.IsNil)
+	if memory != "524288000" {
+		c.Fatalf("Got the wrong memory value, we got %d, expected 524288000(500M).", memory)
+	}
+
+	out, _ := dockerCmd(c, "start", "-a", name)
+	c.Assert(strings.TrimSpace(out), checker.Equals, "524288000")
+}
+
+func (s *DockerSuite) TestUpdatePausedContainer(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	testRequires(c, cpuShare)
+
+	name := "test-update-container"
+	dockerCmd(c, "run", "-d", "--name", name, "--cpu-shares", "1000", "busybox", "top")
+	dockerCmd(c, "pause", name)
+	dockerCmd(c, "update", "--cpu-shares", "500", name)
+
+	out, err := inspectField(name, "HostConfig.CPUShares")
+	c.Assert(err, check.IsNil)
+	if out != "500" {
+		c.Fatalf("Got the wrong cpu shares value, we got %d, expected 500.", out)
+	}
+
+	dockerCmd(c, "unpause", name)
+	file := "/sys/fs/cgroup/cpu/cpu.shares"
+	out, _ = dockerCmd(c, "exec", name, "cat", file)
+	c.Assert(strings.TrimSpace(out), checker.Equals, "500")
+}
+
+func (s *DockerSuite) TestUpdateWithUntouchedFields(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	testRequires(c, memoryLimitSupport)
+	testRequires(c, cpuShare)
+
+	name := "test-update-container"
+	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "--cpu-shares", "800", "busybox", "top")
+	dockerCmd(c, "update", "-m", "500M", name)
+
+	// Update memory and not touch cpus, `cpuset.cpus` should still have the old value
+	out, err := inspectField(name, "HostConfig.CPUShares")
+	c.Assert(err, check.IsNil)
+	c.Assert(out, check.Equals, "800")
+
+	file := "/sys/fs/cgroup/cpu/cpu.shares"
+	out, _ = dockerCmd(c, "exec", name, "cat", file)
+	c.Assert(strings.TrimSpace(out), checker.Equals, "800")
+}
+
+func (s *DockerSuite) TestUpdateContainerInvalidValue(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	testRequires(c, memoryLimitSupport)
+
+	name := "test-update-container"
+	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "true")
+	out, _, err := dockerCmdWithError("update", "-m", "2M", name)
+	c.Assert(err, check.NotNil)
+	expected := "Minimum memory limit allowed is 4MB"
+	c.Assert(out, checker.Contains, expected)
+}
+
+func (s *DockerSuite) TestUpdateContainerWithoutFlags(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	testRequires(c, memoryLimitSupport)
+
+	name := "test-update-container"
+	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "true")
+	_, _, err := dockerCmdWithError("update", name)
+	c.Assert(err, check.NotNil)
+}
+
+func (s *DockerSuite) TestUpdateKernelMemory(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	testRequires(c, kernelMemorySupport)
+
+	name := "test-update-container"
+	dockerCmd(c, "run", "-d", "--name", name, "--kernel-memory", "50M", "busybox", "top")
+	_, _, err := dockerCmdWithError("update", "--kernel-memory", "100M", name)
+	// Update kernel memory to a running container is not allowed.
+	c.Assert(err, check.NotNil)
+
+	out, err := inspectField(name, "HostConfig.KernelMemory")
+	c.Assert(err, check.IsNil)
+	// Update kernel memory to a running container with failure should not change HostConfig
+	if out != "52428800" {
+		c.Fatalf("Got the wrong memory value, we got %d, expected 52428800(50M).", out)
+	}
+
+	dockerCmd(c, "stop", name)
+	dockerCmd(c, "update", "--kernel-memory", "100M", name)
+	dockerCmd(c, "start", name)
+
+	out, err = inspectField(name, "HostConfig.KernelMemory")
+	c.Assert(err, check.IsNil)
+	if out != "104857600" {
+		c.Fatalf("Got the wrong memory value, we got %d, expected 104857600(100M).", out)
+	}
+
+	file := "/sys/fs/cgroup/memory/memory.kmem.limit_in_bytes"
+	out, _ = dockerCmd(c, "exec", name, "cat", file)
+	c.Assert(strings.TrimSpace(out), checker.Equals, "104857600")
+}

+ 93 - 0
man/docker-update.1.md

@@ -0,0 +1,93 @@
+% DOCKER(1) Docker User Manuals
+% Docker Community
+% JUNE 2014
+# NAME
+docker-update - Update resource configs of one or more containers
+
+# SYNOPSIS
+**docker update**
+[**--blkio-weight**[=*[BLKIO-WEIGHT]*]]
+[**--cpu-shares**[=*0*]]
+[**--cpu-period**[=*0*]]
+[**--cpu-quota**[=*0*]]
+[**--cpuset-cpus**[=*CPUSET-CPUS*]]
+[**--cpuset-mems**[=*CPUSET-MEMS*]]
+[**--help**]
+[**--kernel-memory**[=*KERNEL-MEMORY*]]
+[**-m**|**--memory**[=*MEMORY*]]
+[**--memory-reservation**[=*MEMORY-RESERVATION*]]
+[**--memory-swap**[=*MEMORY-SWAP*]]
+CONTAINER [CONTAINER...]
+
+# DESCRIPTION
+
+The `docker update` command dynamically updates container resources.  Use this
+command to prevent containers from consuming too many resources from their
+Docker host.  With a single command, you can place limits on a single
+container or on many. To specify more than one container, provide
+space-separated list of container names or IDs.
+
+With the exception of the `--kernel-memory` value, you can specify these
+options on a running or a stopped container. You can only update
+`--kernel-memory` on a stopped container. When you run `docker update` on
+stopped container, the next time you restart it, the container uses those
+values.
+
+# OPTIONS
+**--blkio-weight**=0
+   Block IO weight (relative weight) accepts a weight value between 10 and 1000.
+
+**--cpu-shares**=0
+   CPU shares (relative weight)
+
+**--cpu-period**=0
+   Limit the CPU CFS (Completely Fair Scheduler) period
+
+**--cpu-quota**=0
+   Limit the CPU CFS (Completely Fair Scheduler) quota
+
+**--cpuset-cpus**=""
+   CPUs in which to allow execution (0-3, 0,1)
+
+**--cpuset-mems**=""
+   Memory nodes(MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.
+
+**--help**
+   Print usage statement
+
+**--kernel-memory**=""
+   Kernel memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)
+
+   Note that you can not update kernel memory to a running container, it can only
+be updated to a stopped container, and affect after it's started.
+
+**-m**, **--memory**=""
+   Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
+
+**--memory-reservation**=""
+   Memory soft limit (format: <number>[<unit>], where unit = b, k, m or g)
+
+**--memory-swap**=""
+   Total memory limit (memory + swap)
+
+# EXAMPLES
+
+The following sections illustrate ways to use this command.
+
+### Update a container with cpu-shares=512
+
+To limit a container's cpu-shares to 512, first identify the container
+name or ID. You can use **docker ps** to find these values. You can also
+use the ID returned from the **docker run** command.  Then, do the following:
+
+```bash
+$ docker update --cpu-shares 512 abebf7571666
+```
+
+### Update a container with cpu-shares and memory
+
+To update multiple resource configurations for multiple containers:
+
+```bash
+$ docker update --cpu-shares 512 -m 300M abebf7571666 hopeful_morse
+```

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio