From 2bfe3a68e31799559c9800d3323a44d62394ac47 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Wed, 15 Apr 2015 14:30:55 -0700 Subject: [PATCH] Updated dockerclient and fixed API changes. Signed-off-by: Andrea Luzzardi --- Godeps/Godeps.json | 2 +- .../samalba/dockerclient/dockerclient.go | 60 ++++++++++++- .../samalba/dockerclient/dockerclient_test.go | 12 +++ .../samalba/dockerclient/engine_mock_test.go | 20 +++++ .../dockerclient/examples/stats/stats.go | 43 +++++++++ .../samalba/dockerclient/interface.go | 6 ++ .../samalba/dockerclient/mockclient/mock.go | 18 ++++ .../github.com/samalba/dockerclient/types.go | 88 ++++++++++++++++++- scheduler/filter/port_test.go | 4 +- 9 files changed, 247 insertions(+), 6 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/samalba/dockerclient/examples/stats/stats.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index d3b9bb334d..578f0bd51e 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -60,7 +60,7 @@ }, { "ImportPath": "github.com/samalba/dockerclient", - "Rev": "0689bcd74173c6abd6394b7ad435df46b0df26f8" + "Rev": "c37a52f55ab5a9edb9ffd4cf6e78692962b29b8d" }, { "ImportPath": "github.com/samuel/go-zookeeper/zk", diff --git a/Godeps/_workspace/src/github.com/samalba/dockerclient/dockerclient.go b/Godeps/_workspace/src/github.com/samalba/dockerclient/dockerclient.go index 630672a771..a3a36400e7 100644 --- a/Godeps/_workspace/src/github.com/samalba/dockerclient/dockerclient.go +++ b/Godeps/_workspace/src/github.com/samalba/dockerclient/dockerclient.go @@ -31,6 +31,7 @@ type DockerClient struct { HTTPClient *http.Client TLSConfig *tls.Config monitorEvents int32 + monitorStats int32 } type Error struct { @@ -60,7 +61,7 @@ func NewDockerClientTimeout(daemonUrl string, tlsConfig *tls.Config, timeout tim } } httpClient := newHTTPClient(u, tlsConfig, timeout) - return &DockerClient{u, httpClient, tlsConfig, 0}, nil + return &DockerClient{u, httpClient, tlsConfig, 0, 0}, nil } func (client *DockerClient) doRequest(method string, path string, body []byte, headers map[string]string) ([]byte, error) { @@ -197,6 +198,20 @@ func (client *DockerClient) ContainerLogs(id string, options *LogOptions) (io.Re return resp.Body, nil } +func (client *DockerClient) ContainerChanges(id string) ([]*ContainerChanges, error) { + uri := fmt.Sprintf("/%s/containers/%s/changes", APIVersion, id) + data, err := client.doRequest("GET", uri, nil, nil) + if err != nil { + return nil, err + } + changes := []*ContainerChanges{} + err = json.Unmarshal(data, &changes) + if err != nil { + return nil, err + } + return changes, nil +} + func (client *DockerClient) StartContainer(id string, config *HostConfig) error { data, err := json.Marshal(config) if err != nil { @@ -266,6 +281,35 @@ func (client *DockerClient) StopAllMonitorEvents() { atomic.StoreInt32(&client.monitorEvents, 0) } +func (client *DockerClient) StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{}) { + atomic.StoreInt32(&client.monitorStats, 1) + go client.getStats(id, cb, ec, args...) +} + +func (client *DockerClient) getStats(id string, cb StatCallback, ec chan error, args ...interface{}) { + uri := fmt.Sprintf("%s/%s/containers/%s/stats", client.URL.String(), APIVersion, id) + resp, err := client.HTTPClient.Get(uri) + if err != nil { + ec <- err + return + } + defer resp.Body.Close() + + dec := json.NewDecoder(resp.Body) + for atomic.LoadInt32(&client.monitorStats) > 0 { + var stats *Stats + if err := dec.Decode(&stats); err != nil { + ec <- err + return + } + cb(id, stats, ec, args...) + } +} + +func (client *DockerClient) StopAllMonitorStats() { + atomic.StoreInt32(&client.monitorStats, 0) +} + func (client *DockerClient) Version() (*Version, error) { uri := fmt.Sprintf("/%s/version", APIVersion) data, err := client.doRequest("GET", uri, nil, nil) @@ -305,6 +349,20 @@ func (client *DockerClient) PullImage(name string, auth *AuthConfig) error { return nil } +func (client *DockerClient) LoadImage(reader io.Reader) error { + data, err := ioutil.ReadAll(reader) + if err != nil { + return err + } + + uri := fmt.Sprintf("/%s/images/load", APIVersion) + _, err = client.doRequest("POST", uri, data, nil) + if err != nil { + return err + } + return nil +} + func (client *DockerClient) RemoveContainer(id string, force, volumes bool) error { argForce := 0 argVolumes := 0 diff --git a/Godeps/_workspace/src/github.com/samalba/dockerclient/dockerclient_test.go b/Godeps/_workspace/src/github.com/samalba/dockerclient/dockerclient_test.go index 8d3ec2ccc4..bb76ad8d76 100644 --- a/Godeps/_workspace/src/github.com/samalba/dockerclient/dockerclient_test.go +++ b/Godeps/_workspace/src/github.com/samalba/dockerclient/dockerclient_test.go @@ -74,6 +74,18 @@ func TestListContainers(t *testing.T) { assertEqual(t, cnt.SizeRw, int64(0), "") } +func TestContainerChanges(t *testing.T) { + client := testDockerClient(t) + changes, err := client.ContainerChanges("foobar") + if err != nil { + t.Fatal("cannot get container changes: %s", err) + } + assertEqual(t, len(changes), 3, "unexpected number of changes") + c := changes[0] + assertEqual(t, c.Path, "/dev", "unexpected") + assertEqual(t, c.Kind, 0, "unexpected") +} + func TestListContainersWithSize(t *testing.T) { client := testDockerClient(t) containers, err := client.ListContainers(true, true, "") diff --git a/Godeps/_workspace/src/github.com/samalba/dockerclient/engine_mock_test.go b/Godeps/_workspace/src/github.com/samalba/dockerclient/engine_mock_test.go index 4d0e6a7e92..00fe441dbd 100644 --- a/Godeps/_workspace/src/github.com/samalba/dockerclient/engine_mock_test.go +++ b/Godeps/_workspace/src/github.com/samalba/dockerclient/engine_mock_test.go @@ -27,6 +27,7 @@ func init() { r.HandleFunc(baseURL+"/info", handlerGetInfo).Methods("GET") r.HandleFunc(baseURL+"/containers/json", handlerGetContainers).Methods("GET") r.HandleFunc(baseURL+"/containers/{id}/logs", handleContainerLogs).Methods("GET") + r.HandleFunc(baseURL+"/containers/{id}/changes", handleContainerChanges).Methods("GET") r.HandleFunc(baseURL+"/containers/{id}/kill", handleContainerKill).Methods("POST") r.HandleFunc(baseURL+"/images/create", handleImagePull).Methods("POST") testHTTPServer = httptest.NewServer(handlerAccessLog(r)) @@ -107,6 +108,25 @@ func handleContainerLogs(w http.ResponseWriter, r *http.Request) { } } +func handleContainerChanges(w http.ResponseWriter, r *http.Request) { + writeHeaders(w, 200, "changes") + body := `[ + { + "Path": "/dev", + "Kind": 0 + }, + { + "Path": "/dev/kmsg", + "Kind": 1 + }, + { + "Path": "/test", + "Kind": 1 + } + ]` + w.Write([]byte(body)) +} + func getBoolValue(boolString string) bool { switch boolString { case "1": diff --git a/Godeps/_workspace/src/github.com/samalba/dockerclient/examples/stats/stats.go b/Godeps/_workspace/src/github.com/samalba/dockerclient/examples/stats/stats.go new file mode 100644 index 0000000000..9027069ddc --- /dev/null +++ b/Godeps/_workspace/src/github.com/samalba/dockerclient/examples/stats/stats.go @@ -0,0 +1,43 @@ +package main + +import ( + "github.com/samalba/dockerclient" + "log" + "os" + "os/signal" + "syscall" +) + +func statCallback(id string, stat *dockerclient.Stats, ec chan error, args ...interface{}) { + log.Println(stat) +} + +func waitForInterrupt() { + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT) + for _ = range sigChan { + os.Exit(0) + } +} + +func main() { + docker, err := dockerclient.NewDockerClient(os.Getenv("DOCKER_HOST"), nil) + if err != nil { + log.Fatal(err) + } + + containerConfig := &dockerclient.ContainerConfig{Image: "busybox", Cmd: []string{"sh"}} + containerId, err := docker.CreateContainer(containerConfig, "") + if err != nil { + log.Fatal(err) + } + + // Start the container + err = docker.StartContainer(containerId, nil) + if err != nil { + log.Fatal(err) + } + docker.StartMonitorStats(containerId, statCallback, nil) + + waitForInterrupt() +} diff --git a/Godeps/_workspace/src/github.com/samalba/dockerclient/interface.go b/Godeps/_workspace/src/github.com/samalba/dockerclient/interface.go index ef6684eeb8..626723132f 100644 --- a/Godeps/_workspace/src/github.com/samalba/dockerclient/interface.go +++ b/Godeps/_workspace/src/github.com/samalba/dockerclient/interface.go @@ -6,12 +6,15 @@ import ( type Callback func(*Event, chan error, ...interface{}) +type StatCallback func(string, *Stats, chan error, ...interface{}) + type Client interface { Info() (*Info, error) ListContainers(all, size bool, filters string) ([]Container, error) InspectContainer(id string) (*ContainerInfo, error) CreateContainer(config *ContainerConfig, name string) (string, error) ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error) + ContainerChanges(id string) ([]*ContainerChanges, error) Exec(config *ExecConfig) (string, error) StartContainer(id string, config *HostConfig) error StopContainer(id string, timeout int) error @@ -19,8 +22,11 @@ type Client interface { KillContainer(id, signal string) error StartMonitorEvents(cb Callback, ec chan error, args ...interface{}) StopAllMonitorEvents() + StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{}) + StopAllMonitorStats() Version() (*Version, error) PullImage(name string, auth *AuthConfig) error + LoadImage(reader io.Reader) error RemoveContainer(id string, force, volumes bool) error ListImages() ([]*Image, error) RemoveImage(name string) ([]*ImageDelete, error) diff --git a/Godeps/_workspace/src/github.com/samalba/dockerclient/mockclient/mock.go b/Godeps/_workspace/src/github.com/samalba/dockerclient/mockclient/mock.go index 5ff76db313..d49fa37069 100644 --- a/Godeps/_workspace/src/github.com/samalba/dockerclient/mockclient/mock.go +++ b/Godeps/_workspace/src/github.com/samalba/dockerclient/mockclient/mock.go @@ -40,6 +40,11 @@ func (client *MockClient) ContainerLogs(id string, options *dockerclient.LogOpti return args.Get(0).(io.ReadCloser), args.Error(1) } +func (client *MockClient) ContainerChanges(id string) ([]*dockerclient.ContainerChanges, error) { + args := client.Mock.Called(id) + return args.Get(0).([]*dockerclient.ContainerChanges), args.Error(1) +} + func (client *MockClient) StartContainer(id string, config *dockerclient.HostConfig) error { args := client.Mock.Called(id, config) return args.Error(0) @@ -68,6 +73,14 @@ func (client *MockClient) StopAllMonitorEvents() { client.Mock.Called() } +func (client *MockClient) StartMonitorStats(id string, cb dockerclient.StatCallback, ec chan error, args ...interface{}) { + client.Mock.Called(id, cb, ec, args) +} + +func (client *MockClient) StopAllMonitorStats() { + client.Mock.Called() +} + func (client *MockClient) Version() (*dockerclient.Version, error) { args := client.Mock.Called() return args.Get(0).(*dockerclient.Version), args.Error(1) @@ -78,6 +91,11 @@ func (client *MockClient) PullImage(name string, auth *dockerclient.AuthConfig) return args.Error(0) } +func (client *MockClient) LoadImage(reader io.Reader) error { + args := client.Mock.Called(reader) + return args.Error(0) +} + func (client *MockClient) RemoveContainer(id string, force, volumes bool) error { args := client.Mock.Called(id, force, volumes) return args.Error(0) diff --git a/Godeps/_workspace/src/github.com/samalba/dockerclient/types.go b/Godeps/_workspace/src/github.com/samalba/dockerclient/types.go index a3dcd123fa..54cc99d479 100644 --- a/Godeps/_workspace/src/github.com/samalba/dockerclient/types.go +++ b/Godeps/_workspace/src/github.com/samalba/dockerclient/types.go @@ -21,6 +21,7 @@ type ContainerConfig struct { Env []string Cmd []string Image string + Labels map[string]string Volumes map[string]struct{} WorkingDir string Entrypoint []string @@ -42,6 +43,7 @@ type HostConfig struct { Dns []string DnsSearch []string VolumesFrom []string + SecurityOpt []string NetworkMode string RestartPolicy RestartPolicy } @@ -94,8 +96,8 @@ type ContainerInfo struct { } Image string NetworkSettings struct { - IpAddress string - IpPrefixLen int + IPAddress string `json:"IpAddress"` + IPPrefixLen int `json:"IpPrefixLen"` Gateway string Bridge string Ports map[string][]PortBinding @@ -106,6 +108,11 @@ type ContainerInfo struct { HostConfig *HostConfig } +type ContainerChanges struct { + Path string + Kind int +} + type Port struct { IP string PrivatePort int @@ -171,3 +178,80 @@ type ImageDelete struct { Deleted string Untagged string } + +// The following are types for the API stats endpoint +type ThrottlingData struct { + // Number of periods with throttling active + Periods uint64 `json:"periods"` + // Number of periods when the container hit its throttling limit. + ThrottledPeriods uint64 `json:"throttled_periods"` + // Aggregate time the container was throttled for in nanoseconds. + ThrottledTime uint64 `json:"throttled_time"` +} + +type CpuUsage struct { + // Total CPU time consumed. + // Units: nanoseconds. + TotalUsage uint64 `json:"total_usage"` + // Total CPU time consumed per core. + // Units: nanoseconds. + PercpuUsage []uint64 `json:"percpu_usage"` + // Time spent by tasks of the cgroup in kernel mode. + // Units: nanoseconds. + UsageInKernelmode uint64 `json:"usage_in_kernelmode"` + // Time spent by tasks of the cgroup in user mode. + // Units: nanoseconds. + UsageInUsermode uint64 `json:"usage_in_usermode"` +} + +type CpuStats struct { + CpuUsage CpuUsage `json:"cpu_usage"` + SystemUsage uint64 `json:"system_cpu_usage"` + ThrottlingData ThrottlingData `json:"throttling_data,omitempty"` +} + +type NetworkStats struct { + RxBytes uint64 `json:"rx_bytes"` + RxPackets uint64 `json:"rx_packets"` + RxErrors uint64 `json:"rx_errors"` + RxDropped uint64 `json:"rx_dropped"` + TxBytes uint64 `json:"tx_bytes"` + TxPackets uint64 `json:"tx_packets"` + TxErrors uint64 `json:"tx_errors"` + TxDropped uint64 `json:"tx_dropped"` +} + +type MemoryStats struct { + Usage uint64 `json:"usage"` + MaxUsage uint64 `json:"max_usage"` + Stats map[string]uint64 `json:"stats"` + Failcnt uint64 `json:"failcnt"` + Limit uint64 `json:"limit"` +} + +type BlkioStatEntry struct { + Major uint64 `json:"major"` + Minor uint64 `json:"minor"` + Op string `json:"op"` + Value uint64 `json:"value"` +} + +type BlkioStats struct { + // number of bytes tranferred to and from the block device + IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive"` + IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive"` + IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive"` + IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive"` + IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive"` + IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive"` + IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive"` + SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"` +} + +type Stats struct { + Read time.Time `json:"read"` + NetworkStats NetworkStats `json:"network,omitempty"` + CpuStats CpuStats `json:"cpu_stats,omitempty"` + MemoryStats MemoryStats `json:"memory_stats,omitempty"` + BlkioStats BlkioStats `json:"blkio_stats,omitempty"` +} diff --git a/scheduler/filter/port_test.go b/scheduler/filter/port_test.go index 74d925a725..41d5e43807 100644 --- a/scheduler/filter/port_test.go +++ b/scheduler/filter/port_test.go @@ -236,8 +236,8 @@ func TestPortFilterRandomAssignment(t *testing.T) { }, }}, NetworkSettings: struct { - IpAddress string - IpPrefixLen int + IPAddress string `json:"IpAddress"` + IPPrefixLen int `json:"IpPrefixLen"` Gateway string Bridge string Ports map[string][]dockerclient.PortBinding