Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
835cd2a
add SecurityOpt to HostConfig
donhcd Mar 31, 2015
3433e5a
Merge pull request #87 from donhcd/securityopt
vieux Apr 6, 2015
4726a55
add NetworkStats struct
donhcd Mar 31, 2015
fc388ff
Merge pull request #86 from donhcd/networkSettings
vieux Apr 6, 2015
f16c233
add TagImage
donhcd Apr 6, 2015
e8b23b5
Add and implement LoadImage interface
Apr 8, 2015
578f4b4
Merge pull request #89 from jimmyxian/master
ehazlett Apr 10, 2015
ccbd3d2
Fix golint issues for IP* members.
aluzzardi Apr 15, 2015
c37a52f
Merge pull request #92 from aluzzardi/golint_ip
vieux Apr 15, 2015
8409be5
Merge pull request #88 from donhcd/tagImagePR
vieux Apr 20, 2015
e711a4c
add support rename
Apr 22, 2015
bcc686f
direct return err
Apr 22, 2015
459cb4e
Merge pull request #95 from jimmyxian/add-support-rename
ehazlett Apr 23, 2015
bc9c3a4
add labels to containers
vieux Apr 23, 2015
8b94272
Merge pull request #96 from vieux/add_labels_container
vieux Apr 23, 2015
7cbe186
add import
Apr 28, 2015
b879c7c
Add LogConfig and Ulimits(new in docker 1.6).
06wagon Apr 29, 2015
c5c297a
code refactored
Apr 30, 2015
336272e
Updated the README with a working example (following the recent updat…
samalba May 1, 2015
b177119
Added maintainers list to the README (closes #100)
samalba May 1, 2015
adc6ab2
Merge pull request #101 from samalba/update-readme
vieux May 1, 2015
9245fd3
use nil instead of bytes
May 4, 2015
0e8316d
Merge pull request #99 from lingdecong/master
vieux May 4, 2015
9f98218
Merge pull request #98 from jimmyxian/add-import
aluzzardi May 5, 2015
a833fc0
ContainerInfo.State: Add new states.
aluzzardi May 7, 2015
5e5020b
Merge pull request #102 from aluzzardi/state-update
vieux May 7, 2015
88d847a
state: Support for String() and StateString()
aluzzardi May 7, 2015
9bdd27a
state: Status() -> String()
aluzzardi May 7, 2015
4e6f4a2
Merge pull request #103 from aluzzardi/state-string
vieux May 7, 2015
7d2ae45
Switch WriteFlusher import to pkg/ioutils :tada:
tianon May 9, 2015
7f60657
Merge pull request #104 from tianon/writeflusher
donhcd May 12, 2015
a318296
add more fields to Info struct
donhcd May 11, 2015
90f6e65
add more version fields
donhcd May 12, 2015
0a4cb06
Merge pull request #106 from donhcd/moreInfoPR
aluzzardi May 12, 2015
b6f99fa
Merge pull request #107 from donhcd/moreVersionFields
aluzzardi May 12, 2015
b56756e
add MonitorEvents which returns channels
donhcd Apr 10, 2015
8fc6e30
accept a passed in stopChan
donhcd May 18, 2015
375f63c
unnest readJSONStream goroutine
donhcd May 18, 2015
80aeeac
simplify readJSONStream
donhcd May 18, 2015
344ef3a
update mock
donhcd May 18, 2015
fcf9c78
fix rename
donhcd May 19, 2015
cad46cb
add InspectImage
donhcd May 19, 2015
c8f6c75
Merge pull request #109 from donhcd/inspectImages
aluzzardi May 19, 2015
979115d
Merge pull request #90 from donhcd/betterMonitorEventsPR
ehazlett May 20, 2015
ccac387
Merge branch 'master' of github.com:samalba/dockerclient into push-im…
cjhubert May 26, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
185 changes: 178 additions & 7 deletions dockerclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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...)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
54 changes: 54 additions & 0 deletions dockerclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package dockerclient

import (
"bytes"
"encoding/json"
"fmt"
"io"
"reflect"
"strings"
"testing"
Expand Down Expand Up @@ -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)
Expand Down
Loading