Updated dockerclient and fixed API changes.

Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
Andrea Luzzardi 2015-04-15 14:30:55 -07:00
parent d492feb7cc
commit 2bfe3a68e3
9 changed files with 247 additions and 6 deletions

2
Godeps/Godeps.json generated
View File

@ -60,7 +60,7 @@
},
{
"ImportPath": "github.com/samalba/dockerclient",
"Rev": "0689bcd74173c6abd6394b7ad435df46b0df26f8"
"Rev": "c37a52f55ab5a9edb9ffd4cf6e78692962b29b8d"
},
{
"ImportPath": "github.com/samuel/go-zookeeper/zk",

View File

@ -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

View File

@ -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, "")

View File

@ -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":

View File

@ -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()
}

View File

@ -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)

View File

@ -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)

View File

@ -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"`
}

View File

@ -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