diff --git a/README.md b/README.md index 26f2528..046a9ce 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ Docker client library in Go Well maintained docker client library. -Example: +# How to use it? + +Here is an example showing how to use it: ```go package main @@ -25,7 +27,7 @@ func main() { docker, _ := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil) // Get only running containers - containers, err := docker.ListContainers(false) + containers, err := docker.ListContainers(false, false, "") if err != nil { log.Fatal(err) } @@ -41,14 +43,19 @@ func main() { } // Create a container - containerConfig := &dockerclient.ContainerConfig{Image: "ubuntu:12.04", Cmd: []string{"bash"}} - containerId, err := docker.CreateContainer(containerConfig) + containerConfig := &dockerclient.ContainerConfig{ + Image: "ubuntu:14.04", + Cmd: []string{"bash"}, + AttachStdin: true, + Tty: true} + containerId, err := docker.CreateContainer(containerConfig, "foobar") if err != nil { log.Fatal(err) } // Start the container - err = docker.StartContainer(containerId) + hostConfig := &dockerclient.HostConfig{} + err = docker.StartContainer(containerId, hostConfig) if err != nil { log.Fatal(err) } @@ -58,6 +65,19 @@ func main() { // Listen to events docker.StartMonitorEvents(eventCallback, nil) + + // Hold the execution to look at the events coming time.Sleep(3600 * time.Second) } ``` + +# Maintainers + +List of people you can ping for feedback on Pull Requests or any questions. + +- [Sam Alba](https://github.com/samalba) +- [Michael Crosby](https://github.com/crosbymichael) +- [Andrea Luzzardi](https://github.com/aluzzardi) +- [Victor Vieux](https://github.com/vieux) +- [Evan Hazlett](https://github.com/ehazlett) +- [Donald Huang](https://github.com/donhcd) diff --git a/dockerclient.go b/dockerclient.go index 9b10ec0..23cb215 100644 --- a/dockerclient.go +++ b/dockerclient.go @@ -66,7 +66,25 @@ func NewDockerClientTimeout(daemonUrl string, tlsConfig *tls.Config, timeout tim func (client *DockerClient) doRequest(method string, path string, body []byte, headers map[string]string) ([]byte, error) { b := bytes.NewBuffer(body) - req, err := http.NewRequest(method, client.URL.String()+path, b) + + reader, err := client.doStreamRequest(method, path, b, headers) + if err != nil { + return nil, err + } + + defer reader.Close() + data, err := ioutil.ReadAll(reader) + if err != nil { + return nil, err + } + return data, nil +} + +func (client *DockerClient) doStreamRequest(method string, path string, in io.Reader, headers map[string]string) (io.ReadCloser, error) { + if (method == "POST" || method == "PUT") && in == nil { + in = bytes.NewReader(nil) + } + req, err := http.NewRequest(method, client.URL.String()+path, in) if err != nil { return nil, err } @@ -83,18 +101,19 @@ func (client *DockerClient) doRequest(method string, path string, body []byte, h } return nil, err } - defer resp.Body.Close() - data, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } if resp.StatusCode == 404 { return nil, ErrNotFound } if resp.StatusCode >= 400 { + defer resp.Body.Close() + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } return nil, Error{StatusCode: resp.StatusCode, Status: resp.Status, msg: string(data)} } - return data, nil + + return resp.Body, nil } func (client *DockerClient) Info() (*Info, error) { @@ -212,6 +231,30 @@ func (client *DockerClient) ContainerChanges(id string) ([]*ContainerChanges, er return changes, nil } +func (client *DockerClient) readJSONStream(stream io.ReadCloser, decode func(*json.Decoder) decodingResult, stopChan <-chan struct{}) <-chan decodingResult { + resultChan := make(chan decodingResult) + + go func() { + decoder := json.NewDecoder(stream) + defer stream.Close() + defer close(resultChan) + for { + decodeResult := decode(decoder) + select { + case <-stopChan: + return + default: + resultChan <- decodeResult + if decodeResult.err != nil { + return + } + } + } + }() + + return resultChan +} + func (client *DockerClient) StartContainer(id string, config *HostConfig) error { data, err := json.Marshal(config) if err != nil { @@ -252,6 +295,64 @@ func (client *DockerClient) KillContainer(id, signal string) error { return nil } +func (client *DockerClient) MonitorEvents(options *MonitorEventsOptions, stopChan <-chan struct{}) (<-chan EventOrError, error) { + v := url.Values{} + if options != nil { + if options.Since != 0 { + v.Add("since", strconv.Itoa(options.Since)) + } + if options.Until != 0 { + v.Add("until", strconv.Itoa(options.Until)) + } + if options.Filters != nil { + filterMap := make(map[string][]string) + if len(options.Filters.Event) > 0 { + filterMap["event"] = []string{options.Filters.Event} + } + if len(options.Filters.Image) > 0 { + filterMap["image"] = []string{options.Filters.Image} + } + if len(options.Filters.Container) > 0 { + filterMap["container"] = []string{options.Filters.Container} + } + if len(filterMap) > 0 { + filterJSONBytes, err := json.Marshal(filterMap) + if err != nil { + return nil, err + } + v.Add("filters", string(filterJSONBytes)) + } + } + } + uri := fmt.Sprintf("%s/%s/events?%s", client.URL.String(), APIVersion, v.Encode()) + resp, err := client.HTTPClient.Get(uri) + if err != nil { + return nil, err + } + + decode := func(decoder *json.Decoder) decodingResult { + var event Event + if err := decoder.Decode(&event); err != nil { + return decodingResult{err: err} + } else { + return decodingResult{result: event} + } + } + decodingResultChan := client.readJSONStream(resp.Body, decode, stopChan) + eventOrErrorChan := make(chan EventOrError) + go func() { + for decodingResult := range decodingResultChan { + event, _ := decodingResult.result.(Event) + eventOrErrorChan <- EventOrError{ + Event: event, + Error: decodingResult.err, + } + } + close(eventOrErrorChan) + }() + return eventOrErrorChan, nil +} + func (client *DockerClient) StartMonitorEvents(cb Callback, ec chan error, args ...interface{}) { atomic.StoreInt32(&client.monitorEvents, 1) go client.getEvents(cb, ec, args...) @@ -310,6 +411,20 @@ func (client *DockerClient) StopAllMonitorStats() { atomic.StoreInt32(&client.monitorStats, 0) } +func (client *DockerClient) TagImage(nameOrID string, repo string, tag string, force bool) error { + v := url.Values{} + v.Set("repo", repo) + v.Set("tag", tag) + if force { + v.Set("force", "1") + } + uri := fmt.Sprintf("/%s/images/%s/tag?%s", APIVersion, nameOrID, v.Encode()) + if _, err := client.doRequest("POST", uri, nil, nil); err != nil { + return err + } + return nil +} + func (client *DockerClient) Version() (*Version, error) { uri := fmt.Sprintf("/%s/version", APIVersion) data, err := client.doRequest("GET", uri, nil, nil) @@ -426,6 +541,34 @@ func (client *DockerClient) PullImage(name string, auth *AuthConfig) error { return nil } +func (client *DockerClient) InspectImage(id string) (*ImageInfo, error) { + uri := fmt.Sprintf("/%s/images/%s/json", APIVersion, id) + data, err := client.doRequest("GET", uri, nil, nil) + if err != nil { + return nil, err + } + info := &ImageInfo{} + err = json.Unmarshal(data, info) + if err != nil { + return nil, err + } + return info, 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 @@ -507,3 +650,31 @@ func (client *DockerClient) Exec(config *ExecConfig) (string, error) { } return createExecResp.Id, nil } + +func (client *DockerClient) RenameContainer(oldName string, newName string) error { + uri := fmt.Sprintf("/containers/%s/rename?name=%s", oldName, newName) + _, err := client.doRequest("POST", uri, nil, nil) + return err +} + +func (client *DockerClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) { + var fromSrc string + v := &url.Values{} + if source == "" { + fromSrc = "-" + } else { + fromSrc = source + } + + v.Set("fromSrc", fromSrc) + v.Set("repo", repository) + if tag != "" { + v.Set("tag", tag) + } + + var in io.Reader + if fromSrc == "-" { + in = tar + } + return client.doStreamRequest("POST", "/images/create?"+v.Encode(), in, nil) +} diff --git a/dockerclient_test.go b/dockerclient_test.go index bb76ad8..0b57518 100644 --- a/dockerclient_test.go +++ b/dockerclient_test.go @@ -2,7 +2,9 @@ package dockerclient import ( "bytes" + "encoding/json" "fmt" + "io" "reflect" "strings" "testing" @@ -155,6 +157,58 @@ func TestContainerLogs(t *testing.T) { } } +func TestMonitorEvents(t *testing.T) { + client := testDockerClient(t) + decoder := json.NewDecoder(bytes.NewBufferString(eventsResp)) + var expectedEvents []Event + for { + var event Event + if err := decoder.Decode(&event); err != nil { + if err == io.EOF { + break + } else { + t.Fatalf("cannot parse expected resp: %s", err.Error()) + } + } else { + expectedEvents = append(expectedEvents, event) + } + } + + // test passing stop chan + stopChan := make(chan struct{}) + eventInfoChan, err := client.MonitorEvents(nil, stopChan) + if err != nil { + t.Fatalf("cannot get events from server: %s", err.Error()) + } + + eventInfo := <-eventInfoChan + if eventInfo.Error != nil || eventInfo.Event != expectedEvents[0] { + t.Fatalf("got:\n%#v\nexpected:\n%#v", eventInfo, expectedEvents[0]) + } + close(stopChan) + for i := 0; i < 3; i++ { + _, ok := <-eventInfoChan + if i == 2 && ok { + t.Fatalf("read more than 2 events successfully after closing stopChan") + } + } + + // test when you don't pass stop chan + eventInfoChan, err = client.MonitorEvents(nil, nil) + if err != nil { + t.Fatalf("cannot get events from server: %s", err.Error()) + } + + for i, expectedEvent := range expectedEvents { + t.Logf("on iter %d\n", i) + eventInfo := <-eventInfoChan + if eventInfo.Error != nil || eventInfo.Event != expectedEvent { + t.Fatalf("index %d, got:\n%#v\nexpected:\n%#v", i, eventInfo, expectedEvent) + } + t.Logf("done with iter %d\n", i) + } +} + func TestDockerClientInterface(t *testing.T) { iface := reflect.TypeOf((*Client)(nil)).Elem() test := testDockerClient(t) diff --git a/engine_mock_test.go b/engine_mock_test.go index 00fe441..114a71f 100644 --- a/engine_mock_test.go +++ b/engine_mock_test.go @@ -10,10 +10,10 @@ import ( "strconv" "time" + "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/jsonlog" "github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/pkg/timeutils" - "github.com/docker/docker/utils" "github.com/gorilla/mux" ) @@ -30,6 +30,7 @@ func init() { 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") + r.HandleFunc(baseURL+"/events", handleEvents).Methods("GET") testHTTPServer = httptest.NewServer(handlerAccessLog(r)) } @@ -74,7 +75,7 @@ func handleImagePull(w http.ResponseWriter, r *http.Request) { func handleContainerLogs(w http.ResponseWriter, r *http.Request) { var outStream, errStream io.Writer - outStream = utils.NewWriteFlusher(w) + outStream = ioutils.NewWriteFlusher(w) // not sure how to test follow if err := r.ParseForm(); err != nil { @@ -228,3 +229,7 @@ func handlerGetContainers(w http.ResponseWriter, r *http.Request) { } w.Write([]byte(body)) } + +func handleEvents(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(eventsResp)) +} diff --git a/example_responses.go b/example_responses.go index 9f683f1..670508c 100644 --- a/example_responses.go +++ b/example_responses.go @@ -9,3 +9,5 @@ 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":"3d894e6f7e63"}{"status":"Already exists","progressDetail":{},"id":"4d949c40bc77"}{"status":"Already exists","progressDetail":{},"id":"55e031889365"}{"status":"Already exists","progressDetail":{},"id":"c7aa675e1876"}{"status":"The image you are pulling has been verified","id":"haproxy:latest"} {"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 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}` diff --git a/interface.go b/interface.go index fb94839..0042763 100644 --- a/interface.go +++ b/interface.go @@ -13,6 +13,7 @@ type Client interface { ListContainers(all, size bool, filters string) ([]Container, error) InspectContainer(id string) (*ContainerInfo, error) BuildImage(image BuildImage, config *ConfigFile) error + InspectImage(id string) (*ImageInfo, error) CreateContainer(config *ContainerConfig, name string) (string, error) ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error) ContainerChanges(id string) ([]*ContainerChanges, error) @@ -21,16 +22,25 @@ type Client interface { StopContainer(id string, timeout int) error RestartContainer(id string, timeout int) error KillContainer(id, signal string) error + // MonitorEvents takes options and an optional stop channel, and returns + // an EventOrError channel. If an error is ever sent, then no more + // events will be sent. If a stop channel is provided, events will stop + // being monitored after the stop channel is closed. + MonitorEvents(options *MonitorEventsOptions, stopChan <-chan struct{}) (<-chan EventOrError, error) StartMonitorEvents(cb Callback, ec chan error, args ...interface{}) StopAllMonitorEvents() StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{}) StopAllMonitorStats() + TagImage(nameOrID string, repo string, tag string, force bool) error Version() (*Version, error) PullImage(name string, auth *AuthConfig) error PushImage(name string, tag string, auth *AuthConfig) error + LoadImage(reader io.Reader) error RemoveContainer(id string, force, volumes bool) error ListImages() ([]*Image, error) RemoveImage(name string) ([]*ImageDelete, error) PauseContainer(name string) error UnpauseContainer(name string) error + RenameContainer(oldName string, newName string) error + ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) } diff --git a/mockclient/mock.go b/mockclient/mock.go index 4238846..0facc9c 100644 --- a/mockclient/mock.go +++ b/mockclient/mock.go @@ -30,6 +30,11 @@ func (client *MockClient) InspectContainer(id string) (*dockerclient.ContainerIn return args.Get(0).(*dockerclient.ContainerInfo), args.Error(1) } +func (client *MockClient) InspectImage(id string) (*dockerclient.ImageInfo, error) { + args := client.Mock.Called(id) + return args.Get(0).(*dockerclient.ImageInfo), args.Error(1) +} + func (client *MockClient) CreateContainer(config *dockerclient.ContainerConfig, name string) (string, error) { args := client.Mock.Called(config, name) return args.String(0), args.Error(1) @@ -65,6 +70,11 @@ func (client *MockClient) KillContainer(id, signal string) error { return args.Error(0) } +func (client *MockClient) MonitorEvents(options *dockerclient.MonitorEventsOptions, stopChan <-chan struct{}) (<-chan dockerclient.EventOrError, error) { + args := client.Mock.Called(options, stopChan) + return args.Get(0).(<-chan dockerclient.EventOrError), args.Error(1) +} + func (client *MockClient) StartMonitorEvents(cb dockerclient.Callback, ec chan error, args ...interface{}) { client.Mock.Called(cb, ec, args) } @@ -73,6 +83,11 @@ func (client *MockClient) StopAllMonitorEvents() { client.Mock.Called() } +func (client *MockClient) TagImage(nameOrID string, repo string, tag string, force bool) error { + args := client.Mock.Called(nameOrID, repo, tag, force) + return args.Error(0) +} + func (client *MockClient) StartMonitorStats(id string, cb dockerclient.StatCallback, ec chan error, args ...interface{}) { client.Mock.Called(id, cb, ec, args) } @@ -91,6 +106,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) @@ -120,3 +140,13 @@ func (client *MockClient) Exec(config *dockerclient.ExecConfig) (string, error) args := client.Mock.Called(config) return args.String(0), args.Error(1) } + +func (client *MockClient) RenameContainer(oldName string, newName string) error { + args := client.Mock.Called(oldName, newName) + return args.Error(0) +} + +func (client *MockClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) { + args := client.Mock.Called(source, repository, tag, tar) + return args.Get(0).(io.ReadCloser), args.Error(1) +} diff --git a/types.go b/types.go index d94986f..fba9283 100644 --- a/types.go +++ b/types.go @@ -1,8 +1,11 @@ package dockerclient import ( + "fmt" "io" "time" + + "github.com/docker/docker/pkg/units" ) type ContainerConfig struct { @@ -18,6 +21,7 @@ type ContainerConfig struct { AttachStderr bool PortSpecs []string ExposedPorts map[string]struct{} + MacAddress string Tty bool OpenStdin bool StdinOnce bool @@ -46,8 +50,11 @@ type HostConfig struct { Dns []string DnsSearch []string VolumesFrom []string + SecurityOpt []string NetworkMode string RestartPolicy RestartPolicy + Ulimits []Ulimit + LogConfig LogConfig } type ExecConfig struct { @@ -68,6 +75,18 @@ type LogOptions struct { Tail int64 } +type MonitorEventsFilters struct { + Event string `json:",omitempty"` + Image string `json:",omitempty"` + Container string `json:",omitempty"` +} + +type MonitorEventsOptions struct { + Since int + Until int + Filters *MonitorEventsFilters `json:",omitempty"` +} + type RestartPolicy struct { Name string MaximumRetryCount int64 @@ -78,28 +97,94 @@ type PortBinding struct { HostPort string } -type ContainerInfo struct { - Id string - Created string - Path string - Name string - Args []string - ExecIDs []string - Config *ContainerConfig - State struct { - Running bool - Paused bool - Restarting bool - Pid int - ExitCode int - StartedAt time.Time - FinishedAt time.Time - Ghost bool +type State struct { + Running bool + Paused bool + Restarting bool + OOMKilled bool + Dead bool + Pid int + ExitCode int + Error string // contains last known error when starting the container + StartedAt time.Time + FinishedAt time.Time + Ghost bool +} + +// String returns a human-readable description of the state +// Stoken from docker/docker/daemon/state.go +func (s *State) String() string { + if s.Running { + if s.Paused { + return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) + } + if s.Restarting { + return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) + } + + return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) + } + + if s.Dead { + return "Dead" + } + + if s.FinishedAt.IsZero() { + return "" + } + + return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) +} + +// StateString returns a single string to describe state +// Stoken from docker/docker/daemon/state.go +func (s *State) StateString() string { + if s.Running { + if s.Paused { + return "paused" + } + if s.Restarting { + return "restarting" + } + return "running" + } + + if s.Dead { + return "dead" } + + return "exited" +} + +type ImageInfo struct { + Architecture string + Author string + Comment string + Config *ContainerConfig + Container string + ContainerConfig *ContainerConfig + Created time.Time + DockerVersion string + Id string + Os string + Parent string + Size int64 + VirtualSize int64 +} + +type ContainerInfo struct { + Id string + Created string + Path string + Name string + Args []string + ExecIDs []string + Config *ContainerConfig + State *State Image string NetworkSettings struct { - IpAddress string - IpPrefixLen int + IPAddress string `json:"IpAddress"` + IPPrefixLen int `json:"IpPrefixLen"` Gateway string Bridge string Ports map[string][]PortBinding @@ -132,6 +217,7 @@ type Container struct { Ports []Port SizeRw int64 SizeRootFs int64 + Labels map[string]string } type Event struct { @@ -142,9 +228,13 @@ type Event struct { } type Version struct { - Version string - GitCommit string - GoVersion string + ApiVersion string + Arch string + GitCommit string + GoVersion string + KernelVersion string + Os string + Version string } type RespContainersCreate struct { @@ -172,19 +262,38 @@ type BuildImage struct { ForceRemove bool } +// Info is the struct returned by /info +// The API is currently in flux, so Debug, MemoryLimit, SwapLimit, and +// IPv4Forwarding are interfaces because in docker 1.6.1 they are 0 or 1 but in +// master they are bools. type Info struct { - ID string - Containers int64 - Driver string - DriverStatus [][]string - ExecutionDriver string - Images int64 - KernelVersion string - OperatingSystem string - NCPU int64 - MemTotal int64 - Name string - Labels []string + ID string + Containers int64 + Driver string + DriverStatus [][]string + ExecutionDriver string + Images int64 + KernelVersion string + OperatingSystem string + NCPU int64 + MemTotal int64 + Name string + Labels []string + Debug interface{} + NFd int64 + NGoroutines int64 + SystemTime time.Time + NEventsListener int64 + InitPath string + InitSha1 string + IndexServerAddress string + MemoryLimit interface{} + SwapLimit interface{} + IPv4Forwarding interface{} + DockerRootDir string + HttpProxy string + HttpsProxy string + NoProxy string } type ImageDelete struct { @@ -192,6 +301,16 @@ type ImageDelete struct { Untagged string } +type EventOrError struct { + Event + Error error +} + +type decodingResult struct { + result interface{} + err error +} + // The following are types for the API stats endpoint type ThrottlingData struct { // Number of periods with throttling active @@ -223,6 +342,17 @@ type CpuStats struct { 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"` @@ -251,19 +381,20 @@ type BlkioStats struct { } type Stats struct { - Read time.Time `json:"read"` - Network 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"` - } + 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"` +} + +type Ulimit struct { + Name string `json:"name"` + Soft uint64 `json:"soft"` + Hard uint64 `json:"hard"` +} - CpuStats CpuStats `json:"cpu_stats,omitempty"` - MemoryStats MemoryStats `json:"memory_stats,omitempty"` - BlkioStats BlkioStats `json:"blkio_stats,omitempty"` +type LogConfig struct { + Type string `json:"type"` + Config map[string]string `json:"config"` }