diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 50705959d5a..63c981ee151 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -47,8 +47,8 @@ }, { "ImportPath": "github.com/fsouza/go-dockerclient", - "Comment": "0.2.1-216-g9061c85", - "Rev": "9061c8580ba4374f24de44bcf9e8b77ce0553933" + "Comment": "0.2.1-241-g0dbb508", + "Rev": "0dbb508e94dd899a6743d035d8f249c7634d26da" }, { "ImportPath": "github.com/golang/glog", diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml index cfcd173a7f3..24bbadba936 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml @@ -2,7 +2,7 @@ language: go go: - 1.1.2 - 1.2 - - 1.3 + - 1.3.1 - tip env: - GOARCH=amd64 diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS index 98ff17b948a..0f08b9c30da 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS @@ -1,5 +1,6 @@ # This is the official list of go-dockerclient authors for copyright purposes. +Aldrin Leal Andreas Jaekle Andrews Medina Andy Goldstein @@ -7,6 +8,8 @@ Ben McCann Cezar Sa Espinola Cheah Chu Yeow cheneydeng +Daniel, Dao Quang Minh +David Huie Ed Eric Anderson Fabio Rehm @@ -19,6 +22,7 @@ Jeff Mitchell Jeffrey Hulten Johan Euphrosine Karan Misra +Kim, Hirokuni Lucas Clemente Omeid Matten Paul Morie diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go index b4a0e556506..436695d959d 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go @@ -264,7 +264,7 @@ func (c *Client) do(method, path string, data interface{}) ([]byte, int, error) return body, resp.StatusCode, nil } -func (c *Client) stream(method, path string, setRawTerminal bool, headers map[string]string, in io.Reader, stdout, stderr io.Writer) error { +func (c *Client) stream(method, path string, setRawTerminal, rawJSONStream bool, headers map[string]string, in io.Reader, stdout, stderr io.Writer) error { if (method == "POST" || method == "PUT") && in == nil { in = bytes.NewReader(nil) } @@ -320,6 +320,12 @@ func (c *Client) stream(method, path string, setRawTerminal bool, headers map[st return newError(resp.StatusCode, body) } if resp.Header.Get("Content-Type") == "application/json" { + // if we want to get raw json stream, just copy it back to output + // without decoding it + if rawJSONStream { + _, err = io.Copy(stdout, resp.Body) + return err + } dec := json.NewDecoder(resp.Body) for { var m jsonMessage diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client_test.go index e623263f90f..9def1716aab 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client_test.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client_test.go @@ -248,16 +248,22 @@ func TestPingFailingWrongStatus(t *testing.T) { type FakeRoundTripper struct { message string status int + header map[string]string requests []*http.Request } func (rt *FakeRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { body := strings.NewReader(rt.message) rt.requests = append(rt.requests, r) - return &http.Response{ + res := &http.Response{ StatusCode: rt.status, Body: ioutil.NopCloser(body), - }, nil + Header: make(http.Header), + } + for k, v := range rt.header { + res.Header.Set(k, v) + } + return res, nil } func (rt *FakeRoundTripper) Reset() { diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go index ab2c873e80d..3e3556b7823 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go @@ -28,25 +28,25 @@ type ListContainersOptions struct { } type APIPort struct { - PrivatePort int64 - PublicPort int64 - Type string - IP string + PrivatePort int64 `json:"PrivatePort,omitempty" yaml:"PrivatePort,omitempty"` + PublicPort int64 `json:"PublicPort,omitempty" yaml:"PublicPort,omitempty"` + Type string `json:"Type,omitempty" yaml:"Type,omitempty"` + IP string `json:"IP,omitempty" yaml:"IP,omitempty"` } // APIContainers represents a container. // // See http://goo.gl/QeFH7U for more details. type APIContainers struct { - ID string `json:"Id"` - Image string - Command string - Created int64 - Status string - Ports []APIPort - SizeRw int64 - SizeRootFs int64 - Names []string + ID string `json:"Id" yaml:"Id"` + Image string `json:"Image,omitempty" yaml:"Image,omitempty"` + Command string `json:"Command,omitempty" yaml:"Command,omitempty"` + Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"` + Status string `json:"Status,omitempty" yaml:"Status,omitempty"` + Ports []APIPort `json:"Ports,omitempty" yaml:"Ports,omitempty"` + SizeRw int64 `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty"` + SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty"` + Names []string `json:"Names,omitempty" yaml:"Names,omitempty"` } // ListContainers returns a slice of containers matching the given criteria. @@ -86,12 +86,12 @@ func (p Port) Proto() string { // State represents the state of a container. type State struct { - Running bool - Paused bool - Pid int - ExitCode int - StartedAt time.Time - FinishedAt time.Time + Running bool `json:"Running,omitempty" yaml:"Running,omitempty"` + Paused bool `json:"Paused,omitempty" yaml:"Paused,omitempty"` + Pid int `json:"Pid,omitempty" yaml:"Pid,omitempty"` + ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"` + StartedAt time.Time `json:"StartedAt,omitempty" yaml:"StartedAt,omitempty"` + FinishedAt time.Time `json:"FinishedAt,omitempty" yaml:"FinishedAt,omitempty"` } // String returns the string representation of a state. @@ -106,19 +106,19 @@ func (s *State) String() string { } type PortBinding struct { - HostIp string - HostPort string + HostIp string `json:"HostIP,omitempty" yaml:"HostIP,omitempty"` + HostPort string `json:"HostPort,omitempty" yaml:"HostPort,omitempty"` } type PortMapping map[string]string type NetworkSettings struct { - IPAddress string - IPPrefixLen int - Gateway string - Bridge string - PortMapping map[string]PortMapping - Ports map[Port][]PortBinding + IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty"` + IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty"` + Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty"` + Bridge string `json:"Bridge,omitempty" yaml:"Bridge,omitempty"` + PortMapping map[string]PortMapping `json:"PortMapping,omitempty" yaml:"PortMapping,omitempty"` + Ports map[Port][]PortBinding `json:"Ports,omitempty" yaml:"Ports,omitempty"` } func (settings *NetworkSettings) PortMappingAPI() []APIPort { @@ -155,55 +155,55 @@ func parsePort(rawPort string) (int, error) { } type Config struct { - Hostname string - Domainname string - User string - Memory int64 - MemorySwap int64 - CpuShares int64 - AttachStdin bool - AttachStdout bool - AttachStderr bool - PortSpecs []string - ExposedPorts map[Port]struct{} - Tty bool - OpenStdin bool - StdinOnce bool - Env []string - Cmd []string - Dns []string // For Docker API v1.9 and below only - Image string - Volumes map[string]struct{} - VolumesFrom string - WorkingDir string - Entrypoint []string - NetworkDisabled bool + Hostname string `json:"Hostname,omitempty" yaml:"Hostname,omitempty"` + Domainname string `json:"Domainname,omitempty" yaml:"Domainname,omitempty"` + User string `json:"User,omitempty" yaml:"User,omitempty"` + Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"` + MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"` + CpuShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"` + AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty"` + AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty"` + AttachStderr bool `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty"` + PortSpecs []string `json:"PortSpecs,omitempty" yaml:"PortSpecs,omitempty"` + ExposedPorts map[Port]struct{} `json:"ExposedPorts,omitempty" yaml:"ExposedPorts,omitempty"` + Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty"` + OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty"` + StdinOnce bool `json:"StdinOnce,omitempty" yaml:"StdinOnce,omitempty"` + Env []string `json:"Env,omitempty" yaml:"Env,omitempty"` + Cmd []string `json:"Cmd,omitempty" yaml:"Cmd,omitempty"` + Dns []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.9 and below only + Image string `json:"Image,omitempty" yaml:"Image,omitempty"` + Volumes map[string]struct{} `json:"Volumes,omitempty" yaml:"Volumes,omitempty"` + VolumesFrom string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"` + WorkingDir string `json:"WorkingDir,omitempty" yaml:"WorkingDir,omitempty"` + Entrypoint []string `json:"Entrypoint,omitempty" yaml:"Entrypoint,omitempty"` + NetworkDisabled bool `json:"NetworkDisabled,omitempty" yaml:"NetworkDisabled,omitempty"` } type Container struct { - ID string + ID string `json:"Id" yaml:"Id"` - Created time.Time + Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty"` - Path string - Args []string + Path string `json:"Path,omitempty" yaml:"Path,omitempty"` + Args []string `json:"Args,omitempty" yaml:"Args,omitempty"` - Config *Config - State State - Image string + Config *Config `json:"Config,omitempty" yaml:"Config,omitempty"` + State State `json:"State,omitempty" yaml:"State,omitempty"` + Image string `json:"Image,omitempty" yaml:"Image,omitempty"` - NetworkSettings *NetworkSettings + NetworkSettings *NetworkSettings `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty"` - SysInitPath string - ResolvConfPath string - HostnamePath string - HostsPath string - Name string - Driver string + SysInitPath string `json:"SysInitPath,omitempty" yaml:"SysInitPath,omitempty"` + ResolvConfPath string `json:"ResolvConfPath,omitempty" yaml:"ResolvConfPath,omitempty"` + HostnamePath string `json:"HostnamePath,omitempty" yaml:"HostnamePath,omitempty"` + HostsPath string `json:"HostsPath,omitempty" yaml:"HostsPath,omitempty"` + Name string `json:"Name,omitempty" yaml:"Name,omitempty"` + Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty"` - Volumes map[string]string - VolumesRW map[string]bool - HostConfig *HostConfig + Volumes map[string]string `json:"Volumes,omitempty" yaml:"Volumes,omitempty"` + VolumesRW map[string]bool `json:"VolumesRW,omitempty" yaml:"VolumesRW,omitempty"` + HostConfig *HostConfig `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty"` } // InspectContainer returns information about a container by its ID. @@ -279,22 +279,56 @@ func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error } type KeyValuePair struct { - Key string - Value string + Key string `json:"Key,omitempty" yaml:"Key,omitempty"` + Value string `json:"Value,omitempty" yaml:"Value,omitempty"` +} + +// RestartPolicy represents the policy for automatically restarting a container. +// +// Possible values are: +// +// - always: the docker daemon will always restart the container +// - on-failure: the docker daemon will restart the container on failures, at +// most MaximumRetryCount times +// - no: the docker daemon will not restart the container automatically +type RestartPolicy struct { + Name string `json:"Name,omitempty" yaml:"Name,omitempty"` + MaximumRetryCount int `json:"MaximumRetryCount,omitempty" yaml:"MaximumRetryCount,omitempty"` +} + +// AlwaysRestart returns a restart policy that tells the Docker daemon to +// always restart the container. +func AlwaysRestart() RestartPolicy { + return RestartPolicy{Name: "always"} +} + +// RestartOnFailure returns a restart policy that tells the Docker daemon to +// restart the container on failures, trying at most maxRetry times. +func RestartOnFailure(maxRetry int) RestartPolicy { + return RestartPolicy{Name: "on-failure", MaximumRetryCount: maxRetry} +} + +// NeverRestart returns a restart policy that tells the Docker daemon to never +// restart the container on failures. +func NeverRestart() RestartPolicy { + return RestartPolicy{Name: "no"} } type HostConfig struct { - Binds []string - ContainerIDFile string - LxcConf []KeyValuePair - Privileged bool - PortBindings map[Port][]PortBinding - Links []string - PublishAllPorts bool - Dns []string // For Docker API v1.10 and above only - DnsSearch []string - VolumesFrom []string - NetworkMode string + Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty"` + CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty"` + CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty"` + ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty"` + LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty"` + Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty"` + PortBindings map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty"` + Links []string `json:"Links,omitempty" yaml:"Links,omitempty"` + PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty"` + Dns []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.10 and above only + DnsSearch []string `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty"` + VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"` + NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"` + RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"` } // StartContainer starts a container, returning an error in case of failure. @@ -309,6 +343,9 @@ func (c *Client) StartContainer(id string, hostConfig *HostConfig) error { if status == http.StatusNotFound { return &NoSuchContainer{ID: id} } + if status == http.StatusNotModified { + return &ContainerAlreadyRunning{ID: id} + } if err != nil { return err } @@ -325,6 +362,9 @@ func (c *Client) StopContainer(id string, timeout uint) error { if status == http.StatusNotFound { return &NoSuchContainer{ID: id} } + if status == http.StatusNotModified { + return &ContainerNotRunning{ID: id} + } if err != nil { return err } @@ -590,7 +630,7 @@ func (c *Client) Logs(opts LogsOptions) error { opts.Tail = "all" } path := "/containers/" + opts.Container + "/logs?" + queryString(opts) - return c.stream("GET", path, opts.RawTerminal, nil, nil, opts.OutputStream, opts.ErrorStream) + return c.stream("GET", path, opts.RawTerminal, false, nil, nil, opts.OutputStream, opts.ErrorStream) } // ResizeContainerTTY resizes the terminal to the given height and width. @@ -617,10 +657,10 @@ type ExportContainerOptions struct { // See http://goo.gl/Lqk0FZ for more details. func (c *Client) ExportContainer(opts ExportContainerOptions) error { if opts.ID == "" { - return NoSuchContainer{ID: opts.ID} + return &NoSuchContainer{ID: opts.ID} } url := fmt.Sprintf("/containers/%s/export", opts.ID) - return c.stream("GET", url, true, nil, nil, opts.OutputStream, nil) + return c.stream("GET", url, true, false, nil, nil, opts.OutputStream, nil) } // NoSuchContainer is the error returned when a given container does not exist. @@ -628,6 +668,26 @@ type NoSuchContainer struct { ID string } -func (err NoSuchContainer) Error() string { +func (err *NoSuchContainer) Error() string { return "No such container: " + err.ID } + +// ContainerAlreadyRunning is the error returned when a given container is +// already running. +type ContainerAlreadyRunning struct { + ID string +} + +func (err *ContainerAlreadyRunning) Error() string { + return "Container already running: " + err.ID +} + +// ContainerNotRunning is the error returned when a given container is not +// running. +type ContainerNotRunning struct { + ID string +} + +func (err *ContainerNotRunning) Error() string { + return "Container not running: " + err.ID +} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container_test.go index c027c92c4e0..f3e19542a88 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container_test.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container_test.go @@ -493,6 +493,15 @@ func TestStartContainerNotFound(t *testing.T) { } } +func TestStartContainerAlreadyRunning(t *testing.T) { + client := newTestClient(&FakeRoundTripper{message: "container already running", status: http.StatusNotModified}) + err := client.StartContainer("a2334", &HostConfig{}) + expected := &ContainerAlreadyRunning{ID: "a2334"} + if !reflect.DeepEqual(err, expected) { + t.Errorf("StartContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) + } +} + func TestStopContainer(t *testing.T) { fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} client := newTestClient(fakeRT) @@ -520,6 +529,15 @@ func TestStopContainerNotFound(t *testing.T) { } } +func TestStopContainerNotRunning(t *testing.T) { + client := newTestClient(&FakeRoundTripper{message: "container not running", status: http.StatusNotModified}) + err := client.StopContainer("a2334", 10) + expected := &ContainerNotRunning{ID: "a2334"} + if !reflect.DeepEqual(err, expected) { + t.Errorf("StopContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) + } +} + func TestRestartContainer(t *testing.T) { fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} client := newTestClient(fakeRT) @@ -1313,8 +1331,12 @@ func TestExportContainerNoId(t *testing.T) { client := Client{} out := stdoutMock{bytes.NewBufferString("")} err := client.ExportContainer(ExportContainerOptions{OutputStream: out}) - if err != (NoSuchContainer{}) { - t.Errorf("ExportContainer: wrong error. Want %#v. Got %#v.", NoSuchContainer{}, err) + e, ok := err.(*NoSuchContainer) + if !ok { + t.Errorf("ExportContainer: wrong error. Want NoSuchContainer. Got %#v.", e) + } + if e.ID != "" { + t.Errorf("ExportContainer: wrong ID. Want %q. Got %q", "", e.ID) } } @@ -1361,3 +1383,34 @@ func TestPassingNameOptToCreateContainerReturnsItInContainer(t *testing.T) { t.Errorf("Container name expected to be TestCreateContainer, was %s", container.Name) } } + +func TestAlwaysRestart(t *testing.T) { + policy := AlwaysRestart() + if policy.Name != "always" { + t.Errorf("AlwaysRestart(): wrong policy name. Want %q. Got %q", "always", policy.Name) + } + if policy.MaximumRetryCount != 0 { + t.Errorf("AlwaysRestart(): wrong MaximumRetryCount. Want 0. Got %d", policy.MaximumRetryCount) + } +} + +func TestRestartOnFailure(t *testing.T) { + const retry = 5 + policy := RestartOnFailure(retry) + if policy.Name != "on-failure" { + t.Errorf("RestartOnFailure(%d): wrong policy name. Want %q. Got %q", retry, "on-failure", policy.Name) + } + if policy.MaximumRetryCount != retry { + t.Errorf("RestartOnFailure(%d): wrong MaximumRetryCount. Want %d. Got %d", retry, retry, policy.MaximumRetryCount) + } +} + +func TestNeverRestart(t *testing.T) { + policy := NeverRestart() + if policy.Name != "no" { + t.Errorf("NeverRestart(): wrong policy name. Want %q. Got %q", "always", policy.Name) + } + if policy.MaximumRetryCount != 0 { + t.Errorf("NeverRestart(): wrong MaximumRetryCount. Want 0. Got %d", policy.MaximumRetryCount) + } +} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/event.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/event.go index 1808e4160c5..262d4ee2554 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/event.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/event.go @@ -20,10 +20,10 @@ import ( // APIEvents represents an event returned by the API. type APIEvents struct { - Status string - ID string - From string - Time int64 + Status string `json:"Status,omitempty" yaml:"Status,omitempty"` + ID string `json:"ID,omitempty" yaml:"ID,omitempty"` + From string `json:"From,omitempty" yaml:"From,omitempty"` + Time int64 `json:"Time,omitempty" yaml:"Time,omitempty"` } type eventMonitoringState struct { diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/example_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/example_test.go index aac99e6091b..8c2c719e6e8 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/example_test.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/example_test.go @@ -8,10 +8,11 @@ import ( "archive/tar" "bytes" "fmt" - "github.com/fsouza/go-dockerclient" "io" "log" "time" + + "github.com/fsouza/go-dockerclient" ) func ExampleClient_AttachToContainer() { diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image.go index 4280d13552f..4ce94c81c07 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image.go @@ -20,28 +20,26 @@ import ( // APIImages represent an image returned in the ListImages call. type APIImages struct { - ID string `json:"Id"` - RepoTags []string `json:",omitempty"` - Created int64 - Size int64 - VirtualSize int64 - ParentId string `json:",omitempty"` - Repository string `json:",omitempty"` - Tag string `json:",omitempty"` + ID string `json:"Id" yaml:"Id"` + RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty"` + Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"` + Size int64 `json:"Size,omitempty" yaml:"Size,omitempty"` + VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty"` + ParentId string `json:"ParentId,omitempty" yaml:"ParentId,omitempty"` } type Image struct { - ID string `json:"id"` - Parent string `json:"parent,omitempty"` - Comment string `json:"comment,omitempty"` - Created time.Time `json:"created"` - Container string `json:"container,omitempty"` - ContainerConfig Config `json:"containerconfig,omitempty"` - DockerVersion string `json:"dockerversion,omitempty"` - Author string `json:"author,omitempty"` - Config *Config `json:"config,omitempty"` - Architecture string `json:"architecture,omitempty"` - Size int64 + ID string `json:"Id" yaml:"Id"` + Parent string `json:"Parent,omitempty" yaml:"Parent,omitempty"` + Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty"` + Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty"` + Container string `json:"Container,omitempty" yaml:"Container,omitempty"` + ContainerConfig Config `json:"ContainerConfig,omitempty" yaml:"ContainerConfig,omitempty"` + DockerVersion string `json:"DockerVersion,omitempty" yaml:"DockerVersion,omitempty"` + Author string `json:"Author,omitempty" yaml:"Author,omitempty"` + Config *Config `json:"Config,omitempty" yaml:"Config,omitempty"` + Architecture string `json:"Architecture,omitempty" yaml:"Architecture,omitempty"` + Size int64 `json:"Size,omitempty" yaml:"Size,omitempty"` } type ImagePre012 struct { @@ -55,7 +53,7 @@ type ImagePre012 struct { Author string `json:"author,omitempty"` Config *Config `json:"config,omitempty"` Architecture string `json:"architecture,omitempty"` - Size int64 + Size int64 `json:"size,omitempty"` } var ( @@ -164,7 +162,7 @@ type PushImageOptions struct { } // AuthConfiguration represents authentication options to use in the PushImage -// method. It represents the authencation in the Docker index server. +// method. It represents the authentication in the Docker index server. type AuthConfiguration struct { Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` @@ -190,7 +188,7 @@ func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error headers["X-Registry-Auth"] = base64.URLEncoding.EncodeToString(buf.Bytes()) - return c.stream("POST", path, true, headers, nil, opts.OutputStream, nil) + return c.stream("POST", path, true, false, headers, nil, opts.OutputStream, nil) } // PullImageOptions present the set of options available for pulling an image @@ -198,10 +196,11 @@ func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error // // See http://goo.gl/PhBKnS for more details. type PullImageOptions struct { - Repository string `qs:"fromImage"` - Registry string - Tag string - OutputStream io.Writer `qs:"-"` + Repository string `qs:"fromImage"` + Registry string + Tag string + OutputStream io.Writer `qs:"-"` + RawJSONStream bool `qs:"-"` } // PullImage pulls an image from a remote registry, logging progress to w. @@ -217,12 +216,41 @@ func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error json.NewEncoder(&buf).Encode(auth) headers["X-Registry-Auth"] = base64.URLEncoding.EncodeToString(buf.Bytes()) - return c.createImage(queryString(&opts), headers, nil, opts.OutputStream) + return c.createImage(queryString(&opts), headers, nil, opts.OutputStream, opts.RawJSONStream) } -func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, w io.Writer) error { +func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool) error { path := "/images/create?" + qs - return c.stream("POST", path, true, headers, in, w, nil) + return c.stream("POST", path, true, rawJSONStream, headers, in, w, nil) +} + +// LoadImageOptions represents the options for LoadImage Docker API Call +// +// See http://goo.gl/Y8NNCq for more details. +type LoadImageOptions struct { + InputStream io.Reader +} + +// LoadImage imports a tarball docker image +// +// See http://goo.gl/Y8NNCq for more details. +func (c *Client) LoadImage(opts LoadImageOptions) error { + return c.stream("POST", "/images/load", true, false, nil, opts.InputStream, nil, nil) +} + +// ExportImageOptions represent the options for ExportImage Docker API call +// +// See http://goo.gl/mi6kvk for more details. +type ExportImageOptions struct { + Name string + OutputStream io.Writer +} + +// ExportImage exports an image (as a tar file) into the stream +// +// See http://goo.gl/mi6kvk for more details. +func (c *Client) ExportImage(opts ExportImageOptions) error { + return c.stream("GET", fmt.Sprintf("/images/%s/get", opts.Name), true, false, nil, nil, opts.OutputStream, nil) } // ImportImageOptions present the set of informations available for importing @@ -257,20 +285,21 @@ func (c *Client) ImportImage(opts ImportImageOptions) error { opts.InputStream = bytes.NewBuffer(b) opts.Source = "-" } - return c.createImage(queryString(&opts), nil, opts.InputStream, opts.OutputStream) + return c.createImage(queryString(&opts), nil, opts.InputStream, opts.OutputStream, false) } // BuildImageOptions present the set of informations available for building // an image from a tarfile with a Dockerfile in it,the details about Dockerfile // see http://docs.docker.io/en/latest/reference/builder/ type BuildImageOptions struct { - Name string `qs:"t"` - NoCache bool `qs:"nocache"` - SuppressOutput bool `qs:"q"` - RmTmpContainer bool `qs:"rm"` - InputStream io.Reader `qs:"-"` - OutputStream io.Writer `qs:"-"` - Remote string `qs:"remote"` + Name string `qs:"t"` + NoCache bool `qs:"nocache"` + SuppressOutput bool `qs:"q"` + RmTmpContainer bool `qs:"rm"` + ForceRmTmpContainer bool `qs:"forcerm"` + InputStream io.Reader `qs:"-"` + OutputStream io.Writer `qs:"-"` + Remote string `qs:"remote"` } // BuildImage builds an image from a tarball's url or a Dockerfile in the input @@ -289,7 +318,7 @@ func (c *Client) BuildImage(opts BuildImageOptions) error { return ErrMissingRepo } return c.stream("POST", fmt.Sprintf("/build?%s", - queryString(&opts)), true, headers, opts.InputStream, opts.OutputStream, nil) + queryString(&opts)), true, false, headers, opts.InputStream, opts.OutputStream, nil) } // TagImageOptions present the set of options to tag an image diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image_test.go index 12928b37c51..97612f25b03 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image_test.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image_test.go @@ -309,6 +309,33 @@ func TestPullImage(t *testing.T) { } } +func TestPullImageWithRawJSON(t *testing.T) { + body := ` + {"status":"Pulling..."} + {"status":"Pulling", "progress":"1 B/ 100 B", "progressDetail":{"current":1, "total":100}} + ` + fakeRT := &FakeRoundTripper{ + message: body, + status: http.StatusOK, + header: map[string]string{ + "Content-Type": "application/json", + }, + } + client := newTestClient(fakeRT) + var buf bytes.Buffer + err := client.PullImage(PullImageOptions{ + Repository: "base", + OutputStream: &buf, + RawJSONStream: true, + }, AuthConfiguration{}) + if err != nil { + t.Fatal(err) + } + if buf.String() != body { + t.Errorf("PullImage: Wrong raw output. Want %q. Got %q", body, buf.String()) + } +} + func TestPullImageWithoutOutputStream(t *testing.T) { fakeRT := &FakeRoundTripper{message: "Pulling 1/100", status: http.StatusOK} client := newTestClient(fakeRT) @@ -514,19 +541,20 @@ func TestBuildImageParameters(t *testing.T) { client := newTestClient(fakeRT) var buf bytes.Buffer opts := BuildImageOptions{ - Name: "testImage", - NoCache: true, - SuppressOutput: true, - RmTmpContainer: true, - InputStream: &buf, - OutputStream: &buf, + Name: "testImage", + NoCache: true, + SuppressOutput: true, + RmTmpContainer: true, + ForceRmTmpContainer: true, + InputStream: &buf, + OutputStream: &buf, } err := client.BuildImage(opts) if err != nil && strings.Index(err.Error(), "build image fail") == -1 { t.Fatal(err) } req := fakeRT.requests[0] - expected := map[string][]string{"t": {opts.Name}, "nocache": {"1"}, "q": {"1"}, "rm": {"1"}} + expected := map[string][]string{"t": {opts.Name}, "nocache": {"1"}, "q": {"1"}, "rm": {"1"}, "forcerm": {"1"}} got := map[string][]string(req.URL.Query()) if !reflect.DeepEqual(got, expected) { t.Errorf("BuildImage: wrong query string. Want %#v. Got %#v.", expected, got) @@ -640,3 +668,45 @@ func TestIsUrl(t *testing.T) { t.Errorf("isURL: wrong match. Expected %#v to not be a url. Got %#v", url, result) } } + +func TestLoadImage(t *testing.T) { + fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} + client := newTestClient(fakeRT) + tar, err := os.Open("testing/data/container.tar") + if err != nil { + t.Fatal(err) + } else { + defer tar.Close() + } + opts := LoadImageOptions{InputStream: tar} + err = client.LoadImage(opts) + if nil != err { + t.Error(err) + } + req := fakeRT.requests[0] + if req.Method != "POST" { + t.Errorf("LoadImage: wrong method. Expected %q. Got %q.", "POST", req.Method) + } + if req.URL.Path != "/images/load" { + t.Errorf("LoadImage: wrong URL. Expected %q. Got %q.", "/images/load", req.URL.Path) + } +} + +func TestExportImage(t *testing.T) { + var buf bytes.Buffer + fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} + client := newTestClient(fakeRT) + opts := ExportImageOptions{Name: "testimage", OutputStream: &buf} + err := client.ExportImage(opts) + if nil != err { + t.Error(err) + } + req := fakeRT.requests[0] + if req.Method != "GET" { + t.Errorf("ExportImage: wrong method. Expected %q. Got %q.", "GET", req.Method) + } + expectedPath := "/images/testimage/get" + if req.URL.Path != expectedPath { + t.Errorf("ExportIMage: wrong path. Expected %q. Got %q.", expectedPath, req.URL.Path) + } +} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go index 432568b005b..42c20e45fc6 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go @@ -12,8 +12,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/fsouza/go-dockerclient" - "github.com/gorilla/mux" mathrand "math/rand" "net" "net/http" @@ -22,6 +20,9 @@ import ( "strings" "sync" "time" + + "github.com/fsouza/go-dockerclient" + "github.com/gorilla/mux" ) // DockerServer represents a programmable, concurrent (not much), HTTP server @@ -102,6 +103,8 @@ func (s *DockerServer) buildMuxer() { s.mux.Path("/images/{name:.*}/push").Methods("POST").HandlerFunc(s.handlerWrapper(s.pushImage)) s.mux.Path("/events").Methods("GET").HandlerFunc(s.listEvents) s.mux.Path("/_ping").Methods("GET").HandlerFunc(s.handlerWrapper(s.pingDocker)) + s.mux.Path("/images/load").Methods("POST").HandlerFunc(s.handlerWrapper(s.loadImage)) + s.mux.Path("/images/{id:.*}/get").Methods("GET").HandlerFunc(s.handlerWrapper(s.getImage)) } // PrepareFailure adds a new expected failure based on a URL regexp it receives @@ -653,3 +656,13 @@ func (s *DockerServer) generateEvent() *docker.APIEvents { Time: time.Now().Unix(), } } + +func (s *DockerServer) loadImage(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func (s *DockerServer) getImage(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/tar") + +} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go index 97c3687ec67..520300438b2 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go @@ -7,7 +7,6 @@ package testing import ( "encoding/json" "fmt" - "github.com/fsouza/go-dockerclient" "math/rand" "net" "net/http" @@ -17,6 +16,8 @@ import ( "strings" "testing" "time" + + "github.com/fsouza/go-dockerclient" ) func TestNewServer(t *testing.T) { diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index 41278370acb..2efdcf20d5e 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -17,10 +17,10 @@ limitations under the License. package v1beta1 import ( + "github.com/fsouza/go-dockerclient" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" - "github.com/GoogleCloudPlatform/kubernetes/third_party/docker-api-structs" ) // Common string formats diff --git a/third_party/docker-api-structs/LICENSE b/third_party/docker-api-structs/LICENSE deleted file mode 100644 index 7a6d8bb69d9..00000000000 --- a/third_party/docker-api-structs/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2014, go-dockerclient authors -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/docker-api-structs/README.third_party.md b/third_party/docker-api-structs/README.third_party.md deleted file mode 100644 index 3ba6150eb80..00000000000 --- a/third_party/docker-api-structs/README.third_party.md +++ /dev/null @@ -1 +0,0 @@ -This package is a fork of part of [go-dockerclient](https://github.com/fsouza/go-dockerclient). diff --git a/third_party/docker-api-structs/container.go b/third_party/docker-api-structs/container.go deleted file mode 100644 index 3e45a7547de..00000000000 --- a/third_party/docker-api-structs/container.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2014 go-dockerclient authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package docker - -import ( - "time" -) - -type Container struct { - ID string `yaml:"ID,omitempty" json:"ID,omitempty"` - Created time.Time `yaml:"Created,omitempty" json:"Created,omitempty"` - Path string `yaml:"Path,omitempty" json:"Path,omitempty"` - Args []string `yaml:"Args,omitempty" json:"Args,omitempty"` - Config *Config `yaml:"Config,omitempty" json:"Config,omitempty"` - State State `yaml:"State,omitempty" json:"State,omitempty"` - Image string `yaml:"Image,omitempty" json:"Image,omitempty"` - - NetworkSettings *NetworkSettings `yaml:"NetworkSettings,omitempty" json:"NetworkSettings,omitempty"` - - SysInitPath string `yaml:"SysInitPath,omitempty" json:"SysInitPath,omitempty"` - ResolvConfPath string `yaml:"ResolvConfPath,omitempty" json:"ResolvConfPath,omitempty"` - HostnamePath string `yaml:"HostnamePath,omitempty" json:"HostnamePath,omitempty"` - HostsPath string `yaml:"HostsPath,omitempty" json:"HostsPath,omitempty"` - Name string `yaml:"Name,omitempty" json:"Name,omitempty"` - Driver string `yaml:"Driver,omitempty" json:"Driver,omitempty"` - - Volumes map[string]string `yaml:"Volumes,omitempty" json:"Volumes,omitempty"` - VolumesRW map[string]bool `yaml:"VolumesRW,omitempty" json:"VolumesRW,omitempty"` - HostConfig *HostConfig `yaml:"HostConfig,omitempty" json:"HostConfig,omitempty"` -} - -type Config struct { - Hostname string `yaml:"Hostname,omitempty" json:"Hostname,omitempty"` - Domainname string `yaml:"Domainname,omitempty" json:"Domainname,omitempty"` - User string `yaml:"User,omitempty" json:"User,omitempty"` - Memory int64 `yaml:"Memory,omitempty" json:"Memory,omitempty"` - MemorySwap int64 `yaml:"MemorySwap,omitempty" json:"MemorySwap,omitempty"` - CpuShares int64 `yaml:"CpuShares,omitempty" json:"CpuShares,omitempty"` - AttachStdin bool `yaml:"AttachStdin,omitempty" json:"AttachStdin,omitempty"` - AttachStdout bool `yaml:"AttachStdout,omitempty" json:"AttachStdout,omitempty"` - AttachStderr bool `yaml:"AttachStderr,omitempty" json:"AttachStderr,omitempty"` - PortSpecs []string `yaml:"PortSpecs,omitempty" json:"PortSpecs,omitempty"` - ExposedPorts map[Port]struct{} `yaml:"ExposedPorts,omitempty" json:"ExposedPorts,omitempty"` - Tty bool `yaml:"Tty,omitempty" json:"Tty,omitempty"` - OpenStdin bool `yaml:"OpenStdin,omitempty" json:"OpenStdin,omitempty"` - StdinOnce bool `yaml:"StdinOnce,omitempty" json:"StdinOnce,omitempty"` - Env []string `yaml:"Env,omitempty" json:"Env,omitempty"` - Cmd []string `yaml:"Cmd,omitempty" json:"Cmd,omitempty"` - Dns []string `yaml:"Dns,omitempty" json:"Dns,omitempty"` - Image string `yaml:"Image,omitempty" json:"Image,omitempty"` - Volumes map[string]struct{} `yaml:"Volumes,omitempty" json:"Volumes,omitempty"` - VolumesFrom string `yaml:"VolumesFrom,omitempty" json:"VolumesFrom,omitempty"` - WorkingDir string `yaml:"WorkingDir,omitempty" json:"WorkingDir,omitempty"` - Entrypoint []string `yaml:"Entrypoint,omitempty" json:"Entrypoint,omitempty"` - NetworkDisabled bool `yaml:"NetworkDisabled,omitempty" json:"NetworkDisabled,omitempty"` -} - -type State struct { - Running bool `yaml:"Running,omitempty" json:"Running,omitempty"` - Paused bool `yaml:"Paused,omitempty" json:"Paused,omitempty"` - Pid int `yaml:"Pid,omitempty" json:"Pid,omitempty"` - ExitCode int `yaml:"ExitCode,omitempty" json:"ExitCode,omitempty"` - StartedAt time.Time `yaml:"StartedAt,omitempty" json:"StartedAt,omitempty"` - FinishedAt time.Time `yaml:"FinishedAt,omitempty" json:"FinishedAt,omitempty"` -} - -type PortBinding struct { - HostIp string `yaml:"HostIp,omitempty" json:"HostIp,omitempty"` - HostPort string `yaml:"HostPort,omitempty" json:"HostPort,omitempty"` -} - -type PortMapping map[string]string - -type NetworkSettings struct { - IPAddress string `yaml:"IPAddress,omitempty" json:"IPAddress,omitempty"` - IPPrefixLen int `yaml:"IPPrefixLen,omitempty" json:"IPPrefixLen,omitempty"` - Gateway string `yaml:"Gateway,omitempty" json:"Gateway,omitempty"` - Bridge string `yaml:"Bridge,omitempty" json:"Bridge,omitempty"` - PortMapping map[string]PortMapping `yaml:"PortMapping,omitempty" json:"PortMapping,omitempty"` - Ports map[Port][]PortBinding `yaml:"Ports,omitempty" json:"Ports,omitempty"` -} - -type KeyValuePair struct { - Key string `yaml:"Key,omitempty" json:"Key,omitempty"` - Value string `yaml:"Value,omitempty" json:"Value,omitempty"` -} - -type Port string - -type HostConfig struct { - Binds []string `yaml:"Binds,omitempty" json:"Binds,omitempty"` - ContainerIDFile string `yaml:"ContainerIDFile,omitempty" json:"ContainerIDFile,omitempty"` - LxcConf []KeyValuePair `yaml:"LxcConf,omitempty" json:"LxcConf,omitempty"` - Privileged bool `yaml:"Privileged,omitempty" json:"Privileged,omitempty"` - PortBindings map[Port][]PortBinding `yaml:"PortBindings,omitempty" json:"PortBindings,omitempty"` - Links []string `yaml:"Links,omitempty" json:"Links,omitempty"` - PublishAllPorts bool `yaml:"PublishAllPorts,omitempty" json:"PublishAllPorts,omitempty"` - Dns []string `yaml:"Dns,omitempty" json:"Dns,omitempty"` - DnsSearch []string `yaml:"DnsSearch,omitempty" json:"DnsSearch,omitempty"` - VolumesFrom []string `yaml:"VolumesFrom,omitempty" json:"VolumesFrom,omitempty"` - NetworkMode string `yaml:"NetworkMode,omitempty" json:"NetworkMode,omitempty"` -}