Skip to content

Commit

Permalink
Merge pull request docker#15078 from hqhq/hq_add_set_api_v2
Browse files Browse the repository at this point in the history
Implement docker update command
  • Loading branch information
calavera committed Dec 28, 2015
2 parents 603d488 + 8799c4f commit 8669ea0
Show file tree
Hide file tree
Showing 22 changed files with 728 additions and 2 deletions.
1 change: 1 addition & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions api/client/lib/container_update.go
Original file line number Diff line number Diff line change
@@ -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 changes: 104 additions & 0 deletions api/client/update.go
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions api/server/router/container/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions api/server/router/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions api/server/router/container/container_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions api/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions cli/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand Down
70 changes: 70 additions & 0 deletions container/container_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}
Expand Down
6 changes: 6 additions & 0 deletions container/container_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions daemon/execdriver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
20 changes: 20 additions & 0 deletions daemon/execdriver/native/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions daemon/execdriver/windows/update.go
Original file line number Diff line number Diff line change
@@ -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")
}
Loading

0 comments on commit 8669ea0

Please sign in to comment.