Bump go-dockerclient.
This commit is contained in:
parent
1cbf0bc910
commit
3956f6ce4d
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@ -50,8 +50,8 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/fsouza/go-dockerclient",
|
||||
"Comment": "0.2.1-185-g4fc0e0d",
|
||||
"Rev": "4fc0e0dbba85f2f799aa77cea594d40267f5bd5a"
|
||||
"Comment": "0.2.1-251-g2e21eae",
|
||||
"Rev": "2e21eaef5e7d46f002e259eb7cde39ed3680a7b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/godbus/dbus",
|
||||
|
4
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml
generated
vendored
4
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml
generated
vendored
@ -1,8 +1,8 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.1.2
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.2.2
|
||||
- 1.3.1
|
||||
- tip
|
||||
env:
|
||||
- GOARCH=amd64
|
||||
|
11
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS
generated
vendored
11
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS
generated
vendored
@ -1,13 +1,20 @@
|
||||
# This is the official list of go-dockerclient authors for copyright purposes.
|
||||
|
||||
Aldrin Leal <aldrin@leal.eng.br>
|
||||
Andreas Jaekle <andreas@jaekle.net>
|
||||
Andrews Medina <andrewsmedina@gmail.com>
|
||||
Andy Goldstein <andy.goldstein@redhat.com>
|
||||
Ben McCann <benmccann.com>
|
||||
Carlos Diaz-Padron <cpadron@mozilla.com>
|
||||
Cezar Sa Espinola <cezar.sa@corp.globo.com>
|
||||
Cheah Chu Yeow <chuyeow@gmail.com>
|
||||
cheneydeng <cheneydeng@qq.com>
|
||||
CMGS <ilskdw@gmail.com>
|
||||
Daniel, Dao Quang Minh <dqminh89@gmail.com>
|
||||
David Huie <dahuie@gmail.com>
|
||||
Ed <edrocksit@gmail.com>
|
||||
Eric Anderson <anderson@copperegg.com>
|
||||
Fabio Rehm <fgrehm@gmail.com>
|
||||
Flavia Missi <flaviamissi@gmail.com>
|
||||
Francisco Souza <f@souza.cc>
|
||||
Jari Kolehmainen <jari.kolehmainen@digia.com>
|
||||
@ -15,12 +22,16 @@ Jason Wilder <jwilder@litl.com>
|
||||
Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
|
||||
Jeff Mitchell <jeffrey.mitchell@gmail.com>
|
||||
Jeffrey Hulten <jhulten@gmail.com>
|
||||
Johan Euphrosine <proppy@google.com>
|
||||
Karan Misra <kidoman@gmail.com>
|
||||
Kim, Hirokuni <hirokuni.kim@kvh.co.jp>
|
||||
Lucas Clemente <lucas@clemente.io>
|
||||
Omeid Matten <public@omeid.me>
|
||||
Paul Morie <pmorie@gmail.com>
|
||||
Peter Jihoon Kim <raingrove@gmail.com>
|
||||
Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
|
||||
Rafe Colton <r.colton@modcloth.com>
|
||||
Robert Williamson <williamson.robert@gmail.com>
|
||||
Salvador Gironès <salvadorgirones@gmail.com>
|
||||
Simon Eskildsen <sirup@sirupsen.com>
|
||||
Simon Menke <simon.menke@gmail.com>
|
||||
|
39
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/README.markdown
generated
vendored
39
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/README.markdown
generated
vendored
@ -11,27 +11,28 @@ For more details, check the [remote API documentation](http://docs.docker.io/en/
|
||||
|
||||
## Example
|
||||
|
||||
package main
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
func main() {
|
||||
endpoint := "unix:///var/run/docker.sock"
|
||||
client, _ := docker.NewClient(endpoint)
|
||||
imgs, _ := client.ListImages(true)
|
||||
for _, img := range imgs {
|
||||
fmt.Println("ID: ", img.ID)
|
||||
fmt.Println("RepoTags: ", img.RepoTags)
|
||||
fmt.Println("Created: ", img.Created)
|
||||
fmt.Println("Size: ", img.Size)
|
||||
fmt.Println("VirtualSize: ", img.VirtualSize)
|
||||
fmt.Println("ParentId: ", img.ParentId)
|
||||
fmt.Println("Repository: ", img.Repository)
|
||||
}
|
||||
}
|
||||
func main() {
|
||||
endpoint := "unix:///var/run/docker.sock"
|
||||
client, _ := docker.NewClient(endpoint)
|
||||
imgs, _ := client.ListImages(true)
|
||||
for _, img := range imgs {
|
||||
fmt.Println("ID: ", img.ID)
|
||||
fmt.Println("RepoTags: ", img.RepoTags)
|
||||
fmt.Println("Created: ", img.Created)
|
||||
fmt.Println("Size: ", img.Size)
|
||||
fmt.Println("VirtualSize: ", img.VirtualSize)
|
||||
fmt.Println("ParentId: ", img.ParentId)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
|
2
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/change.go
generated
vendored
2
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/change.go
generated
vendored
@ -16,7 +16,7 @@ const (
|
||||
|
||||
// Change represents a change in a container.
|
||||
//
|
||||
// See http://goo.gl/DpGyzK for more details.
|
||||
// See http://goo.gl/QkW9sH for more details.
|
||||
type Change struct {
|
||||
Path string
|
||||
Kind ChangeType
|
||||
|
68
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go
generated
vendored
68
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go
generated
vendored
@ -4,7 +4,7 @@
|
||||
|
||||
// Package docker provides a client for the Docker remote API.
|
||||
//
|
||||
// See http://goo.gl/mxyql for more details on the remote API.
|
||||
// See http://goo.gl/G3plxW for more details on the remote API.
|
||||
package docker
|
||||
|
||||
import (
|
||||
@ -21,9 +21,6 @@ import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/fsouza/go-dockerclient/utils"
|
||||
)
|
||||
|
||||
const userAgent = "go-dockerclient"
|
||||
@ -113,11 +110,11 @@ func (version ApiVersion) compare(other ApiVersion) int {
|
||||
// interaction with the API.
|
||||
type Client struct {
|
||||
SkipServerVersionCheck bool
|
||||
HTTPClient *http.Client
|
||||
|
||||
endpoint string
|
||||
endpointURL *url.URL
|
||||
eventMonitor *eventMonitoringState
|
||||
client *http.Client
|
||||
requestedApiVersion ApiVersion
|
||||
serverApiVersion ApiVersion
|
||||
expectedApiVersion ApiVersion
|
||||
@ -142,7 +139,6 @@ func NewVersionedClient(endpoint string, apiVersionString string) (*Client, erro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var requestedApiVersion ApiVersion
|
||||
if strings.Contains(apiVersionString, ".") {
|
||||
requestedApiVersion, err = NewApiVersion(apiVersionString)
|
||||
@ -150,11 +146,10 @@ func NewVersionedClient(endpoint string, apiVersionString string) (*Client, erro
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &Client{
|
||||
HTTPClient: http.DefaultClient,
|
||||
endpoint: endpoint,
|
||||
endpointURL: u,
|
||||
client: http.DefaultClient,
|
||||
eventMonitor: new(eventMonitoringState),
|
||||
requestedApiVersion: requestedApiVersion,
|
||||
}, nil
|
||||
@ -177,29 +172,6 @@ func (c *Client) checkApiVersion() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseApiVersionString(input string) (version uint16, err error) {
|
||||
version = 0
|
||||
|
||||
if !strings.Contains(input, ".") {
|
||||
return 0, fmt.Errorf("Unable to parse version '%s'", input)
|
||||
}
|
||||
|
||||
arr := strings.Split(input, ".")
|
||||
|
||||
major, err := strconv.Atoi(arr[0])
|
||||
if err != nil {
|
||||
return version, err
|
||||
}
|
||||
|
||||
minor, err := strconv.Atoi(arr[1])
|
||||
if err != nil {
|
||||
return version, err
|
||||
}
|
||||
|
||||
version = uint16(major)<<8 | uint16(minor)
|
||||
return version, nil
|
||||
}
|
||||
|
||||
// Ping pings the docker server
|
||||
//
|
||||
// See http://goo.gl/stJENm for more details.
|
||||
@ -223,13 +195,11 @@ func (c *Client) getServerApiVersionString() (version string, err error) {
|
||||
if status != http.StatusOK {
|
||||
return "", fmt.Errorf("Received unexpected status %d while trying to retrieve the server version", status)
|
||||
}
|
||||
|
||||
var versionResponse map[string]string
|
||||
err = json.Unmarshal(body, &versionResponse)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
version = versionResponse["ApiVersion"]
|
||||
return version, nil
|
||||
}
|
||||
@ -243,14 +213,12 @@ func (c *Client) do(method, path string, data interface{}) ([]byte, int, error)
|
||||
}
|
||||
params = bytes.NewBuffer(buf)
|
||||
}
|
||||
|
||||
if path != "/version" && !c.SkipServerVersionCheck && c.expectedApiVersion == nil {
|
||||
err := c.checkApiVersion()
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, c.getURL(path), params)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
@ -277,7 +245,7 @@ func (c *Client) do(method, path string, data interface{}) ([]byte, int, error)
|
||||
}
|
||||
defer clientconn.Close()
|
||||
} else {
|
||||
resp, err = c.client.Do(req)
|
||||
resp, err = c.HTTPClient.Do(req)
|
||||
}
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
@ -296,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)
|
||||
}
|
||||
@ -335,7 +303,7 @@ func (c *Client) stream(method, path string, setRawTerminal bool, headers map[st
|
||||
resp, err = clientconn.Do(req)
|
||||
defer clientconn.Close()
|
||||
} else {
|
||||
resp, err = c.client.Do(req)
|
||||
resp, err = c.HTTPClient.Do(req)
|
||||
}
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
@ -352,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
|
||||
@ -375,7 +349,7 @@ func (c *Client) stream(method, path string, setRawTerminal bool, headers map[st
|
||||
if setRawTerminal {
|
||||
_, err = io.Copy(stdout, resp.Body)
|
||||
} else {
|
||||
_, err = utils.StdCopy(stdout, stderr, resp.Body)
|
||||
_, err = stdCopy(stdout, stderr, resp.Body)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -418,18 +392,17 @@ func (c *Client) hijack(method, path string, success chan struct{}, setRawTermin
|
||||
<-success
|
||||
}
|
||||
rwc, br := clientconn.Hijack()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
errs := make(chan error, 2)
|
||||
exit := make(chan bool)
|
||||
go func() {
|
||||
defer close(exit)
|
||||
var err error
|
||||
if setRawTerminal {
|
||||
_, err = io.Copy(stdout, br)
|
||||
} else {
|
||||
_, err = utils.StdCopy(stdout, stderr, br)
|
||||
_, err = stdCopy(stdout, stderr, br)
|
||||
}
|
||||
errs <- err
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
var err error
|
||||
@ -440,14 +413,9 @@ func (c *Client) hijack(method, path string, success chan struct{}, setRawTermin
|
||||
CloseWrite() error
|
||||
}).CloseWrite()
|
||||
errs <- err
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
close(errs)
|
||||
if err := <-errs; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
<-exit
|
||||
return <-errs
|
||||
}
|
||||
|
||||
func (c *Client) getURL(path string) string {
|
||||
|
19
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client_test.go
generated
vendored
19
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client_test.go
generated
vendored
@ -24,10 +24,9 @@ func TestNewAPIClient(t *testing.T) {
|
||||
if client.endpoint != endpoint {
|
||||
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
|
||||
}
|
||||
if client.client != http.DefaultClient {
|
||||
t.Errorf("Expected http.Client %#v. Got %#v.", http.DefaultClient, client.client)
|
||||
if client.HTTPClient != http.DefaultClient {
|
||||
t.Errorf("Expected http.Client %#v. Got %#v.", http.DefaultClient, client.HTTPClient)
|
||||
}
|
||||
|
||||
// test unix socket endpoints
|
||||
endpoint = "unix:///var/run/docker.sock"
|
||||
client, err = NewClient(endpoint)
|
||||
@ -54,8 +53,8 @@ func TestNewVersionedClient(t *testing.T) {
|
||||
if client.endpoint != endpoint {
|
||||
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
|
||||
}
|
||||
if client.client != http.DefaultClient {
|
||||
t.Errorf("Expected http.Client %#v. Got %#v.", http.DefaultClient, client.client)
|
||||
if client.HTTPClient != http.DefaultClient {
|
||||
t.Errorf("Expected http.Client %#v. Got %#v.", http.DefaultClient, client.HTTPClient)
|
||||
}
|
||||
if reqVersion := client.requestedApiVersion.String(); reqVersion != "1.12" {
|
||||
t.Errorf("Wrong requestApiVersion. Want %q. Got %q.", "1.12", reqVersion)
|
||||
@ -249,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() {
|
||||
|
275
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go
generated
vendored
275
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go
generated
vendored
@ -18,7 +18,7 @@ import (
|
||||
|
||||
// ListContainersOptions specify parameters to the ListContainers function.
|
||||
//
|
||||
// See http://goo.gl/QpCnDN for more details.
|
||||
// See http://goo.gl/XqtcyU for more details.
|
||||
type ListContainersOptions struct {
|
||||
All bool
|
||||
Size bool
|
||||
@ -28,30 +28,30 @@ 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.
|
||||
//
|
||||
// See http://goo.gl/QpCnDN for more details.
|
||||
// See http://goo.gl/XqtcyU for more details.
|
||||
func (c *Client) ListContainers(opts ListContainersOptions) ([]APIContainers, error) {
|
||||
path := "/containers/json?" + queryString(opts)
|
||||
body, _, err := c.do("GET", path, nil)
|
||||
@ -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,60 +155,61 @@ func parsePort(rawPort string) (int, error) {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Hostname string
|
||||
Domainname string
|
||||
User string
|
||||
Memory uint64
|
||||
MemorySwap uint64
|
||||
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"`
|
||||
CpuSet string `json:"CpuSet,omitempty" yaml:"CpuSet,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.
|
||||
//
|
||||
// See http://goo.gl/2o52Sx for more details.
|
||||
// See http://goo.gl/CxVuJ5 for more details.
|
||||
func (c *Client) InspectContainer(id string) (*Container, error) {
|
||||
path := "/containers/" + id + "/json"
|
||||
body, status, err := c.do("GET", path, nil)
|
||||
@ -228,7 +229,7 @@ func (c *Client) InspectContainer(id string) (*Container, error) {
|
||||
|
||||
// ContainerChanges returns changes in the filesystem of the given container.
|
||||
//
|
||||
// See http://goo.gl/DpGyzK for more details.
|
||||
// See http://goo.gl/QkW9sH for more details.
|
||||
func (c *Client) ContainerChanges(id string) ([]Change, error) {
|
||||
path := "/containers/" + id + "/changes"
|
||||
body, status, err := c.do("GET", path, nil)
|
||||
@ -248,7 +249,7 @@ func (c *Client) ContainerChanges(id string) ([]Change, error) {
|
||||
|
||||
// CreateContainerOptions specify parameters to the CreateContainer function.
|
||||
//
|
||||
// See http://goo.gl/WPPYtB for more details.
|
||||
// See http://goo.gl/mErxNp for more details.
|
||||
type CreateContainerOptions struct {
|
||||
Name string
|
||||
Config *Config `qs:"-"`
|
||||
@ -257,7 +258,7 @@ type CreateContainerOptions struct {
|
||||
// CreateContainer creates a new container, returning the container instance,
|
||||
// or an error in case of failure.
|
||||
//
|
||||
// See http://goo.gl/tjihUc for more details.
|
||||
// See http://goo.gl/mErxNp for more details.
|
||||
func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error) {
|
||||
path := "/containers/create?" + queryString(opts)
|
||||
body, status, err := c.do("POST", path, opts.Config)
|
||||
@ -279,27 +280,61 @@ 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 errror in case of failure.
|
||||
// StartContainer starts a container, returning an error in case of failure.
|
||||
//
|
||||
// See http://goo.gl/y5GZlE for more details.
|
||||
// See http://goo.gl/iM5GYs for more details.
|
||||
func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
|
||||
if hostConfig == nil {
|
||||
hostConfig = &HostConfig{}
|
||||
@ -309,6 +344,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
|
||||
}
|
||||
@ -318,13 +356,16 @@ func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
|
||||
// StopContainer stops a container, killing it after the given timeout (in
|
||||
// seconds).
|
||||
//
|
||||
// See http://goo.gl/X2mj8t for more details.
|
||||
// See http://goo.gl/EbcpXt for more details.
|
||||
func (c *Client) StopContainer(id string, timeout uint) error {
|
||||
path := fmt.Sprintf("/containers/%s/stop?t=%d", id, timeout)
|
||||
_, status, err := c.do("POST", path, nil)
|
||||
if status == http.StatusNotFound {
|
||||
return &NoSuchContainer{ID: id}
|
||||
}
|
||||
if status == http.StatusNotModified {
|
||||
return &ContainerNotRunning{ID: id}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -334,7 +375,7 @@ func (c *Client) StopContainer(id string, timeout uint) error {
|
||||
// RestartContainer stops a container, killing it after the given timeout (in
|
||||
// seconds), during the stop process.
|
||||
//
|
||||
// See http://goo.gl/zms73Z for more details.
|
||||
// See http://goo.gl/VOzR2n for more details.
|
||||
func (c *Client) RestartContainer(id string, timeout uint) error {
|
||||
path := fmt.Sprintf("/containers/%s/restart?t=%d", id, timeout)
|
||||
_, status, err := c.do("POST", path, nil)
|
||||
@ -379,6 +420,8 @@ func (c *Client) UnpauseContainer(id string) error {
|
||||
|
||||
// KillContainerOptions represents the set of options that can be used in a
|
||||
// call to KillContainer.
|
||||
//
|
||||
// See http://goo.gl/TFkECx for more details.
|
||||
type KillContainerOptions struct {
|
||||
// The ID of the container.
|
||||
ID string `qs:"-"`
|
||||
@ -390,7 +433,7 @@ type KillContainerOptions struct {
|
||||
|
||||
// KillContainer kills a container, returning an error in case of failure.
|
||||
//
|
||||
// See http://goo.gl/DPbbBy for more details.
|
||||
// See http://goo.gl/TFkECx for more details.
|
||||
func (c *Client) KillContainer(opts KillContainerOptions) error {
|
||||
path := "/containers/" + opts.ID + "/kill" + "?" + queryString(opts)
|
||||
_, status, err := c.do("POST", path, nil)
|
||||
@ -404,6 +447,8 @@ func (c *Client) KillContainer(opts KillContainerOptions) error {
|
||||
}
|
||||
|
||||
// RemoveContainerOptions encapsulates options to remove a container.
|
||||
//
|
||||
// See http://goo.gl/ZB83ji for more details.
|
||||
type RemoveContainerOptions struct {
|
||||
// The ID of the container.
|
||||
ID string `qs:"-"`
|
||||
@ -419,7 +464,7 @@ type RemoveContainerOptions struct {
|
||||
|
||||
// RemoveContainer removes a container, returning an error in case of failure.
|
||||
//
|
||||
// See http://goo.gl/PBvGdU for more details.
|
||||
// See http://goo.gl/ZB83ji for more details.
|
||||
func (c *Client) RemoveContainer(opts RemoveContainerOptions) error {
|
||||
path := "/containers/" + opts.ID + "?" + queryString(opts)
|
||||
_, status, err := c.do("DELETE", path, nil)
|
||||
@ -435,7 +480,7 @@ func (c *Client) RemoveContainer(opts RemoveContainerOptions) error {
|
||||
// CopyFromContainerOptions is the set of options that can be used when copying
|
||||
// files or folders from a container.
|
||||
//
|
||||
// See http://goo.gl/mnxRMl for more details.
|
||||
// See http://goo.gl/rINMlw for more details.
|
||||
type CopyFromContainerOptions struct {
|
||||
OutputStream io.Writer `json:"-"`
|
||||
Container string `json:"-"`
|
||||
@ -445,7 +490,7 @@ type CopyFromContainerOptions struct {
|
||||
// CopyFromContainer copy files or folders from a container, using a given
|
||||
// resource.
|
||||
//
|
||||
// See http://goo.gl/mnxRMl for more details.
|
||||
// See http://goo.gl/rINMlw for more details.
|
||||
func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
|
||||
if opts.Container == "" {
|
||||
return &NoSuchContainer{ID: opts.Container}
|
||||
@ -465,7 +510,7 @@ func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
|
||||
// WaitContainer blocks until the given container stops, return the exit code
|
||||
// of the container status.
|
||||
//
|
||||
// See http://goo.gl/gnHJL2 for more details.
|
||||
// See http://goo.gl/J88DHU for more details.
|
||||
func (c *Client) WaitContainer(id string) (int, error) {
|
||||
body, status, err := c.do("POST", "/containers/"+id+"/wait", nil)
|
||||
if status == http.StatusNotFound {
|
||||
@ -484,7 +529,7 @@ func (c *Client) WaitContainer(id string) (int, error) {
|
||||
|
||||
// CommitContainerOptions aggregates parameters to the CommitContainer method.
|
||||
//
|
||||
// See http://goo.gl/628gxm for more details.
|
||||
// See http://goo.gl/Jn8pe8 for more details.
|
||||
type CommitContainerOptions struct {
|
||||
Container string
|
||||
Repository string `qs:"repo"`
|
||||
@ -496,7 +541,7 @@ type CommitContainerOptions struct {
|
||||
|
||||
// CommitContainer creates a new image from a container's changes.
|
||||
//
|
||||
// See http://goo.gl/628gxm for more details.
|
||||
// See http://goo.gl/Jn8pe8 for more details.
|
||||
func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) {
|
||||
path := "/commit?" + queryString(opts)
|
||||
body, status, err := c.do("POST", path, opts.Run)
|
||||
@ -517,7 +562,7 @@ func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) {
|
||||
// AttachToContainerOptions is the set of options that can be used when
|
||||
// attaching to a container.
|
||||
//
|
||||
// See http://goo.gl/oPzcqH for more details.
|
||||
// See http://goo.gl/RRAhws for more details.
|
||||
type AttachToContainerOptions struct {
|
||||
Container string `qs:"-"`
|
||||
InputStream io.Reader `qs:"-"`
|
||||
@ -552,7 +597,7 @@ type AttachToContainerOptions struct {
|
||||
|
||||
// AttachToContainer attaches to a container, using the given options.
|
||||
//
|
||||
// See http://goo.gl/oPzcqH for more details.
|
||||
// See http://goo.gl/RRAhws for more details.
|
||||
func (c *Client) AttachToContainer(opts AttachToContainerOptions) error {
|
||||
if opts.Container == "" {
|
||||
return &NoSuchContainer{ID: opts.Container}
|
||||
@ -590,7 +635,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.
|
||||
@ -605,7 +650,7 @@ func (c *Client) ResizeContainerTTY(id string, height, width int) error {
|
||||
// ExportContainerOptions is the set of parameters to the ExportContainer
|
||||
// method.
|
||||
//
|
||||
// See http://goo.gl/Lqk0FZ for more details.
|
||||
// See http://goo.gl/hnzE62 for more details.
|
||||
type ExportContainerOptions struct {
|
||||
ID string
|
||||
OutputStream io.Writer
|
||||
@ -614,13 +659,13 @@ type ExportContainerOptions struct {
|
||||
// ExportContainer export the contents of container id as tar archive
|
||||
// and prints the exported contents to stdout.
|
||||
//
|
||||
// See http://goo.gl/Lqk0FZ for more details.
|
||||
// See http://goo.gl/hnzE62 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 +673,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
|
||||
}
|
||||
|
141
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container_test.go
generated
vendored
141
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container_test.go
generated
vendored
@ -225,6 +225,88 @@ func TestInspectContainer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInspectContainerNegativeSwap(t *testing.T) {
|
||||
jsonContainer := `{
|
||||
"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
|
||||
"Created": "2013-05-07T14:51:42.087658+02:00",
|
||||
"Path": "date",
|
||||
"Args": [],
|
||||
"Config": {
|
||||
"Hostname": "4fa6e0f0c678",
|
||||
"User": "",
|
||||
"Memory": 17179869184,
|
||||
"MemorySwap": -1,
|
||||
"AttachStdin": false,
|
||||
"AttachStdout": true,
|
||||
"AttachStderr": true,
|
||||
"PortSpecs": null,
|
||||
"Tty": false,
|
||||
"OpenStdin": false,
|
||||
"StdinOnce": false,
|
||||
"Env": null,
|
||||
"Cmd": [
|
||||
"date"
|
||||
],
|
||||
"Image": "base",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": ""
|
||||
},
|
||||
"State": {
|
||||
"Running": false,
|
||||
"Pid": 0,
|
||||
"ExitCode": 0,
|
||||
"StartedAt": "2013-05-07T14:51:42.087658+02:00",
|
||||
"Ghost": false
|
||||
},
|
||||
"Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
||||
"NetworkSettings": {
|
||||
"IpAddress": "",
|
||||
"IpPrefixLen": 0,
|
||||
"Gateway": "",
|
||||
"Bridge": "",
|
||||
"PortMapping": null
|
||||
},
|
||||
"SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker",
|
||||
"ResolvConfPath": "/etc/resolv.conf",
|
||||
"Volumes": {},
|
||||
"HostConfig": {
|
||||
"Binds": null,
|
||||
"ContainerIDFile": "",
|
||||
"LxcConf": [],
|
||||
"Privileged": false,
|
||||
"PortBindings": {
|
||||
"80/tcp": [
|
||||
{
|
||||
"HostIp": "0.0.0.0",
|
||||
"HostPort": "49153"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Links": null,
|
||||
"PublishAllPorts": false
|
||||
}
|
||||
}`
|
||||
var expected Container
|
||||
err := json.Unmarshal([]byte(jsonContainer), &expected)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK}
|
||||
client := newTestClient(fakeRT)
|
||||
id := "4fa6e0f0c678"
|
||||
container, err := client.InspectContainer(id)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(*container, expected) {
|
||||
t.Errorf("InspectContainer(%q): Expected %#v. Got %#v.", id, expected, container)
|
||||
}
|
||||
expectedURL, _ := url.Parse(client.getURL("/containers/4fa6e0f0c678/json"))
|
||||
if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
|
||||
t.Errorf("InspectContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInspectContainerFailure(t *testing.T) {
|
||||
client := newTestClient(&FakeRoundTripper{message: "server error", status: 500})
|
||||
expected := Error{Status: 500, Message: "server error"}
|
||||
@ -411,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)
|
||||
@ -438,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)
|
||||
@ -1184,9 +1284,9 @@ func TestExportContainerViaUnixSocket(t *testing.T) {
|
||||
endpoint := "unix://" + tempSocket
|
||||
u, _ := parseEndpoint(endpoint)
|
||||
client := Client{
|
||||
HTTPClient: http.DefaultClient,
|
||||
endpoint: endpoint,
|
||||
endpointURL: u,
|
||||
client: http.DefaultClient,
|
||||
SkipServerVersionCheck: true,
|
||||
}
|
||||
listening := make(chan string)
|
||||
@ -1231,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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1279,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)
|
||||
}
|
||||
}
|
||||
|
147
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/engine.go
generated
vendored
147
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/engine.go
generated
vendored
@ -1,147 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fsouza/go-dockerclient/utils"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Handler func(*Job) Status
|
||||
|
||||
var globalHandlers map[string]Handler
|
||||
|
||||
func init() {
|
||||
globalHandlers = make(map[string]Handler)
|
||||
}
|
||||
|
||||
func Register(name string, handler Handler) error {
|
||||
_, exists := globalHandlers[name]
|
||||
if exists {
|
||||
return fmt.Errorf("Can't overwrite global handler for command %s", name)
|
||||
}
|
||||
globalHandlers[name] = handler
|
||||
return nil
|
||||
}
|
||||
|
||||
// The Engine is the core of Docker.
|
||||
// It acts as a store for *containers*, and allows manipulation of these
|
||||
// containers by executing *jobs*.
|
||||
type Engine struct {
|
||||
root string
|
||||
handlers map[string]Handler
|
||||
hack Hack // data for temporary hackery (see hack.go)
|
||||
id string
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
Stdin io.Reader
|
||||
}
|
||||
|
||||
func (eng *Engine) Root() string {
|
||||
return eng.root
|
||||
}
|
||||
|
||||
func (eng *Engine) Register(name string, handler Handler) error {
|
||||
eng.Logf("Register(%s) (handlers=%v)", name, eng.handlers)
|
||||
_, exists := eng.handlers[name]
|
||||
if exists {
|
||||
return fmt.Errorf("Can't overwrite handler for command %s", name)
|
||||
}
|
||||
eng.handlers[name] = handler
|
||||
return nil
|
||||
}
|
||||
|
||||
// New initializes a new engine managing the directory specified at `root`.
|
||||
// `root` is used to store containers and any other state private to the engine.
|
||||
// Changing the contents of the root without executing a job will cause unspecified
|
||||
// behavior.
|
||||
func New(root string) (*Engine, error) {
|
||||
// Check for unsupported architectures
|
||||
if runtime.GOARCH != "amd64" {
|
||||
return nil, fmt.Errorf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
|
||||
}
|
||||
// Check for unsupported kernel versions
|
||||
// FIXME: it would be cleaner to not test for specific versions, but rather
|
||||
// test for specific functionalities.
|
||||
// Unfortunately we can't test for the feature "does not cause a kernel panic"
|
||||
// without actually causing a kernel panic, so we need this workaround until
|
||||
// the circumstances of pre-3.8 crashes are clearer.
|
||||
// For details see http://github.com/dotcloud/docker/issues/407
|
||||
if k, err := utils.GetKernelVersion(); err != nil {
|
||||
log.Printf("WARNING: %s\n", err)
|
||||
} else {
|
||||
if utils.CompareKernelVersion(k, &utils.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 {
|
||||
if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" {
|
||||
log.Printf("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(root, 0700); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Docker makes some assumptions about the "absoluteness" of root
|
||||
// ... so let's make sure it has no symlinks
|
||||
if p, err := filepath.Abs(root); err != nil {
|
||||
log.Fatalf("Unable to get absolute root (%s): %s", root, err)
|
||||
} else {
|
||||
root = p
|
||||
}
|
||||
if p, err := filepath.EvalSymlinks(root); err != nil {
|
||||
log.Fatalf("Unable to canonicalize root (%s): %s", root, err)
|
||||
} else {
|
||||
root = p
|
||||
}
|
||||
|
||||
eng := &Engine{
|
||||
root: root,
|
||||
handlers: make(map[string]Handler),
|
||||
id: utils.RandomString(),
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
Stdin: os.Stdin,
|
||||
}
|
||||
// Copy existing global handlers
|
||||
for k, v := range globalHandlers {
|
||||
eng.handlers[k] = v
|
||||
}
|
||||
return eng, nil
|
||||
}
|
||||
|
||||
func (eng *Engine) String() string {
|
||||
return fmt.Sprintf("%s|%s", eng.Root(), eng.id[:8])
|
||||
}
|
||||
|
||||
// Job creates a new job which can later be executed.
|
||||
// This function mimics `Command` from the standard os/exec package.
|
||||
func (eng *Engine) Job(name string, args ...string) *Job {
|
||||
job := &Job{
|
||||
Eng: eng,
|
||||
Name: name,
|
||||
Args: args,
|
||||
Stdin: NewInput(),
|
||||
Stdout: NewOutput(),
|
||||
Stderr: NewOutput(),
|
||||
env: &Env{},
|
||||
}
|
||||
job.Stderr.Add(utils.NopWriteCloser(eng.Stderr))
|
||||
handler, exists := eng.handlers[name]
|
||||
if exists {
|
||||
job.handler = handler
|
||||
}
|
||||
return job
|
||||
}
|
||||
|
||||
func (eng *Engine) Logf(format string, args ...interface{}) (n int, err error) {
|
||||
prefixedFormat := fmt.Sprintf("[%s] %s\n", eng, strings.TrimRight(format, "\n"))
|
||||
return fmt.Fprintf(eng.Stderr, prefixedFormat, args...)
|
||||
}
|
111
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/engine_test.go
generated
vendored
111
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/engine_test.go
generated
vendored
@ -1,111 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
if err := Register("dummy1", nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := Register("dummy1", nil); err == nil {
|
||||
t.Fatalf("Expecting error, got none")
|
||||
}
|
||||
|
||||
eng := newTestEngine(t)
|
||||
|
||||
//Should fail because global handlers are copied
|
||||
//at the engine creation
|
||||
if err := eng.Register("dummy1", nil); err == nil {
|
||||
t.Fatalf("Expecting error, got none")
|
||||
}
|
||||
|
||||
if err := eng.Register("dummy2", nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := eng.Register("dummy2", nil); err == nil {
|
||||
t.Fatalf("Expecting error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJob(t *testing.T) {
|
||||
eng := newTestEngine(t)
|
||||
job1 := eng.Job("dummy1", "--level=awesome")
|
||||
|
||||
if job1.handler != nil {
|
||||
t.Fatalf("job1.handler should be empty")
|
||||
}
|
||||
|
||||
h := func(j *Job) Status {
|
||||
j.Printf("%s\n", j.Name)
|
||||
return 42
|
||||
}
|
||||
|
||||
eng.Register("dummy2", h)
|
||||
job2 := eng.Job("dummy2", "--level=awesome")
|
||||
|
||||
if job2.handler == nil {
|
||||
t.Fatalf("job2.handler shouldn't be nil")
|
||||
}
|
||||
|
||||
if job2.handler(job2) != 42 {
|
||||
t.Fatalf("handler dummy2 was not found in job2")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngineRoot(t *testing.T) {
|
||||
tmp, err := ioutil.TempDir("", "docker-test-TestEngineCreateDir")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
dir := path.Join(tmp, "dir")
|
||||
eng, err := New(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if st, err := os.Stat(dir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !st.IsDir() {
|
||||
t.Fatalf("engine.New() created something other than a directory at %s", dir)
|
||||
}
|
||||
r := eng.Root()
|
||||
r, _ = filepath.EvalSymlinks(r)
|
||||
dir, _ = filepath.EvalSymlinks(dir)
|
||||
if r != dir {
|
||||
t.Fatalf("Expected: %v\nReceived: %v", dir, r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngineString(t *testing.T) {
|
||||
eng1 := newTestEngine(t)
|
||||
defer os.RemoveAll(eng1.Root())
|
||||
eng2 := newTestEngine(t)
|
||||
defer os.RemoveAll(eng2.Root())
|
||||
s1 := eng1.String()
|
||||
s2 := eng2.String()
|
||||
if eng1 == eng2 {
|
||||
t.Fatalf("Different engines should have different names (%v == %v)", s1, s2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngineLogf(t *testing.T) {
|
||||
eng := newTestEngine(t)
|
||||
defer os.RemoveAll(eng.Root())
|
||||
input := "Test log line"
|
||||
if n, err := eng.Logf("%s\n", input); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if n < len(input) {
|
||||
t.Fatalf("Test: Logf() should print at least as much as the input\ninput=%d\nprinted=%d", len(input), n)
|
||||
}
|
||||
}
|
238
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/env.go
generated
vendored
238
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/env.go
generated
vendored
@ -1,238 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Env []string
|
||||
|
||||
func (env *Env) Get(key string) (value string) {
|
||||
// FIXME: use Map()
|
||||
for _, kv := range *env {
|
||||
if strings.Index(kv, "=") == -1 {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
if parts[0] != key {
|
||||
continue
|
||||
}
|
||||
if len(parts) < 2 {
|
||||
value = ""
|
||||
} else {
|
||||
value = parts[1]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (env *Env) Exists(key string) bool {
|
||||
_, exists := env.Map()[key]
|
||||
return exists
|
||||
}
|
||||
|
||||
func (env *Env) GetBool(key string) (value bool) {
|
||||
s := strings.ToLower(strings.Trim(env.Get(key), " \t"))
|
||||
if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (env *Env) SetBool(key string, value bool) {
|
||||
if value {
|
||||
env.Set(key, "1")
|
||||
} else {
|
||||
env.Set(key, "0")
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Env) GetInt(key string) int {
|
||||
return int(env.GetInt64(key))
|
||||
}
|
||||
|
||||
func (env *Env) GetInt64(key string) int64 {
|
||||
s := strings.Trim(env.Get(key), " \t")
|
||||
val, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (env *Env) SetInt(key string, value int) {
|
||||
env.Set(key, fmt.Sprintf("%d", value))
|
||||
}
|
||||
|
||||
func (env *Env) SetInt64(key string, value int64) {
|
||||
env.Set(key, fmt.Sprintf("%d", value))
|
||||
}
|
||||
|
||||
// Returns nil if key not found
|
||||
func (env *Env) GetList(key string) []string {
|
||||
sval := env.Get(key)
|
||||
if sval == "" {
|
||||
return nil
|
||||
}
|
||||
l := make([]string, 0, 1)
|
||||
if err := json.Unmarshal([]byte(sval), &l); err != nil {
|
||||
l = append(l, sval)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (env *Env) GetJson(key string, iface interface{}) error {
|
||||
sval := env.Get(key)
|
||||
if sval == "" {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal([]byte(sval), iface)
|
||||
}
|
||||
|
||||
func (env *Env) SetJson(key string, value interface{}) error {
|
||||
sval, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.Set(key, string(sval))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Env) SetList(key string, value []string) error {
|
||||
return env.SetJson(key, value)
|
||||
}
|
||||
|
||||
func (env *Env) Set(key, value string) {
|
||||
*env = append(*env, key+"="+value)
|
||||
}
|
||||
|
||||
func NewDecoder(src io.Reader) *Decoder {
|
||||
return &Decoder{
|
||||
json.NewDecoder(src),
|
||||
}
|
||||
}
|
||||
|
||||
type Decoder struct {
|
||||
*json.Decoder
|
||||
}
|
||||
|
||||
func (decoder *Decoder) Decode() (*Env, error) {
|
||||
m := make(map[string]interface{})
|
||||
if err := decoder.Decoder.Decode(&m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
env := &Env{}
|
||||
for key, value := range m {
|
||||
env.SetAuto(key, value)
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
// DecodeEnv decodes `src` as a json dictionary, and adds
|
||||
// each decoded key-value pair to the environment.
|
||||
//
|
||||
// If `src` cannot be decoded as a json dictionary, an error
|
||||
// is returned.
|
||||
func (env *Env) Decode(src io.Reader) error {
|
||||
m := make(map[string]interface{})
|
||||
if err := json.NewDecoder(src).Decode(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range m {
|
||||
env.SetAuto(k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Env) SetAuto(k string, v interface{}) {
|
||||
// FIXME: we fix-convert float values to int, because
|
||||
// encoding/json decodes integers to float64, but cannot encode them back.
|
||||
// (See http://golang.org/src/pkg/encoding/json/decode.go#L46)
|
||||
if fval, ok := v.(float64); ok {
|
||||
env.SetInt64(k, int64(fval))
|
||||
} else if sval, ok := v.(string); ok {
|
||||
env.Set(k, sval)
|
||||
} else if val, err := json.Marshal(v); err == nil {
|
||||
env.Set(k, string(val))
|
||||
} else {
|
||||
env.Set(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Env) Encode(dst io.Writer) error {
|
||||
m := make(map[string]interface{})
|
||||
for k, v := range env.Map() {
|
||||
var val interface{}
|
||||
if err := json.Unmarshal([]byte(v), &val); err == nil {
|
||||
// FIXME: we fix-convert float values to int, because
|
||||
// encoding/json decodes integers to float64, but cannot encode them back.
|
||||
// (See http://golang.org/src/pkg/encoding/json/decode.go#L46)
|
||||
if fval, isFloat := val.(float64); isFloat {
|
||||
val = int(fval)
|
||||
}
|
||||
m[k] = val
|
||||
} else {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
if err := json.NewEncoder(dst).Encode(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Env) WriteTo(dst io.Writer) (n int64, err error) {
|
||||
// FIXME: return the number of bytes written to respect io.WriterTo
|
||||
return 0, env.Encode(dst)
|
||||
}
|
||||
|
||||
func (env *Env) Export(dst interface{}) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = fmt.Errorf("ExportEnv %s", err)
|
||||
}
|
||||
}()
|
||||
var buf bytes.Buffer
|
||||
// step 1: encode/marshal the env to an intermediary json representation
|
||||
if err := env.Encode(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
// step 2: decode/unmarshal the intermediary json into the destination object
|
||||
if err := json.NewDecoder(&buf).Decode(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Env) Import(src interface{}) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = fmt.Errorf("ImportEnv: %s", err)
|
||||
}
|
||||
}()
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(src); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := env.Decode(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Env) Map() map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, kv := range *env {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
m[parts[0]] = parts[1]
|
||||
}
|
||||
return m
|
||||
}
|
127
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/env_test.go
generated
vendored
127
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/env_test.go
generated
vendored
@ -1,127 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewJob(t *testing.T) {
|
||||
job := mkJob(t, "dummy", "--level=awesome")
|
||||
if job.Name != "dummy" {
|
||||
t.Fatalf("Wrong job name: %s", job.Name)
|
||||
}
|
||||
if len(job.Args) != 1 {
|
||||
t.Fatalf("Wrong number of job arguments: %d", len(job.Args))
|
||||
}
|
||||
if job.Args[0] != "--level=awesome" {
|
||||
t.Fatalf("Wrong job arguments: %s", job.Args[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetenv(t *testing.T) {
|
||||
job := mkJob(t, "dummy")
|
||||
job.Setenv("foo", "bar")
|
||||
if val := job.Getenv("foo"); val != "bar" {
|
||||
t.Fatalf("Getenv returns incorrect value: %s", val)
|
||||
}
|
||||
|
||||
job.Setenv("bar", "")
|
||||
if val := job.Getenv("bar"); val != "" {
|
||||
t.Fatalf("Getenv returns incorrect value: %s", val)
|
||||
}
|
||||
if val := job.Getenv("nonexistent"); val != "" {
|
||||
t.Fatalf("Getenv returns incorrect value: %s", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetenvBool(t *testing.T) {
|
||||
job := mkJob(t, "dummy")
|
||||
job.SetenvBool("foo", true)
|
||||
if val := job.GetenvBool("foo"); !val {
|
||||
t.Fatalf("GetenvBool returns incorrect value: %t", val)
|
||||
}
|
||||
|
||||
job.SetenvBool("bar", false)
|
||||
if val := job.GetenvBool("bar"); val {
|
||||
t.Fatalf("GetenvBool returns incorrect value: %t", val)
|
||||
}
|
||||
|
||||
if val := job.GetenvBool("nonexistent"); val {
|
||||
t.Fatalf("GetenvBool returns incorrect value: %t", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetenvInt(t *testing.T) {
|
||||
job := mkJob(t, "dummy")
|
||||
|
||||
job.SetenvInt("foo", -42)
|
||||
if val := job.GetenvInt("foo"); val != -42 {
|
||||
t.Fatalf("GetenvInt returns incorrect value: %d", val)
|
||||
}
|
||||
|
||||
job.SetenvInt("bar", 42)
|
||||
if val := job.GetenvInt("bar"); val != 42 {
|
||||
t.Fatalf("GetenvInt returns incorrect value: %d", val)
|
||||
}
|
||||
if val := job.GetenvInt("nonexistent"); val != -1 {
|
||||
t.Fatalf("GetenvInt returns incorrect value: %d", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetenvList(t *testing.T) {
|
||||
job := mkJob(t, "dummy")
|
||||
|
||||
job.SetenvList("foo", []string{"bar"})
|
||||
if val := job.GetenvList("foo"); len(val) != 1 || val[0] != "bar" {
|
||||
t.Fatalf("GetenvList returns incorrect value: %v", val)
|
||||
}
|
||||
|
||||
job.SetenvList("bar", nil)
|
||||
if val := job.GetenvList("bar"); val != nil {
|
||||
t.Fatalf("GetenvList returns incorrect value: %v", val)
|
||||
}
|
||||
if val := job.GetenvList("nonexistent"); val != nil {
|
||||
t.Fatalf("GetenvList returns incorrect value: %v", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportEnv(t *testing.T) {
|
||||
type dummy struct {
|
||||
DummyInt int
|
||||
DummyStringArray []string
|
||||
}
|
||||
|
||||
job := mkJob(t, "dummy")
|
||||
if err := job.ImportEnv(&dummy{42, []string{"foo", "bar"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dmy := dummy{}
|
||||
if err := job.ExportEnv(&dmy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if dmy.DummyInt != 42 {
|
||||
t.Fatalf("Expected 42, got %d", dmy.DummyInt)
|
||||
}
|
||||
|
||||
if len(dmy.DummyStringArray) != 2 || dmy.DummyStringArray[0] != "foo" || dmy.DummyStringArray[1] != "bar" {
|
||||
t.Fatalf("Expected {foo, bar}, got %v", dmy.DummyStringArray)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestEnviron(t *testing.T) {
|
||||
job := mkJob(t, "dummy")
|
||||
job.Setenv("foo", "bar")
|
||||
val, exists := job.Environ()["foo"]
|
||||
if !exists {
|
||||
t.Fatalf("foo not found in the environ")
|
||||
}
|
||||
if val != "bar" {
|
||||
t.Fatalf("bar not found in the environ")
|
||||
}
|
||||
}
|
25
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/hack.go
generated
vendored
25
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/hack.go
generated
vendored
@ -1,25 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package engine
|
||||
|
||||
type Hack map[string]interface{}
|
||||
|
||||
func (eng *Engine) Hack_GetGlobalVar(key string) interface{} {
|
||||
if eng.hack == nil {
|
||||
return nil
|
||||
}
|
||||
val, exists := eng.hack[key]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (eng *Engine) Hack_SetGlobalVar(key string, val interface{}) {
|
||||
if eng.hack == nil {
|
||||
eng.hack = make(Hack)
|
||||
}
|
||||
eng.hack[key] = val
|
||||
}
|
28
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/helpers_test.go
generated
vendored
28
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/helpers_test.go
generated
vendored
@ -1,28 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var globalTestID string
|
||||
|
||||
func newTestEngine(t *testing.T) *Engine {
|
||||
tmp, err := ioutil.TempDir("", "asd")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eng, err := New(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return eng
|
||||
}
|
||||
|
||||
func mkJob(t *testing.T, name string, args ...string) *Job {
|
||||
return newTestEngine(t).Job(name, args...)
|
||||
}
|
44
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/http.go
generated
vendored
44
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/http.go
generated
vendored
@ -1,44 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
// ServeHTTP executes a job as specified by the http request `r`, and sends the
|
||||
// result as an http response.
|
||||
// This method allows an Engine instance to be passed as a standard http.Handler interface.
|
||||
//
|
||||
// Note that the protocol used in this methid is a convenience wrapper and is not the canonical
|
||||
// implementation of remote job execution. This is because HTTP/1 does not handle stream multiplexing,
|
||||
// and so cannot differentiate stdout from stderr. Additionally, headers cannot be added to a response
|
||||
// once data has been written to the body, which makes it inconvenient to return metadata such
|
||||
// as the exit status.
|
||||
//
|
||||
func (eng *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
jobName := path.Base(r.URL.Path)
|
||||
jobArgs, exists := r.URL.Query()["a"]
|
||||
if !exists {
|
||||
jobArgs = []string{}
|
||||
}
|
||||
w.Header().Set("Job-Name", jobName)
|
||||
for _, arg := range jobArgs {
|
||||
w.Header().Add("Job-Args", arg)
|
||||
}
|
||||
job := eng.Job(jobName, jobArgs...)
|
||||
job.Stdout.Add(w)
|
||||
job.Stderr.Add(w)
|
||||
// FIXME: distinguish job status from engine error in Run()
|
||||
// The former should be passed as a special header, the former
|
||||
// should cause a 500 status
|
||||
w.WriteHeader(http.StatusOK)
|
||||
// The exit status cannot be sent reliably with HTTP1, because headers
|
||||
// can only be sent before the body.
|
||||
// (we could possibly use http footers via chunked encoding, but I couldn't find
|
||||
// how to use them in net/http)
|
||||
job.Run()
|
||||
}
|
197
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/job.go
generated
vendored
197
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/job.go
generated
vendored
@ -1,197 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A job is the fundamental unit of work in the docker engine.
|
||||
// Everything docker can do should eventually be exposed as a job.
|
||||
// For example: execute a process in a container, create a new container,
|
||||
// download an archive from the internet, serve the http api, etc.
|
||||
//
|
||||
// The job API is designed after unix processes: a job has a name, arguments,
|
||||
// environment variables, standard streams for input, output and error, and
|
||||
// an exit status which can indicate success (0) or error (anything else).
|
||||
//
|
||||
// One slight variation is that jobs report their status as a string. The
|
||||
// string "0" indicates success, and any other strings indicates an error.
|
||||
// This allows for richer error reporting.
|
||||
//
|
||||
type Job struct {
|
||||
Eng *Engine
|
||||
Name string
|
||||
Args []string
|
||||
env *Env
|
||||
Stdout *Output
|
||||
Stderr *Output
|
||||
Stdin *Input
|
||||
handler Handler
|
||||
status Status
|
||||
end time.Time
|
||||
onExit []func()
|
||||
}
|
||||
|
||||
type Status int
|
||||
|
||||
const (
|
||||
StatusOK Status = 0
|
||||
StatusErr Status = 1
|
||||
StatusNotFound Status = 127
|
||||
)
|
||||
|
||||
// Run executes the job and blocks until the job completes.
|
||||
// If the job returns a failure status, an error is returned
|
||||
// which includes the status.
|
||||
func (job *Job) Run() error {
|
||||
// FIXME: make this thread-safe
|
||||
// FIXME: implement wait
|
||||
if !job.end.IsZero() {
|
||||
return fmt.Errorf("%s: job has already completed", job.Name)
|
||||
}
|
||||
// Log beginning and end of the job
|
||||
job.Eng.Logf("+job %s", job.CallString())
|
||||
defer func() {
|
||||
job.Eng.Logf("-job %s%s", job.CallString(), job.StatusString())
|
||||
}()
|
||||
var errorMessage string
|
||||
job.Stderr.AddString(&errorMessage)
|
||||
if job.handler == nil {
|
||||
job.Errorf("%s: command not found", job.Name)
|
||||
job.status = 127
|
||||
} else {
|
||||
job.status = job.handler(job)
|
||||
job.end = time.Now()
|
||||
}
|
||||
// Wait for all background tasks to complete
|
||||
if err := job.Stdout.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := job.Stderr.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if job.status != 0 {
|
||||
return fmt.Errorf("%s: %s", job.Name, errorMessage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (job *Job) CallString() string {
|
||||
return fmt.Sprintf("%s(%s)", job.Name, strings.Join(job.Args, ", "))
|
||||
}
|
||||
|
||||
func (job *Job) StatusString() string {
|
||||
// If the job hasn't completed, status string is empty
|
||||
if job.end.IsZero() {
|
||||
return ""
|
||||
}
|
||||
var okerr string
|
||||
if job.status == StatusOK {
|
||||
okerr = "OK"
|
||||
} else {
|
||||
okerr = "ERR"
|
||||
}
|
||||
return fmt.Sprintf(" = %s (%d)", okerr, job.status)
|
||||
}
|
||||
|
||||
// String returns a human-readable description of `job`
|
||||
func (job *Job) String() string {
|
||||
return fmt.Sprintf("%s.%s%s", job.Eng, job.CallString(), job.StatusString())
|
||||
}
|
||||
|
||||
func (job *Job) Getenv(key string) (value string) {
|
||||
return job.env.Get(key)
|
||||
}
|
||||
|
||||
func (job *Job) GetenvBool(key string) (value bool) {
|
||||
return job.env.GetBool(key)
|
||||
}
|
||||
|
||||
func (job *Job) SetenvBool(key string, value bool) {
|
||||
job.env.SetBool(key, value)
|
||||
}
|
||||
|
||||
func (job *Job) GetenvInt64(key string) int64 {
|
||||
return job.env.GetInt64(key)
|
||||
}
|
||||
|
||||
func (job *Job) GetenvInt(key string) int {
|
||||
return job.env.GetInt(key)
|
||||
}
|
||||
|
||||
func (job *Job) SetenvInt64(key string, value int64) {
|
||||
job.env.SetInt64(key, value)
|
||||
}
|
||||
|
||||
func (job *Job) SetenvInt(key string, value int) {
|
||||
job.env.SetInt(key, value)
|
||||
}
|
||||
|
||||
// Returns nil if key not found
|
||||
func (job *Job) GetenvList(key string) []string {
|
||||
return job.env.GetList(key)
|
||||
}
|
||||
|
||||
func (job *Job) GetenvJson(key string, iface interface{}) error {
|
||||
return job.env.GetJson(key, iface)
|
||||
}
|
||||
|
||||
func (job *Job) SetenvJson(key string, value interface{}) error {
|
||||
return job.env.SetJson(key, value)
|
||||
}
|
||||
|
||||
func (job *Job) SetenvList(key string, value []string) error {
|
||||
return job.env.SetJson(key, value)
|
||||
}
|
||||
|
||||
func (job *Job) Setenv(key, value string) {
|
||||
job.env.Set(key, value)
|
||||
}
|
||||
|
||||
// DecodeEnv decodes `src` as a json dictionary, and adds
|
||||
// each decoded key-value pair to the environment.
|
||||
//
|
||||
// If `src` cannot be decoded as a json dictionary, an error
|
||||
// is returned.
|
||||
func (job *Job) DecodeEnv(src io.Reader) error {
|
||||
return job.env.Decode(src)
|
||||
}
|
||||
|
||||
func (job *Job) EncodeEnv(dst io.Writer) error {
|
||||
return job.env.Encode(dst)
|
||||
}
|
||||
|
||||
func (job *Job) ExportEnv(dst interface{}) (err error) {
|
||||
return job.env.Export(dst)
|
||||
}
|
||||
|
||||
func (job *Job) ImportEnv(src interface{}) (err error) {
|
||||
return job.env.Import(src)
|
||||
}
|
||||
|
||||
func (job *Job) Environ() map[string]string {
|
||||
return job.env.Map()
|
||||
}
|
||||
|
||||
func (job *Job) Logf(format string, args ...interface{}) (n int, err error) {
|
||||
prefixedFormat := fmt.Sprintf("[%s] %s\n", job, strings.TrimRight(format, "\n"))
|
||||
return fmt.Fprintf(job.Stderr, prefixedFormat, args...)
|
||||
}
|
||||
|
||||
func (job *Job) Printf(format string, args ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintf(job.Stdout, format, args...)
|
||||
}
|
||||
|
||||
func (job *Job) Errorf(format string, args ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintf(job.Stderr, format, args...)
|
||||
}
|
||||
|
||||
func (job *Job) Error(err error) (int, error) {
|
||||
return fmt.Fprintf(job.Stderr, "%s", err)
|
||||
}
|
84
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/job_test.go
generated
vendored
84
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/job_test.go
generated
vendored
@ -1,84 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJobStatusOK(t *testing.T) {
|
||||
eng := newTestEngine(t)
|
||||
defer os.RemoveAll(eng.Root())
|
||||
eng.Register("return_ok", func(job *Job) Status { return StatusOK })
|
||||
err := eng.Job("return_ok").Run()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected: err=%v\nReceived: err=%v", nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobStatusErr(t *testing.T) {
|
||||
eng := newTestEngine(t)
|
||||
defer os.RemoveAll(eng.Root())
|
||||
eng.Register("return_err", func(job *Job) Status { return StatusErr })
|
||||
err := eng.Job("return_err").Run()
|
||||
if err == nil {
|
||||
t.Fatalf("When a job returns StatusErr, Run() should return an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobStatusNotFound(t *testing.T) {
|
||||
eng := newTestEngine(t)
|
||||
defer os.RemoveAll(eng.Root())
|
||||
eng.Register("return_not_found", func(job *Job) Status { return StatusNotFound })
|
||||
err := eng.Job("return_not_found").Run()
|
||||
if err == nil {
|
||||
t.Fatalf("When a job returns StatusNotFound, Run() should return an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobStdoutString(t *testing.T) {
|
||||
eng := newTestEngine(t)
|
||||
defer os.RemoveAll(eng.Root())
|
||||
// FIXME: test multiple combinations of output and status
|
||||
eng.Register("say_something_in_stdout", func(job *Job) Status {
|
||||
job.Printf("Hello world\n")
|
||||
return StatusOK
|
||||
})
|
||||
|
||||
job := eng.Job("say_something_in_stdout")
|
||||
var output string
|
||||
if err := job.Stdout.AddString(&output); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if expectedOutput := "Hello world"; output != expectedOutput {
|
||||
t.Fatalf("Stdout last line:\nExpected: %v\nReceived: %v", expectedOutput, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobStderrString(t *testing.T) {
|
||||
eng := newTestEngine(t)
|
||||
defer os.RemoveAll(eng.Root())
|
||||
// FIXME: test multiple combinations of output and status
|
||||
eng.Register("say_something_in_stderr", func(job *Job) Status {
|
||||
job.Errorf("Warning, something might happen\nHere it comes!\nOh no...\nSomething happened\n")
|
||||
return StatusOK
|
||||
})
|
||||
|
||||
job := eng.Job("say_something_in_stderr")
|
||||
var output string
|
||||
if err := job.Stderr.AddString(&output); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if expectedOutput := "Something happened"; output != expectedOutput {
|
||||
t.Fatalf("Stderr last line:\nExpected: %v\nReceived: %v", expectedOutput, output)
|
||||
}
|
||||
}
|
196
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/streams.go
generated
vendored
196
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/streams.go
generated
vendored
@ -1,196 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"container/ring"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Output struct {
|
||||
sync.Mutex
|
||||
dests []io.Writer
|
||||
tasks sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewOutput returns a new Output object with no destinations attached.
|
||||
// Writing to an empty Output will cause the written data to be discarded.
|
||||
func NewOutput() *Output {
|
||||
return &Output{}
|
||||
}
|
||||
|
||||
// Add attaches a new destination to the Output. Any data subsequently written
|
||||
// to the output will be written to the new destination in addition to all the others.
|
||||
// This method is thread-safe.
|
||||
// FIXME: Add cannot fail
|
||||
func (o *Output) Add(dst io.Writer) error {
|
||||
o.Mutex.Lock()
|
||||
defer o.Mutex.Unlock()
|
||||
o.dests = append(o.dests, dst)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddPipe creates an in-memory pipe with io.Pipe(), adds its writing end as a destination,
|
||||
// and returns its reading end for consumption by the caller.
|
||||
// This is a rough equivalent similar to Cmd.StdoutPipe() in the standard os/exec package.
|
||||
// This method is thread-safe.
|
||||
func (o *Output) AddPipe() (io.Reader, error) {
|
||||
r, w := io.Pipe()
|
||||
o.Add(w)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// AddTail starts a new goroutine which will read all subsequent data written to the output,
|
||||
// line by line, and append the last `n` lines to `dst`.
|
||||
func (o *Output) AddTail(dst *[]string, n int) error {
|
||||
src, err := o.AddPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.tasks.Add(1)
|
||||
go func() {
|
||||
defer o.tasks.Done()
|
||||
Tail(src, n, dst)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddString starts a new goroutine which will read all subsequent data written to the output,
|
||||
// line by line, and store the last line into `dst`.
|
||||
func (o *Output) AddString(dst *string) error {
|
||||
src, err := o.AddPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.tasks.Add(1)
|
||||
go func() {
|
||||
defer o.tasks.Done()
|
||||
lines := make([]string, 0, 1)
|
||||
Tail(src, 1, &lines)
|
||||
if len(lines) == 0 {
|
||||
*dst = ""
|
||||
} else {
|
||||
*dst = lines[0]
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes the same data to all registered destinations.
|
||||
// This method is thread-safe.
|
||||
func (o *Output) Write(p []byte) (n int, err error) {
|
||||
o.Mutex.Lock()
|
||||
defer o.Mutex.Unlock()
|
||||
var firstErr error
|
||||
for _, dst := range o.dests {
|
||||
_, err := dst.Write(p)
|
||||
if err != nil && firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
}
|
||||
return len(p), firstErr
|
||||
}
|
||||
|
||||
// Close unregisters all destinations and waits for all background
|
||||
// AddTail and AddString tasks to complete.
|
||||
// The Close method of each destination is called if it exists.
|
||||
func (o *Output) Close() error {
|
||||
o.Mutex.Lock()
|
||||
defer o.Mutex.Unlock()
|
||||
var firstErr error
|
||||
for _, dst := range o.dests {
|
||||
if closer, ok := dst.(io.WriteCloser); ok {
|
||||
err := closer.Close()
|
||||
if err != nil && firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
}
|
||||
}
|
||||
o.tasks.Wait()
|
||||
return firstErr
|
||||
}
|
||||
|
||||
type Input struct {
|
||||
src io.Reader
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewInput returns a new Input object with no source attached.
|
||||
// Reading to an empty Input will return io.EOF.
|
||||
func NewInput() *Input {
|
||||
return &Input{}
|
||||
}
|
||||
|
||||
// Read reads from the input in a thread-safe way.
|
||||
func (i *Input) Read(p []byte) (n int, err error) {
|
||||
i.Mutex.Lock()
|
||||
defer i.Mutex.Unlock()
|
||||
if i.src == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return i.src.Read(p)
|
||||
}
|
||||
|
||||
// Add attaches a new source to the input.
|
||||
// Add can only be called once per input. Subsequent calls will
|
||||
// return an error.
|
||||
func (i *Input) Add(src io.Reader) error {
|
||||
i.Mutex.Lock()
|
||||
defer i.Mutex.Unlock()
|
||||
if i.src != nil {
|
||||
return fmt.Errorf("Maximum number of sources reached: 1")
|
||||
}
|
||||
i.src = src
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tail reads from `src` line per line, and returns the last `n` lines as an array.
|
||||
// A ring buffer is used to only store `n` lines at any time.
|
||||
func Tail(src io.Reader, n int, dst *[]string) {
|
||||
scanner := bufio.NewScanner(src)
|
||||
r := ring.New(n)
|
||||
for scanner.Scan() {
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
r.Value = scanner.Text()
|
||||
r = r.Next()
|
||||
}
|
||||
r.Do(func(v interface{}) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
*dst = append(*dst, v.(string))
|
||||
})
|
||||
}
|
||||
|
||||
// AddEnv starts a new goroutine which will decode all subsequent data
|
||||
// as a stream of json-encoded objects, and point `dst` to the last
|
||||
// decoded object.
|
||||
// The result `env` can be queried using the type-neutral Env interface.
|
||||
// It is not safe to query `env` until the Output is closed.
|
||||
func (o *Output) AddEnv() (dst *Env, err error) {
|
||||
src, err := o.AddPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dst = &Env{}
|
||||
o.tasks.Add(1)
|
||||
go func() {
|
||||
defer o.tasks.Done()
|
||||
decoder := NewDecoder(src)
|
||||
for {
|
||||
env, err := decoder.Decode()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
*dst = *env
|
||||
}
|
||||
}()
|
||||
return dst, nil
|
||||
}
|
298
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/streams_test.go
generated
vendored
298
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/engine/streams_test.go
generated
vendored
@ -1,298 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOutputAddString(t *testing.T) {
|
||||
var testInputs = [][2]string{
|
||||
{
|
||||
"hello, world!",
|
||||
"hello, world!",
|
||||
},
|
||||
|
||||
{
|
||||
"One\nTwo\nThree",
|
||||
"Three",
|
||||
},
|
||||
|
||||
{
|
||||
"",
|
||||
"",
|
||||
},
|
||||
|
||||
{
|
||||
"A line\nThen another nl-terminated line\n",
|
||||
"Then another nl-terminated line",
|
||||
},
|
||||
|
||||
{
|
||||
"A line followed by an empty line\n\n",
|
||||
"",
|
||||
},
|
||||
}
|
||||
for _, testData := range testInputs {
|
||||
input := testData[0]
|
||||
expectedOutput := testData[1]
|
||||
o := NewOutput()
|
||||
var output string
|
||||
if err := o.AddString(&output); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if n, err := o.Write([]byte(input)); err != nil {
|
||||
t.Error(err)
|
||||
} else if n != len(input) {
|
||||
t.Errorf("Expected %d, got %d", len(input), n)
|
||||
}
|
||||
o.Close()
|
||||
if output != expectedOutput {
|
||||
t.Errorf("Last line is not stored as return string.\nInput: '%s'\nExpected: '%s'\nGot: '%s'", input, expectedOutput, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type sentinelWriteCloser struct {
|
||||
calledWrite bool
|
||||
calledClose bool
|
||||
}
|
||||
|
||||
func (w *sentinelWriteCloser) Write(p []byte) (int, error) {
|
||||
w.calledWrite = true
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (w *sentinelWriteCloser) Close() error {
|
||||
w.calledClose = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestOutputAddEnv(t *testing.T) {
|
||||
input := "{\"foo\": \"bar\", \"answer_to_life_the_universe_and_everything\": 42}"
|
||||
o := NewOutput()
|
||||
result, err := o.AddEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
o.Write([]byte(input))
|
||||
o.Close()
|
||||
if v := result.Get("foo"); v != "bar" {
|
||||
t.Errorf("Expected %v, got %v", "bar", v)
|
||||
}
|
||||
if v := result.GetInt("answer_to_life_the_universe_and_everything"); v != 42 {
|
||||
t.Errorf("Expected %v, got %v", 42, v)
|
||||
}
|
||||
if v := result.Get("this-value-doesnt-exist"); v != "" {
|
||||
t.Errorf("Expected %v, got %v", "", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputAddClose(t *testing.T) {
|
||||
o := NewOutput()
|
||||
var s sentinelWriteCloser
|
||||
if err := o.Add(&s); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := o.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Write data after the output is closed.
|
||||
// Write should succeed, but no destination should receive it.
|
||||
if _, err := o.Write([]byte("foo bar")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !s.calledClose {
|
||||
t.Fatal("Output.Close() didn't close the destination")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputAddPipe(t *testing.T) {
|
||||
var testInputs = []string{
|
||||
"hello, world!",
|
||||
"One\nTwo\nThree",
|
||||
"",
|
||||
"A line\nThen another nl-terminated line\n",
|
||||
"A line followed by an empty line\n\n",
|
||||
}
|
||||
for _, input := range testInputs {
|
||||
expectedOutput := input
|
||||
o := NewOutput()
|
||||
r, err := o.AddPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
go func(o *Output) {
|
||||
if n, err := o.Write([]byte(input)); err != nil {
|
||||
t.Error(err)
|
||||
} else if n != len(input) {
|
||||
t.Errorf("Expected %d, got %d", len(input), n)
|
||||
}
|
||||
if err := o.Close(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}(o)
|
||||
output, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(output) != expectedOutput {
|
||||
t.Errorf("Last line is not stored as return string.\nExpected: '%s'\nGot: '%s'", expectedOutput, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTail(t *testing.T) {
|
||||
var tests = make(map[string][][]string)
|
||||
tests["hello, world!"] = [][]string{
|
||||
{},
|
||||
{"hello, world!"},
|
||||
{"hello, world!"},
|
||||
{"hello, world!"},
|
||||
}
|
||||
tests["One\nTwo\nThree"] = [][]string{
|
||||
{},
|
||||
{"Three"},
|
||||
{"Two", "Three"},
|
||||
{"One", "Two", "Three"},
|
||||
}
|
||||
for input, outputs := range tests {
|
||||
for n, expectedOutput := range outputs {
|
||||
var output []string
|
||||
Tail(strings.NewReader(input), n, &output)
|
||||
if fmt.Sprintf("%v", output) != fmt.Sprintf("%v", expectedOutput) {
|
||||
t.Errorf("Tail n=%d returned wrong result.\nExpected: '%s'\nGot : '%s'", n, expectedOutput, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputAddTail(t *testing.T) {
|
||||
var tests = make(map[string][][]string)
|
||||
tests["hello, world!"] = [][]string{
|
||||
{},
|
||||
{"hello, world!"},
|
||||
{"hello, world!"},
|
||||
{"hello, world!"},
|
||||
}
|
||||
tests["One\nTwo\nThree"] = [][]string{
|
||||
{},
|
||||
{"Three"},
|
||||
{"Two", "Three"},
|
||||
{"One", "Two", "Three"},
|
||||
}
|
||||
for input, outputs := range tests {
|
||||
for n, expectedOutput := range outputs {
|
||||
o := NewOutput()
|
||||
var output []string
|
||||
if err := o.AddTail(&output, n); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if n, err := o.Write([]byte(input)); err != nil {
|
||||
t.Error(err)
|
||||
} else if n != len(input) {
|
||||
t.Errorf("Expected %d, got %d", len(input), n)
|
||||
}
|
||||
o.Close()
|
||||
if fmt.Sprintf("%v", output) != fmt.Sprintf("%v", expectedOutput) {
|
||||
t.Errorf("Tail(%d) returned wrong result.\nExpected: %v\nGot: %v", n, expectedOutput, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func lastLine(txt string) string {
|
||||
scanner := bufio.NewScanner(strings.NewReader(txt))
|
||||
var lastLine string
|
||||
for scanner.Scan() {
|
||||
lastLine = scanner.Text()
|
||||
}
|
||||
return lastLine
|
||||
}
|
||||
|
||||
func TestOutputAdd(t *testing.T) {
|
||||
o := NewOutput()
|
||||
b := &bytes.Buffer{}
|
||||
o.Add(b)
|
||||
input := "hello, world!"
|
||||
if n, err := o.Write([]byte(input)); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if n != len(input) {
|
||||
t.Fatalf("Expected %d, got %d", len(input), n)
|
||||
}
|
||||
if output := b.String(); output != input {
|
||||
t.Fatalf("Received wrong data from Add.\nExpected: '%s'\nGot: '%s'", input, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputWriteError(t *testing.T) {
|
||||
o := NewOutput()
|
||||
buf := &bytes.Buffer{}
|
||||
o.Add(buf)
|
||||
r, w := io.Pipe()
|
||||
input := "Hello there"
|
||||
expectedErr := fmt.Errorf("This is an error")
|
||||
r.CloseWithError(expectedErr)
|
||||
o.Add(w)
|
||||
n, err := o.Write([]byte(input))
|
||||
if err != expectedErr {
|
||||
t.Fatalf("Output.Write() should return the first error encountered, if any")
|
||||
}
|
||||
if buf.String() != input {
|
||||
t.Fatalf("Output.Write() should attempt write on all destinations, even after encountering an error")
|
||||
}
|
||||
if n != len(input) {
|
||||
t.Fatalf("Output.Write() should return the size of the input if it successfully writes to at least one destination")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInputAddEmpty(t *testing.T) {
|
||||
i := NewInput()
|
||||
var b bytes.Buffer
|
||||
if err := i.Add(&b); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := ioutil.ReadAll(i)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(data) > 0 {
|
||||
t.Fatalf("Read from empty input shoul yield no data")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInputAddTwo(t *testing.T) {
|
||||
i := NewInput()
|
||||
var b1 bytes.Buffer
|
||||
// First add should succeed
|
||||
if err := i.Add(&b1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var b2 bytes.Buffer
|
||||
// Second add should fail
|
||||
if err := i.Add(&b2); err == nil {
|
||||
t.Fatalf("Adding a second source should return an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInputAddNotEmpty(t *testing.T) {
|
||||
i := NewInput()
|
||||
b := bytes.NewBufferString("hello world\nabc")
|
||||
expectedResult := b.String()
|
||||
i.Add(b)
|
||||
result, err := ioutil.ReadAll(i)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(result) != expectedResult {
|
||||
t.Fatalf("Expected: %v\nReceived: %v", expectedResult, result)
|
||||
}
|
||||
}
|
168
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/env.go
generated
vendored
Normal file
168
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/env.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Env represents a list of key-pair represented in the form KEY=VALUE.
|
||||
type Env []string
|
||||
|
||||
// Get returns the string value of the given key.
|
||||
func (env *Env) Get(key string) (value string) {
|
||||
return env.Map()[key]
|
||||
}
|
||||
|
||||
// Exists checks whether the given key is defined in the internal Env
|
||||
// representation.
|
||||
func (env *Env) Exists(key string) bool {
|
||||
_, exists := env.Map()[key]
|
||||
return exists
|
||||
}
|
||||
|
||||
// GetBool returns a boolean representation of the given key. The key is false
|
||||
// whenever its value if 0, no, false, none or an empty string. Any other value
|
||||
// will be interpreted as true.
|
||||
func (env *Env) GetBool(key string) (value bool) {
|
||||
s := strings.ToLower(strings.Trim(env.Get(key), " \t"))
|
||||
if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// SetBool defines a boolean value to the given key.
|
||||
func (env *Env) SetBool(key string, value bool) {
|
||||
if value {
|
||||
env.Set(key, "1")
|
||||
} else {
|
||||
env.Set(key, "0")
|
||||
}
|
||||
}
|
||||
|
||||
// GetInt returns the value of the provided key, converted to int.
|
||||
//
|
||||
// It the value cannot be represented as an integer, it returns -1.
|
||||
func (env *Env) GetInt(key string) int {
|
||||
return int(env.GetInt64(key))
|
||||
}
|
||||
|
||||
// SetInt defines an integer value to the given key.
|
||||
func (env *Env) SetInt(key string, value int) {
|
||||
env.Set(key, strconv.Itoa(value))
|
||||
}
|
||||
|
||||
// GetInt64 returns the value of the provided key, converted to int64.
|
||||
//
|
||||
// It the value cannot be represented as an integer, it returns -1.
|
||||
func (env *Env) GetInt64(key string) int64 {
|
||||
s := strings.Trim(env.Get(key), " \t")
|
||||
val, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// SetInt64 defines an integer (64-bit wide) value to the given key.
|
||||
func (env *Env) SetInt64(key string, value int64) {
|
||||
env.Set(key, strconv.FormatInt(value, 10))
|
||||
}
|
||||
|
||||
// GetJSON unmarshals the value of the provided key in the provided iface.
|
||||
//
|
||||
// iface is a value that can be provided to the json.Unmarshal function.
|
||||
func (env *Env) GetJSON(key string, iface interface{}) error {
|
||||
sval := env.Get(key)
|
||||
if sval == "" {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal([]byte(sval), iface)
|
||||
}
|
||||
|
||||
// SetJSON marshals the given value to JSON format and stores it using the
|
||||
// provided key.
|
||||
func (env *Env) SetJSON(key string, value interface{}) error {
|
||||
sval, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.Set(key, string(sval))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetList returns a list of strings matching the provided key. It handles the
|
||||
// list as a JSON representation of a list of strings.
|
||||
//
|
||||
// If the given key matches to a single string, it will return a list
|
||||
// containing only the value that matches the key.
|
||||
func (env *Env) GetList(key string) []string {
|
||||
sval := env.Get(key)
|
||||
if sval == "" {
|
||||
return nil
|
||||
}
|
||||
var l []string
|
||||
if err := json.Unmarshal([]byte(sval), &l); err != nil {
|
||||
l = append(l, sval)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// SetList stores the given list in the provided key, after serializing it to
|
||||
// JSON format.
|
||||
func (env *Env) SetList(key string, value []string) error {
|
||||
return env.SetJSON(key, value)
|
||||
}
|
||||
|
||||
// Set defines the value of a key to the given string.
|
||||
func (env *Env) Set(key, value string) {
|
||||
*env = append(*env, key+"="+value)
|
||||
}
|
||||
|
||||
// Decode decodes `src` as a json dictionary, and adds each decoded key-value
|
||||
// pair to the environment.
|
||||
//
|
||||
// If `src` cannot be decoded as a json dictionary, an error is returned.
|
||||
func (env *Env) Decode(src io.Reader) error {
|
||||
m := make(map[string]interface{})
|
||||
if err := json.NewDecoder(src).Decode(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range m {
|
||||
env.SetAuto(k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetAuto will try to define the Set* method to call based on the given value.
|
||||
func (env *Env) SetAuto(key string, value interface{}) {
|
||||
if fval, ok := value.(float64); ok {
|
||||
env.SetInt64(key, int64(fval))
|
||||
} else if sval, ok := value.(string); ok {
|
||||
env.Set(key, sval)
|
||||
} else if val, err := json.Marshal(value); err == nil {
|
||||
env.Set(key, string(val))
|
||||
} else {
|
||||
env.Set(key, fmt.Sprintf("%v", value))
|
||||
}
|
||||
}
|
||||
|
||||
// Map returns the map representation of the env.
|
||||
func (env *Env) Map() map[string]string {
|
||||
if len(*env) == 0 {
|
||||
return nil
|
||||
}
|
||||
m := make(map[string]string)
|
||||
for _, kv := range *env {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
m[parts[0]] = parts[1]
|
||||
}
|
||||
return m
|
||||
}
|
349
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/env_test.go
generated
vendored
Normal file
349
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/env_test.go
generated
vendored
Normal file
@ -0,0 +1,349 @@
|
||||
// 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 DOCKER-LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input []string
|
||||
query string
|
||||
expected string
|
||||
}{
|
||||
{[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PATH", "/usr/bin:/bin"},
|
||||
{[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PYTHONPATH", "/usr/local"},
|
||||
{[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PYTHONPATHI", ""},
|
||||
{[]string{"WAT="}, "WAT", ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
env := Env(tt.input)
|
||||
got := env.Get(tt.query)
|
||||
if got != tt.expected {
|
||||
t.Errorf("Env.Get(%q): wrong result. Want %q. Got %q", tt.query, tt.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExists(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input []string
|
||||
query string
|
||||
expected bool
|
||||
}{
|
||||
{[]string{"WAT=", "PYTHONPATH=/usr/local"}, "WAT", true},
|
||||
{[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PYTHONPATH", true},
|
||||
{[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PYTHONPATHI", false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
env := Env(tt.input)
|
||||
got := env.Exists(tt.query)
|
||||
if got != tt.expected {
|
||||
t.Errorf("Env.Exists(%q): wrong result. Want %v. Got %v", tt.query, tt.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBool(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"EMTPY_VAR", false}, {"ZERO_VAR", false}, {"NO_VAR", false},
|
||||
{"FALSE_VAR", false}, {"NONE_VAR", false}, {"TRUE_VAR", true},
|
||||
{"WAT", true}, {"PATH", true}, {"ONE_VAR", true}, {"NO_VAR_TAB", false},
|
||||
}
|
||||
env := Env([]string{
|
||||
"EMPTY_VAR=", "ZERO_VAR=0", "NO_VAR=no", "FALSE_VAR=false",
|
||||
"NONE_VAR=none", "TRUE_VAR=true", "WAT=wat", "PATH=/usr/bin:/bin",
|
||||
"ONE_VAR=1", "NO_VAR_TAB=0 \t\t\t",
|
||||
})
|
||||
for _, tt := range tests {
|
||||
got := env.GetBool(tt.input)
|
||||
if got != tt.expected {
|
||||
t.Errorf("Env.GetBool(%q): wrong result. Want %v. Got %v.", tt.input, tt.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetBool(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input bool
|
||||
expected string
|
||||
}{
|
||||
{true, "1"}, {false, "0"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
var env Env
|
||||
env.SetBool("SOME", tt.input)
|
||||
if got := env.Get("SOME"); got != tt.expected {
|
||||
t.Errorf("Env.SetBool(%v): wrong result. Want %q. Got %q", tt.input, tt.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInt(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
expected int
|
||||
}{
|
||||
{"NEGATIVE_INTEGER", -10}, {"NON_INTEGER", -1}, {"ONE", 1}, {"TWO", 2},
|
||||
}
|
||||
env := Env([]string{"NEGATIVE_INTEGER=-10", "NON_INTEGER=wat", "ONE=1", "TWO=2"})
|
||||
for _, tt := range tests {
|
||||
got := env.GetInt(tt.input)
|
||||
if got != tt.expected {
|
||||
t.Errorf("Env.GetInt(%q): wrong result. Want %d. Got %d", tt.input, tt.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetInt(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input int
|
||||
expected string
|
||||
}{
|
||||
{10, "10"}, {13, "13"}, {7, "7"}, {33, "33"},
|
||||
{0, "0"}, {-34, "-34"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
var env Env
|
||||
env.SetInt("SOME", tt.input)
|
||||
if got := env.Get("SOME"); got != tt.expected {
|
||||
t.Errorf("Env.SetBool(%d): wrong result. Want %q. Got %q", tt.input, tt.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInt64(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
expected int64
|
||||
}{
|
||||
{"NEGATIVE_INTEGER", -10}, {"NON_INTEGER", -1}, {"ONE", 1}, {"TWO", 2},
|
||||
}
|
||||
env := Env([]string{"NEGATIVE_INTEGER=-10", "NON_INTEGER=wat", "ONE=1", "TWO=2"})
|
||||
for _, tt := range tests {
|
||||
got := env.GetInt64(tt.input)
|
||||
if got != tt.expected {
|
||||
t.Errorf("Env.GetInt64(%q): wrong result. Want %d. Got %d", tt.input, tt.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetInt64(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input int64
|
||||
expected string
|
||||
}{
|
||||
{10, "10"}, {13, "13"}, {7, "7"}, {33, "33"},
|
||||
{0, "0"}, {-34, "-34"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
var env Env
|
||||
env.SetInt64("SOME", tt.input)
|
||||
if got := env.Get("SOME"); got != tt.expected {
|
||||
t.Errorf("Env.SetBool(%d): wrong result. Want %q. Got %q", tt.input, tt.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetJSON(t *testing.T) {
|
||||
var p struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
}
|
||||
var env Env
|
||||
env.Set("person", `{"name":"Gopher","age":5}`)
|
||||
err := env.GetJSON("person", &p)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if p.Name != "Gopher" {
|
||||
t.Errorf("Env.GetJSON(%q): wrong name. Want %q. Got %q", "person", "Gopher", p.Name)
|
||||
}
|
||||
if p.Age != 5 {
|
||||
t.Errorf("Env.GetJSON(%q): wrong age. Want %d. Got %d", "person", 5, p.Age)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetJSONAbsent(t *testing.T) {
|
||||
var l []string
|
||||
var env Env
|
||||
err := env.GetJSON("person", &l)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if l != nil {
|
||||
t.Errorf("Env.GetJSON(): get unexpected list %v", l)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetJSONFailure(t *testing.T) {
|
||||
var p []string
|
||||
var env Env
|
||||
env.Set("list-person", `{"name":"Gopher","age":5}`)
|
||||
err := env.GetJSON("list-person", &p)
|
||||
if err == nil {
|
||||
t.Errorf("Env.GetJSON(%q): got unexpected <nil> error.", "list-person")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetJSON(t *testing.T) {
|
||||
var p1 = struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
}{Name: "Gopher", Age: 5}
|
||||
var env Env
|
||||
err := env.SetJSON("person", p1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
var p2 struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
}
|
||||
err = env.GetJSON("person", &p2)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !reflect.DeepEqual(p1, p2) {
|
||||
t.Errorf("Env.SetJSON(%q): wrong result. Want %v. Got %v", "person", p1, p2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetJSONFailure(t *testing.T) {
|
||||
var env Env
|
||||
err := env.SetJSON("person", unmarshable{})
|
||||
if err == nil {
|
||||
t.Error("Env.SetJSON(): got unexpected <nil> error")
|
||||
}
|
||||
if env.Exists("person") {
|
||||
t.Errorf("Env.SetJSON(): should not define the key %q, but did", "person")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetList(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
expected []string
|
||||
}{
|
||||
{"WAT=wat", []string{"wat"}},
|
||||
{`WAT=["wat","wet","wit","wot","wut"]`, []string{"wat", "wet", "wit", "wot", "wut"}},
|
||||
{"WAT=", nil},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
env := Env([]string{tt.input})
|
||||
got := env.GetList("WAT")
|
||||
if !reflect.DeepEqual(got, tt.expected) {
|
||||
t.Errorf("Env.GetList(%q): wrong result. Want %v. Got %v", "WAT", tt.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetList(t *testing.T) {
|
||||
list := []string{"a", "b", "c"}
|
||||
var env Env
|
||||
env.SetList("SOME", list)
|
||||
if got := env.GetList("SOME"); !reflect.DeepEqual(got, list) {
|
||||
t.Errorf("Env.SetList(%v): wrong result. Got %v", list, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
var env Env
|
||||
env.Set("PATH", "/home/bin:/bin")
|
||||
env.Set("SOMETHING", "/usr/bin")
|
||||
env.Set("PATH", "/bin")
|
||||
if expected, got := "/usr/bin", env.Get("SOMETHING"); got != expected {
|
||||
t.Errorf("Env.Set(%q): wrong result. Want %q. Got %q", expected, expected, got)
|
||||
}
|
||||
if expected, got := "/bin", env.Get("PATH"); got != expected {
|
||||
t.Errorf("Env.Set(%q): wrong result. Want %q. Got %q", expected, expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
expectedOut []string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
`{"PATH":"/usr/bin:/bin","containers":54,"wat":["123","345"]}`,
|
||||
[]string{"PATH=/usr/bin:/bin", "containers=54", `wat=["123","345"]`},
|
||||
"",
|
||||
},
|
||||
{"}}", nil, "invalid character '}' looking for beginning of value"},
|
||||
{`{}`, nil, ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
var env Env
|
||||
err := env.Decode(bytes.NewBufferString(tt.input))
|
||||
if tt.expectedErr == "" {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
} else if tt.expectedErr != err.Error() {
|
||||
t.Errorf("Env.Decode(): invalid error. Want %q. Got %q.", tt.expectedErr, err)
|
||||
}
|
||||
got := []string(env)
|
||||
sort.Strings(got)
|
||||
sort.Strings(tt.expectedOut)
|
||||
if !reflect.DeepEqual(got, tt.expectedOut) {
|
||||
t.Errorf("Env.Decode(): wrong result. Want %v. Got %v.", tt.expectedOut, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetAuto(t *testing.T) {
|
||||
buf := bytes.NewBufferString("oi")
|
||||
var tests = []struct {
|
||||
input interface{}
|
||||
expected string
|
||||
}{
|
||||
{10, "10"},
|
||||
{10.3, "10"},
|
||||
{"oi", "oi"},
|
||||
{buf, "{}"},
|
||||
{unmarshable{}, "{}"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
var env Env
|
||||
env.SetAuto("SOME", tt.input)
|
||||
if got := env.Get("SOME"); got != tt.expected {
|
||||
t.Errorf("Env.SetAuto(%v): wrong result. Want %q. Got %q", tt.input, tt.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input []string
|
||||
expected map[string]string
|
||||
}{
|
||||
{[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, map[string]string{"PATH": "/usr/bin:/bin", "PYTHONPATH": "/usr/local"}},
|
||||
{nil, nil},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
env := Env(tt.input)
|
||||
got := env.Map()
|
||||
if !reflect.DeepEqual(got, tt.expected) {
|
||||
t.Errorf("Env.Map(): wrong result. Want %v. Got %v", tt.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type unmarshable struct {
|
||||
}
|
||||
|
||||
func (unmarshable) MarshalJSON() ([]byte, error) {
|
||||
return nil, errors.New("cannot marshal")
|
||||
}
|
8
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/event.go
generated
vendored
8
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/event.go
generated
vendored
@ -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 {
|
||||
|
38
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/example_test.go
generated
vendored
38
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/example_test.go
generated
vendored
@ -7,6 +7,7 @@ package docker_test
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"time"
|
||||
@ -33,7 +34,6 @@ func ExampleClient_AttachToContainer() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println(buf.String())
|
||||
// Attaching to stdout and streaming.
|
||||
buf.Reset()
|
||||
err = client.AttachToContainer(docker.AttachToContainerOptions{
|
||||
Container: "a84849",
|
||||
@ -53,7 +53,6 @@ func ExampleClient_CopyFromContainer() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cid := "a84849"
|
||||
// Copy resulting file
|
||||
var buf bytes.Buffer
|
||||
filename := "/tmp/output.txt"
|
||||
err = client.CopyFromContainer(docker.CopyFromContainerOptions{
|
||||
@ -132,3 +131,38 @@ func ExampleClient_ListenEvents() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func ExampleEnv_Map() {
|
||||
e := docker.Env([]string{"A=1", "B=2", "C=3"})
|
||||
envs := e.Map()
|
||||
for k, v := range envs {
|
||||
fmt.Printf("%s=%q\n", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleEnv_SetJSON() {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p := Person{Name: "Gopher", Age: 4}
|
||||
var e docker.Env
|
||||
err := e.SetJSON("person", p)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleEnv_GetJSON() {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p := Person{Name: "Gopher", Age: 4}
|
||||
var e docker.Env
|
||||
e.Set("person", `{"name":"Gopher","age":4}`)
|
||||
err := e.GetJSON("person", &p)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
167
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image.go
generated
vendored
167
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image.go
generated
vendored
@ -20,28 +20,36 @@ 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"`
|
||||
}
|
||||
|
||||
// ImageHistory represent a layer in an image's history returned by the
|
||||
// ImageHistory call.
|
||||
type ImageHistory struct {
|
||||
ID string `json:"Id" yaml:"Id"`
|
||||
Tags []string `json:"Tags,omitempty" yaml:"Tags,omitempty"`
|
||||
Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"`
|
||||
CreatedBy string `json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty"`
|
||||
Size int64 `json:"Size,omitempty" yaml:"Size,omitempty"`
|
||||
}
|
||||
|
||||
type ImagePre012 struct {
|
||||
@ -55,7 +63,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 (
|
||||
@ -73,7 +81,7 @@ var (
|
||||
|
||||
// ListImages returns the list of available images in the server.
|
||||
//
|
||||
// See http://goo.gl/dkMrwP for more details.
|
||||
// See http://goo.gl/VmcR6v for more details.
|
||||
func (c *Client) ListImages(all bool) ([]APIImages, error) {
|
||||
path := "/images/json?all="
|
||||
if all {
|
||||
@ -93,9 +101,28 @@ func (c *Client) ListImages(all bool) ([]APIImages, error) {
|
||||
return images, nil
|
||||
}
|
||||
|
||||
// ImageHistory returns the history of the image by its name or ID.
|
||||
//
|
||||
// See http://goo.gl/2oJmNs for more details.
|
||||
func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
|
||||
body, status, err := c.do("GET", "/images/"+name+"/history", nil)
|
||||
if status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchImage
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var history []ImageHistory
|
||||
err = json.Unmarshal(body, &history)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return history, nil
|
||||
}
|
||||
|
||||
// RemoveImage removes an image by its name or ID.
|
||||
//
|
||||
// See http://goo.gl/7hjHHy for more details.
|
||||
// See http://goo.gl/znj0wM for more details.
|
||||
func (c *Client) RemoveImage(name string) error {
|
||||
_, status, err := c.do("DELETE", "/images/"+name, nil)
|
||||
if status == http.StatusNotFound {
|
||||
@ -106,7 +133,7 @@ func (c *Client) RemoveImage(name string) error {
|
||||
|
||||
// InspectImage returns an image by its name or ID.
|
||||
//
|
||||
// See http://goo.gl/pHEbma for more details.
|
||||
// See http://goo.gl/Q112NY for more details.
|
||||
func (c *Client) InspectImage(name string) (*Image, error) {
|
||||
body, status, err := c.do("GET", "/images/"+name+"/json", nil)
|
||||
if status == http.StatusNotFound {
|
||||
@ -149,7 +176,7 @@ func (c *Client) InspectImage(name string) (*Image, error) {
|
||||
|
||||
// PushImageOptions represents options to use in the PushImage method.
|
||||
//
|
||||
// See http://goo.gl/GBmyhc for more details.
|
||||
// See http://goo.gl/pN8A3P for more details.
|
||||
type PushImageOptions struct {
|
||||
// Name of the image
|
||||
Name string
|
||||
@ -164,7 +191,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"`
|
||||
@ -176,7 +203,7 @@ type AuthConfiguration struct {
|
||||
// An empty instance of AuthConfiguration may be used for unauthenticated
|
||||
// pushes.
|
||||
//
|
||||
// See http://goo.gl/GBmyhc for more details.
|
||||
// See http://goo.gl/pN8A3P for more details.
|
||||
func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error {
|
||||
if opts.Name == "" {
|
||||
return ErrNoSuchImage
|
||||
@ -190,23 +217,24 @@ 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
|
||||
// from a registry.
|
||||
//
|
||||
// See http://goo.gl/PhBKnS for more details.
|
||||
// See http://goo.gl/ACyYNS 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.
|
||||
//
|
||||
// See http://goo.gl/PhBKnS for more details.
|
||||
// See http://goo.gl/ACyYNS for more details.
|
||||
func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error {
|
||||
if opts.Repository == "" {
|
||||
return ErrNoSuchImage
|
||||
@ -217,12 +245,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,24 +314,30 @@ 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/
|
||||
// BuildImageOptions present the set of informations available for building an
|
||||
// image from a tarfile with a Dockerfile in it.
|
||||
//
|
||||
// For more details about the Docker building process, see
|
||||
// http://goo.gl/tlPXPu.
|
||||
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:"-"`
|
||||
RawJSONStream bool `qs:"-"`
|
||||
Remote string `qs:"remote"`
|
||||
}
|
||||
|
||||
// BuildImage builds an image from a tarball's url or a Dockerfile in the input
|
||||
// stream.
|
||||
//
|
||||
// See http://goo.gl/wRsW76 for more details.
|
||||
func (c *Client) BuildImage(opts BuildImageOptions) error {
|
||||
if opts.OutputStream == nil {
|
||||
return ErrMissingOutputStream
|
||||
@ -289,17 +352,21 @@ 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, opts.RawJSONStream, headers, opts.InputStream, opts.OutputStream, nil)
|
||||
}
|
||||
|
||||
// TagImageOptions present the set of options to tag an image
|
||||
// TagImageOptions present the set of options to tag an image.
|
||||
//
|
||||
// See http://goo.gl/5g6qFy for more details.
|
||||
type TagImageOptions struct {
|
||||
Repo string
|
||||
Tag string
|
||||
Force bool
|
||||
}
|
||||
|
||||
// TagImage adds a tag to the image 'name'
|
||||
// TagImage adds a tag to the image identified by the given name.
|
||||
//
|
||||
// See http://goo.gl/5g6qFy for more details.
|
||||
func (c *Client) TagImage(name string, opts TagImageOptions) error {
|
||||
if name == "" {
|
||||
return ErrNoSuchImage
|
||||
|
168
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image_test.go
generated
vendored
168
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image_test.go
generated
vendored
@ -21,9 +21,9 @@ func newTestClient(rt *FakeRoundTripper) Client {
|
||||
endpoint := "http://localhost:4243"
|
||||
u, _ := parseEndpoint("http://localhost:4243")
|
||||
client := Client{
|
||||
HTTPClient: &http.Client{Transport: rt},
|
||||
endpoint: endpoint,
|
||||
endpointURL: u,
|
||||
client: &http.Client{Transport: rt},
|
||||
SkipServerVersionCheck: true,
|
||||
}
|
||||
return client
|
||||
@ -122,6 +122,48 @@ func TestListImagesParameters(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageHistory(t *testing.T) {
|
||||
body := `[
|
||||
{
|
||||
"Id": "25daec02219d2d852f7526137213a9b199926b4b24e732eab5b8bc6c49bd470e",
|
||||
"Tags": [
|
||||
"debian:7.6",
|
||||
"debian:latest",
|
||||
"debian:7",
|
||||
"debian:wheezy"
|
||||
],
|
||||
"Created": 1409856216,
|
||||
"CreatedBy": "/bin/sh -c #(nop) CMD [/bin/bash]"
|
||||
},
|
||||
{
|
||||
"Id": "41026a5347fb5be6ed16115bf22df8569697139f246186de9ae8d4f67c335dce",
|
||||
"Created": 1409856213,
|
||||
"CreatedBy": "/bin/sh -c #(nop) ADD file:1ee9e97209d00e3416a4543b23574cc7259684741a46bbcbc755909b8a053a38 in /",
|
||||
"Size": 85178663
|
||||
},
|
||||
{
|
||||
"Id": "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158",
|
||||
"Tags": [
|
||||
"scratch:latest"
|
||||
],
|
||||
"Created": 1371157430
|
||||
}
|
||||
]`
|
||||
var expected []ImageHistory
|
||||
err := json.Unmarshal([]byte(body), &expected)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client := newTestClient(&FakeRoundTripper{message: body, status: http.StatusOK})
|
||||
history, err := client.ImageHistory("debian:latest")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !reflect.DeepEqual(history, expected) {
|
||||
t.Errorf("ImageHistory: Wrong return value. Want %#v. Got %#v.", expected, history)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveImage(t *testing.T) {
|
||||
name := "test"
|
||||
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
|
||||
@ -309,6 +351,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 +583,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)
|
||||
@ -551,7 +621,7 @@ func TestBuildImageParametersForRemoteBuild(t *testing.T) {
|
||||
expected := map[string][]string{"t": {opts.Name}, "remote": {opts.Remote}, "q": {"1"}}
|
||||
got := map[string][]string(req.URL.Query())
|
||||
if !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("ImportImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||||
t.Errorf("BuildImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,6 +650,44 @@ func TestBuildImageMissingOutputStream(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildImageWithRawJSON(t *testing.T) {
|
||||
body := `
|
||||
{"stream":"Step 0 : FROM ubuntu:latest\n"}
|
||||
{"stream":" ---\u003e 4300eb9d3c8d\n"}
|
||||
{"stream":"Step 1 : MAINTAINER docker <eng@docker.com>\n"}
|
||||
{"stream":" ---\u003e Using cache\n"}
|
||||
{"stream":" ---\u003e 3a3ed758c370\n"}
|
||||
{"stream":"Step 2 : CMD /usr/bin/top\n"}
|
||||
{"stream":" ---\u003e Running in 36b1479cc2e4\n"}
|
||||
{"stream":" ---\u003e 4b6188aebe39\n"}
|
||||
{"stream":"Removing intermediate container 36b1479cc2e4\n"}
|
||||
{"stream":"Successfully built 4b6188aebe39\n"}
|
||||
`
|
||||
fakeRT := &FakeRoundTripper{
|
||||
message: body,
|
||||
status: http.StatusOK,
|
||||
header: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
client := newTestClient(fakeRT)
|
||||
var buf bytes.Buffer
|
||||
opts := BuildImageOptions{
|
||||
Name: "testImage",
|
||||
RmTmpContainer: true,
|
||||
InputStream: &buf,
|
||||
OutputStream: &buf,
|
||||
RawJSONStream: true,
|
||||
}
|
||||
err := client.BuildImage(opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if buf.String() != body {
|
||||
t.Errorf("BuildImage: Wrong raw output. Want %q. Got %q.", body, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildImageRemoteWithoutName(t *testing.T) {
|
||||
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||||
client := newTestClient(fakeRT)
|
||||
@ -640,3 +748,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)
|
||||
}
|
||||
}
|
||||
|
44
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/misc.go
generated
vendored
44
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/misc.go
generated
vendored
@ -6,42 +6,54 @@ package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/fsouza/go-dockerclient/engine"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version returns version information about the docker server.
|
||||
//
|
||||
// See http://goo.gl/IqKNRE for more details.
|
||||
func (c *Client) Version() (*engine.Env, error) {
|
||||
// See http://goo.gl/BOZrF5 for more details.
|
||||
func (c *Client) Version() (*Env, error) {
|
||||
body, _, err := c.do("GET", "/version", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := engine.NewOutput()
|
||||
remoteVersion, err := out.AddEnv()
|
||||
if err != nil {
|
||||
var env Env
|
||||
if err := env.Decode(bytes.NewReader(body)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.Copy(out, bytes.NewReader(body)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return remoteVersion, nil
|
||||
return &env, nil
|
||||
}
|
||||
|
||||
// Info returns system-wide information, like the number of running containers.
|
||||
// Info returns system-wide information about the Docker server.
|
||||
//
|
||||
// See http://goo.gl/LOmySw for more details.
|
||||
func (c *Client) Info() (*engine.Env, error) {
|
||||
// See http://goo.gl/wmqZsW for more details.
|
||||
func (c *Client) Info() (*Env, error) {
|
||||
body, _, err := c.do("GET", "/info", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var info engine.Env
|
||||
var info Env
|
||||
err = info.Decode(bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
// ParseRepositoryTag gets the name of the repository and returns it splitted
|
||||
// in two parts: the repository and the tag.
|
||||
//
|
||||
// Some examples:
|
||||
//
|
||||
// localhost.localdomain:5000/samalba/hipache:latest -> localhost.localdomain:5000/samalba/hipache, latest
|
||||
// localhost.localdomain:5000/samalba/hipache -> localhost.localdomain:5000/samalba/hipache, ""
|
||||
func ParseRepositoryTag(repoTag string) (repository string, tag string) {
|
||||
n := strings.LastIndex(repoTag, ":")
|
||||
if n < 0 {
|
||||
return repoTag, ""
|
||||
}
|
||||
if tag := repoTag[n+1:]; !strings.Contains(tag, "/") {
|
||||
return repoTag[:n], tag
|
||||
}
|
||||
return repoTag, ""
|
||||
}
|
||||
|
42
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/misc_test.go
generated
vendored
42
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/misc_test.go
generated
vendored
@ -10,8 +10,6 @@ import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/fsouza/go-dockerclient/engine"
|
||||
)
|
||||
|
||||
type DockerVersion struct {
|
||||
@ -81,7 +79,7 @@ func TestInfo(t *testing.T) {
|
||||
}`
|
||||
fakeRT := FakeRoundTripper{message: body, status: http.StatusOK}
|
||||
client := newTestClient(&fakeRT)
|
||||
expected := engine.Env{}
|
||||
expected := Env{}
|
||||
expected.SetInt("Containers", 11)
|
||||
expected.SetInt("Images", 16)
|
||||
expected.SetBool("Debug", false)
|
||||
@ -121,3 +119,41 @@ func TestInfoError(t *testing.T) {
|
||||
t.Error("Info(): unexpected <nil> error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRepositoryTag(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
expectedRepo string
|
||||
expectedTag string
|
||||
}{
|
||||
{
|
||||
"localhost.localdomain:5000/samalba/hipache:latest",
|
||||
"localhost.localdomain:5000/samalba/hipache",
|
||||
"latest",
|
||||
},
|
||||
{
|
||||
"localhost.localdomain:5000/samalba/hipache",
|
||||
"localhost.localdomain:5000/samalba/hipache",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"tsuru/python",
|
||||
"tsuru/python",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"tsuru/python:2.7",
|
||||
"tsuru/python",
|
||||
"2.7",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
repo, tag := ParseRepositoryTag(tt.input)
|
||||
if repo != tt.expectedRepo {
|
||||
t.Errorf("ParseRepositoryTag(%q): wrong repository. Want %q. Got %q", tt.input, tt.expectedRepo, repo)
|
||||
}
|
||||
if tag != tt.expectedTag {
|
||||
t.Errorf("ParseRepositoryTag(%q): wrong tag. Want %q. Got %q", tt.input, tt.expectedTag, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
91
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/stdcopy.go
generated
vendored
Normal file
91
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/stdcopy.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
stdWriterPrefixLen = 8
|
||||
stdWriterFdIndex = 0
|
||||
stdWriterSizeIndex = 4
|
||||
)
|
||||
|
||||
var errInvalidStdHeader = errors.New("Unrecognized input header")
|
||||
|
||||
func stdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error) {
|
||||
var (
|
||||
buf = make([]byte, 32*1024+stdWriterPrefixLen+1)
|
||||
bufLen = len(buf)
|
||||
nr, nw int
|
||||
er, ew error
|
||||
out io.Writer
|
||||
frameSize int
|
||||
)
|
||||
for {
|
||||
for nr < stdWriterPrefixLen {
|
||||
var nr2 int
|
||||
nr2, er = src.Read(buf[nr:])
|
||||
if er == io.EOF {
|
||||
if nr < stdWriterPrefixLen && nr2 < stdWriterPrefixLen {
|
||||
return written, nil
|
||||
}
|
||||
nr += nr2
|
||||
break
|
||||
} else if er != nil {
|
||||
return 0, er
|
||||
}
|
||||
nr += nr2
|
||||
}
|
||||
switch buf[stdWriterFdIndex] {
|
||||
case 0:
|
||||
fallthrough
|
||||
case 1:
|
||||
out = dstout
|
||||
case 2:
|
||||
out = dsterr
|
||||
default:
|
||||
return 0, errInvalidStdHeader
|
||||
}
|
||||
frameSize = int(binary.BigEndian.Uint32(buf[stdWriterSizeIndex : stdWriterSizeIndex+4]))
|
||||
if frameSize+stdWriterPrefixLen > bufLen {
|
||||
buf = append(buf, make([]byte, frameSize+stdWriterPrefixLen-len(buf)+1)...)
|
||||
bufLen = len(buf)
|
||||
}
|
||||
for nr < frameSize+stdWriterPrefixLen {
|
||||
var nr2 int
|
||||
nr2, er = src.Read(buf[nr:])
|
||||
if er == io.EOF {
|
||||
if nr == 0 {
|
||||
return written, nil
|
||||
}
|
||||
nr += nr2
|
||||
break
|
||||
} else if er != nil {
|
||||
return 0, er
|
||||
}
|
||||
nr += nr2
|
||||
}
|
||||
bound := frameSize + stdWriterPrefixLen
|
||||
if bound > nr {
|
||||
bound = nr
|
||||
}
|
||||
nw, ew = out.Write(buf[stdWriterPrefixLen:bound])
|
||||
if nw > 0 {
|
||||
written += int64(nw)
|
||||
}
|
||||
if ew != nil {
|
||||
return 0, ew
|
||||
}
|
||||
if nw != frameSize {
|
||||
return written, io.ErrShortWrite
|
||||
}
|
||||
copy(buf, buf[frameSize+stdWriterPrefixLen:])
|
||||
nr -= frameSize + stdWriterPrefixLen
|
||||
}
|
||||
}
|
@ -2,10 +2,11 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package utils
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
@ -28,7 +29,7 @@ func TestStdCopy(t *testing.T) {
|
||||
input.Write([]byte("just kidding"))
|
||||
input.Write([]byte{0, 0, 0, 0, 0, 0, 0, 6})
|
||||
input.Write([]byte("\nyeah!"))
|
||||
n, err := StdCopy(&stdout, &stderr, &input)
|
||||
n, err := stdCopy(&stdout, &stderr, &input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -36,19 +37,19 @@ func TestStdCopy(t *testing.T) {
|
||||
t.Errorf("Wrong number of bytes. Want %d. Got %d.", expected, n)
|
||||
}
|
||||
if got := stderr.String(); got != "something happened!" {
|
||||
t.Errorf("StdCopy: wrong stderr. Want %q. Got %q.", "something happened!", got)
|
||||
t.Errorf("stdCopy: wrong stderr. Want %q. Got %q.", "something happened!", got)
|
||||
}
|
||||
if got := stdout.String(); got != "just kidding\nyeah!" {
|
||||
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q.", "just kidding\nyeah!", got)
|
||||
t.Errorf("stdCopy: wrong stdout. Want %q. Got %q.", "just kidding\nyeah!", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdCopyStress(t *testing.T) {
|
||||
var input, stdout, stderr bytes.Buffer
|
||||
value := strings.Repeat("something ", 4096)
|
||||
writer := NewStdWriter(&input, Stdout)
|
||||
writer := newStdWriter(&input, Stdout)
|
||||
writer.Write([]byte(value))
|
||||
n, err := StdCopy(&stdout, &stderr, &input)
|
||||
n, err := stdCopy(&stdout, &stderr, &input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -56,22 +57,22 @@ func TestStdCopyStress(t *testing.T) {
|
||||
t.Errorf("Wrong number of bytes. Want 40960. Got %d.", n)
|
||||
}
|
||||
if got := stderr.String(); got != "" {
|
||||
t.Errorf("StdCopy: wrong stderr. Want empty string. Got %q", got)
|
||||
t.Errorf("stdCopy: wrong stderr. Want empty string. Got %q", got)
|
||||
}
|
||||
if got := stdout.String(); got != value {
|
||||
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q", value, got)
|
||||
t.Errorf("stdCopy: wrong stdout. Want %q. Got %q", value, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdCopyInvalidStdHeader(t *testing.T) {
|
||||
var input, stdout, stderr bytes.Buffer
|
||||
input.Write([]byte{3, 0, 0, 0, 0, 0, 0, 19})
|
||||
n, err := StdCopy(&stdout, &stderr, &input)
|
||||
n, err := stdCopy(&stdout, &stderr, &input)
|
||||
if n != 0 {
|
||||
t.Errorf("StdCopy: wrong number of bytes. Want 0. Got %d", n)
|
||||
t.Errorf("stdCopy: wrong number of bytes. Want 0. Got %d", n)
|
||||
}
|
||||
if err != ErrInvalidStdHeader {
|
||||
t.Errorf("StdCopy: wrong error. Want ErrInvalidStdHeader. Got %#v", err)
|
||||
if err != errInvalidStdHeader {
|
||||
t.Errorf("stdCopy: wrong error. Want ErrInvalidStdHeader. Got %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,7 +80,7 @@ func TestStdCopyBigFrame(t *testing.T) {
|
||||
var input, stdout, stderr bytes.Buffer
|
||||
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 18})
|
||||
input.Write([]byte("something happened!"))
|
||||
n, err := StdCopy(&stdout, &stderr, &input)
|
||||
n, err := stdCopy(&stdout, &stderr, &input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -87,10 +88,10 @@ func TestStdCopyBigFrame(t *testing.T) {
|
||||
t.Errorf("Wrong number of bytes. Want %d. Got %d.", expected, n)
|
||||
}
|
||||
if got := stderr.String(); got != "something happened" {
|
||||
t.Errorf("StdCopy: wrong stderr. Want %q. Got %q.", "something happened", got)
|
||||
t.Errorf("stdCopy: wrong stderr. Want %q. Got %q.", "something happened", got)
|
||||
}
|
||||
if got := stdout.String(); got != "" {
|
||||
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||
t.Errorf("stdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,41 +99,41 @@ func TestStdCopySmallFrame(t *testing.T) {
|
||||
var input, stdout, stderr bytes.Buffer
|
||||
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 20})
|
||||
input.Write([]byte("something happened!"))
|
||||
n, err := StdCopy(&stdout, &stderr, &input)
|
||||
n, err := stdCopy(&stdout, &stderr, &input)
|
||||
if err != io.ErrShortWrite {
|
||||
t.Errorf("StdCopy: wrong error. Want ShortWrite. Got %#v", err)
|
||||
t.Errorf("stdCopy: wrong error. Want ShortWrite. Got %#v", err)
|
||||
}
|
||||
if expected := int64(19); n != expected {
|
||||
t.Errorf("Wrong number of bytes. Want %d. Got %d.", expected, n)
|
||||
}
|
||||
if got := stderr.String(); got != "something happened!" {
|
||||
t.Errorf("StdCopy: wrong stderr. Want %q. Got %q.", "something happened", got)
|
||||
t.Errorf("stdCopy: wrong stderr. Want %q. Got %q.", "something happened", got)
|
||||
}
|
||||
if got := stdout.String(); got != "" {
|
||||
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||
t.Errorf("stdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdCopyEmpty(t *testing.T) {
|
||||
var input, stdout, stderr bytes.Buffer
|
||||
n, err := StdCopy(&stdout, &stderr, &input)
|
||||
n, err := stdCopy(&stdout, &stderr, &input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != 0 {
|
||||
t.Errorf("StdCopy: wrong number of bytes. Want 0. Got %d.", n)
|
||||
t.Errorf("stdCopy: wrong number of bytes. Want 0. Got %d.", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdCopyCorruptedHeader(t *testing.T) {
|
||||
var input, stdout, stderr bytes.Buffer
|
||||
input.Write([]byte{2, 0, 0, 0, 0})
|
||||
n, err := StdCopy(&stdout, &stderr, &input)
|
||||
n, err := stdCopy(&stdout, &stderr, &input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != 0 {
|
||||
t.Errorf("StdCopy: wrong number of bytes. Want 0. Got %d.", n)
|
||||
t.Errorf("stdCopy: wrong number of bytes. Want 0. Got %d.", n)
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,7 +141,7 @@ func TestStdCopyTruncateWriter(t *testing.T) {
|
||||
var input, stdout, stderr bytes.Buffer
|
||||
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 19})
|
||||
input.Write([]byte("something happened!"))
|
||||
n, err := StdCopy(&stdout, iotest.TruncateWriter(&stderr, 7), &input)
|
||||
n, err := stdCopy(&stdout, iotest.TruncateWriter(&stderr, 7), &input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -148,28 +149,28 @@ func TestStdCopyTruncateWriter(t *testing.T) {
|
||||
t.Errorf("Wrong number of bytes. Want %d. Got %d.", expected, n)
|
||||
}
|
||||
if got := stderr.String(); got != "somethi" {
|
||||
t.Errorf("StdCopy: wrong stderr. Want %q. Got %q.", "somethi", got)
|
||||
t.Errorf("stdCopy: wrong stderr. Want %q. Got %q.", "somethi", got)
|
||||
}
|
||||
if got := stdout.String(); got != "" {
|
||||
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||
t.Errorf("stdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdCopyHeaderOnly(t *testing.T) {
|
||||
var input, stdout, stderr bytes.Buffer
|
||||
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 19})
|
||||
n, err := StdCopy(&stdout, iotest.TruncateWriter(&stderr, 7), &input)
|
||||
n, err := stdCopy(&stdout, iotest.TruncateWriter(&stderr, 7), &input)
|
||||
if err != io.ErrShortWrite {
|
||||
t.Errorf("StdCopy: wrong error. Want ShortWrite. Got %#v", err)
|
||||
t.Errorf("stdCopy: wrong error. Want ShortWrite. Got %#v", err)
|
||||
}
|
||||
if n != 0 {
|
||||
t.Errorf("Wrong number of bytes. Want 0. Got %d.", n)
|
||||
}
|
||||
if got := stderr.String(); got != "" {
|
||||
t.Errorf("StdCopy: wrong stderr. Want %q. Got %q.", "", got)
|
||||
t.Errorf("stdCopy: wrong stderr. Want %q. Got %q.", "", got)
|
||||
}
|
||||
if got := stdout.String(); got != "" {
|
||||
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||
t.Errorf("stdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,7 +178,7 @@ func TestStdCopyDataErrReader(t *testing.T) {
|
||||
var input, stdout, stderr bytes.Buffer
|
||||
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 19})
|
||||
input.Write([]byte("something happened!"))
|
||||
n, err := StdCopy(&stdout, &stderr, iotest.DataErrReader(&input))
|
||||
n, err := stdCopy(&stdout, &stderr, iotest.DataErrReader(&input))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -185,10 +186,10 @@ func TestStdCopyDataErrReader(t *testing.T) {
|
||||
t.Errorf("Wrong number of bytes. Want %d. Got %d.", expected, n)
|
||||
}
|
||||
if got := stderr.String(); got != "something happened!" {
|
||||
t.Errorf("StdCopy: wrong stderr. Want %q. Got %q.", "something happened!", got)
|
||||
t.Errorf("stdCopy: wrong stderr. Want %q. Got %q.", "something happened!", got)
|
||||
}
|
||||
if got := stdout.String(); got != "" {
|
||||
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||
t.Errorf("stdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,9 +197,9 @@ func TestStdCopyTimeoutReader(t *testing.T) {
|
||||
var input, stdout, stderr bytes.Buffer
|
||||
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 19})
|
||||
input.Write([]byte("something happened!"))
|
||||
_, err := StdCopy(&stdout, &stderr, iotest.TimeoutReader(&input))
|
||||
_, err := stdCopy(&stdout, &stderr, iotest.TimeoutReader(&input))
|
||||
if err != iotest.ErrTimeout {
|
||||
t.Errorf("StdCopy: wrong error. Want ErrTimeout. Got %#v.", err)
|
||||
t.Errorf("stdCopy: wrong error. Want ErrTimeout. Got %#v.", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,11 +208,48 @@ func TestStdCopyWriteError(t *testing.T) {
|
||||
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 19})
|
||||
input.Write([]byte("something happened!"))
|
||||
var stdout, stderr errorWriter
|
||||
n, err := StdCopy(stdout, stderr, &input)
|
||||
n, err := stdCopy(stdout, stderr, &input)
|
||||
if err.Error() != "something went wrong" {
|
||||
t.Errorf("StdCopy: wrong error. Want %q. Got %q", "something went wrong", err)
|
||||
t.Errorf("stdCopy: wrong error. Want %q. Got %q", "something went wrong", err)
|
||||
}
|
||||
if n != 0 {
|
||||
t.Errorf("StdCopy: wrong number of bytes. Want 0. Got %d.", n)
|
||||
t.Errorf("stdCopy: wrong number of bytes. Want 0. Got %d.", n)
|
||||
}
|
||||
}
|
||||
|
||||
type StdType [8]byte
|
||||
|
||||
var (
|
||||
Stdin = StdType{0: 0}
|
||||
Stdout = StdType{0: 1}
|
||||
Stderr = StdType{0: 2}
|
||||
)
|
||||
|
||||
type StdWriter struct {
|
||||
io.Writer
|
||||
prefix StdType
|
||||
sizeBuf []byte
|
||||
}
|
||||
|
||||
func (w *StdWriter) Write(buf []byte) (n int, err error) {
|
||||
if w == nil || w.Writer == nil {
|
||||
return 0, errors.New("Writer not instanciated")
|
||||
}
|
||||
binary.BigEndian.PutUint32(w.prefix[4:], uint32(len(buf)))
|
||||
buf = append(w.prefix[:], buf...)
|
||||
|
||||
n, err = w.Writer.Write(buf)
|
||||
return n - 8, err
|
||||
}
|
||||
|
||||
func newStdWriter(w io.Writer, t StdType) *StdWriter {
|
||||
if len(t) != 8 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &StdWriter{
|
||||
Writer: w,
|
||||
prefix: t,
|
||||
sizeBuf: make([]byte, 4),
|
||||
}
|
||||
}
|
27
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go
generated
vendored
27
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go
generated
vendored
@ -12,9 +12,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
"github.com/fsouza/go-dockerclient/utils"
|
||||
"github.com/gorilla/mux"
|
||||
mathrand "math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -23,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
|
||||
@ -31,7 +31,7 @@ import (
|
||||
// It can used in standalone mode, listening for connections or as an arbitrary
|
||||
// HTTP handler.
|
||||
//
|
||||
// For more details on the remote API, check http://goo.gl/yMI1S.
|
||||
// For more details on the remote API, check http://goo.gl/G3plxW.
|
||||
type DockerServer struct {
|
||||
containers []*docker.Container
|
||||
cMut sync.RWMutex
|
||||
@ -103,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
|
||||
@ -224,6 +226,11 @@ func (s *DockerServer) listImages(w http.ResponseWriter, r *http.Request) {
|
||||
ID: image.ID,
|
||||
Created: image.Created.Unix(),
|
||||
}
|
||||
for tag, id := range s.imgIDs {
|
||||
if id == image.ID {
|
||||
result[i].RepoTags = append(result[i].RepoTags, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
s.cMut.RUnlock()
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
@ -408,7 +415,7 @@ func (s *DockerServer) attachContainer(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
outStream := utils.NewStdWriter(w, utils.Stdout)
|
||||
outStream := newStdWriter(w, stdout)
|
||||
fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||
if container.State.Running {
|
||||
fmt.Fprintf(outStream, "Container %q is running\n", container.ID)
|
||||
@ -649,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")
|
||||
|
||||
}
|
||||
|
10
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go
generated
vendored
10
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go
generated
vendored
@ -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) {
|
||||
@ -780,7 +781,7 @@ func addImages(server *DockerServer, n int, repo bool) {
|
||||
|
||||
func TestListImages(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
addImages(&server, 2, false)
|
||||
addImages(&server, 2, true)
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
request, _ := http.NewRequest("GET", "/images/json?all=1", nil)
|
||||
@ -791,8 +792,9 @@ func TestListImages(t *testing.T) {
|
||||
expected := make([]docker.APIImages, 2)
|
||||
for i, image := range server.images {
|
||||
expected[i] = docker.APIImages{
|
||||
ID: image.ID,
|
||||
Created: image.Created.Unix(),
|
||||
ID: image.ID,
|
||||
Created: image.Created.Unix(),
|
||||
RepoTags: []string{"docker/python-" + image.ID},
|
||||
}
|
||||
}
|
||||
var got []docker.APIImages
|
||||
|
43
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/writer.go
generated
vendored
Normal file
43
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/writer.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
// 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 testing
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
type stdType [8]byte
|
||||
|
||||
var (
|
||||
stdin stdType = stdType{0: 0}
|
||||
stdout stdType = stdType{0: 1}
|
||||
stderr stdType = stdType{0: 2}
|
||||
)
|
||||
|
||||
type stdWriter struct {
|
||||
io.Writer
|
||||
prefix stdType
|
||||
sizeBuf []byte
|
||||
}
|
||||
|
||||
func (w *stdWriter) Write(buf []byte) (n int, err error) {
|
||||
if w == nil || w.Writer == nil {
|
||||
return 0, errors.New("Writer not instanciated")
|
||||
}
|
||||
binary.BigEndian.PutUint32(w.prefix[4:], uint32(len(buf)))
|
||||
buf = append(w.prefix[:], buf...)
|
||||
|
||||
n, err = w.Writer.Write(buf)
|
||||
return n - 8, err
|
||||
}
|
||||
|
||||
func newStdWriter(w io.Writer, t stdType) *stdWriter {
|
||||
if len(t) != 8 {
|
||||
return nil
|
||||
}
|
||||
return &stdWriter{Writer: w, prefix: t, sizeBuf: make([]byte, 4)}
|
||||
}
|
20
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/random.go
generated
vendored
20
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/random.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
)
|
||||
|
||||
func RandomString() string {
|
||||
id := make([]byte, 32)
|
||||
_, err := io.ReadFull(rand.Reader, id)
|
||||
if err != nil {
|
||||
panic(err) // This shouldn't happen
|
||||
}
|
||||
return hex.EncodeToString(id)
|
||||
}
|
168
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/stdcopy.go
generated
vendored
168
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/stdcopy.go
generated
vendored
@ -1,168 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
StdWriterPrefixLen = 8
|
||||
StdWriterFdIndex = 0
|
||||
StdWriterSizeIndex = 4
|
||||
)
|
||||
|
||||
type StdType [StdWriterPrefixLen]byte
|
||||
|
||||
var (
|
||||
Stdin StdType = StdType{0: 0}
|
||||
Stdout StdType = StdType{0: 1}
|
||||
Stderr StdType = StdType{0: 2}
|
||||
)
|
||||
|
||||
type StdWriter struct {
|
||||
io.Writer
|
||||
prefix StdType
|
||||
sizeBuf []byte
|
||||
}
|
||||
|
||||
func (w *StdWriter) Write(buf []byte) (n int, err error) {
|
||||
if w == nil || w.Writer == nil {
|
||||
return 0, errors.New("Writer not instanciated")
|
||||
}
|
||||
binary.BigEndian.PutUint32(w.prefix[4:], uint32(len(buf)))
|
||||
buf = append(w.prefix[:], buf...)
|
||||
|
||||
n, err = w.Writer.Write(buf)
|
||||
return n - StdWriterPrefixLen, err
|
||||
}
|
||||
|
||||
// NewStdWriter instanciates a new Writer.
|
||||
// Everything written to it will be encapsulated using a custom format,
|
||||
// and written to the underlying `w` stream.
|
||||
// This allows multiple write streams (e.g. stdout and stderr) to be muxed into a single connection.
|
||||
// `t` indicates the id of the stream to encapsulate.
|
||||
// It can be utils.Stdin, utils.Stdout, utils.Stderr.
|
||||
func NewStdWriter(w io.Writer, t StdType) *StdWriter {
|
||||
if len(t) != StdWriterPrefixLen {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &StdWriter{
|
||||
Writer: w,
|
||||
prefix: t,
|
||||
sizeBuf: make([]byte, 4),
|
||||
}
|
||||
}
|
||||
|
||||
var ErrInvalidStdHeader = errors.New("Unrecognized input header")
|
||||
|
||||
// StdCopy is a modified version of io.Copy.
|
||||
//
|
||||
// StdCopy will demultiplex `src`, assuming that it contains two streams,
|
||||
// previously multiplexed together using a StdWriter instance.
|
||||
// As it reads from `src`, StdCopy will write to `dstout` and `dsterr`.
|
||||
//
|
||||
// StdCopy will read until it hits EOF on `src`. It will then return a nil error.
|
||||
// In other words: if `err` is non nil, it indicates a real underlying error.
|
||||
//
|
||||
// `written` will hold the total number of bytes written to `dstout` and `dsterr`.
|
||||
func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error) {
|
||||
var (
|
||||
buf = make([]byte, 32*1024+StdWriterPrefixLen+1)
|
||||
bufLen = len(buf)
|
||||
nr, nw int
|
||||
er, ew error
|
||||
out io.Writer
|
||||
frameSize int
|
||||
)
|
||||
|
||||
for {
|
||||
// Make sure we have at least a full header
|
||||
for nr < StdWriterPrefixLen {
|
||||
var nr2 int
|
||||
nr2, er = src.Read(buf[nr:])
|
||||
if er == io.EOF {
|
||||
if nr < StdWriterPrefixLen && nr2 < StdWriterPrefixLen {
|
||||
return written, nil
|
||||
}
|
||||
nr += nr2
|
||||
break
|
||||
} else if er != nil {
|
||||
return 0, er
|
||||
}
|
||||
nr += nr2
|
||||
}
|
||||
|
||||
// Check the first byte to know where to write
|
||||
switch buf[StdWriterFdIndex] {
|
||||
case 0:
|
||||
fallthrough
|
||||
case 1:
|
||||
// Write on stdout
|
||||
out = dstout
|
||||
case 2:
|
||||
// Write on stderr
|
||||
out = dsterr
|
||||
default:
|
||||
Debugf("Error selecting output fd: (%d)", buf[StdWriterFdIndex])
|
||||
return 0, ErrInvalidStdHeader
|
||||
}
|
||||
|
||||
// Retrieve the size of the frame
|
||||
frameSize = int(binary.BigEndian.Uint32(buf[StdWriterSizeIndex : StdWriterSizeIndex+4]))
|
||||
|
||||
// Check if the buffer is big enough to read the frame.
|
||||
// Extend it if necessary.
|
||||
if frameSize+StdWriterPrefixLen > bufLen {
|
||||
Debugf("Extending buffer cap.")
|
||||
buf = append(buf, make([]byte, frameSize+StdWriterPrefixLen-len(buf)+1)...)
|
||||
bufLen = len(buf)
|
||||
}
|
||||
|
||||
// While the amount of bytes read is less than the size of the frame + header, we keep reading
|
||||
for nr < frameSize+StdWriterPrefixLen {
|
||||
var nr2 int
|
||||
nr2, er = src.Read(buf[nr:])
|
||||
if er == io.EOF {
|
||||
if nr == 0 {
|
||||
return written, nil
|
||||
}
|
||||
nr += nr2
|
||||
break
|
||||
} else if er != nil {
|
||||
Debugf("Error reading frame: %s", er)
|
||||
return 0, er
|
||||
}
|
||||
nr += nr2
|
||||
}
|
||||
|
||||
// Write the retrieved frame (without header)
|
||||
bound := frameSize + StdWriterPrefixLen
|
||||
if bound > nr {
|
||||
bound = nr
|
||||
}
|
||||
nw, ew = out.Write(buf[StdWriterPrefixLen:bound])
|
||||
if nw > 0 {
|
||||
written += int64(nw)
|
||||
}
|
||||
if ew != nil {
|
||||
Debugf("Error writing frame: %s", ew)
|
||||
return 0, ew
|
||||
}
|
||||
// If the frame has not been fully written: error
|
||||
if nw != frameSize {
|
||||
Debugf("Error Short Write: (%d on %d)", nw, frameSize)
|
||||
return written, io.ErrShortWrite
|
||||
}
|
||||
|
||||
// Move the rest of the buffer to the beginning
|
||||
copy(buf, buf[frameSize+StdWriterPrefixLen:])
|
||||
// Move the index
|
||||
nr -= frameSize + StdWriterPrefixLen
|
||||
}
|
||||
}
|
17
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/uname_darwin.go
generated
vendored
17
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/uname_darwin.go
generated
vendored
@ -1,17 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Utsname struct {
|
||||
Release [65]byte
|
||||
}
|
||||
|
||||
func uname() (*Utsname, error) {
|
||||
return nil, errors.New("Kernel version detection is not available on darwin")
|
||||
}
|
20
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/uname_linux.go
generated
vendored
20
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/uname_linux.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type Utsname syscall.Utsname
|
||||
|
||||
func uname() (*syscall.Utsname, error) {
|
||||
uts := &syscall.Utsname{}
|
||||
|
||||
if err := syscall.Uname(uts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return uts, nil
|
||||
}
|
1114
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/utils.go
generated
vendored
1114
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/utils.go
generated
vendored
File diff suppressed because it is too large
Load Diff
535
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/utils_test.go
generated
vendored
535
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/utils_test.go
generated
vendored
@ -1,535 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBufReader(t *testing.T) {
|
||||
reader, writer := io.Pipe()
|
||||
bufreader := NewBufReader(reader)
|
||||
|
||||
// Write everything down to a Pipe
|
||||
// Usually, a pipe should block but because of the buffered reader,
|
||||
// the writes will go through
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
writer.Write([]byte("hello world"))
|
||||
writer.Close()
|
||||
done <- true
|
||||
}()
|
||||
|
||||
// Drain the reader *after* everything has been written, just to verify
|
||||
// it is indeed buffering
|
||||
<-done
|
||||
output, err := ioutil.ReadAll(bufreader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(output, []byte("hello world")) {
|
||||
t.Error(string(output))
|
||||
}
|
||||
}
|
||||
|
||||
type dummyWriter struct {
|
||||
buffer bytes.Buffer
|
||||
failOnWrite bool
|
||||
}
|
||||
|
||||
func (dw *dummyWriter) Write(p []byte) (n int, err error) {
|
||||
if dw.failOnWrite {
|
||||
return 0, errors.New("Fake fail")
|
||||
}
|
||||
return dw.buffer.Write(p)
|
||||
}
|
||||
|
||||
func (dw *dummyWriter) String() string {
|
||||
return dw.buffer.String()
|
||||
}
|
||||
|
||||
func (dw *dummyWriter) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestWriteBroadcaster(t *testing.T) {
|
||||
writer := NewWriteBroadcaster()
|
||||
|
||||
// Test 1: Both bufferA and bufferB should contain "foo"
|
||||
bufferA := &dummyWriter{}
|
||||
writer.AddWriter(bufferA, "")
|
||||
bufferB := &dummyWriter{}
|
||||
writer.AddWriter(bufferB, "")
|
||||
writer.Write([]byte("foo"))
|
||||
|
||||
if bufferA.String() != "foo" {
|
||||
t.Errorf("Buffer contains %v", bufferA.String())
|
||||
}
|
||||
|
||||
if bufferB.String() != "foo" {
|
||||
t.Errorf("Buffer contains %v", bufferB.String())
|
||||
}
|
||||
|
||||
// Test2: bufferA and bufferB should contain "foobar",
|
||||
// while bufferC should only contain "bar"
|
||||
bufferC := &dummyWriter{}
|
||||
writer.AddWriter(bufferC, "")
|
||||
writer.Write([]byte("bar"))
|
||||
|
||||
if bufferA.String() != "foobar" {
|
||||
t.Errorf("Buffer contains %v", bufferA.String())
|
||||
}
|
||||
|
||||
if bufferB.String() != "foobar" {
|
||||
t.Errorf("Buffer contains %v", bufferB.String())
|
||||
}
|
||||
|
||||
if bufferC.String() != "bar" {
|
||||
t.Errorf("Buffer contains %v", bufferC.String())
|
||||
}
|
||||
|
||||
// Test3: Test eviction on failure
|
||||
bufferA.failOnWrite = true
|
||||
writer.Write([]byte("fail"))
|
||||
if bufferA.String() != "foobar" {
|
||||
t.Errorf("Buffer contains %v", bufferA.String())
|
||||
}
|
||||
if bufferC.String() != "barfail" {
|
||||
t.Errorf("Buffer contains %v", bufferC.String())
|
||||
}
|
||||
// Even though we reset the flag, no more writes should go in there
|
||||
bufferA.failOnWrite = false
|
||||
writer.Write([]byte("test"))
|
||||
if bufferA.String() != "foobar" {
|
||||
t.Errorf("Buffer contains %v", bufferA.String())
|
||||
}
|
||||
if bufferC.String() != "barfailtest" {
|
||||
t.Errorf("Buffer contains %v", bufferC.String())
|
||||
}
|
||||
|
||||
writer.CloseWriters()
|
||||
}
|
||||
|
||||
type devNullCloser int
|
||||
|
||||
func (d devNullCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d devNullCloser) Write(buf []byte) (int, error) {
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
// This test checks for races. It is only useful when run with the race detector.
|
||||
func TestRaceWriteBroadcaster(t *testing.T) {
|
||||
writer := NewWriteBroadcaster()
|
||||
c := make(chan bool)
|
||||
go func() {
|
||||
writer.AddWriter(devNullCloser(0), "")
|
||||
c <- true
|
||||
}()
|
||||
writer.Write([]byte("hello"))
|
||||
<-c
|
||||
}
|
||||
|
||||
// Test the behavior of TruncIndex, an index for querying IDs from a non-conflicting prefix.
|
||||
func TestTruncIndex(t *testing.T) {
|
||||
index := NewTruncIndex()
|
||||
// Get on an empty index
|
||||
if _, err := index.Get("foobar"); err == nil {
|
||||
t.Fatal("Get on an empty index should return an error")
|
||||
}
|
||||
|
||||
// Spaces should be illegal in an id
|
||||
if err := index.Add("I have a space"); err == nil {
|
||||
t.Fatalf("Adding an id with ' ' should return an error")
|
||||
}
|
||||
|
||||
id := "99b36c2c326ccc11e726eee6ee78a0baf166ef96"
|
||||
// Add an id
|
||||
if err := index.Add(id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Get a non-existing id
|
||||
assertIndexGet(t, index, "abracadabra", "", true)
|
||||
// Get the exact id
|
||||
assertIndexGet(t, index, id, id, false)
|
||||
// The first letter should match
|
||||
assertIndexGet(t, index, id[:1], id, false)
|
||||
// The first half should match
|
||||
assertIndexGet(t, index, id[:len(id)/2], id, false)
|
||||
// The second half should NOT match
|
||||
assertIndexGet(t, index, id[len(id)/2:], "", true)
|
||||
|
||||
id2 := id[:6] + "blabla"
|
||||
// Add an id
|
||||
if err := index.Add(id2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Both exact IDs should work
|
||||
assertIndexGet(t, index, id, id, false)
|
||||
assertIndexGet(t, index, id2, id2, false)
|
||||
|
||||
// 6 characters or less should conflict
|
||||
assertIndexGet(t, index, id[:6], "", true)
|
||||
assertIndexGet(t, index, id[:4], "", true)
|
||||
assertIndexGet(t, index, id[:1], "", true)
|
||||
|
||||
// 7 characters should NOT conflict
|
||||
assertIndexGet(t, index, id[:7], id, false)
|
||||
assertIndexGet(t, index, id2[:7], id2, false)
|
||||
|
||||
// Deleting a non-existing id should return an error
|
||||
if err := index.Delete("non-existing"); err == nil {
|
||||
t.Fatalf("Deleting a non-existing id should return an error")
|
||||
}
|
||||
|
||||
// Deleting id2 should remove conflicts
|
||||
if err := index.Delete(id2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// id2 should no longer work
|
||||
assertIndexGet(t, index, id2, "", true)
|
||||
assertIndexGet(t, index, id2[:7], "", true)
|
||||
assertIndexGet(t, index, id2[:11], "", true)
|
||||
|
||||
// conflicts between id and id2 should be gone
|
||||
assertIndexGet(t, index, id[:6], id, false)
|
||||
assertIndexGet(t, index, id[:4], id, false)
|
||||
assertIndexGet(t, index, id[:1], id, false)
|
||||
|
||||
// non-conflicting substrings should still not conflict
|
||||
assertIndexGet(t, index, id[:7], id, false)
|
||||
assertIndexGet(t, index, id[:15], id, false)
|
||||
assertIndexGet(t, index, id, id, false)
|
||||
}
|
||||
|
||||
func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult string, expectError bool) {
|
||||
if result, err := index.Get(input); err != nil && !expectError {
|
||||
t.Fatalf("Unexpected error getting '%s': %s", input, err)
|
||||
} else if err == nil && expectError {
|
||||
t.Fatalf("Getting '%s' should return an error", input)
|
||||
} else if result != expectedResult {
|
||||
t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
|
||||
}
|
||||
}
|
||||
|
||||
func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) {
|
||||
if r := CompareKernelVersion(a, b); r != result {
|
||||
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareKernelVersion(t *testing.T) {
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
0)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
-1)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
|
||||
1)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "0"},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "16"},
|
||||
0)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
1)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20, Flavor: "25"},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "0"},
|
||||
-1)
|
||||
}
|
||||
|
||||
func TestHumanSize(t *testing.T) {
|
||||
|
||||
size := strings.Trim(HumanSize(1000), " \t")
|
||||
expect := "1 kB"
|
||||
if size != expect {
|
||||
t.Errorf("1000 -> expected '%s', got '%s'", expect, size)
|
||||
}
|
||||
|
||||
size = strings.Trim(HumanSize(1024), " \t")
|
||||
expect = "1.024 kB"
|
||||
if size != expect {
|
||||
t.Errorf("1024 -> expected '%s', got '%s'", expect, size)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRAMInBytes(t *testing.T) {
|
||||
assertRAMInBytes(t, "32", false, 32)
|
||||
assertRAMInBytes(t, "32b", false, 32)
|
||||
assertRAMInBytes(t, "32B", false, 32)
|
||||
assertRAMInBytes(t, "32k", false, 32*1024)
|
||||
assertRAMInBytes(t, "32K", false, 32*1024)
|
||||
assertRAMInBytes(t, "32kb", false, 32*1024)
|
||||
assertRAMInBytes(t, "32Kb", false, 32*1024)
|
||||
assertRAMInBytes(t, "32Mb", false, 32*1024*1024)
|
||||
assertRAMInBytes(t, "32Gb", false, 32*1024*1024*1024)
|
||||
|
||||
assertRAMInBytes(t, "", true, -1)
|
||||
assertRAMInBytes(t, "hello", true, -1)
|
||||
assertRAMInBytes(t, "-32", true, -1)
|
||||
assertRAMInBytes(t, " 32 ", true, -1)
|
||||
assertRAMInBytes(t, "32 mb", true, -1)
|
||||
assertRAMInBytes(t, "32m b", true, -1)
|
||||
assertRAMInBytes(t, "32bm", true, -1)
|
||||
}
|
||||
|
||||
func assertRAMInBytes(t *testing.T, size string, expectError bool, expectedBytes int64) {
|
||||
actualBytes, err := RAMInBytes(size)
|
||||
if (err != nil) && !expectError {
|
||||
t.Errorf("Unexpected error parsing '%s': %s", size, err)
|
||||
}
|
||||
if (err == nil) && expectError {
|
||||
t.Errorf("Expected to get an error parsing '%s', but got none (bytes=%d)", size, actualBytes)
|
||||
}
|
||||
if actualBytes != expectedBytes {
|
||||
t.Errorf("Expected '%s' to parse as %d bytes, got %d", size, expectedBytes, actualBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHost(t *testing.T) {
|
||||
var (
|
||||
defaultHttpHost = "127.0.0.1"
|
||||
defaultHttpPort = 4243
|
||||
defaultUnix = "/var/run/docker.sock"
|
||||
)
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, "0.0.0.0"); err != nil || addr != "tcp://0.0.0.0:4243" {
|
||||
t.Errorf("0.0.0.0 -> expected tcp://0.0.0.0:4243, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, "0.0.0.1:5555"); err != nil || addr != "tcp://0.0.0.1:5555" {
|
||||
t.Errorf("0.0.0.1:5555 -> expected tcp://0.0.0.1:5555, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, ":6666"); err != nil || addr != "tcp://127.0.0.1:6666" {
|
||||
t.Errorf(":6666 -> expected tcp://127.0.0.1:6666, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, "tcp://:7777"); err != nil || addr != "tcp://127.0.0.1:7777" {
|
||||
t.Errorf("tcp://:7777 -> expected tcp://127.0.0.1:7777, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, ""); err != nil || addr != "unix:///var/run/docker.sock" {
|
||||
t.Errorf("empty argument -> expected unix:///var/run/docker.sock, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, "unix:///var/run/docker.sock"); err != nil || addr != "unix:///var/run/docker.sock" {
|
||||
t.Errorf("unix:///var/run/docker.sock -> expected unix:///var/run/docker.sock, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, "unix://"); err != nil || addr != "unix:///var/run/docker.sock" {
|
||||
t.Errorf("unix:///var/run/docker.sock -> expected unix:///var/run/docker.sock, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, "udp://127.0.0.1"); err == nil {
|
||||
t.Errorf("udp protocol address expected error return, but err == nil. Got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultHttpPort, defaultUnix, "udp://127.0.0.1:4243"); err == nil {
|
||||
t.Errorf("udp protocol address expected error return, but err == nil. Got %s", addr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRepositoryTag(t *testing.T) {
|
||||
if repo, tag := ParseRepositoryTag("root"); repo != "root" || tag != "" {
|
||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "", repo, tag)
|
||||
}
|
||||
if repo, tag := ParseRepositoryTag("root:tag"); repo != "root" || tag != "tag" {
|
||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "tag", repo, tag)
|
||||
}
|
||||
if repo, tag := ParseRepositoryTag("user/repo"); repo != "user/repo" || tag != "" {
|
||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "", repo, tag)
|
||||
}
|
||||
if repo, tag := ParseRepositoryTag("user/repo:tag"); repo != "user/repo" || tag != "tag" {
|
||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "tag", repo, tag)
|
||||
}
|
||||
if repo, tag := ParseRepositoryTag("url:5000/repo"); repo != "url:5000/repo" || tag != "" {
|
||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "", repo, tag)
|
||||
}
|
||||
if repo, tag := ParseRepositoryTag("url:5000/repo:tag"); repo != "url:5000/repo" || tag != "tag" {
|
||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "tag", repo, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetResolvConf(t *testing.T) {
|
||||
resolvConfUtils, err := GetResolvConf()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(resolvConfUtils) != string(resolvConfSystem) {
|
||||
t.Fatalf("/etc/resolv.conf and GetResolvConf have different content.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckLocalDns(t *testing.T) {
|
||||
for resolv, result := range map[string]bool{`# Dynamic
|
||||
nameserver 10.0.2.3
|
||||
search dotcloud.net`: false,
|
||||
`# Dynamic
|
||||
#nameserver 127.0.0.1
|
||||
nameserver 10.0.2.3
|
||||
search dotcloud.net`: false,
|
||||
`# Dynamic
|
||||
nameserver 10.0.2.3 #not used 127.0.1.1
|
||||
search dotcloud.net`: false,
|
||||
`# Dynamic
|
||||
#nameserver 10.0.2.3
|
||||
#search dotcloud.net`: true,
|
||||
`# Dynamic
|
||||
nameserver 127.0.0.1
|
||||
search dotcloud.net`: true,
|
||||
`# Dynamic
|
||||
nameserver 127.0.1.1
|
||||
search dotcloud.net`: true,
|
||||
`# Dynamic
|
||||
`: true,
|
||||
``: true,
|
||||
} {
|
||||
if CheckLocalDns([]byte(resolv)) != result {
|
||||
t.Fatalf("Wrong local dns detection: {%s} should be %v", resolv, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, result int) {
|
||||
var (
|
||||
a *KernelVersionInfo
|
||||
)
|
||||
a, _ = ParseRelease(release)
|
||||
|
||||
if r := CompareKernelVersion(a, b); r != result {
|
||||
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRelease(t *testing.T) {
|
||||
assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, 0)
|
||||
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54}, 0)
|
||||
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: "1"}, 0)
|
||||
assertParseRelease(t, "3.8.0-19-generic", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "19-generic"}, 0)
|
||||
}
|
||||
|
||||
func TestDependencyGraphCircular(t *testing.T) {
|
||||
g1 := NewDependencyGraph()
|
||||
a := g1.NewNode("a")
|
||||
b := g1.NewNode("b")
|
||||
g1.AddDependency(a, b)
|
||||
g1.AddDependency(b, a)
|
||||
res, err := g1.GenerateTraversalMap()
|
||||
if res != nil {
|
||||
t.Fatalf("Expected nil result")
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error (circular graph can not be resolved)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDependencyGraph(t *testing.T) {
|
||||
g1 := NewDependencyGraph()
|
||||
a := g1.NewNode("a")
|
||||
b := g1.NewNode("b")
|
||||
c := g1.NewNode("c")
|
||||
d := g1.NewNode("d")
|
||||
g1.AddDependency(b, a)
|
||||
g1.AddDependency(c, a)
|
||||
g1.AddDependency(d, c)
|
||||
g1.AddDependency(d, b)
|
||||
res, err := g1.GenerateTraversalMap()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
t.Fatalf("Unexpected nil result")
|
||||
}
|
||||
|
||||
if len(res) != 3 {
|
||||
t.Fatalf("Expected map of length 3, found %d instead", len(res))
|
||||
}
|
||||
|
||||
if len(res[0]) != 1 || res[0][0] != "a" {
|
||||
t.Fatalf("Expected [a], found %v instead", res[0])
|
||||
}
|
||||
|
||||
if len(res[1]) != 2 {
|
||||
t.Fatalf("Expected 2 nodes for step 2, found %d", len(res[1]))
|
||||
}
|
||||
|
||||
if (res[1][0] != "b" && res[1][1] != "b") || (res[1][0] != "c" && res[1][1] != "c") {
|
||||
t.Fatalf("Expected [b, c], found %v instead", res[1])
|
||||
}
|
||||
|
||||
if len(res[2]) != 1 || res[2][0] != "d" {
|
||||
t.Fatalf("Expected [d], found %v instead", res[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePortMapping(t *testing.T) {
|
||||
data, err := PartParser("ip:public:private", "192.168.1.1:80:8080")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(data) != 3 {
|
||||
t.FailNow()
|
||||
}
|
||||
if data["ip"] != "192.168.1.1" {
|
||||
t.Fail()
|
||||
}
|
||||
if data["public"] != "80" {
|
||||
t.Fail()
|
||||
}
|
||||
if data["private"] != "8080" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNameserversAsCIDR(t *testing.T) {
|
||||
for resolv, result := range map[string][]string{`
|
||||
nameserver 1.2.3.4
|
||||
nameserver 40.3.200.10
|
||||
search example.com`: {"1.2.3.4/32", "40.3.200.10/32"},
|
||||
`search example.com`: {},
|
||||
`nameserver 1.2.3.4
|
||||
search example.com
|
||||
nameserver 4.30.20.100`: {"1.2.3.4/32", "4.30.20.100/32"},
|
||||
``: {},
|
||||
` nameserver 1.2.3.4 `: {"1.2.3.4/32"},
|
||||
`search example.com
|
||||
nameserver 1.2.3.4
|
||||
#nameserver 4.3.2.1`: {"1.2.3.4/32"},
|
||||
`search example.com
|
||||
nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4/32"},
|
||||
} {
|
||||
test := GetNameserversAsCIDR([]byte(resolv))
|
||||
if !StrSlicesEqual(test, result) {
|
||||
t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func StrSlicesEqual(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, v := range a {
|
||||
if v != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
17
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/utils_windows.go
generated
vendored
17
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/utils/utils_windows.go
generated
vendored
@ -1,17 +0,0 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Utsname struct {
|
||||
Release [65]byte
|
||||
}
|
||||
|
||||
func uname() (*Utsname, error) {
|
||||
return nil, errors.New("Kernel version detection is not available on windows")
|
||||
}
|
Loading…
Reference in New Issue
Block a user