add support some run options

Signed-off-by: Xian Chaobo <xianchaobo@huawei.com>
This commit is contained in:
Xian Chaobo 2016-03-03 19:53:47 +08:00
parent 204a51c805
commit ba4ad74de3
8 changed files with 193 additions and 41 deletions

2
Godeps/Godeps.json generated
View File

@ -171,7 +171,7 @@
}, },
{ {
"ImportPath": "github.com/samalba/dockerclient", "ImportPath": "github.com/samalba/dockerclient",
"Rev": "b5aaea9adc8168fe8ce00731d3025ffbfb389c87" "Rev": "5747eb30667cf7088aea1de37b93856b5cd2f010"
}, },
{ {
"ImportPath": "github.com/samuel/go-zookeeper/zk", "ImportPath": "github.com/samuel/go-zookeeper/zk",

View File

@ -16,7 +16,16 @@ import (
"time" "time"
) )
var _ Client = (*DockerClient)(nil)
const ( const (
// APIVersion is currently hardcoded to v1.15
// TODO: bump the API version or allow users to choose which API version to
// use the client with. The current value does not make sense for many
// methods, such as ContainerStats, StartMonitorStats, and StopAllMonitorStats
// (v1.17) and
// ListVolumes, {Remove,Create}Volume, ListNetworks,
// {Inspect,Create,Connect,Disconnect,Remove}Network (v1.21)
APIVersion = "v1.15" APIVersion = "v1.15"
) )
@ -256,6 +265,36 @@ func (client *DockerClient) ContainerChanges(id string) ([]*ContainerChanges, er
return changes, nil return changes, nil
} }
func (client *DockerClient) ContainerStats(id string, stopChan <-chan struct{}) (<-chan StatsOrError, error) {
uri := fmt.Sprintf("/%s/containers/%s/stats", APIVersion, id)
resp, err := client.HTTPClient.Get(client.URL.String() + uri)
if err != nil {
return nil, err
}
decode := func(decoder *json.Decoder) decodingResult {
var containerStats Stats
if err := decoder.Decode(&containerStats); err != nil {
return decodingResult{err: err}
} else {
return decodingResult{result: containerStats}
}
}
decodingResultChan := client.readJSONStream(resp.Body, decode, stopChan)
statsOrErrorChan := make(chan StatsOrError)
go func() {
for decodingResult := range decodingResultChan {
stats, _ := decodingResult.result.(Stats)
statsOrErrorChan <- StatsOrError{
Stats: stats,
Error: decodingResult.err,
}
}
close(statsOrErrorChan)
}()
return statsOrErrorChan, nil
}
func (client *DockerClient) readJSONStream(stream io.ReadCloser, decode func(*json.Decoder) decodingResult, stopChan <-chan struct{}) <-chan decodingResult { func (client *DockerClient) readJSONStream(stream io.ReadCloser, decode func(*json.Decoder) decodingResult, stopChan <-chan struct{}) <-chan decodingResult {
resultChan := make(chan decodingResult) resultChan := make(chan decodingResult)
@ -719,6 +758,31 @@ func (client *DockerClient) RemoveImage(name string, force bool) ([]*ImageDelete
return imageDelete, nil return imageDelete, nil
} }
func (client *DockerClient) SearchImages(query, registry string, auth *AuthConfig) ([]ImageSearch, error) {
term := query
if registry != "" {
term = registry + "/" + term
}
uri := fmt.Sprintf("/%s/images/search?term=%s", APIVersion, term)
headers := map[string]string{}
if auth != nil {
if encodedAuth, err := auth.encode(); err != nil {
return nil, err
} else {
headers["X-Registry-Auth"] = encodedAuth
}
}
data, err := client.doRequest("GET", uri, nil, headers)
if err != nil {
return nil, err
}
var imageSearches []ImageSearch
if err := json.Unmarshal(data, &imageSearches); err != nil {
return nil, err
}
return imageSearches, nil
}
func (client *DockerClient) PauseContainer(id string) error { func (client *DockerClient) PauseContainer(id string) error {
uri := fmt.Sprintf("/%s/containers/%s/pause", APIVersion, id) uri := fmt.Sprintf("/%s/containers/%s/pause", APIVersion, id)
_, err := client.doRequest("POST", uri, nil, nil) _, err := client.doRequest("POST", uri, nil, nil)

View File

@ -10,4 +10,6 @@ var haproxyPullOutput = `{"status":"The image you are pulling has been verified"
{"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"ecb4b23ca7ce"}{"status":"Already exists","progressDetail":{},"id":"f453e940c177"}{"status":"Already exists","progressDetail":{},"id":"fc5ea1bc05ab"}{"status":"Already exists","progressDetail":{},"id":"380557f8f7b3"}{"status":"Status: Image is up to date for haproxy"} {"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"ecb4b23ca7ce"}{"status":"Already exists","progressDetail":{},"id":"f453e940c177"}{"status":"Already exists","progressDetail":{},"id":"fc5ea1bc05ab"}{"status":"Already exists","progressDetail":{},"id":"380557f8f7b3"}{"status":"Status: Image is up to date for haproxy"}
` `
var statsResp = `{"read":"2015-02-02T17:06:08.187833376-05:00","network":{"rx_bytes":99988,"rx_packets":928,"rx_errors":0,"rx_dropped":0,"tx_bytes":1786548,"tx_packets":877,"tx_errors":0,"tx_dropped":0},"cpu_stats":{"cpu_usage":{"total_usage":170018598,"percpu_usage":[170018598],"usage_in_kernelmode":30000000,"usage_in_usermode":70000000},"system_cpu_usage":9020930000000,"throttling_data":{"periods":0,"throttled_periods":0,"throttled_time":0}},"memory_stats":{"usage":18022400,"max_usage":20541440,"stats":{"active_anon":6213632,"active_file":176128,"cache":11808768,"hierarchical_memory_limit":9223372036854775807,"hierarchical_memsw_limit":9223372036854775807,"inactive_anon":0,"inactive_file":11632640,"mapped_file":5165056,"pgfault":2535,"pgmajfault":13,"pgpgin":4293,"pgpgout":1937,"rss":6213632,"rss_huge":2097152,"swap":0,"total_active_anon":6213632,"total_active_file":176128,"total_cache":11808768,"total_inactive_anon":0,"total_inactive_file":11632640,"total_mapped_file":5165056,"total_pgfault":2535,"total_pgmajfault":13,"total_pgpgin":4293,"total_pgpgout":1937,"total_rss":6213632,"total_rss_huge":2097152,"total_swap":0,"total_unevictable":0,"unevictable":0},"failcnt":0,"limit":1041051648},"blkio_stats":{"io_service_bytes_recursive":[{"major":7,"minor":0,"op":"Read","value":28672},{"major":7,"minor":0,"op":"Write","value":0},{"major":7,"minor":0,"op":"Sync","value":0},{"major":7,"minor":0,"op":"Async","value":28672},{"major":7,"minor":0,"op":"Total","value":28672},{"major":253,"minor":0,"op":"Read","value":28672},{"major":253,"minor":0,"op":"Write","value":0},{"major":253,"minor":0,"op":"Sync","value":0},{"major":253,"minor":0,"op":"Async","value":28672},{"major":253,"minor":0,"op":"Total","value":28672},{"major":253,"minor":7,"op":"Read","value":11718656},{"major":253,"minor":7,"op":"Write","value":0},{"major":253,"minor":7,"op":"Sync","value":0},{"major":253,"minor":7,"op":"Async","value":11718656},{"major":253,"minor":7,"op":"Total","value":11718656},{"major":202,"minor":0,"op":"Read","value":0},{"major":202,"minor":0,"op":"Write","value":0},{"major":202,"minor":0,"op":"Sync","value":0},{"major":202,"minor":0,"op":"Async","value":0},{"major":202,"minor":0,"op":"Total","value":0}],"io_serviced_recursive":[{"major":7,"minor":0,"op":"Read","value":7},{"major":7,"minor":0,"op":"Write","value":0},{"major":7,"minor":0,"op":"Sync","value":0},{"major":7,"minor":0,"op":"Async","value":7},{"major":7,"minor":0,"op":"Total","value":7},{"major":253,"minor":0,"op":"Read","value":7},{"major":253,"minor":0,"op":"Write","value":0},{"major":253,"minor":0,"op":"Sync","value":0},{"major":253,"minor":0,"op":"Async","value":7},{"major":253,"minor":0,"op":"Total","value":7},{"major":253,"minor":7,"op":"Read","value":312},{"major":253,"minor":7,"op":"Write","value":0},{"major":253,"minor":7,"op":"Sync","value":0},{"major":253,"minor":7,"op":"Async","value":312},{"major":253,"minor":7,"op":"Total","value":312},{"major":202,"minor":0,"op":"Read","value":0},{"major":202,"minor":0,"op":"Write","value":0},{"major":202,"minor":0,"op":"Sync","value":0},{"major":202,"minor":0,"op":"Async","value":0},{"major":202,"minor":0,"op":"Total","value":0}],"io_queue_recursive":[],"io_service_time_recursive":[],"io_wait_time_recursive":[],"io_merged_recursive":[],"io_time_recursive":[],"sectors_recursive":[]}}`
var eventsResp = `{"status":"pull","id":"nginx:latest","time":1428620433}{"status":"create","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620433}{"status":"start","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620433}{"status":"die","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620442}{"status":"create","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"start","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"die","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"pull","id":"debian:latest","time":1428620453}{"status":"create","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"start","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"die","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"create","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620458}{"status":"start","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620458}{"status":"pause","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620462}{"status":"unpause","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620466}{"status":"die","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620469}` var eventsResp = `{"status":"pull","id":"nginx:latest","time":1428620433}{"status":"create","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620433}{"status":"start","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620433}{"status":"die","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620442}{"status":"create","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"start","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"die","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"pull","id":"debian:latest","time":1428620453}{"status":"create","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"start","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"die","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"create","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620458}{"status":"start","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620458}{"status":"pause","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620462}{"status":"unpause","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620466}{"status":"die","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620469}`

View File

@ -16,6 +16,12 @@ type Client interface {
CreateContainer(config *ContainerConfig, name string, authConfig *AuthConfig) (string, error) CreateContainer(config *ContainerConfig, name string, authConfig *AuthConfig) (string, error)
ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error) ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error)
ContainerChanges(id string) ([]*ContainerChanges, error) ContainerChanges(id string) ([]*ContainerChanges, error)
// ContainerStats takes a container ID and an optional stop channel and
// returns a StatsOrError channel. If an error is ever sent, then no
// more stats will be sent on that channel. If a stop channel is
// provided, events will stop being monitored after the stop channel is
// closed.
ContainerStats(id string, stopChan <-chan struct{}) (<-chan StatsOrError, error)
ExecCreate(config *ExecConfig) (string, error) ExecCreate(config *ExecConfig) (string, error)
ExecStart(id string, config *ExecConfig) error ExecStart(id string, config *ExecConfig) error
ExecResize(id string, width, height int) error ExecResize(id string, width, height int) error
@ -42,6 +48,7 @@ type Client interface {
RemoveContainer(id string, force, volumes bool) error RemoveContainer(id string, force, volumes bool) error
ListImages(all bool) ([]*Image, error) ListImages(all bool) ([]*Image, error)
RemoveImage(name string, force bool) ([]*ImageDelete, error) RemoveImage(name string, force bool) ([]*ImageDelete, error)
SearchImages(query, registry string, auth *AuthConfig) ([]ImageSearch, error)
PauseContainer(name string) error PauseContainer(name string) error
UnpauseContainer(name string) error UnpauseContainer(name string) error
RenameContainer(oldName string, newName string) error RenameContainer(oldName string, newName string) error

View File

@ -50,6 +50,11 @@ func (client *MockClient) ContainerChanges(id string) ([]*dockerclient.Container
return args.Get(0).([]*dockerclient.ContainerChanges), args.Error(1) return args.Get(0).([]*dockerclient.ContainerChanges), args.Error(1)
} }
func (client *MockClient) ContainerStats(id string, stopChan <-chan struct{}) (<-chan dockerclient.StatsOrError, error) {
args := client.Mock.Called(id, stopChan)
return args.Get(0).(<-chan dockerclient.StatsOrError), args.Error(1)
}
func (client *MockClient) AttachContainer(id string, options *dockerclient.AttachOptions) (io.ReadCloser, error) { func (client *MockClient) AttachContainer(id string, options *dockerclient.AttachOptions) (io.ReadCloser, error) {
args := client.Mock.Called(id, options) args := client.Mock.Called(id, options)
return args.Get(0).(io.ReadCloser), args.Error(1) return args.Get(0).(io.ReadCloser), args.Error(1)
@ -141,6 +146,11 @@ func (client *MockClient) RemoveImage(name string, force bool) ([]*dockerclient.
return args.Get(0).([]*dockerclient.ImageDelete), args.Error(1) return args.Get(0).([]*dockerclient.ImageDelete), args.Error(1)
} }
func (client *MockClient) SearchImages(query, registry string, authConfig *dockerclient.AuthConfig) ([]dockerclient.ImageSearch, error) {
args := client.Mock.Called(query, registry, authConfig)
return args.Get(0).([]dockerclient.ImageSearch), args.Error(1)
}
func (client *MockClient) PauseContainer(name string) error { func (client *MockClient) PauseContainer(name string) error {
args := client.Mock.Called(name) args := client.Mock.Called(name)
return args.Error(0) return args.Error(0)

View File

@ -46,6 +46,10 @@ func (client *NopClient) ContainerChanges(id string) ([]*dockerclient.ContainerC
return nil, ErrNoEngine return nil, ErrNoEngine
} }
func (client *NopClient) ContainerStats(id string, stopChan <-chan struct{}) (<-chan dockerclient.StatsOrError, error) {
return nil, ErrNoEngine
}
func (client *NopClient) AttachContainer(id string, options *dockerclient.AttachOptions) (io.ReadCloser, error) { func (client *NopClient) AttachContainer(id string, options *dockerclient.AttachOptions) (io.ReadCloser, error) {
return nil, ErrNoEngine return nil, ErrNoEngine
} }
@ -122,6 +126,10 @@ func (client *NopClient) RemoveImage(name string, force bool) ([]*dockerclient.I
return nil, ErrNoEngine return nil, ErrNoEngine
} }
func (client *NopClient) SearchImages(query, registry string, authConfig *dockerclient.AuthConfig) ([]dockerclient.ImageSearch, error) {
return nil, ErrNoEngine
}
func (client *NopClient) PauseContainer(name string) error { func (client *NopClient) PauseContainer(name string) error {
return ErrNoEngine return ErrNoEngine
} }

View File

@ -49,46 +49,64 @@ type ContainerConfig struct {
} }
type HostConfig struct { type HostConfig struct {
Binds []string Binds []string
ContainerIDFile string ContainerIDFile string
LxcConf []map[string]string LxcConf []map[string]string
Memory int64 Memory int64
MemoryReservation int64 MemoryReservation int64
MemorySwap int64 MemorySwap int64
KernelMemory int64 KernelMemory int64
CpuShares int64 CpuShares int64
CpuPeriod int64 CpuPeriod int64
CpusetCpus string CpusetCpus string
CpusetMems string CpusetMems string
CpuQuota int64 CpuQuota int64
BlkioWeight int64 BlkioWeight int64
OomKillDisable bool OomKillDisable bool
MemorySwappiness int64 MemorySwappiness int64
Privileged bool Privileged bool
PortBindings map[string][]PortBinding PortBindings map[string][]PortBinding
Links []string Links []string
PublishAllPorts bool PublishAllPorts bool
Dns []string Dns []string
DNSOptions []string DNSOptions []string
DnsSearch []string DnsSearch []string
ExtraHosts []string ExtraHosts []string
VolumesFrom []string VolumesFrom []string
Devices []DeviceMapping Devices []DeviceMapping
NetworkMode string NetworkMode string
IpcMode string IpcMode string
PidMode string PidMode string
UTSMode string UTSMode string
CapAdd []string CapAdd []string
CapDrop []string CapDrop []string
GroupAdd []string GroupAdd []string
RestartPolicy RestartPolicy RestartPolicy RestartPolicy
SecurityOpt []string SecurityOpt []string
ReadonlyRootfs bool ReadonlyRootfs bool
Ulimits []Ulimit Ulimits []Ulimit
LogConfig LogConfig LogConfig LogConfig
CgroupParent string CgroupParent string
ConsoleSize [2]int ConsoleSize [2]int
VolumeDriver string VolumeDriver string
OomScoreAdj int
Tmpfs map[string]string
ShmSize int64
BlkioWeightDevice []WeightDevice
BlkioDeviceReadBps []ThrottleDevice
BlkioDeviceWriteBps []ThrottleDevice
BlkioDeviceReadIOps []ThrottleDevice
BlkioDeviceWriteIOps []ThrottleDevice
}
type WeightDevice struct {
Path string
Weight uint16
}
type ThrottleDevice struct {
Path string
Rate uint64
} }
type DeviceMapping struct { type DeviceMapping struct {
@ -220,6 +238,14 @@ type ImageInfo struct {
VirtualSize int64 VirtualSize int64
} }
type ImageSearch struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"`
IsOfficial bool `json:"is_official,omitempty" yaml:"is_official,omitempty"`
IsAutomated bool `json:"is_automated,omitempty" yaml:"is_automated,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
StarCount int `json:"star_count,omitempty" yaml:"star_count,omitempty"`
}
type ContainerInfo struct { type ContainerInfo struct {
Id string Id string
Created string Created string
@ -381,6 +407,11 @@ type ImageDelete struct {
Untagged string Untagged string
} }
type StatsOrError struct {
Stats
Error error
}
type EventOrError struct { type EventOrError struct {
Event Event
Error error Error error
@ -406,6 +437,7 @@ type ThrottlingData struct {
ThrottledTime uint64 `json:"throttled_time"` ThrottledTime uint64 `json:"throttled_time"`
} }
// All CPU stats are aggregated since container inception.
type CpuUsage struct { type CpuUsage struct {
// Total CPU time consumed. // Total CPU time consumed.
// Units: nanoseconds. // Units: nanoseconds.

View File

@ -99,6 +99,35 @@ function teardown() {
[[ "${output}" == *"someDnsOption"* ]] [[ "${output}" == *"someDnsOption"* ]]
# stop-signal # stop-signal
[[ "${output}" == *"\"StopSignal\": \"SIGKILL\""* ]] [[ "${output}" == *"\"StopSignal\": \"SIGKILL\""* ]]
# following options are introduced in docker 1.10, skip older version
run docker --version
if [[ "${output}" == "Docker version 1.9"* ]]; then
skip
fi
docker_swarm run -d --name test_container2 \
--oom-score-adj=350 \
--tmpfs=/tempfs:rw \
--device-read-iops=/dev/null:351 \
--device-write-iops=/dev/null:352 \
--device-read-bps=/dev/null:1mb \
--device-write-bps=/dev/null:2mb \
busybox sleep 1000
run docker_swarm inspect test_container2
# oom-score-adj
[[ "${output}" == *"\"OomScoreAdj\": 350"* ]]
# tmpfs
[[ "${output}" == *"\"/tempfs\": \"rw\""* ]]
# device-read-iops
[[ "${output}" == *"351"* ]]
# device-write-iops
[[ "${output}" == *"352"* ]]
# device-read-bps
[[ "${output}" == *"1048576"* ]]
# device-write-bps
[[ "${output}" == *"2097152"* ]]
} }
@test "docker run --ip" { @test "docker run --ip" {